Posted by: Wesley Willard on Apr 08, 2008
I have resisted taking the time to learn much about the many scripting languages which have been created in the last few years, but Groovy is one which, as a Java developer, has really caught my eye. It is built on Java, but provides additional features which have been inspired by other languages such as Ruby and Objective-C.
I find Groovy to be my current scripting language of choice for a few simple reasons:
- Java-like syntax is extremely easy to pick up
- Ability to leverage the many powerful Java APIs
- Useful built-in features like Categories, which enable dynamic extension of any class
In the following code, I’ll demonstrate some of these things. I will be making a remote connection to a JBoss Application Server in order to retrieve MBean attributes, as well as invoke operations on the MBean.
First, I will import some classes, and make a connection to the server:
package com.groovy
import javax.management.ObjectName
import javax.management.MBeanServerConnection
import javax.naming.InitialContext
import org.apache.xpath.XPathAPI
import groovy.xml.dom.DOMCategory
import org.w3c.dom.Element
def password = ""
def initialContextFactory = "org.jboss.security.jndi.
JndiLoginInitialContextFactory"
def JMX_INVOKER_RMI_ADAPTOR = "jmx/invoker/RMIAdaptor"
def port = 1099
def host = "localhost"
def username = ""
def password = ""
def initialContextFactory = "org.jboss.security.jndi.
JndiLoginInitialContextFactory"
//
// Connect to JBoss
//
Properties props = new Properties(System.getProperties())
props.put("java.naming.factory.initial", initialContextFactory)
props.put("java.naming.provider.url", host + ":" + port)
props.put("java.naming.security.principal", username)
props.put("java.naming.security.credentials", password)
def ctx = new InitialContext(props)
Object obj = ctx.lookup(JMX_INVOKER_RMI_ADAPTOR)
def mbsc = (MBeanServerConnection) obj
ctx.close()
Once I have made my connection, I will then use it to invoke an operation on a JBoss MBean.
//
// Create a class to be used as a String Category
// StringCategory extends the String class with
// a new method that splits a String at the newline delimiter
//
class MyStringCategory {
public static List splitOnNewLine(String string) {
return string.tokenize("\n")
}
}
//
// Invoke the operation listDeployedURLs on the Groovy MBean
// Use the category defined above to split up the return string
//
use (MyStringCategory) {
def deploymentScanner =
new GroovyMBean(mbsc, "jboss.deployment:flavor=URL,
type=DeploymentScanner")
def deployedURLs = deploymentScanner.listDeployedURLs()
def deployedList = deployedURLs.splitOnNewLine()
deployedList.each( { println it } )
}
This snippet illustrates a couple of interesting Groovy features. The class MyStringCategory is an example of a Groovy Category. A Category in Groovy is a class which defines methods which can extend any specified class. In the example, the method splitOnNewLine is a category method on the Groovy class String, due to the fact that the method is static, and the first argument (or target) is of type String. This method can be called on any instance of a String, and will return a List containing one entry per new line found in the String. To enable the category method to be called, the call must be enclosed in a use statement, which references the name of the category.
Also notice that I have created an instance of a GroovyMBean, constructing it using the server connection and the name of the JBoss MBean. The GroovyMBean is a built-in Groovy class, which allows an MBean to be used as a regular Groovy object. I use this object to call the listDeployedURLs() method on the JBoss MBean, which returns a String representation of the currently deployed URLs on the server. I then use my splitOnNewLine() category method to create a List, which can then be iterated.
The next snippet of code illustrates how easy it is to make use of an existing Java API within Groovy.
//
// Create GroovyMBean to retrieve the JBoss Mail Service
// configuration information, which is returned by JBoss
// as an XML Element object
//
def mailBean = new GroovyMBean(mbsc, "jboss:service=Mail")
def configurationXML = mailBean.Configuration
//
// Use the Apache XPath API to select the nodes,
// and iterate through them with a closure
//
def nodeList = XPathAPI.selectNodeList(configurationXML,
"//property[@name]")
def printNode = { println it.getAttribute("name") + "=" +
it.getAttribute("value")
nodeList.each(printNode)
After the creation of another GroovyMBean, the Apache XPath API is used to select the nodes in the returned XML Element. The resulting NodeList is then iterated. Using an existing Java API in this fashion is just as simple as importing the appropriate class, and then calling its methods.
The final part of this example uses another category, MyDOMCategory, in conjunction with a built-in Groovy category, DOMCategory, to process the same XML Element. The call to list() is a category method which transforms a NodeList object into a List object. The List object is iterated, and its attributes are retrieved using the Element category method attribute(Element node, String attrName) to retrieve the name and value attributes.
//
// The class MyDOMCategory extends the Element class
// to return the value for the specified attribute
//
class MyDOMCategory {
static String attribute(Element node, String attrName) {
return node.attributes[attrName]
}
}
//
// Use Groovy Categories to iterate through the NodeList
//
use (DOMCategory, MyDOMCategory) {
configurationXML."property".list().each(
{ println "MyDOMCategory: " + it.attribute("name") + "="
+ it.attribute("value") } )
}
Hopefully, these short snippets of code illustrate the simplicity that I think makes Groovy a very attractive option for scripting use in the Java development world. I would highly recommend it as a useful addition to your software developer's toolkit.
In Scripts, Scripting Language, Groovy
