Home > groovy, spring framework > Integrating Groovy in Legacy Spring Applications

Integrating Groovy in Legacy Spring Applications

There are times when you have (or want ;-)) to integrate Groovy in a productive application or, in more complex applications, you just want to realize your application’s controller and/or view part (think M-V-C) in a dynamic language, but leave the rest in good-old Spring. Starting with version 2.0, Spring offers support for dynamic languages and comes with support for integrating beans written in Groovy out-of-the-box.

Dynamic Language Support in the Spring Framework

Spring’s dynamic language support currently supports Groovy, JRuby and BeanShell. The integration layer itself consists of several classes dealing with dynamic class loading/creation and class reloading, all of them can be found in org.springframework.scripting which is part of the org.springframework.context Maven module.

Let us first of all take a look at how the integration of Groovy scripts works. In order to declare a controller bean, we would have to use the following XML snippet in the MVC dispatcher context file:

<?xml version="1.0" encoding="UTF-8"?>
<beans...>
  <lang:groovy id="personController" script-source="classpath:PersonController.groovy">
    <lang:property name="personRepository" ref="personRepository" />
  </lang:groovy>

  <!-- an otherwise normal bean that will be injected by the Groovy-backed Messenger -->
  <bean id="personRepository" class="org.some.domain.PersonRepositoryJDBC">
      <!-- ... -->
  </bean>
</beans>

Notice, that the lang namespace is used as a special namespace. Spring’s LangNamespaceHandler registers for groovy, jruby and bsh elements and, for each language, logically links them to custom ScriptFactory implementations:

public class LangNamespaceHandler extends NamespaceHandlerSupport {
  // ...  
  registerScriptBeanDefinitionParser("groovy", "org.springframework.scripting.groovy.GroovyScriptFactory");
  registerScriptBeanDefinitionParser("jruby", "org.springframework.scripting.jruby.JRubyScriptFactory");
  registerScriptBeanDefinitionParser("bsh", "org.springframework.scripting.bsh.BshScriptFactory");
  // ...

At run-time, this task is done by an instance of ScriptBeanDefinitionParser: for each bean definition prefixed with lang it generates a script factory bean according to the language element being specified. All XML attributes of lang:groovy are indeed used to specify a script factory and not the Groovy object itself.

Let us take a look at the ScriptFactory interface:


public interface ScriptFactory {

  String getScriptSourceLocator();

  Class[] getScriptInterfaces();

  boolean requiresConfigInterface();

  Object getScriptedObject(ScriptSource scriptSource, Class[] actualInterfaces);

  Class getScriptedObjectType(ScriptSource scriptSource);

  boolean requiresScriptedObjectRefresh(ScriptSource scriptSource);
}

The most interesting methods are getScriptedObject, getScriptedType and requiresScriptedObjectRefresh.

getScriptedType is mainly used by Spring’s IoC container to determine the bean type and check whether it implements certain interfaces, not much magic implied here.

requiresScriptedObjectRefresh can be used for a certain script source to determine whether it has been modified and requires a refresh. Refreshing of dynamic beans is the main feature justifying using dynamic language support. One could easily configure the application to refresh Groovy beans during development, but keeping them static in the production environment (who would ever consider to take a change directly in a source-file in the production environment ;-)), tremendously fastening development.

getScriptedObject is the heart of each script factory implementation. It is used as a factory method to create scripted Java objects.

Scripted Java Objects

Let us take a look at the Groovy script factory implementation, which is found in GroovyScriptFactory.

public Object getScriptedObject(ScriptSource scriptSource, Class[] actualInterfaces)  {

  synchronized (this.scriptClassMonitor) {
    // some caching stuff...
					
    this.scriptClass = getGroovyClassLoader().parseClass(scriptSource.getScriptAsString(),         scriptSource.suggestedClassName());

  if (Script.class.isAssignableFrom(this.scriptClass)) {
    // A Groovy script, probably creating an instance: let's execute it.
    Object result = executeScript(scriptSource, this.scriptClass);
    this.scriptResultClass = (result != null ? result.getClass() : null);
    return result;
  } else {
    this.scriptResultClass = this.scriptClass;
  }
  }
  scriptClassToExecute = this.scriptClass;
}

// Process re-execution outside of the synchronized block.
return executeScript(scriptSource, scriptClassToExecute);
		

and in executeScript:

protected Object executeScript(ScriptSource scriptSource, Class scriptClass) throws ScriptCompilationException {
			
  // ...
  GroovyObject goo = (GroovyObject) scriptClass.newInstance();

  if (this.groovyObjectCustomizer != null) {
    // Allow metaclass and other customization.
    this.groovyObjectCustomizer.customize(goo);
  }

  if (goo instanceof Script) {
    // A Groovy script, probably creating an instance: let's execute it.
    return ((Script) goo).run();
  } else {
    // An instance of the scripted class: let's return it as-is.
    return goo;
  }
  // ...
}

