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, 208 days ago. Viewed 1,193 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.

Icon-Comment m4ktub, 208 days ago. Icon-Permalink

Glad you did something that still is on my todo list … together with 1000 other nice languages smiley

Now seriously, it would be interesting to compare (at several levels) at least 3 languages: Groovy, Scala, and Beanshell.

You've mentioned Groovy. Scala is quite performance oriented and Beanshell uses a more straightforward approach than Groovy (and is more mature).

What do you think?

Icon-Comment pabrantes, 207 days ago. Icon-Permalink

I'm glad you enjoyed the topic!

Regarding your suggestion it's a very good one. Scala is also one of the languages that is in my "should check" list, specially because I have a friend that is currently doing heavy developing in Scala and he's always saying good things about it! smiley

So, I guess it's a possible post, the hard part is to understand what should be compared between those three languages. Even though comparing performance is always intesting, I'm sure that there are other interesting metrics interesting to watch (and you probably have some in your mind since you mentioned several levels). smiley

Icon-Comment m4ktub, 207 days ago. Icon-Permalink

Well the several levels I was thinking about are in something like:
  • Goal
  • Language features (related to the stated goal)
  • Available tools
  • Integration in other projects (aka, as hyper or community adoption)
  • Performance
  • Etc (love this one)
But, as you can see, is not something that I thought really well. My idea is that there is a rising number of languages build around the JVM. Giving you a more or less the same infrastructure as in the .Net side. So I'm interested givin a look to all the languages out there that tap into the JVM: Java, Beanshell, Groovy, Scala, JRuby, JPython, J...

Now that you've raised the question, what would be a good comparison framework for this task?

Icon-Comment pabrantes, 207 days ago. Icon-Permalink

Now that you've raised the question, what would be a good comparison framework for this task?
m4ktub

Still haven't thought much about the subject, but here are some of the things I think should be compared:

  • Performance
  • Integration on Developer's IDE
  • Needed effort to start developing in that new language
  • Process of integration with Java
Please login to www.pabrantes.net.
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

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
YALM: Yet Another Layout Modification

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

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