Wednesday, July 26, 2006

Jython Spring Services

Using the Spring Framework is fun in java applications, and I tried to write a transactional service bean in Jython that accesses a Hibernate DAO implementation. My experiences are described in the following.

In the Spring configuration I left out the configuration parts of the DataSource and Hibernate SessionFactory because it is already described sufficiently at other places. As you see the service and DAO bean configurations are identical in the Jython and Java versions.

<beans>

<!-- DataSource and SessionFactory bean descriptions come here -->

<bean id="testService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref local="myTransactionManager"/></property>
<property name="target"><ref local="testTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<bean id="testTarget" class="mypackage.TestServiceImpl">
<property name="testDao"><ref local="testDao"/></property>
</bean>

<bean id="testDao" class="mypackage.TestDaoImpl">
<property name="sessionFactory"><ref local="mySessionFactory"/></property>
</bean>
</beans>


The service bean could not be simpler:





=== TestServiceImpl.py ===
from java.lang import Object

class TestServiceImpl(Object):
def setTestDao(self, testDao):
"@sig public void setTestDao(mypackage.TestDaoImpl testDao)"
self.testDao = testDao

def save(self, object):
self.testDao.save(object)

def getTests(self):
return self.testDao.getTests()




Spring uses java reflection mechanisms to instantiate the bean singleton that is different from Jythons way to manage instances. To make Spring see the Jython classes, they have to behave like native Java classes. Such classes are called Java-compatible classes. The jythonc compiler can generate the Java source code of the Jython class that can be compiled later with javac. Here are some guidelines for Java-compatible Jython classes (see: Jython for Java Programers by Robert W. Bill):

  • The Jython class must subclass a Java class or interface

  • The Jython class may include Java method signature hints called sig-strings

  • The Jython class must be within a module of the same name. The Java-compatible class "A" should be within the file "A.py"


The use of the sig-strings is necessary so that methods with the given declarations are automatically generated in the Java class, and don't forget to extend from java.lang.Object.
The DAO class ist also instatiated by Spring and thus has to be a java-compatible class too.




=== TestDaoImpl.py ===
from java.lang import Object
from org.springframework.orm.hibernate.support import HibernateDaoSupport

class TestDaoImpl(HibernateDaoSupport):
def save(self, object):
self.hibernateTemplate.save(object)

def getTests(self):
return self.hibernateTemplate.find("from mypackage.Test")




All DAO methods are called from the Jython testService class so that here we don't need sig-strings. The class mypackage.Test is the domain object that can be generated by the hbm2java Hibernate ant task. The mapping itself looks like



<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping>
<class
name="mypackage.Test" table="TEST">
<composite-id>
<key-property name="typ" column="TYP" type="string" length="8" />
<key-property name="wert" column="WERT" type="string" length="10" />
</composite-id>

<!-- associations -->

</class>
</hibernate-mapping>



Now we can persist a Test instance from a Jython controller class that itself delegates the task to the Spring Jython service and further to the Jython DAO implementation:




test = Test(typ="t1", wert="w1")
testService.save(test)
System.out.println("test: " + str(testService.getTests()))




In Java the same would look like

public class SomeController {
public foo() {
mypackage.Test test = mypackage.Test();
test.setTyp("t1");
test.setWert("w1");
testService.save(test);
System.out.println("test: " + testService.getTests());
}
}

No comments: