Scripting in Spring

Spring has a rather nice scripting framework that I haven’t paid much attention to, more from a lack of need than any real lack of interest.

One thing I recently noticed, is that there are only 3 supported scripting languages: Beanshell, Groovy and JRuby. Now Beanshell (for myself at least) was interesting about 4 or 5 years ago (maybe longer… since I can’t quite recall when it was first released), and I have little interest in Groovy (read “little interest” as “no interest”).

A bit of googling only uncovered a brief discussion about adding Jython support back in 2004 (I haven’t been able to find any code to go with that discussion), and Rhino-in-Spring which, as far as I can tell, is aimed at the web tier. Interesting that 2 of the most popular (IMO) scripting languages are rather conspicious by their absence from the core Spring distribution. Or if not absent, then certainly not very visible.

I’ve been looking for a reason to hack around with Spring extensions for a while, so this seemed like the opportune moment. Following one of the comments in that 2004 discussion, I started with cloning GroovyScriptFactory and a few other classes from the scripting package, and then hacked away the bits I didn’t want or need.

GenericScriptFactory is my abstract base class, and contains most of the code from GroovyScriptFactory. Subclass JythonScriptFactory is the workhorse for Jython scripting, while RhinoScriptFactory is obviously for Javascript code.

The cool part — I’ve implemented a NamespaceHandler for both, so bean configuration looks like this for Jython:

<script:jython id="test1" script-source="test.py"
               bean-name="mytest" return-type="test.TestInterface"
               interpreter="pythonInterpreter">
    <script:property name="test" value="jython prop from spring" />
</script:jython>

and for Javascript (Rhino):

<script:rhino id="test2" script-source="test.js"
              bean-name="mytest" return-type="test.TestInterface"
              scope="jsScope">
    <script:property name="test" value="javascript prop from spring" />
</script:rhino>

You’ll note that in both cases the attributes are passed to the factory, but properties are injected after the bean (in whatever language) has been created. A distinction which is rather useful when you think about it. This is the same as the other scripting languages already built-in to Spring.

You can see the full configuration file here, which includes setting up a PythonInterpreter for Jython, and a global scope for Rhino.

The sweet spot is, of course, this configuration:

<script:jython id="test3" script-source="test.py" bean-name="mytest" return-type="test.TestInterface" interpreter="pythonInterpreter">
    <script:property name="testInterface" ref="test2" />
</script:jython>

test2 is the object created by the Javascript code, and you’ll note that it’s being injected into the object created (in this case) by Jython code. The opposite works as well, meaning you can call Jython code from Javascript and Javascript code from Jython (well, at least through Spring injection). Exactly why you’d want to do this, I don’t know, but it’s a fun feature.

In either case, my classes implement a simple Java interface, meaning the beans created by both scripts are useable from Java as well — as you can see in the Test code:

public static final void main(String[] args) throws Exception {
    ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "beans.xml" });
    BeanFactory factory = (BeanFactory) context;

    TestInterface ti1 = (TestInterface) factory.getBean("test1");
    System.out.println("jython test: " + ti1.getTest());

    TestInterface ti2 = (TestInterface) factory.getBean("test2");
    System.out.println("rhino test: " + ti2.getTest());
}

Anyone interested can find the whole thing (very much a work in progress) in a Mercurial repository here.

RSS feed | Trackback URI

10 Comments »

Comment by Donnchadh Ó Donnabháin
2007-11-22 07:44:44

I’m using the jython support and haven’t had any problems so far.

I’ve created a JIRA issue to get this into spring at:
http://opensource.atlassian.com/projects/spring/browse/SPR-4058

Comment by jrbriggs
2007-11-22 08:30:01

Cool. Thanks.

 
 
Comment by Steven Githens
2007-11-25 15:36:22

Is the syntax of the tags for this the same as the tags for the 3 languages that are shipped with Spring2?

Comment by jrbriggs
2007-11-25 17:23:25

Not quite. The Spring tags are prefixed by “lang” (i.e. lang:groovy).
Mine is in a different namespace, thus the “script” prefix.

 
Comment by Steven Githens
2007-11-26 05:23:25

Ah, ok. I guess I was wondering, other than the namespace prefix, are the attributes and children nodes all the same? So if one wanted to hack this back into the Spring ones, the only thing that would have to be changed would be the namespace.

 
Comment by jrbriggs
2007-11-26 08:30:42

Again, not quite. I’ve had to add a couple of properties — such as “bean-name”, for example, to distinguish an actual bean in a script.

 
 
Comment by MaTT
2008-04-04 16:13:43

Thanks Jrbriggs .. was looking for a clean approach like this for days…

Spring 2.5.x has some changes in the scripting package that throws an error

script.jython.JythonScriptFactory is not abstract and does not override abstract method requiresScriptedObjectRefresh(org.springframework.scripting.ScriptSource)

Can you point me how to solve this?
I am not a developer, just wanna have jython/spring/blazeds/flex running but its not easy!!

thanks for your help

#### From Spring 2.5.x changelog ######
Package org.springframework.scripting
* introduced “ScriptFactory.requiresScriptedObjectRefresh(ScriptSource)” for object refresh even after a type check call
* GroovyScriptFactory and JRubyScriptFactory refresh scripted object instances even after an intermediate type check
* introduced “ScriptSource.suggestedClassName()” for dynamic Groovy scripts that do not define a class themselves
* GroovyScriptFactory uses resource filename (for files) or bean name (for inline scripts) as suggested class name
* added “depends-on” attribute to “lang:bsh/groovy/jruby” configuration element

 
Comment by MaTT
2008-04-04 16:58:38

I just did this..

public boolean requiresScriptedObjectRefresh(ScriptSource scriptSource){
return scriptSource.isModified();

}

and compiled… :) now have to get it working

 
Comment by MaTT
2008-04-05 05:58:52

Thanks Jason,
thanks to your tutorial and a lot of test-error i finally was able to play with spring jython blazeds and flex…

its rather slow, seems that each request builds a new python interpreter but works!..

maybe some of this could be usefull??

http://wiki.python.org/jython/JythonMonthly/Articles/October2006/3

cheers

 
Name (required)
E-mail (required - never shown publicly)
URI
Your Comment (smaller size | larger size)
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.

Trackback responses to this post