How to Benefit from Grails 4 Upgrade

Vladimír Oraný
Dec 2, 2019 · 5 min read

If you are not familiar with Micronaut then you may not understand how important the upgrade to Grails 4 is. But if you already discovered the productivity of Micronaut then you know there is a whole new world of tools and best practises to take advantage of. Let’s start with mentioning some of the issues which may occur during the upgrade and then I will share some of the ways how to benefit from having Micronaut parent context.

Grails 4 Upgrade Gotchas

There are detailed upgrade notes for Grails and GORM which you should read first:

Except these, I would like to mention some particular issues we were facing while upgrading our applications. This is not an exhausting list of all problem but I will try to keep it up to date if we face another issue:

This one is actually already mentioned in GORM for Hibernate Upgrade Notes. If you have any GORM operation happening outside of services annotated with @Transactional then you will see TransactionRequiredException start to pop up in your console.

There is quite a simple fix for this issue — just visit all your services which works with the database and annotate them with @Transactional.

There is a limited number of Micronaut dependencies bundled in Grails BOM (bill-of-material — dependency versions' blueprint) but it points to the old version of Micronaut (1.1.4 as a time of writing). If you want to take advantage of the latest version of Micronaut as well as being able to write Micronaut dependencies without version then you have to also add Micronaut BOM into your Gradle build.

dependencyManagement {
imports {
mavenBom "org.grails:grails-bom:$grailsVersion"
}
imports {
mavenBom "io.micronaut:micronaut-bom:$micronautVersion"
}
applyMavenExclusions false
}

The Micronaut BOM should come after Grails BOM to override the versions from the Grails BOM.micronautVersion property is declared alongside grailsVersion in gradle.properties. We are currently running with the latest version 1.2.6 without any issues.

There is one side effect caused by having Micronaut context initialised during the Grails application startup. Both Grails and Micronaut are independently reading the configuration files including the script calledapplication.groovy. The problem is that Micronaut is adding @CompileStatic annotation and some AST transformation to be able to compile the script statically. Therefore if your application.groovy file contains references to the properties not known to Micronaut then the compilation fails and the application will not start. These could be appName and appVersion which are injected into script's binding by Grails automatically. You can replace these with calls to Metadata class such as Metadata.current.getApplicationName().

The other source of compilation errors are closures referring to some DSL unknown to Micronaut, typically GORM default mappings:

grails.gorm.default.mapping = { 
version false
}

One way how to work around this issue is to move the closure definition into a method annotated with @CompileDynamic.

grails.gorm.default.mapping = defaultMapping()@groovy.transform.CompileDynamic
private static Closure defaultMapping() {
return {
version false
}
}

This is not an upgrade issue but just a remember-me note for further Micronaut integration. Grails 4 are still injecting the beans into the services by name. On the other hand, Micronaut names the beans usually with the fully qualified name of the class. You must annotate any bean you want to inject from Micronaut with @Inject.

class AwesomeController {

@Inject AwesomeMicronautService ams // any name
SomeGrailsService someGrailsService // strict naming convetion // ...}

As a side-effect, you can now name your services with any name as the injection happens by type.

There were a war task for bundling the WAR for Tomcat deployments in Grails 3.x. Thanks to the upgraded Spring Boot plugin the bundling happens using a bootWar task so if your delivery pipeline relies on running war task explicitly then you need to update your build commands. Also, any Gradle customisation using the war task directly will stop working as the task is disabled by default:

war {
from('src/main/extra') {
into('extra')
}
}

One way to fix it is to enable the war task again manually and disable the bootWar task.

war.enabled = true
bootWar.enabled = false

Another way is to change your configuration to use bootWar instead:

bootWar {
from('src/main/extra') {
into('extra')
}
}

Taking Advantage of Micronaut in Grails 4

The upgrade itself is generally pretty smooth but you should not get satisfied by just the upgrade. You should consider some of the following steps to gain the most of the Micronaut integration into Grails 4.

We in Agorapulse are continuously trying to migrate our internal and open-source plugins to Micronaut configurations. The reason is obvious — we want to share the code between the Grails applications and Micronaut functions. For example, we have migrated a Facebook SDK plugin into Micronaut Facebook SDK library and also AWS SDK plugin into Micronaut AWS SDK library.

Another opportunity is to migrate to libraries and capabilities provided by Micronaut

Switching from plugins into configuration actually takes the most of the time we spent on the migration.

There are several reasons why you should prefer writing services as Micronaut services instead of Grails ones.

  • Micronaut services are avoiding reflection and should have a positive impact on startup time and reduced memory footprint
  • Micronaut context resolution provides much richer capabilities to create conditional beans
  • Micronaut provides support for configuration properties beans
  • Micronaut provides a reflection-free validation framework usable in any bean
  • Micronaut services' names do not have to end with Service suffix
  • You may extract the service code into a library shared with other Micronaut applications and functions

Also, if you migrate your services to Micronaut then it will easier for you to migrate your whole application to Micronaut if you find out that Grails framework no longer meets your needs. In a case of Grails plugins, it will allow you to migrate them to configurations.

Stories by Agorapulse

AgoraPulse is a leading Social Media Management platform. We are run as a semi-remote structure and 100% Bootstrapped. This is our story and feedbacks from the ground.

Vladimír Oraný

Written by

Full Stack Developer and Test Facilitator at @agorapulse

Stories by Agorapulse

AgoraPulse is a leading Social Media Management platform. We are run as a semi-remote structure and 100% Bootstrapped. This is our story and feedbacks from the ground.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade