Vita Rara: A Life Uncommon

Groovy Works


Categories: | | | |

What is Groovy Works?

Groovy Works is a marriage of the Groovy programming language, Struts 2 and the Spring Framework.

Using Groovy Works you can develop your "Java" web application and avoid time consuming re-deployments and re-starts. Using Spring's support for dynamic re-compilation of scripted beans you can simply code, save, test in your browser. No need for costly recompilation, package, deploy, restart cycles.

Right now Groovy Works exists as a Struts 2 Plug-in, and an example application. Groovy Works depends on Spring 2.0.3.

Why call it "Groovy Works"?

Primarily because I conceptually see this going beyond just being a Struts 2 plug-in. Groovy, and all the dynamic scripting languages, seem to be changing the face of web development. My vision of Groovy Works is focused on eliminating the re-compile re-deploy cycle, and dramatically improving developer productivity. I remember the days of working in PHP, saving my file, and hitting reload in my browser.

Anyway, I tried coming up with some contraction of Groovy and Struts, and it really didn't quite work. Struts 2 is based off of WebWork 2, which is based on XWork 2. So, I knocked the Web off the beginning of WebWork, and put Groovy there, added an "s" on the end, and voila, Groovy Works. In the end it's just a name. Got a better one?

I thought of Groovy Struts, but seeing as this only works with Struts 2, Groovy Struts 2 just didn't have the right ring to it. Thus, Groovy Works.

Is Groovy Works a new Web Framework?

Not really, at the moment. Currently Groovy Works is simply a method of using the Groovy language to develop an application using Struts 2 and Spring. It's just a Struts 2 plug-in and a methodology, with an example.

What can Groovy Works do for me?

Save time. This was my primary motivation in getting all of the moving parts working together.

I'm in the middle of a large Struts 2 project, and recently moved it to Maven and started using the Jetty plugin. The beauty of being able to edit my JSP files and just hit re-load in my browser got me thinking. Then I started implementing my Struts 2 actions in Groovy. I did some more research and the idea of reloading the actions automatically came to light, along with Spring's support for scripted beans. So, I thought, "wouldn't it be nice to just start the servlet container and code, without the hassle of restarts?!?!"

Can I use Groovy Works with my existing projects?

Most likely. If your project is already using Spring 2 and Struts 2 then you should be able to start using the techniques of Groovy Works simply by adding Groovy, and the plug-in to your project and adopting its techniques. Groovy Works is really just a technique of using Groovy, Spring and Struts 2 together.

How do I get started with Groovy Works?

Download the plug-in and install it to your Maven repository.

  • Unzip it in a directory: tar xfzv groovyworks-spring-plugin.tar_.gz
  • Change directory to the groovyworks-spring-plugin directory: cd groovyworks-spring-plugin
  • Install the artifact with Maven: mvn install

Then download the sample application and try it out.

  • Unzip it in a directory: tar xfzv groovyworks.tar_.gz
  • Change directory to the groovyworks: cd groovyworks
  • Run the application with the Jetty plugin: mvn jetty:run
  • Point your browser at http://localhost:8080/groovyworks/

Now what?

Edit src/main/groovy/groovyworks/MyServiceImpl.groovy and change the message, save the file. Now, reload your browser. You should see whatever you changed your message to on the page. No application restart. You can edit your service bean to your hearts content and just keep reloading. (You do need to run 'mvn compile' after you save to copy the Groovy files to the target/classes directory. I'd like to eliminate that step though.) You can do the same thing with you action. You can also edit the JSP and just hit reload, thanks to Jetty.

What else needs to be done?

A lot.

It would be nice if the Maven Jetty Plugin could either be configured or modified to place the groovy files from the source tree into the classpath. (Update: I have this working using the testClassesDirectory setting for the Jetty plugin. It's kind of hackish, but really shows the potential.) It would also be nice if Jetty could be taught to re-start when a compiled Java class changes, but just keep running when a scripted bean is changed.

The plugin itself needs to be optimized to only recompile the Groovy actions when they change. Right now I believe it is doing it every time. (This is a proof of concept.)

Then there is the issue of boot strapping an application, much like Grails does. This isn't as important to me. I already have a lot of Struts 2 actions in Groovy, and my service and data layers are largely laid out. These features would be nice for new projects though.

Can I use Groovy Works techniques with <insert Java web framework>?

I would think you can. If you are integrating with Spring already you just need to intercept the call to create a bean and see if it is a Groovy script. Take a look at the code in the plug-in for ideas.

Eventually I would like to eliminate the plug-in and just have Spring instantiate the action beans, but there are some issues with that right now.

Some Thank Yous

I would like to thank a few people/groups for all they have done to support Java development:

  • The WebWork and Struts 2 Teams: Thanks for a great framework.
  • Juergen Hoeller and the Spring Team: Thanks for saving me from EJB's! Also, the quick fix of the scripting issues.
  • Chris Tam: Thanks for helping chase down the script beans singleton and AOP issues.
AttachmentSize
groovyworks.tar_.gz36.96 KB
groovyworks-spring-plugin.tar_.gz3.46 KB

The same issue with Spring Reloading Script Proxy

Hi,

I stumbled in the same problem of Spring giving a Proxy object for scripts that are reloaded. I did some reflection investigation and found out that the actual script instance is behind $Proxy.targetSource.target. Now, if only Struts 2 could take ognl expressions for <action method="...">, we could write

<action ... method="myAction.targetSource.target.execute">...

and we could use the auto-reloading scripted beans from Spring context. (myAction is obviously the Spring bean)

I really don't see how the problem with reloaded script could be solved - because the reference to the script instance is already given away to other parts of the code, and you cannot change it, so you are forced to use a proxy.

On the other hand let's assume you added a new method (let's say "execute"). At the time of initial instantiation this method didn't exist, so you couldn't expose it in the Proxy. Then you add it, but the Proxy instance is already referenced by other parts of the application. So if someone did $Proxy.getClass().getMethod("execute"...) on the Proxy, there's no way for the Proxy to intercept it and return the newly defined script method (at least that I know of). Therefore the proxy is limited to exposing only well-known methods, defined in the script, such as those of the superclass and implemented interfaces.

Re: The same issue with Spring Reloading Script Proxy

As you can see I punted when it came to having Spring instantiate my action instances. Having to define an interface for each one that the proxy would implement was just too much busy work for no gain. I wanted to write less code not more.

The fact that Spring wraps everything manufactured bean in a proxy hurts. I wish there was a way to instruct Spring that I just want it to instantiate the bean, and just the bean, fill in the dependencies and hand it back to me. I haven't found that this functionality exists in the framework.

I've wondered if Guice might be able to do this, but haven't really dug into it too far.

Now as to the actual process of the proxy, it is my experience that even if the method exists on the class written in Groovy, if it is not exposed on an interface that the class implements the Proxy will not have the method. So, if you are using some simple properties to track form inputs you have to express all of those methods setThis(), getThat(), on an interface someplace. *bleck*

All of these issues is what drove me to make the Groovy loader that instantiates the Groovy actions and then hands the instance to Spring for wiring. The drawback is that I've lost control of the wiring, but I've saved a lot of boilerplate unnecessary coding.

Mark

PS: Who are you?

Spring issue solved

I modified a bit your Groovy Works class, and now Groovy defined beans are exposed as expected by Struts. It's pretty simple actually, all you have to do is to add this method:

public Object buildBean(String beanName, Map extraContext)
throws Exception
{
Object o = super.buildBean(beanName, extraContext);

if (o != null) {
String name = o.getClass().getName();
if (name.startsWith("$Proxy")) {
Method m = o.getClass().getMethod("getTargetSource", null);
o = m.invoke(o, null);
m = o.getClass().getMethod("getTarget", null);
o = m.invoke(o, null);
}
}

return o;
}

My name is Martin Mavrov, and I came to your page while evaluating different web frameworks that have expressive power at least matching JSF's. At the same time I want to be able to do most things without restarting the J2EE server, so the framework should support Groovy. This is how your solution got in my field of view. But I still wanted to have everything nicely embedded in Spring, so I tried to find a way to have reloading scripted Spring beans as actions in Struts 2. Now hopefully I have settled for Struts 2 + Spring + Groovy + JPA for future projects.

BTW, if you are satisfied with the solution, you are free to put it in GroovyWorks.

Re: Spring issue solved

Martin,

That's pretty neat. I'm going to think about using that. As you say that would allow us to keep all of the definition of beans in the Spring application config file. Something to think about.

I'm looking at adding some convention over configuration style stuff to Groovy Works, and for that this would not work. More on that later.

Mark

Thanks Mark. I have

Thanks Mark. I have downloaded your Groovy Works and try it. The most attractive feature of Groovy Works to me is "save time" and leverage my existing skills. Groovy Works is a really interesting project.

cheers
chris tam
xenium

Great plugin

I have a question. Zero configration feature can be implemented by this plugin? Now we should write action setting in struts.xml.

Zero Config

As Phil mentioned Groovy 1.0 does not have support for annotations. I'm giving serious thought to starting to work with the Groovy TRUNK, so I can get annotation support.

I'm looking at this for two reasons. 1. I'd like to try Guice with GW. I think annotation driven dependencies looks very interesting, and it would allow the service layer beans to change their dependencies without requiring a context restart. (I think. I've been meaning to write Crazy Bob and run the idea past him.) 2. I'd like to get rid of the struts.xml file, for all but the most exotic situations. I really want to get to the point of code it, run it.

One of the motivations for me in doing this was that as I worked out a query in a DAO every time I tweaked the query Jetty would need to restart the application, which caused Spring to restart, which caused Hibernate to reload its session. I could only get a handful of restart before I needed to kill -9 the jetty process and restart. Plus there's the time sitting there waiting for Spring to wire my over 100 beans. Now I just edit the query in the DAO, save and reload in my browser. Done.

Now, I'd like to take it to the next level. Write an action, annotate it with results and dependencies, save and run it.

Mark Menard

Re: Great plugin

About the zero configuration: .. I'm not sure. Since Groovy doesn't support annotations (yet - it's in the trunk, thanks to Alexandru), you can't alter defaults by annotations.

Mark: apologies for not getting back - too busy, you know how it goes, congrats on getting the Spring/Groovy integration working. I've released a standalone GroovyObjectFactory a couple of days ago as well. If you don't already have a project location, maybe you're willing to host it at the Struts 2 Scripting Support project page ?

Zero configuration

>You can't alter defaults by annotations.
Yes, Groovy doesn't support annotations. If default mapper for Groovy action is supported, I will be satisfied. Groovy makes action easy, and I don't want to make it complex by introduce annotation.

Default Mapper?

I have to admit I don't know what default mapper is, got a link?

Mark

Re: Default Mapper

Mark, you can find more info about the (default) Action Mapper here.

Basically, the action mapper works as a two way translator: it will map requests to actions, and vice versa (the S2 tags will use the Action mapper to create urls).

Anonymous: no, afaict, the default action mapping will not work (only .class files are checked, and they use a ClassLoader.loadClass() method to look for actions). Désolé, maybe later.
Otoh: since we have wildcard support for action mappings in struts.xml .. one could argue if this is really such a big problem.

Cheers,

Phil