Inside Paulo Abrantes' head
[ start | index | login or register ]
start > 2007-12-10 > 1

Java Programming: Getting your code spicy with Groovy

Created by pabrantes. Last edited by pabrantes, 2 years and 61 days ago. Viewed 4,316 times. #4
[diff] [history] [edit] [rdf]
labels

Java Programming: Getting your code spicy with Groovy

Groovy isn't a new language, it has been around since 2003. Hence, some of the readers might already know it, although others probably never heard of it. And of course, there will be the ones - like me - who know the language but never explored it.

Well, a few weeks ago I had some time to read part of >>Groovy's Documentation and started creating some test code and exploring Groovy's potential.

For the ones who don't know, Groovy is a dynamic language influenced by languages such as Ruby - which currently has a huge hype around it -, perl and Java.
A huge attractive that Groovy has for Java developers is that it can be compiled into byte code which runs directly on the JVM. But, there are various other ways to run Groovy:

  • Running Groovy code as a script using a standalone interpreter
  • Running Groovy within Java Code through a Groovy Java Shell
  • Integrating Groovy into Java code using a Script engine
The possibilities of integration with Java with very low effort made me put Groovy in my "should check" list.

For anyone with Java background - or probably any OO language - starting with Groovy isn't that hard, a few syntax modifications but that's it. Of course that there are some advanced issues that may take their time but, for what I've seen, I don't think they are so many. Also, since Groovy supports >>closures the code style can change a bit, becoming possible to use functional style (some might like, some might dislike).

Since I'm talking about a new language, I'll follow tradition and present the Hello World source code:

public class Hello { def hello(String name) { println "Hello " + name; } }

Hello hello = new Hello(); hello.hello("world");

Or in another way:

println "Hello world";

I think both ways are self explanatory and there's no need to comment any of the previous code.

Revisiting an example

While exploring what could be done with Groovy I remembered what I've written a few months ago regarding Java Hot Deploy, >>Java Programming: Hot Deploy.

The example presented in the Hot Deploy post was a simple command line application, where the commands - implemented via Command Pattern - could be added, removed or modified in runtime.

To achieve that solution, I had to write a custom class loader that would allow the JVM to load the modified classes into memory and, a file system watcher to know when a certain class was changed.
Even though the example presented in the post was a simple proof of concept, there was lots of code involved. More code means more work, the possibility of more bugs and a bigger effort to maintain.
Groovy can help in this problem by reducing the amount of code.

Since Groovy code can be dynamic, it's interesting to integrate new Groovy code with existing Java code through a script engine. In order to implement this solution it's important to understand what should be implemented has Java code and what should be implemented has Groovy.
Such problem is quite simple to solve since, what is intended to be dynamic are the commands, hence, the commands should be implemented as Groovy code.

The figure below represents the modifications in the application's design (top design explained in detail in the Hot deploy post), as it can be seen, the design becomes simpler.

refactoring

Regarding the GroovyScriptEngine there are a few things that deserve to be mentioned:

  • The constructor receives an array of Strings that specifying the directory or directories where groovy scripts can be found (which I called groovy script repository).
  • To execute a given script which is in the script repository the run(String name, Binding binding) method should be ran.
  • The Binding object is the bridge between Java and Groovy that allows to pass variables into Groovy or retrieve values from Groovy scripts.
Show CoreEngine Source
package net.pabrantes.groovyTests;

import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; import groovy.util.ResourceException; import groovy.util.ScriptException;

import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader;

public class CoreEngine {

private GroovyScriptEngine scriptEngine;

public CoreEngine(String[] roots) throws IOException { scriptEngine = new GroovyScriptEngine(roots);

}

public void start() throws ResourceException, IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String input = ""; Boolean leave = Boolean.FALSE; Binding binding = new Binding(); binding.setVariable("leave", leave); while (!Boolean.TRUE.equals(leave)) { System.out.print("$ ");

input = in.readLine(); if (input != null) { String[] commands = input.split(" "); String[] arguments = getArguments(commands); binding.setVariable("args", arguments); try { long time1 = System.currentTimeMillis(); scriptEngine.run(commands[0] + ".groovy", binding); long time2 = System.currentTimeMillis(); int time = new Long(time2 - time1).intValue(); System.out.println(commands[0] + " took " + time + " ms"); } catch (ScriptException e) { if (e.getCause().getClass().equals( FileNotFoundException.class)) { System.out.println(commands[0] + " command not found!"); } else { System.err.println(e.getMessage()); }

} } leave = (Boolean) binding.getVariable("leave"); }

}

public static void main(String[] args) throws IOException, ResourceException {

String[] roots = new String[] { "groovyCommands" }; CoreEngine commandLine = new CoreEngine(roots); commandLine.start(); }

private String[] getArguments(String[] commands) { String[] arguments = commands.length > 1 ? new String[commands.length - 1] : new String[] {}; for (int i = 0; i < commands.length - 1; i++) { arguments[i] = commands[i + 1]; } return arguments; } }

Show ListDir Groovy script
// This is a closure // "it" refers to the argument passed to the closure. def getDir = { it.length == 0 ? "/" : it[0] };

File dir = new File(getDir(args)); File[] files = dir.listFiles();

files.length.times { println files[it]; }

The amount of code reduced in the engine itself and in the commands. Although, not everything is good, using Groovy scripts will have a negative impact in the applications' performance. To understand such impact, a simple benchmark was done using both implementations.

The benchmark consisted in running five times a given command and on the 4th time create a change on the command being run in order to create a reload action (which obviously will affect execution time). The benchmark was ran three times and the table below presents the time averages.

Time table For Java + Groovy Versus Java Only (average in 3 test runs)

Java +
Groovy
ls /
(in ms)
echo string
(in ms)
Java
Only
ls /
(in ms)
echo string
(in ms)
1st time26654261st time2613
2nd time190592nd time30
3rd time100443rd time740
4th time (*)10426414th time (*)82
5th time103105th time21

(*) - commands were modified, causing command reload.

Anyone interested can also >>download the full source code which includes Java and Groovy source code.

The code for the the Java only implementation can be found in the Hot Deploy post.

Conclusions

As can be seen in the table above, the delay introduced by Groovy is quite big. In this particular case, user's output, the delay isn't very relevant since, most of the times, it's still under a second. But, in other cases this delay won't be acceptable.

As any other solution it contains trade-offs that should be well thought through. The example allowed to understand that the integration of Java with Groovy is quite simple and has great potential but it also has its share of problems.

Also, should be kept in mind that Groovy is not all about integration with Java, the language by itself contains really great features such as the >>native support for markup languages and a >>set of features that allow to create Domain Specific Languages very easily..

In my opinion, Groovy really has lots of potential which I'm definitely interested in exploring.

4 comments (by pabrantes, m4ktub) | post comment
Who am I?
paulo-roca2My name is Paulo Abrantes AKA pabrantes and I'm a software developer. I'm currently employed at >>CIIST working as a Java developer in >>FenixEDU.

This blog is mostly about Java programming, domain driven design and snipsnap bliki developing. Everything written in this blog is my personal opinion and it may not reflect the opinions of my employer and co-workers.


Blog subscription
subscribe by rss subscribe by email

Links
>> Home
>> Paulo's Profile
>> Post History
>> Add to Technorati Favorites
>> Paulo's Photo Gallery
>> WishList
>> Posting without Login

Search Blog
Fellow Bloggers

Recent Posts

Blog: Almost an year since last post
Java Programming: Bytecode Injection
Intermission: Sorry For Downtime
Software Developing: Studying The Bliki Domain Model
SnipSnap Developing: Trying to settle a roadmap
System Administration: Load Balancing with Apache
Blogging: Two years have passed
Software Developing: The SnipSnap Saga
Java Programming: Getting your code spicy with Groovy
Software Developing: Fluent Interfaces
Software Developing: Implementing a ShoutBox on SnipsSnip
Software Developing: SnipSnap, SnipIt and SnipSnip
Java Programming: Proxies and Access Control
Java Programming: Proxies and References
Java Programming: References' Package

For older posts, please refer to post-history for a complete Post History

Logged in Users: (0)
… and 5 Guests.
This is a modified version of snipsnap.org created by >>Paulo Abrantes