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.
Thursday, May 22, 2008
Wednesday, April 30, 2008
Sharing spring container between applications
How applications share spring container instead of
creating a separate copy for each other.
Let’s consider following example:
A web application (mywebapp.war) created to provide business services for the general clients.
This application uses spring container to manage the beans and transactions.
In the same web application we have an axis based web service application.
This axis application also needs to have a spring container.
Now we'll look at how code can be modified to share the spring container/context.
In mywebapp.war, we’ll look at the web.xml
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value> myShareSpringContext </param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-config-empty.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
‘locatorFactorySelector’ will be pointing to the xml file which contains the
spring bean configuration file names. In our example file name is beanRefFactory.xml.
If we look at the contents of this file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="myShareSpringContext"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>spring-config.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
For the ‘parentContextKey’ parameter, we have assigned ‘myShareSpringContext’.
For the ‘contextConfigLocation’ parameter we specify a empty spring
configuration xml file. In our example the file name is ‘spring-config-empty.xml’.
Because we will be using a common spring configuration file in a different location.
This spring-config-empty.xml will have the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans></beans>
In web application we are loading the spring context from the following
listener ‘org.springframework.web.context.ContextLoaderListener’.
If we analysis this code, it’s designed to load the new context or use an existing context.
Simply share the context.
Now if we look at the axis web service implementation,
xxxSoapBindingImpl.java:
String locatorFactorySelector = "classpath:beanRefFactory.xml";
String parentContextKey = " myShareSpringContext ";
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
BeanFactoryReference parentContextRef = locator.useBeanFactory(parentContextKey);
context = (ApplicationContext) parentContextRef.getFactory();
Now from the ‘context’ object reference we can retrieve the spring managed beans.
We have used ‘ContextSingletonBeanFactoryLocator’ to retrieve the
spring container location. If we analysis this class,
it has its own hashmap to store the applicationcontext. Key will be the parentContextKey.
Now both web application and soap impl will use the same spring container.
For more information please refer following links:
1] SingletonBeanFactoryLocator & ContextSingletonBeanFactoryLocator,
class documentations from spring framework documentation
2] http://slawek-zachcial.blogspot.com/2005/03/mdbstrutsspringhibernate-weblogic-way.html
creating a separate copy for each other.
Let’s consider following example:
A web application (mywebapp.war) created to provide business services for the general clients.
This application uses spring container to manage the beans and transactions.
In the same web application we have an axis based web service application.
This axis application also needs to have a spring container.
Now we'll look at how code can be modified to share the spring container/context.
In mywebapp.war, we’ll look at the web.xml
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value> myShareSpringContext </param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-config-empty.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
‘locatorFactorySelector’ will be pointing to the xml file which contains the
spring bean configuration file names. In our example file name is beanRefFactory.xml.
If we look at the contents of this file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="myShareSpringContext"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>spring-config.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
For the ‘parentContextKey’ parameter, we have assigned ‘myShareSpringContext’.
For the ‘contextConfigLocation’ parameter we specify a empty spring
configuration xml file. In our example the file name is ‘spring-config-empty.xml’.
Because we will be using a common spring configuration file in a different location.
This spring-config-empty.xml will have the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans></beans>
In web application we are loading the spring context from the following
listener ‘org.springframework.web.context.ContextLoaderListener’.
If we analysis this code, it’s designed to load the new context or use an existing context.
Simply share the context.
Now if we look at the axis web service implementation,
xxxSoapBindingImpl.java:
String locatorFactorySelector = "classpath:beanRefFactory.xml";
String parentContextKey = " myShareSpringContext ";
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
BeanFactoryReference parentContextRef = locator.useBeanFactory(parentContextKey);
context = (ApplicationContext) parentContextRef.getFactory();
Now from the ‘context’ object reference we can retrieve the spring managed beans.
We have used ‘ContextSingletonBeanFactoryLocator’ to retrieve the
spring container location. If we analysis this class,
it has its own hashmap to store the applicationcontext. Key will be the parentContextKey.
Now both web application and soap impl will use the same spring container.
For more information please refer following links:
1] SingletonBeanFactoryLocator & ContextSingletonBeanFactoryLocator,
class documentations from spring framework documentation
2] http://slawek-zachcial.blogspot.com/2005/03/mdbstrutsspringhibernate-weblogic-way.html
Subscribe to:
Posts (Atom)