A prototype of Jython ScriptFactory for Spring Framework
SpringFramework 2.0 M2 does not include Jython support for its scripting framework. I don't know the reason. So I decide to create a prototype to prove it is doable.
Suppose we have a Java interface Messenger:
package org.yweng.spike.Spring;
public interface Messenger {
String getMessage();
}
And we have a Jython Script, Messenger.py, which implementes this interface:
from org.yweng.spike.Spring import Messenger
class PythonMessenger(Messenger):
def getMessage(self):
return 'Message from Python Messenger'
def getInstance():
return PythonMessenger()
Notice the getInstance method in the Jython module. The idea is using "naming convention" to help the Jython ScriptFactory instantiate Jython object.
The Spring xml configuration:
<beans>
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
<bean id="PyMessenger" class="org.yweng.spike.Spring.JythonScriptFactory">
<constructor-arg
value="classpath:org/yweng/spike/Spring/Messenger.py" />
<constructor-arg
value="org.yweng.spike.Spring.Messenger" />
</bean>
</beans>
The JythonScriptFactory class looks like this:
package org.yweng.spike.Spring;
import java.io.IOException;
import org.springframework.scripting.ScriptCompilationException;
import org.springframework.scripting.ScriptFactory;
import org.springframework.scripting.ScriptSource;
import org.springframework.util.Assert;
import org.apache.log4j.Logger;
import org.python.util.PythonInterpreter;
public class JythonScriptFactory implements ScriptFactory {
private static Logger logger =
Logger.getLogger(JythonScriptFactory.class);
private final String scriptSourceLocator;
private final Class[] scriptInterfaces;
public JythonScriptFactory(String scriptSourceLocator,
Class[] scriptInterfaces) {
Assert.hasText(scriptSourceLocator);
Assert.notEmpty(scriptInterfaces);
this.scriptSourceLocator = scriptSourceLocator;
this.scriptInterfaces = scriptInterfaces;
}
public String getScriptSourceLocator() {
return this.scriptSourceLocator;
}
public Class[] getScriptInterfaces() {
return this.scriptInterfaces;
}
public boolean requiresConfigInterface() {
return true;
}
public Object getScriptedObject(ScriptSource scriptSourceLocator,
Class[] scriptInterfaces)
throws IOException, ScriptCompilationException {
String strScript = scriptSourceLocator.getScriptAsString();
if (scriptInterfaces.length > 0) {
try {
PythonInterpreter interp = new PythonInterpreter();
interp.exec(strScript);
interp.exec("this = getInstance()");
return interp.get("this", scriptInterfaces[0]);
} catch (Exception ex) {
throw new ScriptCompilationException(ex.getMessage());
}
}
logger.error("No scriptInterfaces provided. ");
return null;
}
}
Junit Test case:
package org.yweng.spike.Spring;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.apache.log4j.Logger;
public class TestJythonScriptFactory extends TestCase {
private static Logger logger = Logger.getLogger(TestJythonScriptFactory.class);
private ApplicationContext ctx =
new ClassPathXmlApplicationContext("org/yweng/spike/Spring/ApplicationContext.xml");
public void testPyMessenger() {
Messenger messenger = (Messenger)ctx.getBean("PyMessenger");
assertNotNull(messenger);
String msg = messenger.getMessage();
logger.info(msg);
}
}
It's not pretty but it works. :)
