Sunday, June 24, 2007

Practices on Using Groovy, Beanshell and JRuby in Spring 2.0

I am using Spring 2.0 dynamic language support in my current project, including both Beanshell, Groovy, JRuby and Java. I want to share my experience learnt from my project with others and wish my experience can help them to solve their problems.

Use springframework 2.0.3

The previous version has some problems which are fixed in version 2.0.3. If you only use Groovy, then previous version is ok. But I still recommend to use version 2.0.3.

Great Decopuling and standardize

The most amazing feature of spring 2.0 dynamic language support is decoupling. The client program is decoupled from the implementation programming language of the spring bean class. For example:
Validator validator = (Validator)context.getBean("validator") ;
The spring bean "validator" can be implemented in either Groovy, Beanshell, JRuby or Java. In Spring 2.0, beans written in Java and beans written in Groovy, Beanshell or JRuby are equal. Client programs use the same getBean method to retrieve both types of bean. You may need to change the implementation language of your bean, but you don't need to change your client program. In my project, there are some legacy Beanshell programs and I am migrating these Beanshell program to Groovy. During migration, the Beanshell programs are replaced by Groovy programs one by one. But I DON'T need to change any client code. Please note the above sample code also can be used in Groovy, Beanshell or JRuby script.

Scripting bean "scope" problem

There is one VERY IMPORTANT attribute of the dynamic language support you must remember. The "scope" of the Groovy, Beanshell and JRuby bean is "singleton" and there is no way to change the scope of the the scripting beans in the application context xml. It can ONLY be "singleton". What wrong with scope "singleton"? Your class variable values will be OVERRIDE or some previous values are still stored in the scripting bean. In the web module my project, it becomes a problem in a multi thread environment. I have submited a request to springframework JIRA for adding a new attribute "script-scope" to allow setting the scope of any script bean and it is assigned to Rick Evan. I don't know whether there is solution or when will the solution be available. At this moment, you must remember the "scope" of all scripting bean is "singleton". Be careful with all the class variables especially in web application.

Autowire the scripting bean requirement

If you want to autowire any scripting bean, Your scripting bean MUST implements a Java Interface. Please note the word "MUST". If your scripting beans do not implements any java interface and you try to inject one scripting bean into another scripting bean as property, your program will throw Exception because springframework cannot use reflection to find the proper set method. Please note that this rule does not apply when inject a pure java object to any scripting bean.

Why Interface for scripting bean is Good

My project requires to use Groovy, Beanshell and JRuby. Interface for scripting bean is my really god blessing. Without the interface for scripting bean, I cannot inject a Groovy script bean to JRuby bean. Without the interface, I cannot use my Groovy script in my Java program. etc... If you only want to use Groovy only, what is the benefit of inteface for scripting bean? Would you like to try to save your Groovy bean into database using Hibernate? It works with some trick. Would you like to use iBatis to load record from database? It works. I have not tested everything but most work. But you must pay the price of creating a java interface.

Beware the catch of loading process of the scripting bean

I will describe the logical process of loading a scripting bean in application context in the following paragraph. It is not the actual program logic. The actual program logic is much much much compilcated.
When ClassPathXmlApplicationContext enounters a scripting bean definition. It will create the script factory. Then setup all the property of the script factory. After the factory is setup, execute the script to create a script object and store this script as singleton. There is a catch in the above process. If your script object incorrectly contains code which will indirectly trigger the creation of application context. Your program will be in an infinite loop and DIE. In the springframework support forum, I had seen a message from Rod Johnson who mentions that the future script factory may use Bean Scripting Factory as the factory. Will this solve this problem? No from my experiment. Beside I found out some strange problem with JRuby BSFEngine. JRuby BSFEngine will return a Java object after execute the script. If the require return class is int, it will always return long and force my program to throw exception.

Lazy-init does not work with scripting bean

No attribute in lang namespace allows lazy-init. So please don't try to lazy-init a scripting bean.

Global settings

Global settings like default-autowire are not inherited by the scripting bean. Need furthur investigate.

No comments: