Blogroll
A great source of information for Lotus Domino administrators
Jeff Potts' excellent ECM/Portal blog
Lots of very useful Alfresco articles
| Groovy, RCP, JasperReports and Code Reuse |
|
|
|
| Written by Nathan McMinn | ||||||||||
| Monday, 04 July 2011 19:22 | ||||||||||
|
One of my many responsibilities at my day job is guiding the architecture of and helping to develop a fairly large Eclipse RCP application. The RCP is a pretty good platform for a desktop Java app. SWT keeps the look and feel mostly native, and you get a lot of freebies like a help system and update engine that would have to be developed if one were to build a Java application from scratch. Recently I was getting frustrated with the reusability of the service layer of this application and started looking for a new way. The service layer of this application is a set of POJOs that implement the business logic of the application, mostly importantly a complex set of calculations. The idea here was that we could reuse the same objects in the web version, ensuring that the calculations were performed consistently across both the client application and the web application (which serve different audiences, but a very similar purpose).
This approach worked well, but it did turn out to have some drawbacks. The service objects had dependencies that make it difficult to work with our reports (based on JasperReports). The result is that the reports end up with their own code for handling the calculations, either in the field expression or in the report query itself. This doubles the amount of work we have to do when a calculation is altered. The service layer objects themselves are compiled and are part of the RCP plugin, meaning that an update to the calculation logic required an update to the plugins. The update process, while very powerful and useful, can be cumbersome to the user community. I don't like forcing a big update on the community any more often than I have to. Finally, we want to have our analysts (who define the calculations) more involved in the development and maintenance of the service layer. These folks are bright, but they aren't coders. Java's syntax is a bit too verbose and obtuse for them to work with directly.
So, there are few problems to solve. How can I build a service layer for this RCP app that can easily be reused in both a set of web services and a web application, and can also be easily called from JasperReports? How can I deploy this layer in such a way that I don't have to push out a bulky RCP feature update every time an analyst wants to change a calculation? How can I bake-in unit testing? How can I get my analysts more involved in maintaining the calculations and double-checking them for correctness?
Groovy neatly fits all of my requirements. It runs on the JVM, so it should play nicely alongside both the RCP and our existing web environment. Groovy also can be used to develop scriptlets for JasperReports (with a little work). It's a simple matter to have the RCP application download the latest scripts from the server when it has a chance, and the scripts are tiny making this a very quick operation and much less disruptive to the users (we do it via an embedded Subversion client). Finally, Groovy's syntax is simple and clean enough that the analysts will be able to at least read it, if not write it. As an added bonus, Groovy is also very well suited to writing a DSL (Domain Specific Language). While this isn't planned for the first go, this capability could be handy in the future if we decide to define a DSL for this app.
Enough about the "why", time for the "how". Let's look at some code! The simplest way to embed Groovy in an existing Java application of any sort is by using the GroovyScriptEngine. This class handles finding, loading, compiling and running a Groovy script. Almost all of the complexity is hidden from the developer. It's a simple as giving the script engine one or more paths to search, binding some variables (if you need them) and telling it which script to run.
Let's create a simplified example in which we take the unit price for an item and the quantity, using these values to calculate an extended price. For this example we'll assume a working directory of /home/groovytest/scripts. Here's what the Groovy script might look like:
Pretty simple stuff. We're creating a new extended_price variable and assigning it the value of unit_price multiplied by quantity. You might be asking yourself, why define the calculation in a class and not just do it in the script and return the result? We'll get to that later. Now, here's how we might call that script from the Java application:
Looking at the code, it should be pretty clear what is happening here. First, we're setting up an array of Strings that lists all the paths in which scripts are located. Next, we instantiate the script engine and give it the list. Once the script engine is ready, we create a Binding object to hold the variables that we will pass to the script as arguments and give us access to variables in the script itself. Finally, we run the script and get the results back from the Binding object. Simple, simple, simple, and this approach works for both the RCP application and our J2EE web app. Of course our actual calculations are much more complex, but conceptually it's the same thing.
It's worth noting that you can pass ANY Java object to your Groovy script using the Binding object. Using Hibernate? Just pass in your mapped objects. Alternately, you can pass in the db connection details and use Groovy's built in database features to pull whatever data you need.
So we've seen how Groovy can be used to externalize calculations (or any other logic, really) from an RCP application. Now what about that promise of reuse? Now that I've gone through all the effort of embedding Groovy and externalizing my calculations into Groovy scripts, how can I reuse these, say, in a web service? There are a couple of options. Probably the simplest way to reuse the scripts as a service is to use the GroovyWS module to expose the script as a WS-I compliant service. How easy is this? Surprisingly easy.
Earlier, when we defined the ExtendedPrice class, it would have been visibly simpler to just create a script that took the two variables and returned a calculated value. The reason that we put the calculation in a class is so that we could reference it here and easily expose its methods as a service. It is also pretty simple to take the class defined in ExtendedPrice and use it in a server framework like Grails. It's nice to have options, right?
Now, what about JasperReports? The whole point of this exercise is to build a single set of scripts that encapsulate my complex calculations and make them available to all of the parts of this application including the RCP app, web app / services and reporting. This part is a bit trickier. While JasperReports supports Groovy as an expression language (which greatly simplifies report development, IMHO), there is no direct support for scriptlets written in Groovy. However, all is not lost. It just takes a little extra work. Since Groovy can be compiled to a regular old Java class (via groovyc), there's nothing stopping you from writing your scriptlets in Groovy, compiling them and using them in JasperReports. Or, write the scriptlets in Java and call the calculation scripts via the GroovyScriptEngine. Either way works. It's not a perfect solution, but it's pretty simple to whip up an Ant script to compile the Groovy scriptlet wrappers and jar 'em up. I'll have another article coming soon that details exactly how this works with an extended JRDefaultScriptlet that works nicely with Groovy.
Now that the service script can be used in the RCP and web apps, as a web service, and in the reporting engine, there's just one last piece of the puzzle. Unit testing. Again, Groovy makes this very simple. JUnit is baked into the Groovy runtime. It's simply a matter of creating a class that extends GroovyTestCase. Here's a simple example that tests the calcExtended method of the ExtendedPriceService class that contains the calculation used throughout this article:
That's all there is to it. Classes that extend GroovyTestCase can either be run directly from the command line, or they can be compiled and run like any other JUnit test case.
Groovy is a relative newcomer (version 1.0 came out in 2007), but it's already finding a place in my toolbox. Java has its fair share of detractors, but languages like Groovy help make it fun again and can give some significant gains in productivity when applied properly. |
||||||||||
| Last Updated on Tuesday, 05 July 2011 13:21 | ||||||||||
About Me

My name is Nathan McMinn. I'm a software engineer, beer geek, wannabe adventurer and genuinely curious guy. Find me on Facebook, Linkedin or Twitter
Latest Comments
- Belgian Stout Recipe
Chris, I'd love to try it! I live in NJ, about 30 ... - Belgian Stout Recipe
I finally brewed it. I used the Belgian Ardennes 5... - iPad + Alfresco = Mobile Aweso...
Hi Nathan, Any update on the offline caching and s... - Calling Web Services from the ...
Hi, This is just want I am looking to do and thank... - WordPress CMIS Plugin - New Pr...
forex indonesia broker provides historical stock d...



Comments
That's a darn good idea! We have done something similar in the past using Jython and JasperReports, I don't see any downside to using the same approach with Groovy. Thanks for the perspective.
It may not apply in this case but have you considered feeding the JasperReports run manager a datasource with the calculated results rather than telling the Jasper template how to do the calculations?
For http://rightspro.com we started with custom Java datasources which worked well and were simple enough since all calculations were done in the domain objects and service layers, but it still required classes unique to Jasper, so we later moved to XML datasources retrieved directly from the API.
Again, may not apply or help, but you could produce a Jasper Java datasource from the Groovy code and feed that to more standard Jasper templates.
RSS feed for comments to this post.