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.