As can be seen in the source snippet, getScriptedObject uses a GroovyClassLoader to parse the given script source string and to create a Java class object instance from it [3]. Since every script factory is directly connected to a single script source, the embedded caching mechanism is as easy as holding an instance variable to the scripted object and class.

Another possibility for the GroovyScriptFactory would have been to use Groovy’s GroovyScriptEngine. In contrast to Groovy’s class loader it recognizes changes in the class inheritance paths and reloads affected class files directly. If you do not want to customize Spring’s behavior you have to be aware of that circumstance.

So far we have seen how a single XML bean definition relates to a script factory and how that script factory can be used to create dynamic objects, but where is the place that calls getScriptedObject? The answer is: that place needs to be generated by the ScriptFactoryPostProcessor, which is a descendant of Spring’s bean-post processor interface.

ScriptFactoryPostProcessor

First of all, the BeanPostProcessor interface specifies callbacks which can be used as hook-ins for custom modification of container-managed beans. ApplicationContexts will automatically detect beans of this type and apply them on subsequent bean creations. If you use your favorite IDE’s command to show all descendants of BeanPostProcessor you will notice that there are plenty of them. One with particular importance in the case of dynamic language support is the InstantiationAwareBeanPostProcessor.

InstantiationAwareBeanPostProcessor is used to intercept the application container before instantiation and after instantiating a managed-bean. This post-processor type is typically used to suppress object instantiation of the specified bean and to create some proxy or other place-holder to intercept bean method calls.

Spring’s Groovy integration provides an implementation of that post-processor: ScriptFactoryPostProcessor.

The script factory post-processor hooks into the post-process after object instantiation. As we have already seen the bean definition parser created script factory object beans. It is the script factory post-processor which intercepts creation of script factories before the factory object has been instantiated.

When we take a look at XML bean definition again

<lang:groovy id="personController" script-source="classpath:PersonController.groovy">
  <lang:property name="personRepository" ref="personRepository" />
</lang:groovy>

we know that the definition is some kind of hybrid between script factory (defined by the XML attributes) and scripted object (defined by the properties) definition.

Splitting into two separate bean definitions, a factory bean definition and a scripted object bean definition, is exactly what the ScriptFactoryPostProcessor does:

// ...
this.scriptBeanFactory.registerBeanDefinition(scriptFactoryBeanName, createScriptFactoryBeanDefinition(bd));
// ...
BeanDefinition objectBd = createScriptedObjectBeanDefinition(bd, scriptFactoryBeanName, scriptSource, scriptedInterfaces);
// ...

The code creating the object bean definition looks the following way:

protected BeanDefinition createScriptedObjectBeanDefinition(BeanDefinition bd, String scriptFactoryBeanName, ScriptSource scriptSource, Class[] interfaces) {

    GenericBeanDefinition objectBd = new GenericBeanDefinition(bd);
    objectBd.setFactoryBeanName(scriptFactoryBeanName);
    objectBd.setFactoryMethodName("getScriptedObject");
    objectBd.getConstructorArgumentValues().clear();
    objectBd.getConstructorArgumentValues().addIndexedArgumentValue(0, scriptSource);
    objectBd.getConstructorArgumentValues().addIndexedArgumentValue(1, interfaces);
    return objectBd;
}

The BeanDefinition instance which is handed as the first argument, holds the initial bean definition. It is copied and the bean rewritten to be a bean created by a factory method. After all, Spring’s Groovy integration and related components modify the initial XML definition to something like:

<bean name="scriptFactory.personController" class="org.springframework.scripting.groovy.GroovyScriptFactory">
   <constructor-arg type="java.lang.String" value="classpath:PersonController.groovy"/> 
</bean>

<bean id="personController" name="scriptedObject.personController" factory-bean="scriptFactory.messenger" factory-method="getScriptedObject" class="groovy.script.PersonController">
   <!-- ... -->
</bean>

Scripted Beans in Spring

As already mentioned, one of the main reasons you may want to use Spring’s Groovy integration is dynamic refreshing of Groovy classes during run-time. The question which might raise is: why is it worth to use Spring’s integration and not a custom solution like throwing GroovyClassLoader in the class loader hierarchy?

Refreshing Groovy Beans

Refreshing Groovy beans can be comfortable during development. In fact, refreshing beans comes down to reloading modified Groovy classes at run-time. I guess you already know that this is a potential trap for memory leaks (for an introduction to this problem take a look at [0] or [1]).

