Sublime Simplicity of Scripting with Groovy
Many of my blog posts are, as my blog’s title suggests, inspired by actual events. Three experiences this week have reminded me yet again of the simple simplicity of scripting with Groovy. In this post, I briefly look at each of these three events that are only related because they tie to Groovy and to scripting with Groovy.
Groovy Provides Easy Scripting for Java Developers
In the post I Use the main() Method, Bozhidar Bozhanov writes that he “[uses] a tool class with a main method” rather than scripting in Linux/bash or Windows PowerShell. Bozhanov goes explains this decision:
Java has a lot of powerful libraries that allow me to ditch the need of doing stuff in the shell or using bash scripts. And the good thing is – it’s Java, it can be versioned if needed, and it’s easy to use, with no additional learning curve, no man pages, etc.
Although I have been caught writing and maintaining Perl and have been known to use other scripting languages such as Ruby, Python, PHP, and shell/bash or PowerShell, I do also appreciate Bozhanov’s reasons for preferring a Java class with a main() function for developing tools. The JDK and JVM provide powerful and feature-rich libraries and capabilities and are familiar to me as a Java developer.
Groovy is a great scripting language for a person who is more comfortable with the JDK and JVM than with the underlying operating system. Groovy is easy for the Java developer to pick up and makes some things even easier than when written in Java. Groovy is particularly nice in providing “script-style” characteristics such as no need to write explicit objects, no need to write public static void main
method definitions, no need to catch exceptions (checked or unchecked), built in command-line support, numerous useful GDK extensions of the JDK, dynamic typing, concise syntax (one-liners are extreme examples), easy access to root class loader for internal class loading, report-friendly output features, integrated Ant support, easy SQL access, easy XML parsing, improved regular expression support, and more.
One advantage of scripting with Groovy over some other scripting languages is the ability to run the Groovy scripts on any platform supporting a Java Virtual Machine.
Groovy Increases Java Knowledge
I have also blogged on using simple Java main classes to learn about the language. Earlier this week, a colleague ran into a surprising discovery related to Double.MIN_VALUE. When he asked if I knew what the value of Double.MIN_VALUE is, I told him that I don’t know off the top of my head, but then in a matter of seconds was able to tell him by running the following simple command on the command line:
groovy -e "println Double.MIN_VALUE"
The output of running this single line command was, as shown in the next screen snapshot, 4.9E-324. He asked me because he was (and now I was) surprised that it was not negative. After reading the Javadoc for Double.MIN_VALUE, this value made sense, but it was quicker for me to find the value by running that simple Groovy script than it was to access the Javadoc or even Google the question. I repeatedly find Groovy to be highly useful for quickly learning more about Java.
By the way, the Javadoc for Double.MIN_VALUE states (I added the emphasis), “A constant holding the smallest positive nonzero value of type double, 2-1074. It is equal to the hexadecimal floating-point literal 0x0.0000000000001P-1022 and also equal to Double.longBitsToDouble(0x1L).” I had assumed it was a negative number because I had never used it before and because of my previous experiences with negative values for Integer.MIN_VALUE and Long.MIN_VALUE. A great discussion on the reason for this selection of Double.MIN_VALUE
(and applicable to Float.MIN_VALUE) is available on the StackOverflow thread Minimum values and Double.MIN_VALUE in Java?
Groovy Simplifies Native Integration
Although one of the advantages of scripting with Groovy is the ability to run Groovy scripts on any platform that supports a JVM, there are many times when scripts need to access the native operating system. Groovy has access to the native operating system via Java’s java.lang.Runtime and java.lang.Process. Groovy simplifies use of these classes through its JDK extension of the Process class.
The JDK Process class has a waitFor() method whose Javadoc description states (I added emphasis):
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated. This method returns immediately if the subprocess has already terminated. If the subprocess has not yet terminated, the calling thread will be blocked until the subprocess exits.
The class-level Javadoc for Process more clearly outlines the potential negative impact (I added emphasis):
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
The excellent 2000 JavaWorld article When Runtime.exec() won’t shows examples of using Java code to write input and read output to avoid the block or deadlock, but Groovy’s Process GDK enhancement makes this far simpler to apply. For example, the Groovy GDK Process.waitForProcessOutput(Appendable, Appendable) method allows two StringBuilder instances, for example, representing standard output and standard error buffers to prevent “the process from blocking due to a full output buffer.”
The next two code listings and associated screen snapshots with the scripts’ outputs demonstrate this.
demoDeadlock.groovy – Process.waitFor() Deadlocks
#!/usr/bin/env groovy def recursiveDirCmd = "cmd /c dir /s ${args[0]}" println "Running command ${recursiveDirCmd}..." def result = recursiveDirCmd.execute() result.waitFor() println result.in.text
demoNoDeadlock.groovy – Process.waitForProcessOutput(Appendable, Appendable) Does Not Deadlock
#!/usr/bin/env groovy def recursiveDirCmd = "cmd /c dir /s ${args[0]}" println "Running command ${recursiveDirCmd}..." StringBuilder standard = new StringBuilder(450000) StringBuilder error = new StringBuilder(450000) def result = recursiveDirCmd.execute() result.waitForProcessOutput(standard, error) println standard
The above code listing and associated screen snapshots demonstrate that Groovy’s GDK extension of Process
makes it easy to avoid blocking on commands executed against the underlying operating system. A good article on calling shell commands from Groovy code is Executing shell commands in Groovy.
Conclusion
Three different events this week reinforced my opinion of Groovy delivering a combination of Java’s power with Groovy’s scripting simplicity.