Recently I've been slogging through the crud of trying to upgrade a semi-large grails 1.3.7 project into grails 2.0.1. I wanted to document some of the problems I've run into the upgrade process, their fixes if available, and some thoughts. See also
http://joesondow.blogspot.com/2012/02/ye-olde-tragic-journey-of-attempting-to.html for another attempt to upgrade to Grails 2. They decided not to do it, but we have finished the conversion and everything is working as expected.
1) BuildConfig
The first issue I ran into was a continuous "Do you want to upgrade to this version of this plugin" every time I ran the application or compiled. It was often switching between two versions. This was a rather easy fix - I had old versions of some core plugins in BuildConfig (hibernate and tomcat). Replacing the custom 1.3.7 version number I had in there with $grailsVersion did the trick. This variable is built into BuildConfig and there is no reason to set it yourself.
I also had the app-info plugin installed, which is apparently incompatible with Grails 2, so I had to remove it.
2) Spring Security Not Working
After upgrading, my spring security configuration no longer worked - I couldn't log in, I received errors all over the place. I was finally able to resolve these issues by running the s2-quickstart script provided by the spring security core plugin and then reintegrating my configuration again. Namely my user and authority classes were replaced by the newer versions. After I did this, it worked just fine. Another small issue related to this was at one point I wrapped my
tags in the tag or else I received errors.
3) Logging Changes
There were quite a few logging configuration changes that needed to occur. Namely grails.app.service was renamed to grails.app.services and grails.app.tagLib was renamed to grails.app.taglib, little things like that. I also had to add 'grails.app.resourceMappers' to the error log4j configuration or else I received lots and lots of unnecessary debug output from them. One more thing - the grails.app.bootstrap logger was renamed to grails.app.conf.
4) Groovy Stricter Compiler
There were some minor tweaks I had to make to classes to get groovy to even compile. Namely I had some static classes, which apparently are not allowed in Java and now not allowed in groovy either. I had to remove the static keyword on the class definition to even compile.
5) Errors on Startup
I had listener and servlet errors on startup and trying to load pages, but then I remembered that I had installed the templates and the web.xml file is not updated on a grails upgrade. So running grails install-templates again fixed the issue, I just needed to revert my custom scaffolding classes.
6) Groovy Properties with Maps
Maybe this is just me - but suddenly doing obj.properties = params no longer worked with Maps! For example, I had a domain with a property called config of type Map, and when trying to do databinding with .properties = params, the config property was not set at all. Adding this in manually after the .properties setting worked fine, but that's annoying to do every time at the least. I have not tested if this fails with Grails databinding as well, but I
hope that it works.
7) Domain Class Changes
We use json-lib for some JSON/XML conversion, and it started complaining mightily about how it couldn't convert some nested properties on ALL domain classes. Turns out that the injection of certain properties - namely "errors" and "attached" - must have been changed. I'm thinking that previously it was done through the metaClass, but it is now injected at compile time through AST transformations or something. Setting the excludes property to "errors" and "attached" or applying a filter for the same names and corresponding classes fixed the issues.
8) Testing
This is the big catch as I see it with the upgrade to Grails 2. I spent hours upon hours converting unit tests to work with the new testing mixins, etc. The JUnit tests were fine for the most part, although there were some changes I had to make to fix newer groovy problems, etc.
The big problem was with our spock tests. Grails 2 is touted as being easier to use with spock with the new testing mixin framework instead of concrete classes that you need to extend. However, I found that the documentation was
severely lacking for some key usage points with Spock. Namely...
- The registerMetaClass method is now gone from the mixins, which I took as an immediate red flag. However, after finally searching the tickets in jira and the grails source, I realized that the mixins actually register a meta class listener with Groovy. At the end of the test class, they revert all meta class changes and you're good to go. Where is this in the documentation!? I'm only slightly angry about this that I spent hours putting in the ConfineMetaClassChanges annotation from the spock code, only to remove it after I realized it was unnecessary.
- The documentation assumes that you are always testing a typical grails artefact, such as Controllers, Services, etc. However, we have many custom artefacts that we need to test as well. Trying to use the technique shown here proved unfruitful but promising. I think after changing our custom artefacts to by the same type as their suffix (see artefact handlers), it will work as expected.
- So for now, we have classes that need testing that have no "TestFor" annotation. After trying to use @Mixin(GrailsUnitTestMixin) unsuccessfully, I finally found another annotation by perusing the source - @TestMixin. Using @TestMixin(GrailsUnitTestMixin) successfully fixed all of our problems with the spock tests, as far as getting them to actually run goes.
- The implicit "TestFor" annotation doesn't work for seemingly random controllers, services, etc. Not sure what is going on here, but I found that sometimes it worked and sometimes it didn't.
- The name resolution for TestFor is messy and doesn't work as expected pretty often (see above point). For example, we have a domain class called "Service" (yes, I know, bad naming). So ServiceSpec and ServiceServiceSpec do not resolve their TestFor(Service) and TestFor(ServiceService) correctly. It gives me errors such as "Service is not a service class!" I know that, that's why I tried to get you do it as a domain class, doh! In the end, I ended up mixing in the DomainClassUnitTestMixin and the ServiceUnitTestMixin and doing mockDomain and mockService to get this working.
After all is said and done, I'm still working on resolving some leakage from my domain classes between tests during unit testing (leaking in unit tests, what!? yeah, that's the problem). But things are starting to look pretty good, and we get the added advantages of grails 2. Writing spock tests will be easier in the future - no worrying about registerMetaClass. But I wish the conversion process for the spock tests wasn't so painful. Besides that, the upgrade would have taken me a few hours at most!
UPDATE:
After writing this up, we found a couple of more bugs in the process -
- The default environment for maven-deploy in the release plugin suddenly became development instead of production for some reason. Mass chaos ensued until we changed our builds to use prod maven-deploy.
- I had seen this somewhere but unwisely ignored it - the serverURL setting in Config.groovy was set to something like http://localhost:8080/${appName}. This turned out to cause all redirects to send to this URL, localhost and all. Removing the setting completely did the trick.