Spring takes on a burden and solves the problem of class reloading and memory leaks by implementing a mechanism which allows beans to refresh themselves at some time during container life-time. AOP proxies are used to utilize this behavior. In the case of its dynamic language support, it provides a RefreshableScriptTargetSource which is responsible for providing a fresh bean whenever a certain delay has occurred. Creating a fresh mean is simply done by calling getBean of the current bean factory:

protected Object obtainFreshBean(BeanFactory beanFactory, String beanName) {
    return beanFactory.getBean(beanName);
}

This code only works since the scriptable bean post-processor checks whether a delay has been set in the XML configuration and, if yes, applies the PROTOTYPE scope to the scripted object bean:

long refreshCheckDelay = resolveRefreshCheckDelay(bd);
if (refreshCheckDelay >= 0) {
    objectBd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}

The BeanDefinition.SCOPE_PROTOTYPE scope causes the application container to create a new instance whenever the bean is retrieved via getBean et. al. methods. Whenever a bean needs to be refreshed the factory method of the script factory, getScriptedObject, will be called to get a newly created scripted object bean.

Additional Features

If you know Grails and its object-relational mapping framework GORM, you surely have seen dynamically added methods, e.g. dynamic findes like User.findAllByLogin("mustermann"). Spring’s integration layer provides a way for Groovy objects to register so-called GroovyObjectCustomizer implementations with Groovy script factories.

The purpose of a GroovyObjectCustomizer is to allow customization of all Groovy objects, generated by the given script factory. The interface is as easy as:

public interface GroovyObjectCustomizer {
  void customize(GroovyObject goo);
}

This interface’s implementations can be specified via the customizer-ref XML attribute.

<bean id="groovyObjectCustomizer" class="org.some.groovy.GlobalGroovyObjectCustomizer"/>

<lang:groovy id="personController" script-source="classpath:PersonController.groovy" customizer-ref="groovyObjectCustomizer">
  <lang:property name="personRepository" ref="personRepository" />
</lang:groovy>

The groovyObjectCustomizer can than be used to do meta-programming, etc.

Alternatives

Notice, integrating Groovy in Spring does not need to be that complicated anyway if you don’t care about dynamically reloading Groovy sources. Since Groovy comes with its own compiler, it all comes down to JVM byte-code at the end. That means you are free to use all of Spring’s features as long as the outcoming byte-code conforms to that of its Java equivalent. E.g. you can use Spring MVC annotations to declare controllers:

@RequestMapping("/person/**")
@Controller
class PersonController {

  @Autowired
  def PersonRepository personRepository

  @RequestMapping(value = "/person", method = RequestMethod.POST)
  def String create(@Valid Person person, BindingResult result, ModelMap model) {

    if (result.hasErrors()) {
      model["person"] = person
      "person/create"
    } else {

      personRepository.persist person

      redirect "/person/${person.getId()}"
    }
  }

  @RequestMapping(value = "/person/form", method = RequestMethod.GET)
  def String createForm(ModelMap model) {
    model["person"] = new Person()

    "person/create"
  }

  @RequestMapping(value = "/person/{id}", method = RequestMethod.GET)
  def String show(@PathVariable("id") Long id, ModelMap model) {
    model["person"] = personRepository.findPerson(id)

    "person/show"
  }
  // ...
}

There is no additional configuration effort. This makes it even more interesting to implement the controller and view completely on top of Groovy and realize the domain in statically typed, good-old Java.

Summary

Spring’s dynamic language support covers Groovy, JRuby and BeanShell out-of-the-box and could be easily extended. The framwork’s approach is to support certain artifacts to be implemented by Groovy, e.g. Controllers, Repositories, Services etc.

The main feature that would suffice to actually use its integration layer, instead of compiling classes and getting the application’s build-processes in the correct order, is refreshing of scripted objects. Although you might not want to enable this feature in production, it indeed saves you a lot of time during development and it saves a lot of headaches for taking care of class loading memory leaks.

[0] Reloading Java Classes: Objects, Classes and ClassLoaders
[1] ClassLoader leaks: the dreaded “Out of Memory” Exception
[2] Spring Documentation – Dynamic Language Support
[3] Embedding Groovy – Groovy Project Codehaus

Advertisements
Categories: groovy, spring framework
  1. E
    November 17, 2010 at 5:40 pm

    Hi, wonderful article. a bit hard to find on the internet though. I like your explanation, which is clear and complete. I have a goal to learn the insides of how spring works so that i can customize my applications, and your post helped alot. can you tell me how you learn this information. just by digging through the code? Thanks!

    • November 17, 2010 at 8:04 pm

      in that particular case a lot of code digging has been involved, yes. but in general i think when you have to know bloody details the actual source code is always your best friend 🙂

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: