Thursday, May 22, 2008

Enabling web contexts(applications) to communicate via Spring RMI

While dealing with web applications, we came across a suituation where
two web applications (deployed in the same application server) needs to communicate
each other to fulfill the user functionality.

For example,
web context, Service1, provides database functionality by using JPA and also provides
web services.
another web context, Useradmin1, uses JSF and provides the business functionality.

'Useradmin1' needs to use the 'Service1' in order to perform the database functionality.

Spring RMI is used to power up the communication.

While implementing, we face some problems.
Below I have listed them and solution to over come them.
Some of the solutions might need more improvement.
Since its a agile process, in latter iteration we will re factor the code to improve more.

1. Spring code to export services as RMI service:

<bean id="businessService" class="com.service.impl.BusinessServiceImpl">
...
</bean>

now this businessService bean needs to exported as a RMI service.
This is done by using the following configuration

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service" ref="businessService" />
<property name="serviceName" value="businessServiceRMI" />
<property name="serviceInterface" value="com.service.BusinessService" />
<property name="registryPort" value="56001" />
</bean>

we are creating a new instance RmiServiceExporter, this will be responsible for
exporting the service (methods) and performing the marshallings /demarshallings.
Original busniessService object is ref by "service" property.

RMI needs a interface and we are assigning to the "serviceInterface" property.
And exporting the service name as "businessServiceRMI".
So we will be using this name in the Rmi Registry to look up.

We are using the registry port as 56001,
If RMI registry already listening then this service will be registered.
Else new new RMI registry will be created on port 56001 and this service will be
registered there. (For more info please refer the spring source code)

From this way we have exported the services as RMI services.


2. Spring code to utilize the exported RMI service:

In this section, we have to import the service interface files
and DTO (Data Transfer Object) class definitions to perform the marshallings
and demarshallings functionality. (this can be done by using .jar with all the definitions)

(RMI supports dynamically to download the class definitions from the codebase if they
don't exisits. But we haven't tried this feature with Spring RMI )

<bean id="businessService"
class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl">
<value>rmi://rmihostname:56001/businessServiceRMI</value>
</property>
<property name="serviceInterface">
<value>
com.service.BusinessService
</value>
</property>
<property name="refreshStubOnConnectFailure">
<value>true</value>
</property>
<property name="lookupStubOnStartup">
<value>false</value>
</property>
<property name="cacheStub">
<value>false</value>
</property>
</bean>

serviceUrl property contains the rmi protocol to find the rmi service.

property "refreshStubOnConnectFailure" is set to true to recreate the connection
when the client side stub fails.

property "lookupStubOnStartup" is set to false.
By default when RmiProxyFactoryBean is created, it will try to access the
rmi registry and create the stub.
Problem is rmi registry should be running else this will cause
error and web context doesn't get loaded.
To avoid this and support hot deployment this property is
set to false.
So when the first rmi connection is made then the rmi stub creation is done.

property "cacheStub" is set to false.
Once the stub is created Spring tries to cache it and then use it rather then recreate it for a certain time frame.
For our need we have disabled this feature.
so each request a connection is made.

Thats it,
Now this bean is injected to the JSF backing bean via faces-config.xml.

3. Lazy loading feature of the JPA and DTO pattern.

Objects are Marshalled when their are invoked or return as a method execution.

JPA has a feature of lazy loading, means child records are loaded only when
its been invoked from the parent object.
Example, in customer order, order items are lazy loaded when the the print order items functionality is invoked.

Now in RMI, this customer order object might already been sent to the
client side. In client side, when print order item is invoked its tries load the
order items with the closed JPA session.
This will throw interesting exception.
Simple stating please initialize the object before the serialization.

To over come this is by simply using getOrderItems.size(),
This make sure the order items also loaded when the Customer order is fetched from the database.

Or design a coarse grain DTO and filled it with necessary data and serialize it and
send to the client side.


So from this way, by using the spring RMI, web applications can communicate.

Hope this knowledge might be helpful.