Upgrading to Grails 3
Preface
I’m upgrading an app from Grails 2.4 to 3.1 and thought I’d take share my thoughts and notes.
First, I want to say that I’m very grateful to the maintainers for all their hard work. I know this wasn’t easy.
Second, I’m not a full time coder anymore, but a consulting CTO. So although I’ve been doing Grails for a few years now, and Spring/Hibernate before that, consider me a casual user. Still, I have opinions – don’t we all? I’m sharing them because I want Grails to thrive.
As you know, Grails began as a way to make Spring/Hibernate development much easier. Over time, it grew a wonderful ecosystem, but it’s still Spring/Hibernate at its core.
When Spring 4 was launched, I attended an event where all the new features were explained, along with Spring Boot. The Spring people were very excited. But I kept thinking, “This is nice, but it’s still well behind Grails.”
The problem is, SpringSource made a lot of changes under the hood to deliver those features. And Grails, being a layer on top, had to be rewritten to remain compatible and up to date.
That means this upgrade is a necessary evil. Because I left Spring for Grails a few years ago, for me it means work and frustration with no benefit. That’s not meant to be an insult, but rather a compliment to Grails 2.x, which worked so well for me and thousands of others. I know the maintainers didn’t have a choice, but at no point during my Grails dev did I ever think, “Gosh, I wish Grails had a completely different architecture under the hood. It’s really holding me back!” Most of the Grails devs I talk to feel the same way. So I’m sorry this post isn’t quite ebullient.
I’m doing this now because I know this is the future,1 and doing it with a project still in development will be way easier. I have another an app in production and I want to take the lumps with a pre-launch project first.
Docs on upgrading are decent, but I had a lot of questions it didn’t answer. Hopefully this is helpful.
New Tech
Grails 3 incorporates the following technologies you’ll have to understand:
- Gradle (replacing Gant)
- YAML
- logback (replacing log4j)
- Asset Pipeline plugin
Gradle does seem to be a improvement over Ant (and I never liked Maven), but the Grails build system always worked great for me. YAML and logback seem simple enough, but I don’t see the value added, especially after hosting a talk on log4j 2, which looks great.
Asset Pipeline is a hands down win, and something you should be using in 2.x if you’re not already. I know a lot of people are still using the Resources plugin, but it’s time to move on.
I’ll add that the command line expects you to be running with JDK 8. run-app crashed immediately with a bytecode error and the answer was to set the GRAILS_OPTS environment variable to “-XX:-UseSplitVerifier -Xverify:none”. Later, I simply changed my JAVA_HOME to JDK 8, which doesn’t need that. I think we’re all ready to move on to Java 8 now anyway.
IntelliJ
Update: I want to share some inside info from a JetBrains employee. JetBrains uses votes and social media (Twitter complaints) to prioritize work. The Grails community has been pretty lax in voting for IntelliJ issues, hence we’re seeing core functionality like reload/debug half-broken for Grails 3. Please upvote issues, and comment if an issue is holding you back.
The Grails community is unanimous in it’s support for IntelliJ, and I previously wrote about why it’s better than GGTS (Eclipse). I believe GGTS has been discontinued as it was maintained not by the Grails team, but by the SpringSource tools team that makes STS. If your employer was too cheap to buy you IntelliJ before, you have an ironclad argument now.
I had to upgrade to the latest version to use Grails 3. I had put this off because I knew the Grails view was missing. Now it’s there for Grails 2 projects, but not 3. I didn’t realize how helpful it was until I lost it.
I used it to create my project, since in Grails 3 you create a blank project and copy your old project into it. First thing you need to do is pull up the Gradle tool window and click the refresh icon. As brilliant as IntellliJ is, it’s too dumb to realize this is a Grails project, even though it just created it. It thinks it’s only a Gradle project until you pull up the Gradle tool window and click the refresh icon. After this, you’ll see the Grails menus and your app available in the run configuration menus. Absolutely critical, yet not in the docs or automated for you, leaving some confusion.
Once Gradle is refreshed you can use the usual way of running it, but that has a glaring bug – environment variables you set in the run configuration are not passed on! This is a big deal, as you’ll read below, because environment variables are the standard way to do external config now that specifying files is not supported out of the box.
To get around this, use the Application run configuration. Go to ../grails-app/init and right click on Application.groovy to run/debug. Note: this is not your config file. That’s under conf, with a lowercase ‘a,’ see below.
However, there’s another problem – if you need to test SSL connections, you need run-app -https, and as mentioned that doesn’t work with environment variables. Since it’s local, I’m running with an insecure channel, but I’d like that fixed.
environments { development { grails.plugin.springsecurity.secureChannel.definition = [ [pattern: '/**', access: 'REQUIRES_SECURE_CHANNEL'] ] } }
IntelliJ is so smart it notices when you’re using something without the source and docs, and suggests you grab it. That is terrific.
But like the Gradle refresh, it also does things that are confusing. If I click on just about any file, including settings.gradle, the editor tab shows the file name. But when I click on build.gradle, the tab has the app name. I know I’ll get used to this, but it adds to the learning curve. And I’m chuckling as I write this and realize this is trivial compared to the fact that I now have to learn Gradle. Hey, at least it’s not Maven 🙂
Update: auto-reload broken?
I find that auto-reload on edit is now broken. Not sure why, but as linked there is a similar issue. Auto-reload works fine when I run ‘grails run-app’ on the command line. I can run that in the background and edit with IntelliJ, but I can’t debug. Losing the edit/test cycle with the debugger means troubleshooting is much slower now, especially with controllers, which are a real pain to unit test.
Update 2: Workaround (Thanks Josh!)
I’ve done more investigating. Basic reload via the grails run-app configuration works. However, there is a significant disconnect between the editor window and compiled code – the editor uses the code built on startup when determining if a line can have a breakpoint. So if you change the code and want to set a new breakpoint, it won’t work
In the comments, Josh pointed out that it was working for him when run via Gradle. Follow the docs to run/debug via Gradle. It worked, but I couldn’t find the console output. To see it, in the Console window that is showing Gradle tasks/times, click the toggle button with the tooltip “Toggle tasks executions/text mode.” Now you’ll see the console output.
Note that this still does not allow you to run via HTTPS like with run-app. It’s good enough for now, but please upvote this issue to make it better for everyone.
Configuration
Grails 3 introduces an application.yml file that is used by default. Even though the docs describe it as “an alternative format,”I have found at least one setting that must be set in application.yml:
https://github.com/grails/grails-core/issues/10005
We do not have a definitive list of what must be defined in the YAML file. That is the only one I noticed, but there certainly could be more. If you’re going to use application.groovy, you may want to add a check in BootStrap.groovy and exit on missing settings.
So why was YAML chosen? Because Spring Boot uses it, and according to Graeme Rocher it’s fully machine writable where Groovy sometimes isn’t (or not easily). But for whatever reason, the helpful comments in Config.groovy are gone. If you’re upgrading, you can just copy them all over.
If you’re converting to Groovy or vice versa, this SO question may help:
One thing you’ll need to change is your defaultPackage, since Grails create-app doesn’t ask for it. I had originally converted all the YAML to Groovy, then found defaultPackage is ignored when in application.groovy. For now, this is my whole application.yml, but expect I’ll need to add more:
grails: codegen: defaultPackage: com.madeupname.web
Which takes precedence if you use both? Short answer is “don’t do that!” Make sure there is zero overlap between the two config files. You won’t get an error or even a warning, and in my test YAML consistently overrode Groovy, but you can’t assume that will always be the case. The recommended approach is to stick with application.yml and use Groovy whenever you need to programmatically set values. You’ll also have to keep an eye out for plugins modifying either file.
If you need external configuration files, you’ll have to do some hacking. I recommend reading this thread on the dev list. But the general recommendation is to use system variables/command line arguments (which take precedence) or environment variables (see Spring PropertySource). This is probably what you need to do if you’re deploying to a PaaS like Elastic Beanstalk, Cloudbees, Jelastic, etc.
Update: external config
Thanks again to Josh in the comments, I found very clear instructions from Haki on how to enable external config in Grails 3, and it’s pretty easy. As instructed, I added the VM option to my run config, updated my Gradle config file, and made sure the format was YAML. Maybe you can use Groovy config for this, but I played it safe.
With reload not working properly for debug in the Application and run-app run configs, you have to use Gradle, which requires external config files because IntelliJ doesn’t support setting environment variables for Gradle tasks.
So it would be great if you could upvote this issue to support environment variables.
Plugins
Because of the sweeping changes to the architecture, most (if not all) plugins need to be updated to work with Grails 3. In many cases the change is small, but you’ll want to check what the latest compatible version is.
Unfortunately, the Grails plugin site, while improved, still has issues. The good news is that it separates Grails 3 compatible plugins. The bad news is that the current version could be way off. For example, Spring Security UI needs a lot of supporting libraries, including Hibernate4 plugin. You can find it here:
https://grails.org/plugin/hibernate4
Problem is, the actual latest version is 5.0.7 and definitely not 5.0.0.RC1. There’s also no clear list of required and optional dependencies, you need to look to the plugin docs for that. If an official/core plugin has the wrong version listed, I’m forced to distrust every version on the plugin site. I’m also wondering how that info is added, and I suspect it’s manually, since the fields plugin has its source and issues links reversed.2
Another concern is that after a year, the Grails 3 compatible Mail plugin is still a release candidate, and Spring Security UI is just a milestone release. This is going to give a lot of people pause when considering upgrading.
Mail
The MailService class changed, but it’s no longer needed. Config is largely unchanged, except for the environment variable placeholders. This is for Gmail:
grails { mail { host = '${MAIL_HOST}' port = '${MAIL_PORT}' username = '${MAIL_PASSWORD}' password = '${MAIL_USERNAME}' props = ["mail.smtp.auth":"true", "mail.smtp.socketFactory.port":'${MAIL_PORT}', "mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory", "mail.smtp.socketFactory.fallback":"false"] } } grails.mail.default.from = "[email protected]" grails.mail.default.to = "[email protected]"
Scaffolding
You have to change very instance of:
static scaffold = true
to
static scaffold =
Classname
I also found a potential bug where the configuration setting domainSuffix is only changing the model name and not what is expected in the view. The workaround is to remove the domainSuffix setting and update all your controller responses to remove the word “Instance.” So companyInstanceList would be changed to companyList, and the same with count, etc.
Spring Security
I use S2 core and UI plugins, which must be updated. For S2 UI, you must specify plugin and library dependencies. In particular, you need to specify a number of GORM libraries, and it’s not clear why. The docs do not list in one place every plugin you (likely) need. Here’s my build.gradle config for both plugins:
compile 'org.grails.plugins:spring-security-core:3.1.1' compile 'org.grails.plugins:spring-security-ui:3.0.0.M2' compile 'org.grails:grails-datastore-core:5.0.7.RELEASE' compile 'org.grails:grails-datastore-gorm-support:5.0.7.RELEASE' compile 'org.grails:grails-datastore-gorm:5.0.7.RELEASE' compile 'org.grails:grails-datastore-simple:5.0.7.RELEASE' compile 'org.grails:grails-datastore-gorm-hibernate4:5.0.7.RELEASE' compile 'org.grails:grails-datastore-gorm-hibernate-core:5.0.7.RELEASE' compile 'org.grails.plugins:mail:2.0.0.RC6'
Other
Traits
It’s not config, but I had a class in src/groovy that used the @Validateable annotation, and now wouldn’t compile. You need to make it implement the Validateable trait (interface) instead.
Looking back, it does not seem that bad, but without knowing any of the above it was an exercise in frustration. Hope I save you some trouble!