<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eric Helgeson</title>
    <description>The latest articles on Forem by Eric Helgeson (@erichelgeson).</description>
    <link>https://forem.com/erichelgeson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F168361%2Fc372198b-2005-4e5c-b48c-3f5dc8b9b17f.jpeg</url>
      <title>Forem: Eric Helgeson</title>
      <link>https://forem.com/erichelgeson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/erichelgeson"/>
    <language>en</language>
    <item>
      <title>Caching SPA static assets in Grails</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Wed, 29 Apr 2020 19:32:23 +0000</pubDate>
      <link>https://forem.com/erichelgeson/caching-spa-static-assets-in-grails-5d76</link>
      <guid>https://forem.com/erichelgeson/caching-spa-static-assets-in-grails-5d76</guid>
      <description>&lt;p&gt;If you deploy static assets embedded in your Grails 3 or 4 apps you might notice by default they are not cached - meaning every time a user loads your site - they re-download each asset.&lt;/p&gt;

&lt;p&gt;Grails does have a configuration to cache static assets:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;application.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;grails&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;cachePeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3600&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The problem with this is the entry point &lt;code&gt;index.html&lt;/code&gt; is cached as well meaning users won't see updates until after the cache period has elapsed.&lt;/p&gt;

&lt;p&gt;The solution is to add a &lt;code&gt;ResourceHandler&lt;/code&gt; that matches &lt;code&gt;index.html&lt;/code&gt; and sets the &lt;code&gt;cachePeriod&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt; &lt;span class="c1"&gt;// or add to resources.groovy&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpaResolverConfig&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;WebMvcConfigurer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// WebMvcConfigurerAdapter for Grails 3.x&lt;/span&gt;

    &lt;span class="c1"&gt;// Copied from the GrailsWebMvcConfigurer &lt;/span&gt;
    &lt;span class="c1"&gt;//  - https://github.com/grails/grails-core/blob/28556c0a1a01d2958d6d3aed251dcacc2a6e38da/grails-plugin-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersGrailsPlugin.groovy#L179-L193&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;RESOURCE_LOCATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"classpath:/META-INF/resources/"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"classpath:/resources/"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"classpath:/static/"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"classpath:/public/"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addResourceHandlers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ResourceHandlerRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addResourceHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/static/**/index.html'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ant matcher pattern&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addResourceLocations&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RESOURCE_LOCATIONS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCachePeriod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- no-cache&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now css/javascript/images from the SPA are cached for &lt;code&gt;3600&lt;/code&gt; seconds while the &lt;code&gt;index.html&lt;/code&gt; file is never cached.&lt;/p&gt;

&lt;p&gt;Note: This has no affect on assets using the &lt;code&gt;asset-pipeline&lt;/code&gt; plugin - those are cached correctly.&lt;/p&gt;

&lt;p&gt;Links: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First saw this issue - &lt;a href="https://github.com/grails/grails-core/issues/10410#issuecomment-611572172"&gt;https://github.com/grails/grails-core/issues/10410#issuecomment-611572172&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploying SPA combined with Grails Guide - &lt;a href="https://guides.grails.org/grails-vue-combined/guide/index.html"&gt;https://guides.grails.org/grails-vue-combined/guide/index.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>grails</category>
      <category>spa</category>
    </item>
    <item>
      <title>Wiring Micronaut beans in Grails Services</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Thu, 02 Apr 2020 15:43:28 +0000</pubDate>
      <link>https://forem.com/erichelgeson/wiring-micronaut-beans-in-grails-services-56p9</link>
      <guid>https://forem.com/erichelgeson/wiring-micronaut-beans-in-grails-services-56p9</guid>
      <description>&lt;p&gt;Micronaut beans in Grails 4 are wired by type - not the default Grails users are used to using - wire by name.&lt;/p&gt;

&lt;p&gt;For example if we have a &lt;code&gt;micronaut-http-client&lt;/code&gt; bean named &lt;code&gt;MyRestClient&lt;/code&gt; under &lt;code&gt;src/main/groovy/&amp;lt;package&amp;gt;/MyRestClient.groovy&lt;/code&gt; and we want to wire it into the Grails service &lt;code&gt;SomeService&lt;/code&gt; we must supply the type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ❌ wont work, as Grails will look for a bean name that wont exist.&lt;/span&gt;
    &lt;span class="c1"&gt;// def myRestClient&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ Works!&lt;/span&gt;
    &lt;span class="n"&gt;MyRestClient&lt;/span&gt; &lt;span class="n"&gt;myRestClient&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Tested on Grails 4.0.2.&lt;/p&gt;

</description>
      <category>micronaut</category>
      <category>grails</category>
      <category>groovy</category>
    </item>
    <item>
      <title>Deploying Grails 4 to Heroku</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Tue, 03 Mar 2020 13:58:50 +0000</pubDate>
      <link>https://forem.com/erichelgeson/deploying-grails-4-to-heroku-1h8g</link>
      <guid>https://forem.com/erichelgeson/deploying-grails-4-to-heroku-1h8g</guid>
      <description>&lt;p&gt;&lt;a href="https://www.heroku.com/"&gt;Heroku&lt;/a&gt; is a PaaS(Platform as a Service) for running web apps that makes it as simple as a git push to deploy. They offer a free tier that is great for demos or quick testing environments and services such as redis, postgres, and many others. &lt;/p&gt;

&lt;p&gt;There are a few minor changes from the default heroku gradle build pack to get your Grails 4 app run.&lt;/p&gt;

&lt;p&gt;By default &lt;code&gt;heroku&lt;/code&gt; will run the task &lt;code&gt;stage&lt;/code&gt; to get your app ready. You could add a gradle task named &lt;code&gt;stage&lt;/code&gt; or change the default task by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ heroku config:set GRADLE_TASK="assemble"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Procfile&lt;/code&gt; is used to tell &lt;code&gt;heroku&lt;/code&gt; how to start your app, in this case, we need to pass the dynamically assigned port to our app, along with heroku's JAVA_OPTS. Lastly, tell it where gradle put our war/jar.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Procfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: java -Dserver.port=$PORT $JAVA_OPTS -jar build/libs/*.war
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you'd like to run on OpenJDK 11 - create a &lt;code&gt;system.properties&lt;/code&gt; file with the following: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;system.properties&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.runtime.version=11
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And thats it. Push to Heroku and your app will build and deploy in about a minute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git push heroku master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 227 bytes | 227.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----&amp;gt; Gradle app detected
remote: -----&amp;gt; Installing JDK 11... done
remote: -----&amp;gt; Building Gradle app...
remote: -----&amp;gt; executing ./gradlew assemble
remote:        &amp;gt; Task :compileJava NO-SOURCE
remote:        &amp;gt; Task :compileGroovy
remote:        &amp;gt; Task :buildProperties
remote:        &amp;gt; Task :processResources
remote:        &amp;gt; Task :classes
remote:        &amp;gt; Task :compileGsonViews
remote:        &amp;gt; Task :findMainClass
remote:        &amp;gt; Task :bootWar
remote:        &amp;gt; Task :war SKIPPED
remote:        &amp;gt; Task :assemble
remote:
remote:        BUILD SUCCESSFUL in 25s
remote:        6 actionable tasks: 6 executed
remote: -----&amp;gt; Discovering process types
remote:        Procfile declares types -&amp;gt; (none)
remote:
remote: -----&amp;gt; Compressing...
remote:        Done: 117.2M
remote: -----&amp;gt; Launching...
remote:        Released v21
remote:        https://my-app.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/my-app.git
   905744b..b239ff4  master -&amp;gt; master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;See also:&lt;br&gt;
&lt;a href="https://devcenter.heroku.com/categories/java-support"&gt;https://devcenter.heroku.com/categories/java-support&lt;/a&gt;&lt;br&gt;
&lt;a href="https://devcenter.heroku.com/articles/deploying-gradle-apps-on-heroku"&gt;https://devcenter.heroku.com/articles/deploying-gradle-apps-on-heroku&lt;/a&gt;&lt;/p&gt;

</description>
      <category>grails</category>
      <category>heroku</category>
      <category>groovy</category>
      <category>java</category>
    </item>
    <item>
      <title>Grails CI with Github Actions</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Thu, 14 Nov 2019 00:56:58 +0000</pubDate>
      <link>https://forem.com/erichelgeson/grails-ci-with-github-actions-25ff</link>
      <guid>https://forem.com/erichelgeson/grails-ci-with-github-actions-25ff</guid>
      <description>&lt;p&gt;I recently was playing with &lt;a href="https://github.com/features/actions"&gt;Github Actions&lt;/a&gt; and a Grails 3 app. Was a bit of trial and error though I finally got a working CI workflow.&lt;/p&gt;

&lt;p&gt;Github recently added build caching &lt;a href="https://github.com/actions/cache#cache-limits"&gt;with limitations&lt;/a&gt; - this dramatically speeds up your CI builds.&lt;/p&gt;

&lt;p&gt;A nice aspect about Actions is you can have actions based on not only pushes/PRs/tags but also things like Github comments. For example &lt;a href="https://github.com/cirrus-actions/rebase"&gt;&lt;code&gt;/rebase&lt;/code&gt;&lt;/a&gt; - there's an entire &lt;a href="https://github.com/marketplace?type=actions"&gt;marketplace of actions&lt;/a&gt; you can try out.&lt;/p&gt;

&lt;p&gt;I've commented the &lt;code&gt;yml&lt;/code&gt; file to help you get an understanding of whats going on and how it works.&lt;/p&gt;

&lt;p&gt;Full syntax for this yml file can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions"&gt;https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've also included customized test reporting below - since there is no "results" step to see a junit type report. You can find that below the ream of yml.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.github/workflows/grails.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Grails CI&lt;/span&gt;

&lt;span class="c1"&gt;# When this workflow will run - here it will run on all pushes but &lt;/span&gt;
&lt;span class="c1"&gt;# not on branch 'master&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches-ignore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;master'&lt;/span&gt;

&lt;span class="c1"&gt;# A list jobs to run when the condition is met - here we've parallelized&lt;/span&gt;
&lt;span class="c1"&gt;# the build to run test, integrationTest, and assemble.&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="c1"&gt;# A list of Docker containers you require to run the test&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;

    &lt;span class="c1"&gt;# Steps in this build &lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
    &lt;span class="c1"&gt;# We are caching the node_modules, ~/.gradle and app/.gradle   &lt;/span&gt;
    &lt;span class="c1"&gt;# to speed up our build. Caches are automatically stored and restored&lt;/span&gt;
    &lt;span class="c1"&gt;# based on a file hash - in this case yarn.lock and build.gradle&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache node modules&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/main/vue/node_modules&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}&lt;/span&gt;
        &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ runner.os }}-node-&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache .gradle&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.gradle&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-dotgradle-${{ hashFiles('**/build.gradle') }}&lt;/span&gt;
        &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ runner.os }}-dotgradle-&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache gradle&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/runner/.gradle&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}&lt;/span&gt;
        &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ runner.os }}-gradle-&lt;/span&gt;
    &lt;span class="c1"&gt;# Setup JDK 8&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up JDK &lt;/span&gt;&lt;span class="m"&gt;1.8&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.8&lt;/span&gt;
    &lt;span class="c1"&gt;# Finally run the test.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./gradlew test --no-daemon&lt;/span&gt;

  &lt;span class="c1"&gt;# Integration and assemble are much the same&lt;/span&gt;
  &lt;span class="c1"&gt;# though it's yml so not DRY - omitted the copy/paste&lt;/span&gt;
  &lt;span class="na"&gt;integrationTest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integrationTest&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./gradlew integrationTest --no-daemon&lt;/span&gt;
  &lt;span class="na"&gt;assemble&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assemble&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./gradlew assemble --no-daemon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here is a test formatter that gives you the Spock errors to stdout nicely for debugging as well as a summary:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.gradle/testFormatter.gradle&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// From https://stackoverflow.com/a/40656862&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.gradle.api.tasks.testing.logging.TestExceptionFormat&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.gradle.api.tasks.testing.logging.TestLogEvent&lt;/span&gt;

&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;testLogging&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// set options for log level LIFECYCLE&lt;/span&gt;
        &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FAILED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PASSED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SKIPPED&lt;/span&gt;
        &lt;span class="n"&gt;exceptionFormat&lt;/span&gt; &lt;span class="n"&gt;TestExceptionFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SHORT&lt;/span&gt;
        &lt;span class="n"&gt;showExceptions&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;showCauses&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;showStackTraces&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

        &lt;span class="c1"&gt;// set options for log level DEBUG and INFO&lt;/span&gt;
        &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STARTED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FAILED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PASSED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;TestLogEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SKIPPED&lt;/span&gt;
            &lt;span class="n"&gt;exceptionFormat&lt;/span&gt; &lt;span class="n"&gt;TestExceptionFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SHORT&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;events&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exceptionFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exceptionFormat&lt;/span&gt;

        &lt;span class="n"&gt;afterSuite&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// will match the outermost suite&lt;/span&gt;
                &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"&lt;/span&gt;
                &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;startItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'|  '&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'  |'&lt;/span&gt;
                &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;repeatLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;startItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;endItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\n'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;repeatLength&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;startItem&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;endItem&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;repeatLength&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>grails</category>
      <category>github</category>
      <category>java</category>
      <category>groovy</category>
    </item>
    <item>
      <title>Removing illegal reflective access warnings in Grails 4</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Mon, 15 Jul 2019 14:53:57 +0000</pubDate>
      <link>https://forem.com/erichelgeson/removing-illegal-reflective-access-warnings-in-grails-4-393o</link>
      <guid>https://forem.com/erichelgeson/removing-illegal-reflective-access-warnings-in-grails-4-393o</guid>
      <description>&lt;p&gt;When using JDK11 with Grails 4 you likely have seen these messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedConstructor$1 (file:/Users/erichelgeson/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to constructor com.sun.jmx.mbeanserver.JmxMBeanServer(java.lang.String,javax.management.MBeanServer,javax.management.MBeanServerDelegate)
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedConstructor$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is because Groovy 2.5 uses reflection. In Groovy 3 they've already made &lt;a href="https://opencollective.com/friends-of-groovy/updates/progressing-towards-groovy-3-0-0-with-greatly-reduced-illegal-access-errors"&gt;great strides on this&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;For now we can add the correct packages to the &lt;code&gt;--add-opens&lt;/code&gt; JVM args to get rid of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This is the same way Groovy 2.5 removes them when calling &lt;code&gt;groovy&lt;/code&gt; with the &lt;a href="https://github.com/apache/groovy/commit/a7f7cdd352a40dd7acd2def999eb560c2b76904e"&gt;&lt;code&gt;GROOVY_TURN_OFF_JAVA_WARNINGS&lt;/code&gt;&lt;/a&gt; environment variable.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;build.gradle&lt;/code&gt; file we can define a list of modules to open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;jvmOpenModulesArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.io=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.lang=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.lang.annotation=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.lang.invoke=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.lang.module=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.lang.ref=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.math=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.net=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.net.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.channels=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.channels.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.charset=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.charset.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.file=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.file.attribute=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.nio.file.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.security=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.security.acl=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.security.cert=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.security.interfaces=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.security.spec=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.text=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.text.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.time=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.time.chrono=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.time.format=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.time.temporal=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.time.zone=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.function=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.jar=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.regex=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.stream=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.base/java.util.zip=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.datatransfer/java.awt.datatransfer=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.applet=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.color=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.desktop=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.dnd=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.dnd.peer=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.event=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.font=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.geom=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.im=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.im.spi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.image=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.image.renderable=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.peer=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.awt.print=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.beans=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.desktop/java.beans.beancontext=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.instrument/java.lang.instrument=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.logging/java.util.logging=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.management/java.lang.management=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.rmi/java.rmi=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.rmi/java.rmi.activation=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.rmi/java.rmi.dgc=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.rmi/java.rmi.registry=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.rmi/java.rmi.server=ALL-UNNAMED"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"--add-opens=java.sql/java.sql=ALL-UNNAMED"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then add them to our &lt;code&gt;bootRun&lt;/code&gt; task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;bootRun&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;jvmArgs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-noverify'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'-XX:TieredStopAtLevel=1'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'-Xmx1024m'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;jvmArgs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jvmOpenModulesArgs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And any task with &lt;code&gt;GroovyCompile&lt;/code&gt; &amp;amp; &lt;code&gt;Test&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GroovyCompile&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forkOptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jvmArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jvmOpenModulesArgs&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;jvmArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jvmOpenModulesArgs&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's it - no more pesky reflective access warnings when the JVM starts up. Thanks to Daniel Sun helping me find the right modules.&lt;/p&gt;

</description>
      <category>grails</category>
      <category>groovy</category>
    </item>
    <item>
      <title>Codenarc IntelliJ Plugin Updated</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Tue, 21 May 2019 15:52:42 +0000</pubDate>
      <link>https://forem.com/erichelgeson/codenarc-intellij-plugin-updated-2fge</link>
      <guid>https://forem.com/erichelgeson/codenarc-intellij-plugin-updated-2fge</guid>
      <description>&lt;p&gt;&lt;a href="http://codenarc.sourceforge.net/" rel="noopener noreferrer"&gt;Codenarc&lt;/a&gt; is a static code analysis tool for Groovy. It's been around for quite a while but one feature it lacked was the ability to see the rule violations right inside of IntelliJ.&lt;/p&gt;

&lt;p&gt;Luckily one of my co-workers saw that Daniel Demus recently updated the IntelliJ plugin that was originally created by Cédric Champeau.&lt;/p&gt;

&lt;p&gt;An example of a rule violation right inside IntelliJ:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjjy1vo5wnihoba2rpz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjjy1vo5wnihoba2rpz7.png" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can get the plugin &lt;a href="https://plugins.jetbrains.com/plugin/12106-codenarc-updated" rel="noopener noreferrer"&gt;here&lt;/a&gt; and check out the code or file a bug on the &lt;a href="https://github.com/demus-nine/CodeNarc-Updated" rel="noopener noreferrer"&gt;Github project&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;One caveat is the plugin currently does not automatically import your &lt;code&gt;codenarc.groovy|xml&lt;/code&gt; rule files - you have to enable rules manually in the inspection settings dialog. Though there is a &lt;a href="https://github.com/demus-nine/CodeNarc-Updated/issues/2" rel="noopener noreferrer"&gt;feature request&lt;/a&gt; open to do just that.&lt;/p&gt;

</description>
      <category>groovy</category>
      <category>grails</category>
      <category>ide</category>
    </item>
    <item>
      <title>Grails 3 and Java 11+?</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Mon, 20 May 2019 16:41:49 +0000</pubDate>
      <link>https://forem.com/erichelgeson/grails-3-and-java-11-3e4l</link>
      <guid>https://forem.com/erichelgeson/grails-3-and-java-11-3e4l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Sorting out Oracles new JDK release schedule and Grails 3. &lt;br&gt;
&lt;em&gt;Originally posted on &lt;a href="https://grails3book.com/"&gt;https://grails3book.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oracle announced they will be drastically &lt;a href="http://www.oracle.com/technetwork/java/eol-135779.html"&gt;speeding up their JDK releases – one every 6 months&lt;/a&gt; with &lt;a href="https://jaxenter.com/end-life-comes-early-jdk-8-140824.html"&gt;no overlapping public support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Java 9 was the first in this accelerated release cycle. While these JDK releases come quick, they end just as quickly. As you’re reading this JDK 9 &amp;amp; 10 are already End Of Life(EOL). JDK 11 is the next Long Term Support (LTS) release of the JDK.&lt;/p&gt;

&lt;p&gt;With all this news, there has been some confusion about what will support what when and how people should be planning for in regards to Java releases and Grails. I’ll provide a few facts (and a few of my opinions) on what this means to you as a Grails developer.&lt;/p&gt;

&lt;p&gt;Let’s start by answering a few concrete questions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Will Grails 3 build on JDK 9+?
&lt;/h1&gt;

&lt;p&gt;Grails 3.3.x is based on Spring-Boot 1.5. Spring-Boot 1.5 does not and will not support building in JDK 9+.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-with-Java-9#requirements"&gt;From Spring-boot's wiki&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spring Boot &lt;strong&gt;2&lt;/strong&gt; is the first version to support Java 9 (Java 8 is also supported). If you are using 1.5 and wish to use Java 9 you should upgrade to 2.0 as &lt;strong&gt;we have no plans to support Java 9 on Spring Boot 1.5.x&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s &lt;em&gt;definitive&lt;/em&gt; – you won’t be building Grails 3 apps in anything but JDK7/8 (and that’s ok, keep reading!)&lt;/p&gt;

&lt;h1&gt;
  
  
  Will Grails 3 run on JDK9+?
&lt;/h1&gt;

&lt;p&gt;This is a &lt;strong&gt;much&lt;/strong&gt; different question -&lt;/p&gt;

&lt;p&gt;The answer is yes – Groovy runs (with a few warnings that can be disabled). You will also need to add &lt;a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-with-Java-9#jaxb"&gt;&lt;code&gt;jaxb&lt;/code&gt; explicitly to your CLI or in your dependencies&lt;/a&gt; – but it will run. If you use other 3rd party libraries though you will need to validate they will run on JDK9+ as well.&lt;/p&gt;

&lt;p&gt;This is great news for user’s environments where you are not able to control your runtime and are forced to run on a newer JDK.&lt;/p&gt;

&lt;h1&gt;
  
  
  If I’m on JDK8 how long will it be supported?
&lt;/h1&gt;

&lt;p&gt;Oracle has stopped shipping public JDK8 updates as of &lt;a href="http://www.oracle.com/technetwork/java/eol-135779.html"&gt;January 2019&lt;/a&gt;. Oracle &lt;a href="https://jaxenter.com/end-life-comes-early-jdk-8-140824.html"&gt;plans no overlapping publicly supported binaries&lt;/a&gt; giving no breathing room for developers.&lt;/p&gt;

&lt;p&gt;My recommendation is to switch to one of the many OpenJDK 8 (&lt;a href="https://www.azul.com/downloads/zulu/"&gt;zulu&lt;/a&gt;, &lt;a href="https://aws.amazon.com/corretto/"&gt;amzn&lt;/a&gt;, &lt;a href="https://adoptopenjdk.net"&gt;adpt&lt;/a&gt;, &lt;a href="https://bell-sw.com/pages/java-8u212"&gt;librca&lt;/a&gt;, etc!) distributions which will be supported until at least October 2020+ or pay Oracle for support. If you run on any PaaS such as AWS Elastic Beanstalk you’ve already been running on OpenJDK. &lt;a href="http://sdkman.io/"&gt;sdkman&lt;/a&gt; makes it easy to setup OpenJDK on your workstation as well.)&lt;/p&gt;

