18 March 2014

As part of a current project, I recently had the need to determine which of my Grails applications made use of certain Spring beans in order to prepare for a migration. While it is possible to use the normal XML-based configuration in a Grails application (which in turn would have made parsing the configuration fairly straight forward), Grails applications typically make use of the built-in Spring beans DSL to declare their Spring configuration. However, because the DSL is represented in Groovy, it meant that parsing it would not be as easy as if it were in XML. I decided that the best approach would be to use the grails.spring.BeanBuilder outside of a Grails application in a Groovy script. I was particularly interested in finding beans of type org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean class, which have serviceInterface and serviceUrl properties, so I wrote a script that loads and parses the DSL and then loops over each bean definition for beans that contained those properties:

    @Grab(group='org.grails', module='grails-spring', version='2.3.6'),
    @Grab(group='org.springframework', module='spring-web', version='4.0.2.RELEASE')

import grails.spring.BeanBuilder

// The resources.groovy file must have:
// beans {
// and not
// beans = {
// for this script to work!

BeanBuilder beanBuilder = new BeanBuilder()

beanBuilder.springConfig.beanConfigs.each {
    if(it.value.definition.propertyValues.getPropertyValue('serviceInterface')) {
        println "${it.key} -> ${it.value.clazz.name}"
        println "\tserviceUrl = ${it.value.definition.propertyValues.getPropertyValue('serviceUrl').value}"
        println "\tserviceInterface = ${it.value.definition.propertyValues.getPropertyValue('serviceInterface').value}"

There are a couple of important things to note in the example above:

  1. The grails.spring.BeanBuilder can be used outside of Grails applications by including the org.grails:grails-spring module.

  2. The resources.groovy file (or any file that contains the proper DSL syntax) must have beans closure be the top level of the DSL and not be a variable (i.e. beans = { …​ }) for the script to parse the file correctly.

  3. In the example above, I did not provide a full classpath for all of the possible beans in the file. This means that you cannot create the application context from the builder without getting ClassNotFoundException errors.

  4. Command line argument parsing could be added to allow for the script to be run from anywhere and not just the root of the Grails application, as is the case in the example above.

All and all, this script was pretty easy to create and can be used as part of a larger process to identify, convert and/or even re-write the resources.groovy file. Additionally, this DSL has now been incorporated directly into the Spring Framework), which means that not only can you now declare Spring beans via the Groovy DSL just like in Grails, but that you could use the same solution above to inspect that configuration outside of the application if the need should arise.

comments powered by Disqus