Vita Rara: A Life Uncommon

Toward a Groovy'er Struts 2


Categories: | | | |

See latest update: Groovy Works

A further update is available here.

Last week I started implementing my Struts 2 actions in Groovy. After a few days I decided I liked Groovy, but I wanted more. I didn't want to have to restart my servlet container every time I recompiled a class. I just wanted to be able to edit, save, and reload in my browser.

I researched using Spring to instantiate my actions, but Spring's scripting integration only supports singletons. So, to get around this I extended the Struts 2 Spring Plugin to integrate the GroovyClassLoader. That created problems with Spring's use of AspectJ's pointcuts to support declarative transactions. (Thread on Spring forum.)

So, I dropped back to Spring, and with some guidance from Chris Tam on the Spring JIRA I implemented the prototype scope for scripted beans. This is what the whole thing rests on. Spring takes care of instantiating our actions and watches to see if the source file has been changed. If the source has been changed Spring recompiles the script and returns the new action instance.

Struts 2 Groovy Goals

  • Write actions in Groovy, and have them dynamically reloaded as changes are made, no server restarts,
  • Integrate with Spring to wire action dependencies, transactions, security, etc.,
  • Use Spring to allow for writing service beans in Groovy, that can also be auto recompiled/reloaded.

Status

At this point I can write my actions in Groovy and have them be instantiated and recompiled on the fly by Spring. I can do the same for my service beans, also using Spring.

Unfortunately this requires a patched version of Spring that supports scoped scripted beans. (Total of about 15 lines of code and comments, patch attached to the JIRA issue referenced above.)

My project is managed using Maven. I started with the Struts 2 archetype some time ago, which includes support for the Jetty plugin. So, my development process is now edit my Groovy file, save, and run 'mvn compile'. This copies my .groovy file to the target/classes directory so it will be on the classpath.

Future Directions

  • Create a Maven archetype that demonstrates the "framework."
  • Get the scope support for scripted beans adopted into Spring. (This is necessary to make this redistributable. I have no intention of supporting a Spring fork for this.)
  • Create a Maven plugin for creating scaffolding code, like what Grails does.
  • Drive Groovy down to the persistence layer if possible, and also automate it with Maven plugins.
  • Improve the Jetty plugin support, so Jetty will watch my source directory of Groovy files, so I don't need to run 'mvn compile' to copy the files to target/classes for the changes to be picked up.

My ultimate goal is to have a simple environment for Struts 2, where I don't need to reload the server for every change in the application. I can edit, and test in my browser directly without requiring a "compile" or "deployment" phase. The only time I want to need to restart the server is for changes to my Struts 2 config, such as a new action; changes to my Spring context configuration; or changes to my persistence configuration.

Additionally any framework that integrates with Spring could use these techniques to eliminate restarts, and move toward using dynamic scripting languages for implementation.

Update: 2/24/07

I started mixing in Spring's declarative transactions using aop pointcuts and it blows up pretty solidly. I think I'm dealing with a classloader issue. It appears to me that within the Spring container AspectJ has a classloader that is a child of the main classloader for the Spring container. When Spring creates a GroovyClassLoader instance it is also a child of the main Spring classloader and therefore a sibling of the AspectJ classloader. When AspectJ tries to figure out what the groovy bean is it can't because it has no visibility to the GroovyClassLoader, which is a sibling.

I'm not sure what the resolution to this could be. It seems somewhat intractable. It would almost seem that if you're going to mix scripting languages into a Spring context the classloaders needs to be created very carefully. Where all scripting language classloaders are a parent of the AspectJ classloader. Problem is Spring seems to create a class loader per scripted bean that it loads.

I will report more as I find more.

I started mixing in Spring's

I started mixing in Spring's declarative transactions using aop pointcuts and it blows up pretty solidly. I think I'm dealing with a classloader issue. It appears to me that within the Spring container AspectJ has a classloader that is a child of the main classloader for the Spring container. When Spring creates a GroovyClassLoader instance it is also a child of the main Spring classloader and therefore a sibling of the AspectJ classloader. When AspectJ tries to figure out what the groovy bean is it can't because it has no visibility to the GroovyClassLoader, which is a sibling.Lingerie Wholesale

Use Grails

As a former WebWork user who went down the same path, it is well worth it. However, it only took me so far on the road to nirvana, so I started Grails.

http://grails.org

Give it a go ;-)