Technipelago Blog Stuff that we learned...
Grails 1.3.7 to 2.0.1 upgrade experiences
Today I finally took the time to upgrade my largest Grails 1.3.7 application to Grails 2.0.1. This blog post describe how I did it.
I started to develop this application during spring 2008. What was the Grails version back then? Was it 1.0 or maybe 1.1? I'm not sure what version I started with. Anyway I've been eager to upgrade to new Grails versions fairly quickly after each release so I've been using all Grails versions since 1.1.
This particular application is in production use by a handfull of clients. It's a mission critical CRM system so quality is important. That's the main reason why I didn't jump on the Grails 2.0 wagon as early as I used to. All my Grails apps in production are still on Grails 1.3.7.
I constantly monitor the Grails mailing list to hear what people say about upgrade experiences and the quality of Grails 2.0. I did not touch Grails 1.4/2.0 until 2.0.0 was released. Then I felt comfortable enough to upgrade a few plugins and simple apps. But I still was a little afraid to upgrade my largest and most important Grails app to 2.0.
When Grails 2.0.1 was released I upgraded some more plugins and started a completely new client project on Grails 2.0.1. After two weeks working full days with 2.0.1 I decided to try and upgrade my major 1.3.7 application to 2.0.1.
This is a transcript of my upgrade session:
2012-03-01 12:30 - Starting Grails 1.3.7 to 2.0.1 upgrade
# grails upgrade
SUCCESS
# Manually updated BuildConfig.groovy by looking at a freash 2.0.1 application. I also bumbed the version number for all plugin dependencies to the latest and greatest.
# grails compile
/Users/goran/.grails/2.0.1/projects/myapp/plugins/persistent-result-0.6/src/java/grails/plugins/persistentresult/QueryResult.java:42: cannot find symbol
symbol : constructor PagedResultList(java.util.List)
location: class grails.orm.PagedResultList
super(list);
^
This code is in one of my own developed plugins. I have a custom class derived from grails.orm.PagedResultList and it seems like it has changed a lot between 1.3.7 and 2.0
I looked at my implementation and decided I could subclass java.util.AbstractList instead of PagedResultList. It took some time to change the implementation but I finally got it to work.
# grails compile
SUCCESS
# grails test-app
| Error Error executing script TestApp: org.codehaus.groovy.grails.exceptions.GrailsConfigurationException: Database driver [org.hsqldb.jdbcDriver] for HSQLDB not found. Since Grails 2.0 H2 is now the default database. You need to either add the 'org.h2.Driver' class as your database driver and change the connect URL format (for example 'jdbc:h2:mem:devDb') in DataSource.groovy or add HSQLDB as a dependency of your application. (Use --stacktrace to see the full trace)
Ahh. I must modify DataSource.groovy and change HSQLDB to H2. Since this is a plugin a standard DataSource.groovy is sufficient so I copied one from a fresh Grails 2.0.1 plugin.
# grails test-app
Tests PASSED
# grails publish-plugin --repository=myCompanyRepository
OK
Now I went back to the main Grails app and updated BuildConfig.groovy to depend on the fixed plugin.
# grails compile
| Warning The [saveQuery] action in [PersonController] accepts a parameter of type [PersonQueryCommand] which has not been marked with @grails.validation.Validateable. This may lead to an intermittent MissingMethodException for validate(). We strongly recommend that you mark command object classes with @Validateable in order to avoid the problem.
I added @Validateable to all commands objects
# grails compile
SUCCESS
# grails run-app
| Running Grails application
| Error 2012-03-01 13:31:06,814 [pool-7-thread-1] ERROR [localhost].[/myapp] - Error configuring application listener of class org.codehaus.groovy.grails.web.util.Log4jConfigListener
Message: org.codehaus.groovy.grails.web.util.Log4jConfigListener
Line | Method
->> 46 | findClass in org.grails.plugins.tomcat.ParentDelegatingClassLoader
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 306 | loadClass in java.lang.ClassLoader
| 247 | loadClass in ''
Hmmm.. Something with logging. I copied Config.groovy from a fresh Grails 2.0.1 application and merged my Config.groovy file manually inspecting every line.
# grails run-app
SAME PROBLEM
Googling for: grails org.codehaus.groovy.grails.web.util.Log4jConfigListener
Ahh I must upgrade applicationContext.xml and sitemesh.xml in web-app/WEB-INF.
I copied applicationContext.xml and sitemesh.xml from a fresh Grails 2.0.1 application.
# grails run-app
SAME PROBLEM
NOW I READ THE UPGRADE NOTES! :-)
http://grails.org/doc/2.0.x/guide/gettingStarted.html#upgradingFromPreviousVersionsOfGrails
Ahhh stupid me! I had a a custom src/templates/war/web.xml
I removed web.xml since I could not find any customizations in it.
# grails run-app
SUCCESS!!! ALMOST…
| Error 2012-03-01 13:58:48,901 [http-bio-8080-exec-9] ERROR [/myapp].[default] - Servlet.service() for servlet [default] in context with path [/myapp] threw exception [Filtered request failed.] with root cause
Message: It looks like you are missing some calls to the r:layoutResources tag. After rendering your page the following have not been rendered: [head]
Updated my layouts to use Resources.
Application is up and running and I browsed through the main pages. (No I don't have any functional tests yet)
PROBLEM
One controller that subclasses a base controller in src/groovy did not work (404).
I changed all closures to methods in the abstract controller and did the same in the controller subclass.
SAME PROBLEM
I added all methods that only existed in the abstract class to my subclass and made them call super.method()
(this was a quick fix. I need to go back later and find out what really was the problem)
ANOTHER PROBLEM
In the problematic controller class I had a lot of redirects that referenced action closure properties directly instead of their name.
redirect(action: show, id:params.id)
I changed all to:
redirect(action: "show", id:params.id)
SUCCESS!!!
FINISHED 14:30
TOTAL TIME FOR UPGRADE: 2 HOURS
There are lot more tests I need to perform before I can put this in production but it feels great to have come this far. And it went faster than I expected. I was prepared for a few days of banging my head against the keyboard.
« Back