&lt;h1&gt;
  
  
  When can I use Java 11 with Grails?
&lt;/h1&gt;

&lt;p&gt;Spring-Boot 2.1 was &lt;a href="https://spring.io/blog/2018/10/30/spring-boot-2-1-0"&gt;released (October 30th, 2018.)&lt;/a&gt; The Grails team is already working on Grails 4 based on it. Quarter 2 of this year (2019) is the targeted release.&lt;/p&gt;

&lt;h1&gt;
  
  
  Now for a few of my opinions.
&lt;/h1&gt;

&lt;p&gt;Grails builds on top of many projects such as Spring-Boot, Hibernate, Gradle, etc. All of these projects need to support the latest JDK before Grails can. This IMO is a good thing – it means many compatibility issues and bug fixes can be worked out before I start using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should we do for now?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spring-Boot 1.5 is maintained until &lt;a href="https://spring.io/blog/2018/07/30/spring-boot-1-x-eol-aug-1st-2019"&gt;August 1st, 2019&lt;/a&gt; developed and Grails 3 will continue to use it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upgrade your apps to Grails 3.3.10 now to make the upgrade path to 4.0.0 simpler. See my &lt;a href="https://dev.to/erichelgeson/grails-4-upgrade-notes-3cja"&gt;Grails 4 Upgrade notes&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you are using Oracles JDK 8 switch to OpenJDK 8 for security updates or pay Oracle for extended support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continue to build on JDK 8 and you should be fine running on JDK 11 if you need to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stick to LTS JDK releases such as 8 and 11.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don’t adopt Short Term Support (STS) or end of life JDK’s such as 9/10/13/etc&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I use Grails so I don’t have to worry about these compatibility issues – I get a nice package of things that are recent and work together. I’ll be happy to let this JDK madness shake out before I get in the middle of it – I have features to ship to users rather than worry about JDK’s that are only supported for 6 months.&lt;/p&gt;

</description>
      <category>grails</category>
      <category>groovy</category>
    </item>
    <item>
      <title>Grails 4 Upgrade Notes</title>
      <dc:creator>Eric Helgeson</dc:creator>
      <pubDate>Fri, 17 May 2019 03:17:12 +0000</pubDate>
      <link>https://forem.com/erichelgeson/grails-4-upgrade-notes-3cja</link>
      <guid>https://forem.com/erichelgeson/grails-4-upgrade-notes-3cja</guid>
      <description>&lt;p&gt;Grails 4 is the next iteration of the Grails web framework. Grails is a developer friendly framework built on top of Spring-boot, Groovy, and now Micronaut.&lt;/p&gt;

&lt;h1&gt;
  
  
  Status
&lt;/h1&gt;

&lt;p&gt;Grails 4 is &lt;a href="https://objectcomputing.com/news/2019/07/11/grails-4-ga-released"&gt;released&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;I've ported a non-trivial app from Grails 3.3.9 to 4.0.0 as well as updated to JDK11. The total effort was only a few hours - welcome news to many who are upgrading. &lt;/p&gt;

&lt;p&gt;Initial app startup time is about 15 seconds - 2x faster than the same app in Grails 3.3.9. &lt;/p&gt;

&lt;p&gt;Reloading has changed and there are a few options. The default option is from &lt;code&gt;spring-dev-tools&lt;/code&gt; - fast restart. This restarts your app faster then a normal restart, but still can be extremely slow for large projects.&lt;/p&gt;

&lt;p&gt;Hot reloading options are &lt;a href="https://jrebel.com/software/jrebel/"&gt;JRebel&lt;/a&gt; - a paid option that works for JDK8/11 - or &lt;code&gt;spring-loaded&lt;/code&gt; though it only supports JDK8 and is no longer maintained. &lt;a href="https://github.com/grails/grails-core/pull/11441"&gt;Instructions for spring-loaded&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IDE Support all works in Grails 4 (same as 3.3.9) - IntelliJ is still my recommended IDE for any Grails, Groovy, or Java developer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Updates
&lt;/h1&gt;

&lt;p&gt;There are tons of updates to Grails 4 and the frameworks it builds upon. Here's a list of updates and release notes you should review if you're updating your apps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grails 4 

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.grails.org/4.0.x/guide/upgrading.html"&gt;Upgrade notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/erichelgeson/grails-versions/compare/3.3.10...4.0.0"&gt;New app diff from 3.3.10 -&amp;gt; 4.0.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://micronaut.io"&gt;Micronaut 1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.1-Release-Notes"&gt;Spring Boot 2.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-5.x"&gt;Spring 5.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://groovy-lang.org/releasenotes/groovy-2.5.html"&gt;Groovy 2.5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://hibernate.org/orm/releases/5.4/"&gt;Hibernate 5.3/5.4 support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/technetwork/java/javase/11all-relnotes-5013287.html"&gt;Java 11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Install
&lt;/h1&gt;

&lt;p&gt;&lt;a href="http://sdkman.io/"&gt;SDKMan&lt;/a&gt; makes installing and managing a JVM and many frameworks a snap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;sdk &lt;span class="nb"&gt;install &lt;/span&gt;java 11.0.2-open
sdk &lt;span class="nb"&gt;install &lt;/span&gt;grails 4.0.3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Plugins
&lt;/h1&gt;

&lt;p&gt;Plugins are an essential part of the Grails developer experience. Luckily many plugins that worked in Grails 3 work out of the box in Grails 4 - or with only slight modifications due. &lt;/p&gt;

&lt;h2&gt;
  
  
  Working Plugins
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Spring-Security-Core &lt;code&gt;4.0.0&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New delegation password strategy:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/spring-security-5-password-storage"&gt;https://www.baeldung.com/spring-security-5-password-storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If you already used bcrypt can do it all at once too:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UPDATE my_users SET password = CONCAT('{bcrypt}', password);&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Spring-session-redis (boot starter)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updated dependencies
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s2"&gt;"org.springframework.boot:spring-boot-starter-data-redis"&lt;/span&gt;
&lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s2"&gt;"org.springframework.session:spring-session-data-redis"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Spring-Websocket
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s2"&gt;"org.grails.plugins:grails-spring-websocket:2.5.0.RC1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Grails-Quartz
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s1"&gt;'org.grails.plugins:quartz:2.0.13'&lt;/span&gt; &lt;span class="c1"&gt;// Needed to compile *Job classes&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s1"&gt;'org.grails.plugins:quartz:2.0.13'&lt;/span&gt;
    &lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s1"&gt;'org.quartz-scheduler:quartz:2.2.1'&lt;/span&gt; &lt;span class="c1"&gt;// Is not pulled in by default&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Database Migration Plugin &lt;code&gt;3.1.0.RC1&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Asset-Pipeline &lt;code&gt;3.1.0&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;External Config&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works - use RC1+
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;    &lt;span class="n"&gt;compile&lt;/span&gt; &lt;span class="s1"&gt;'org.grails.plugins:external-config:2.0.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Spring Security UI&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/grails-plugins/grails-spring-security-ui/issues/111"&gt;https://github.com/grails-plugins/grails-spring-security-ui/issues/111&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Schwartz Plugin&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2.0.0.RC1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Grails Cache Plugin&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;5.0.0.RC1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Spring Security OAuth2 Provider&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;4.0.0-RC1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sentry&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Grails Mail &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;3.0.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not working
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Audit-Logging Plugin

&lt;ul&gt;
&lt;li&gt;WIP @ &lt;a href="https://github.com/robertoschwald/grails-audit-logging-plugin/pull/183"&gt;https://github.com/robertoschwald/grails-audit-logging-plugin/pull/183&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resolved
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;del&gt;Asset Pipeline Handlebars issue:&lt;/del&gt;

&lt;ul&gt;
&lt;li&gt;Needed to update to change &lt;code&gt;//= require handlebars/handlebars.js&lt;/code&gt; to &lt;code&gt;//= require handlebars-runtime&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2019-01-13 16:59:42.280  WARN --- [nio-8080-exec-1] asset.pipeline.DirectiveProcessor        : Unable to Locate Asset: /handlebars/handlebars.js
Warning: Nashorn engine is planned to be removed from a future JDK release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;del&gt;Gson Views&lt;/del&gt;

&lt;ul&gt;
&lt;li&gt;Resolved.&lt;/li&gt;
&lt;li&gt;Unable to render json with simple objects (just get &lt;code&gt;null&lt;/code&gt; response)&lt;/li&gt;
&lt;li&gt;Domain classes dont render:
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Model variable [someDomain] of with value [Description] type [com.x.SomeDomain] is not of the correct type [com.x.SomeDomain]. Stacktrace follows:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Opened Issue &lt;a href="https://github.com/grails/grails-views/issues/202"&gt;https://github.com/grails/grails-views/issues/202&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other notes Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hot reload for JDK8 only&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;spring-loaded&lt;/code&gt; supports only up to JDK8.&lt;/li&gt;
&lt;li&gt;Fast reload takes about 5-6 seconds (hopefully can improve)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;&lt;code&gt;org.grails:grails-datastore-rest-client&lt;/code&gt; no longer maintained&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a different http client such as

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;micronaut-http-client&lt;/code&gt;, &lt;code&gt;http-builder-ng&lt;/code&gt;, &lt;code&gt;feign&lt;/code&gt;, &lt;code&gt;okhttp&lt;/code&gt;, etc&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Or use the old unmaintained GORM 6 version:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'org.grails:grails-datastore-rest-client:6.1.9.RELEASE'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="nl"&gt;group:&lt;/span&gt;&lt;span class="s1"&gt;'org.grails'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;module:&lt;/span&gt;&lt;span class="s1"&gt;'grails-plugin-converters'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Groovy 2.5.x&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn't include the Date Utils helpers by default - need to add them in.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compile 'org.codehaus.groovy:groovy-dateutil'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;(Should just update to &lt;code&gt;java.time.*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;JDK 11 Warnings (expected)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/erichelgeson/removing-illegal-reflective-access-warnings-in-grails-4-393o"&gt;https://dev.to/erichelgeson/removing-illegal-reflective-access-warnings-in-grails-4-393o&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.pop()&lt;/code&gt; &amp;amp; collections behavior changed to align with Java - &lt;a href="https://issues.apache.org/jira/browse/GROOVY-9388"&gt;https://issues.apache.org/jira/browse/GROOVY-9388&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Dependencies changes (Gradle 5.1)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An issue where dependencies like &lt;code&gt;com.jameskleeh:excel-builder&lt;/code&gt; - don't pull in their dependencies (eg: Apache POI)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hibernate Deprecated warnings (expected)&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hibernate's legacy org.hibernate.Criteria API is deprecated; use the JPA javax.persistence.criteria.CriteriaQuery instead
// and
HHH020100: The Ehcache second-level cache provider for Hibernate is deprecated.  See https://hibernate.atlassian.net/browse/HHH-12441 for details.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Hide them in &lt;code&gt;logback.groovy&lt;/code&gt; 

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;logger('org.hibernate.orm.deprecation', ERROR, ['STDOUT'], false)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Notes or tips of your own?
&lt;/h1&gt;

&lt;p&gt;Join us over on the &lt;a href="https://grails-slack.cfapps.io/"&gt;grails-community&lt;/a&gt; slack channel or comment below!&lt;/p&gt;

</description>
      <category>grails</category>
      <category>groovy</category>
      <category>micronaut</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
