Tuesday, 7 August 2012

Problems with Grails 2.0.4


The Background


We have a complex grails project consisting of a base plugin called dm-cms to render content retrieved from a content-management service.

This plugin consists of among other objects a controller called PageController (under package com.dm.cms), a service called PageService (under package com.dm.cms) and a UrlMappings called CMSUrlMappings (under the default package).

This plugin is installed on an application that acts as the front end to the users.

We then have another plugin called cms-admin which has dm-cms installed and this is installed on an appllication used by the administration to edit content.

This plugin will proxy the content from the front end application and render this to the screen with options to edit and publish changes, while allowing you to revert to previous versions of the page.

This plugin consists of among other objects a controller called PageController (under package com.dm.grails.cms.admin), a service called PageService (under package com.dm.grails.cms.admin) and a UrlMappings called CMSAdminUrlMappings (under the default package).

We have similar URL mappings in each of the mappings objects set up in the plugins below are shown as examples:

From CMSUrlMappings:

"/" {
    controller="page"
    action = "view"
    }

From CMSAdminUrlMappings:

"/" {
controller="page"
action = "view"
url= "index"
}

Each of these need to point to the PageController defined in scope of the project.

These plugins and applications were built using grails 1.3.7. In order for us to provide some updates and new functionality to the client we decided to upgrade these to Grails version 2.0.4 (at this point the latest stabel release)

We had preiviously upgraded other applications for different clients from 1.3.7 to 2.0.1 without hitting any problems.

The Problems


During the upgrade from 1.3.7 to 2.0.4 we seem to have stumbled upon two different issues.

1. The first issue is centered around the overriding of URL mappings from between each plugin. From running some basic commands agains the admin application and looking through the debug logs of grails it seems as though when the mappings are resolved they are added to a list of mappings and then requests are matched against the mappings sequentially. Once a request is matched against a mapping the request mapped and is forwared to the controller. The prblem seems to be that if you have two mappings the same as above there is no way of defining which mapping overrides the other.

My first thorugh was to set the loadAfter property in the GrailsPlugin.groovy but this seemed to have no affect.

As a side note the mapping itself does point the request to the correct controller in the scope of the project the UrlMappings object exists, ie. The CMSAdminController mapping points to the PageController in the CMS-Admin plugin.

This was not a problem under the grails version 1.3.7 therefore I believe there may have been changes in the implementation of the UrlMappings between 1.3.7 and 2.0.4.

2. The second issue is the creation and resolution of services within the grails controllers.

When a request is made to the CMSAdmin PageController and there are no conflicts in the UrlMappings wehave hit a problem whereby the PageService defined in the PageController is not of the correct type. In this instance the PageService defined in the ProductController (under package com.dm.grails.cms.admin) is the PageService defined in the dm-cms plugin (under package com.dm.cms).

Interestingly neither of these problems arise if the application has the cms-admin plugin source linked. I assume this is because it treats the source linked plugin as an extension of the source of the application itself, therefore treating the compilation and bean registration differently.

The Next Step


In order to establish this theory as a possible issue with the underlying framework itslef I have decied to create two different experimental applications to replicate the issues. Each application will install a plugin that itself has a plugin installed. I will attempt to replicate each problem seperatly.

By recreating the problem this way I can prove that each issue is present without other code interfering and causing unexpected results.

The Process


Issue 1.

I created a plugin called exp-base. Ths contained a controller called "PageController" in package com.dm.exp, a view for that controller and a URLMapping as follows

"/" {
controller="page"
action="view"
}

All the controller did was render the view and the view contained an h1 with the name of the plugin.

I then created another plugin called exp-admin which contained a controller called "PageController" in package com.dm.admin.exp, a view for that controller and a URLMapping as follows

"/" {
controller="page"
action="view"
}

Again all the controller did was render the view and the view contained an h1 with the name of the plugin.

The plugin exp-admin had exp-base installed.

I then created an application with exp-admin installed as a plugin and started this application up.

Issue 2.


I created a plugin calles exp-service-base which contained a service called PageService in a package com.dm.exp.service. This had a method getOutputString that returned "BaseService"

I also created a plugin called exp-service-admin. This contained a service called PageService in a package com.dm.admin.exp.service. This had a method getOutputString that returned "AdminService".

Exp-service-admin also had a controller called AdminController that made a call to the getOutputString method in the pageService and put that the rerturn in the model. This then rendered a view that output the retrun from the getOutputString in an h1.

The plugin exp-service-admin had exp-service-base installed.

I then created an application with exp-service-admin installed as a plugin and started this application up.

The Result


Issue 1.

When accessing the root URL (the one for which I set up the URL mapping) I was showing the name of the exp-base. From examining the logging what seems to happen is that on startup the URL mappings are read and put in a list. Then on a request the list is read and the all matches to the URL of the request coming in are then placed in a list.

It then looks like that list is read and the first mapping in the list is handled by creating the controller bean and then delegating the handling of the request to that controller.

Issue 2.

When accessing the AdminController which calls the service I was shown the "BaseService" string. This means that the wrong service bean is created and added to the controller.

Possible Solutions


Issue 1.

Through trail and error it seems that the way of resolving this was to add the URL mappings into the application itself. For future applications that use the plugin the URL mappings will need to be added to the application itself.

Another option is to rename the controllers and change the URL mappings in to point to the renamed controllers.

Issue 2.

A possible solution is to define the bean for the service required in the application within the applications resources.groovy. This would cause all autowired pageServices in the application to use the bena defined in the application.

The other option is to rename the page service and all the calls to it in the application.