1) Introduction
Spring makes it easier to provide integration support for scripting languages. The currently supported scripting languages are Groovy, JRuby and BeanShell. However, this article concentrates only on Spring’s support for the Groovy language. Plenty of code samples are given in each section for much clarity. The article assumes that the reader is comfortable in both Spring and Groovy concepts. If you are new to Spring and Groovy frameworks, please read the introduction articles that have been published in javabeat for Spring Introduction to Spring Web Framework and Introduction to Groovy – Scripting Language
also read:
2) Groovy Objects in Spring’s Environment
2.1) Introduction
Let us see a sample Application that will illustrate the integration of Groovy in Spring’s Environment. It will discuss how a Groovy file (the file with extension as ‘.groovy’) can be integrated with Spring to take the power of a dynamic language. In this sample, a Java Application tries to access Groovy code with the help of the configuration information provided by Spring.
2.2) Server interface
Let us define a common interface called Server which is to be given implementation by both Java and Groovy. This interface has three methods start()
, restart()
and stop()
. The following code illustrates for the same.
Server.java
package javabeat.net.articles.spring.groovy.integration.simple; public interface Server { public void start(); public void restart(); public void stop(); }
2.3) Groovy Server
The following code presents the Groovy implementation for the above Server
interface. The syntax of Groovy resembles almost the same as the Java language. However, Groovy enjoys the benefits of a scripting language like loose type checking, lenient syntax etc.. For example, it is not necessary to suffix a statement terminator with a colon (:) in Groovy.
GroovyServer.java
package javabeat.net.articles.spring.groovy.integration.simple public class GroovyServer implements Server { public void start() { println ("Groovy Server started...") } public void restart() { println ("Groovy Server restarted...") } public void stop() { println ("Groovy Server stopped...") } }
2.4) Java Server
Next comes the implementation in the Java language for the Server interface. The implementation class JavaServer
contains a reference with name 'groovyServer'
to the Server
interface. Later on, through the configuration file, we will see that how we populate this 'groovyServer'
reference with the implementation given in Groovy. We also have added a test method by name 'testGroovyServer'
that will call the Groovy implementation.
JavaServer.java
package javabeat.net.articles.spring.groovy.integration.simple; public class JavaServer implements Server{ private Server groovyServer; public JavaServer() { } public void start() { System.out.println("Java server started...."); } public void restart() { System.out.println("Java server re-started...."); } public void stop() { System.out.println("Java server stopped...."); } public void setGroovyServer(Server groovyServer) { this.groovyServer = groovyServer; } public void testGroovyServer(){ groovyServer.start(); groovyServer.restart(); groovyServer.stop(); } }
2.5) Configuration file
spring-groovy.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"> <lang:groovy id="groovyServer" script-source="classpath:javabeat/net/articles/spring/groovy/integration/simple/GroovyServer.groovy"> </lang:groovy> <bean id="javaServer" > <property name = "groovyServer" ref = "groovyServer" /> </bean> </beans>
In the above configuration file, we have added a new element called 'groovy'
taken from the xml name-space 'lang'
to provide Groovy support. The value for the 'script-source'
attribute says that look into the classpath of the application (classpath: ) for a file by name GroovyServer.groovy
within the directory (javabeat/net/articles/spring/groovy/integration/simple
). By this, we have imported the Groovy code into the Spring Environment and now we have a Groovy bean identified by 'groovyServer'
.
In this Xml configuration file, we have also defined a Spring bean called 'javaServer'
by making a reference to the previously declared Groovy Bean 'groovyServer'
. So now the Java Code can access Groovy Server through the Groovy reference.
2.6) Client Application
The following client application tries to access the configuration file using the Spring Api to get a reference to the javaServer
object. Then the javaServer
object accesses the Groovy code by calling the method testGroovy()
within which an instance of Groovy Server object is already populated and thereby the Groovy code gets executed.
SpringGroovyIntegrationTest.java
package javabeat.net.articles.spring.groovy.integration.simple; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class SpringGroovyIntegrationTest { public static void main(String args[]){ ApplicationContext context = new FileSystemXmlApplicationContext( "./src/resources/spring/groovy/spring-groovy.xml"); JavaServer javaServer = (JavaServer)context.getBean("javaServer"); javaServer.start(); javaServer.restart(); javaServer.stop(); javaServer.testGroovyServer(); } }
3) Inlining Groovy objects
3.1) Introduction
It is also possible to inline Groovy code directly in the Spring’s configuration file rather than having the code in a separate .groovy file. This will be useful at times when the size of the Groovy code is comparatively smaller.
3.2) Spring’s Configuration file
inline.xml
< <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"> <lang:groovy id="person"> <lang:inline-script> package javabeat.net.articles.spring.groovy.integration.inline public class Person { String name int age double salary public void toString(){ println (name + "::" + age + "::" + salary); } } </lang:inline-script> <lang:property name="name" value="Nicolas" /> <lang:property name="age" value="43" /> <lang:property name="salary" value="56789" /> </lang:groovy> </beans>
In the above configuration file, the new Groovy type called Person is created within a well-defined package. Note that the Groovy code should be embedded with the tags lang:inline
. It is also possible to define an object of type Person
as well as to pass parameters to the defined object with the help of the 'property'
tags.
4) Reloading Groovy code
4.1) Introduction
This is a handy feature available in Spring-Groovy Integration. It is basically reloading a Groovy file to get the updated information. Note that when a Groovy file is compiled and made to run, any changes made to the Groovy source file wont affect the running code. This is the default behavior. However, this can be overridden by setting a property in the configuration file. This is illustrated with the help of the following sample,
4.2) The Person interface
Person.java
package javabeat.net.articles.spring.groovy.integration.refresh; public interface Person { public String getName(); public void setName(String name); }
We have declared a Person
interface for manipulating the name of a person through a getter and a setter.
4.3) PersonImpl class
The following code provides a simple implementation in Groovy for the above Person class.
PersonImpl.java
package javabeat.net.articles.spring.groovy.integration.refresh public class PersonImpl implements Person { String name; public String getName(){ return name; } public void setName(String name){ this.name = name; } }
4.4) Person Wrapper class
This is just a wrapper for the above Person
class. Since a defined Groovy object in the Spring’s configuration file cannot be directly referenced in the Client Application, this java class is introduced. This Java class will basically wrap the underlying Groovy object.
PersonWrapper.java
package javabeat.net.articles.spring.groovy.integration.refresh; public class PersonWrapper { private Person person; public void setPerson(Person person) { this.person = person; } public Person getPerson() { return person; } }
4.5) Configuration file
refresh.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"> <lang:groovy id="personImpl" script-source="classpath:javabeat/net/articles/spring/groovy/integration/refresh/PersonImpl.groovy" refresh-check-delay="5000"> <lang:property name = "name" value = "John"/> </lang:groovy> <bean id = "personWrapper" class = "javabeat.net.articles.spring.groovy.integration.refresh.PersonWrapper"> <property name = "person" ref = "personImpl"/> </bean> </beans>
It is seen that we have defined a Groovy object called ‘personImpl’ for the type PersonImpl.groovy. We find the introduction of the new property ‘refresh-check-delay’, which tells that for every 5 seconds check the Groovy source file (PersonImpl.groovy in our case) and if it updated, compile the file to take the updated information. If the updated source contains syntax errors, then a Run-time Exception will be thrown.
4.6) Client Application
RefreshTest.java
package javabeat.net.articles.spring.groovy.integration.refresh; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class RefreshTest { public static void main(String args[]) throws Exception{ ApplicationContext context = new FileSystemXmlApplicationContext( "./src/resources/spring/groovy/refresh.xml"); PersonWrapper wrapperForPerson = (PersonWrapper)context.getBean( "personWrapper"); System.out.println("Old Name"); System.out.println(wrapperForPerson.getPerson().getName()); Thread.sleep(10 * 1000); System.out.println("Updated Name"); System.out.println(wrapperForPerson.getPerson().getName()); } }
The Client Application makes a reference to the Person Wrapper object which wraps the underlying Groovy object (PersonImpl). When the program runs, it initially prints the name of the person as 'John'
(that is how we have configured in the Xml file). Later the execution is paused for 10 seconds, which gives enough time to modify the Groovy source file. Change the old name John to something like 'Johny'
to see the updated output.
5) Customizing Groovy objects creation
5.1) Introduction
In this final section, we will see how it is possible to customize the Groovy object creation process. The solution lies in the hands of GroovyObjectCustomizer
interface. All we have to do is to override the interface to provide the custom logic for the object once it is created. Let us look into that.
5.2) Test Class
We have defined a Test Class in Groovy with a single property called strProp. Whenever an object for this class is created the strProp
will soon be populated with the value 'A String Value'
. Let us see in the subsequent sections how to modify the original value through customization process.
TestClass.java
package javabeat.net.articles.spring.groovy.integration.customization public class TestClass { String strProp = "A String Value" }
5.3) Test Wrapper class
Since Spring doesn’t provide direct support for accessing a Groovy object defined in the Configuration file, we provide a Java wrapper object here. Note that this class contains a reference to GroovyObject
. All objects in Groovy are of the generic type 'GroovyObject'
. So, it means that even the Groovy object of type 'TestClass'
is a kind of 'GroovyObject'
object.
TestClassWrapper.java
package javabeat.net.articles.spring.groovy.integration.customization; import groovy.lang.GroovyObject; public class TestClassWrapper { private GroovyObject groovyObject; public TestClassWrapper() { } public void setGroovyObject(GroovyObject groovyObject) { this.groovyObject = groovyObject; } public void testIt() { System.out.println("Str value is ---> " + groovyObject.getProperty("strProp")); } }
5.4) Test Class Customizer
Now let us define the customization class for Groovy. As mentioned, the class should conform to the interface GroovyObjectCustomizer
and we should over-ride the method customize()
which will be called once the Groovy object is created.
TestClassCustomizer.java
package javabeat.net.articles.spring.groovy.integration.customization; import groovy.lang.GroovyObject; import org.springframework.scripting.groovy.GroovyObjectCustomizer; public class TestClassCustomizer implements GroovyObjectCustomizer { public void customize(GroovyObject groovyObject) { groovyObject.setProperty("strProp", "new value for string property"); } }
In the above method, we have modified the value of the string property 'strProp'
to a new value.
5.5) Configuration file
Let us see how all these classes get wired in a single Xml file. To enable customization, we have defined the customization class 'TestClassCustomizer'
that we created just before by giving an identifier 'testClassCustomizer'
. Note that the declaration of the Groovy object testClass
contains a reference to the Test Customizer class through its 'customizer-ref'
attribute.
custom.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"> <bean id="testClassCustomizer" /> <lang:groovy id="testClass" script-source="classpath:javabeat/net/articles/spring/groovy/integration/customization/TestClass.groovy" customizer-ref="testClassCustomizer"> </lang:groovy> <bean id = "testClassWrapper" class = "javabeat.net.articles.spring.groovy.integration.customization.TestClassWrapper"> <property name = "groovyObject" ref = "testClass"/> </bean> </beans>
5.6) The Client Application
The Client Application is quite simple. It makes a reference to the Test Wrapper class and then calls the testIt()
method to ensure that the property for the groovy object is changed.
RunTestClass.java
package javabeat.net.articles.spring.groovy.integration.customization; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class RunTestClass { public static void main(String args[]){ ApplicationContext context = new FileSystemXmlApplicationContext( "./src/resources/spring/groovy/custom.xml"); TestClassWrapper wrapper = (TestClassWrapper)context.getBean( "testClassWrapper"); wrapper.testIt(); } }
also read:
6) Conclusion
In this article, we have seen how Spring and Groovy provides Integration support to Groovy through code samples. The initial section of the article showed a simple Application that defines how Integration for Scripting languages are wired through the Configuration information. Then it described the inlining feature where it is possible to code the Groovy logic directly into the Configuration file. Later on, it talked about the Reloading nature of the Groovy source code. Finally the article was concluded by illustrating the Object Creation Customization process.