Meera has posted 70 posts at DZone. You can read more from them at their website. View Full User Profile

Spring and Scripting

08.11.2008
| 17124 views |
  • submit to reddit

Spring is among the handful of Java application frameworks that is widely used in many enterprise projects. It has become a viable alternative to the more standard JEE technologies. One of its stengths is the support for scripting languages. Spring 2.0 introduced comprehensive support to use dynamic languages. It supports three different scripting languages; JRuby, Groovy and BeanShell.

There are times when in your application you have certain modules that require frequent changes, and based on these changes you need to change the business logic within your modules. If these modules were written in Java, you can imagine what needs to be done at this point; recompile, package, redeploy. This is where modules written in these dynamic languages come in handy, there is no need to recompile, or redeploy for these changes to take effect. In most cases, you want the Spring container to be able to detect these changes and also pick up the new state from the changed script source. Spring allows you to do this as well by setting one simple attribute.

"Spring Recipes - A Problem -Solution Approach" written by Gary Mak has a complete chapter dedicated to Scripting in Spring. The author covers all the three dynamic languages supported by Spring; JRuby, Groovy and BeanShell.

Hello World Example: It is customary to start any tutorial by writing a simple HelloWorld program, right? We are going to use Groovy in this tutorial. This tutorial assumes you have some knowledge of Spring and Groovy. I used Eclipse IDE for this tutorial, and to work with Spring and Groovy in Eclipse, you need the following libraries in your build path.

So, lets begin with the HelloWorld example here.

Step 1: Lets define an interface for our HelloWorld Service.

package com.springandgroovy;

public interface HelloWorldService {

String sayHello();

}

Step 2 : Implement the interface in Groovy.

Next, we implement this interface in Groovy by creating a simple script within the com.springandgroovy package as such:


import com.springandgroovy.HelloWorldService;
class HelloWorldServiceImpl implements HelloWorldService {

String name

String sayHello()
{
"Hello $name. Welcome to Scripting in Groovy."
}

}

Step 3: Make changes to Spring's configuration file.

Here comes the Spring's bean configuration file, in which you have to include the <strong>lang schema</strong> to use the custom dynamic language tags.

<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.5.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">

 

the bean definition for the Groovy backed HelloWorldService looks like this:

<lang:groovy id="helloWorldService"
script-source="classpath:com/springandgroovy/HelloWorldServiceImpl.groovy">
<lang:property name="name" value="meera"/>
</lang:groovy>

That's all you need to use Groovy backed beans in Spring. So, how do we know this works, right? Lets write a simple Main class and test it within our IDE:

Step 4: Run the HelloWorldService.

package com.springandgroovy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

public static void main(String[] args) throws Exception {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorldService service = (HelloWorldService) context.getBean("helloWorldService");

System.out.println(service.sayHello());

}
}

And you should be able to see output in the console as such:

Step 5: Refreshable Beans.

As I mentioned earlier also, to turn on this feature we have to specify one simple attribute refresh-check-delay on the <lang:groovy> element of our bean definition as such:

<lang:groovy id="helloWorldService"
script-source="classpath:com/springandgroovy/HelloWorldServiceImpl.groovy"
refresh-check-delay="5000">
<lang:property name="name" value="meera"/>
</lang:groovy>

Again, how do we know this works when we make changes to our Groovy Script? A small change in our Main class and, and you should be set to test that this works as well:

package com.springandgroovy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

public static void main(String[] args) throws Exception {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorldService service = (HelloWorldService) context.getBean("helloWorldService");
System.in.read();
System.out.println(service.sayHello());

}
}

Now, run the Main class in your IDE, it halts at the System.in.read line, make a few changes within your script, save it, hit enter in your console window in your IDE. The Spring container reads the changes and prints the results within the console window.


The script change can be as simple as adding a few special characters as such within the sayHello() method:

   String sayHello()
{
"Hello $name!!!. Welcome to Scripting in Groovy."
}

And the console window reflects these changes:

Step 6: Inline Scripts.

Spring also allows you to embed scripts directly within the Spring bean definitions inside your Spring configuration file. I am no fan of doing this, because it has a drawback; the refresh attribute is not applicable for inline scripts. Lets take a look at this in case you need to use this feature:

Copy the script we wrote in Step 2 and paste it within the lang:inline-script element as such:

   <lang:groovy id="helloWorldService">
<lang:inline-script>
<![CDATA[
import com.springandgroovy.HelloWorldService;
class HelloWorldServiceImpl implements HelloWorldService {

String name

String sayHello()
{
"Hello $name. Welcome to Scripting in Groovy"
}
}
]]>
</lang:inline-script>
<lang:property name="name" value="meera" />
</lang:groovy>

Run the Main class and you should see the same output in your console window.

 

In this tutorial we learned how to use Groovy with Spring using an external script source file, how to refresh changes when a script source file is changed, and finally saw an inline script which was embedded within the Spring configuration file. You can refer to the Spring documentation in case you need to use either JRuby or BeanShell. The Spring Recipes book has source code for all the three scripting languages.

Additional Resources:

1. JRuby
2. Groovy
3. BeanShell
4. Spring Framework
5. Spring Recipes

Published at DZone with permission of its author, Meera Subbarao.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Menlon Da replied on Tue, 2011/03/01 - 4:57pm

I am using Springsource Tool Suite 2.6.0.M2, apache-maven-3.0.2.

I followed this example.

When running the application I got "org.springframework.beans.factory.BeanCreationExc eption: Error creating bean with name 'helloWorldService': Could not determine scripted object type for GroovyScriptFactory"

Do I missing something? Thanks in advance!

My pom.xml list:
4.0.0

com.springandgroovy springandgroovy 1.0-SNAPSHOT jar

springandgroovy http://maven.apache.org

UTF-8

junit junit 3.8.1 test org.springframework spring-beans 3.0.5.RELEASE jar compile org.springframework spring-context 3.0.5.RELEASE jar compile org.springframework spring-context-support 3.0.5.RELEASE jar compile org.springframework spring-core 3.0.5.RELEASE jar compile groovy groovy-all 1.1-rc-1 jar compile



My beans.xml list: <?xml version="1.0" encoding="UTF-8"?>



The app output:

Mar 1, 2011 3:30:32 PM org.springframework.context.support.AbstractApplic ationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlAp plicationContext@1c1ea29: startup date [Tue Mar 01 15:30:32 CST 2011]; root of context hierarchy Mar 1, 2011 3:30:32 PM org.springframework.beans.factory.xml.XmlBeanDefin itionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [beans.xml] Mar 1, 2011 3:30:32 PM org.springframework.beans.factory.support.DefaultL istableBeanFactory preInstantiateSingletons INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultL istableBeanFactory@f42ad0: defining beans [org.springframework.scripting.config.scriptFactory PostProcessor,helloWorldService]; root of factory hierarchy Mar 1, 2011 3:30:32 PM org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry destroySingletons INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultL istableBeanFactory@f42ad0: defining beans [org.springframework.scripting.config.scriptFactory PostProcessor,helloWorldService]; root of factory hierarchy Mar 1, 2011 3:30:32 PM org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry destroySingletons INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultL istableBeanFactory@17431b9: defining beans [scriptFactory.helloWorldService,scriptedObject.hel loWorldService]; parent: org.springframework.beans.factory.support.DefaultL istableBeanFactory@f42ad0 Exception in thread "main" org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'helloWorldService': BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'helloWorldService': Could not determine scripted object type for GroovyScriptFactory: script source locator [classpath:com/springandgroovy/HelloWorldServiceImp.groovy]; nested exception is java.io.FileNotFoundException: class path resource [com/springandgroovy/HelloWorldServiceImp.groovy] cannot be opened because it does not exist at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:452) at org.springframework.beans.factory.support.Abstract BeanFactory$1.getObject(AbstractBeanFactory.java:2 91) at org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry.getSingleton(DefaultSingleton BeanRegistry.java:222) at org.springframework.beans.factory.support.Abstract BeanFactory.doGetBean(AbstractBeanFactory.java:288 ) at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:190) at org.springframework.beans.factory.support.DefaultL istableBeanFactory.preInstantiateSingletons(Defaul tListableBeanFactory.java:580) at org.springframework.context.support.AbstractApplic ationContext.finishBeanFactoryInitialization(Abstr actApplicationContext.java:895) at org.springframework.context.support.AbstractApplic ationContext.refresh(AbstractApplicationContext.ja va:425) at org.springframework.context.support.ClassPathXmlAp plicationContext.(ClassPathXmlApplicationCon text.java:139) at org.springframework.context.support.ClassPathXmlAp plicationContext.(ClassPathXmlApplicationCon text.java:83) at com.springandgroovy.App.main(App.java:14) Caused by: org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'helloWorldService': Could not determine scripted object type for GroovyScriptFactory: script source locator [classpath:com/springandgroovy/HelloWorldServiceImp.groovy]; nested exception is java.io.FileNotFoundException: class path resource [com/springandgroovy/HelloWorldServiceImp.groovy] cannot be opened because it does not exist at org.springframework.scripting.support.ScriptFactor yPostProcessor.postProcessBeforeInstantiation(Scri ptFactoryPostProcessor.java:297) at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.applyBeanPostProcessors BeforeInstantiation(AbstractAutowireCapableBeanFac tory.java:848) at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.resolveBeforeInstantiat ion(AbstractAutowireCapableBeanFactory.java:820) at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:446) ... 10 more Caused by: java.io.FileNotFoundException: class path resource [com/springandgroovy/HelloWorldServiceImp.groovy] cannot be opened because it does not exist at org.springframework.core.io.ClassPathResource.getI nputStream(ClassPathResource.java:158) at org.springframework.scripting.support.ResourceScri ptSource.getScriptAsString(ResourceScriptSource.ja va:82) at org.springframework.scripting.groovy.GroovyScriptF actory.getScriptedObjectType(GroovyScriptFactory.j ava:201) at org.springframework.scripting.support.ScriptFactor yPostProcessor.postProcessBeforeInstantiation(Scri ptFactoryPostProcessor.java:290) ... 13 more

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.