Java Development News:

Groovin' with Webwork2

By Christian Parker

04 Aug 2004 | TheServerSide.com

Rod Cope's recent talk on Groovy at our last Boulder JUG meeting inspired me to do something Groovy.

For the uninitiated Groovy is a new scripting language for the JVM. There is even a movement afoot to make it the 'standard' scripting language for Java (see JSR # 241 here).

Groovy is the most semantically dense scripting language I have ever seen; there are shortcuts for everything. I've talked to a few active Groovy users who say that Groovy = 50% java code and 50% the development time for simple tasks (your mileage may vary).

There is one particular aspect of Groovy that really caught my attention at Rod's talk : the Java language is (99%) a sub-set of Groovy. Rod explained that there are still a few Java language features that aren't part of Groovy yet, but the Groovy evangelists don't seem to miss them. But basically 99% of the time you can take a .java, rename it .groovy, run it through the groovy interpreter and get expected results.

This feature got me to thinking about writing Webwork actions in Groovy. Webwork actions seem like a perfect candidate for scripting because a) they are pretty simple b) I want to change them a lot while I develop and not have to recompile and redeploy my web application. For this to work well there has to be a smooth mechanism for interacting with groovy scripts from Java. Turns out there is.

You can invoke a method on an object which you build from a .groovy script by doing the following:

1) create a plain old .java interface for the class you want to write in groovy. 2) write the implementation for the class just as you would in java and save it to a .groovy file. 3) use something like the following java code to instantiate an object from the .groovy script and invoke a method:

 ClassLoader cl = Thread.currentThread().getContextClassLoader(); GroovyClassLoader groovyCl = new GroovyClassLoader(cl); Class groovyClass = groovyCl.parseClass( cl.getResourceAsStream("FooImplementation.groovy")); FooInterface foo = (FooInterface) groovyClass.newInstance(); foo.callSomeMethod();

So, to write Webwork actions in Groovy, a little surgery has to be done on the mechanism that loads action classes in xwork.

In xwork (the guts of Webwork) there is a simple ObjectFactory with a getClassInstance(String classname) method. This method simply loads the specified class by name and returns it. This only works for .java classes. A simple addition allows this to work with .groovy scripts too using the classloading method above. The final com.opensymphony.xwork.ObjectFactory.getClassInstance(String classname) method looks like this :

 public Class getClassInstance(String className) throws ClassNotFoundException { Class clazz; // check className if (className.matches(".*.groovy")) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); GroovyClassLoader groovyCl = new GroovyClassLoader(cl); try { clazz = groovyCl.parseClass(cl.getResourceAsStream(className)); } catch (CompilationFailedException e) { throw new ClassNotFoundException("Error parsing " + className, e); } catch (IOException e) { throw new ClassNotFoundException("Error reading " + className, e); } } else { clazz = (Class) classes.get(className); if (clazz == null) { clazz = ClassLoaderUtil.loadClass(className, this.getClass()); classes.put(className, clazz); } } return clazz; }

A groovy action configured in the xwork.xml file looks like this :

 <action name="TestGroovyAction" class="TestGroovyAction.groovy"> <param name="foo">bar</param> <result name="success">/index.jsp</result> <result name="error">/hello.jsp</result> </action>

The TestGroovyAction.groovy looks like this :

 import com.opensymphony.xwork.Action; import org.apache.log4j.Logger; public class TestGroovyAction implements Action { Logger logger = Logger.getLogger("com.vitagroup.TestGroovyAction"); String foo; public TestGroovyAction() { } public String execute () { logger.debug("TestGroovyAction is executing foo is " + foo); return "error"; } public void setFoo (String foo) { this.foo = foo; } }

Notice there is no throws Exception clause on the execute method. This is one of the Java language features that hasn't been added to Groovy yet.

The only piece left to work out is reloading the groovy classes automatically when the .groovy script is changed. As it stands the web application has to be reloaded in order for the action class to be re built. Hopefully that won't be to hard to solve.

This is working well for me so far and I'm enjoying writing webwork actions in groovy. Check back soon to see the solution to the reloading problem. ;)

Part Two

Scripting webwork actions is down right groovy.

Earlier I posted about modifying webwork to allow actions to be written in Groovy. The modification is actually to xwork which is used by webwork.

After the first pass the basic functionality was working fine except that modifications to the .groovy scripts while the application was deployed in the servlet container had no effect. I suspected xwork was caching the actions somewhere. Actually Tomcat was the culprit. Tomcat uses it's own classloader implementation called WebappClassloader. This classloader caches calls to getResourceAsStream() and the cache doesn't get invalidated until the webapp is reloaded. However calls to getResoruce which return URLs are not cached. So changing to using getResource solved the caching problem.

Now it's possible to load a page, edit the .groovy behind it, reload the page, and see it change. Pretty slick for speedy developing.

But it's not necessary to re-compile the .groovy each time the webpage is reloaded. I took a tip from Brian McCallister and looked at Nanocontainer at Codehaus. Nanocontainer has a feature called nanoweb which is an ultra light WW like action framework that allows actions to be implemented in groovy.

Instead of recompiling the .groovy action each time instead it's easy to add the compiled groovy class to a cache using the file's timestamp as a key. With each page reload the timestamp of the .groovy is compared against the cache timestamp and the .groovy is recompiled only if it's newer than the cached version. With this in place there is no difference performance-wise between .groovy actions and .java actions.

Now it's on to sorting out how to implement a base action in groovy.

CP



About the author

Christian Parker christian@adigio.com
Blog: http://adigio.com/blogs/christian/

Christian Parker recently left his Senior Software Engineering position in the interactive TV world to co found Adigio Inc. (http://adigio.com). Christian has 10 years of software development experience and is currently active in the Java community. He is enjoying hacking away on Groovy, Spring, WebWork2, and other technology.

Related Resources