SLSB to Spring
Recently we were stuck-up in a seemingly easy interaction of EJB and Spring. More specifically Stateless Session Bean (SLSB) to Spring.
We went through a number of examples present on the Internet, but nothing seemed to provide a clear-cut approach. Or, maybe, they expected me to be intelligent enough :).
Here, I present the solution which finally worked for us. This example uses Weblogic 8.1 server. We've not tested it on any other server. I'll update this post as and when we test it on other servers.
Our requirement is to make a business call to a Spring bean -- where our business logic resides. We use Stateless Session Beans as our business delegates to provide a remote interface to our Spring beans.
Spring framework provides convinience classes for this purpose. We utilise one such class called AbstractStatelessSessionBean. Refer to [1] for other classes, and their uses.
First of all, let's start with the business interface:
/**
* IGreet.java
*/
package pugtex;
import java.rmi.RemoteException;
public interface IGreet {
public String greetMe(String myName) throws RemoteException;
// declare other business methods
}
Our bean, would implement this IGreet
interface:
/**
* GreeterBean.java
*/
package pugtex;
import java.rmi.RemoteException;
public class GreeterBean implements IGreet {
public String greetMe(String myName) throws RemoteException{
return "Salut "+myName+"!";
}
// implement other business methods
}
Now for the configuration files for the Spring beans, beanRefContext
points to the actual application context
beanRefContext.xml
<?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="beanContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<value>applicationContext.xml</value>
</constructor-arg>
</bean>
</beans>
And the application context holds the bean desciption:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="autodetect">
<bean id="GreeterBean"
class="pugtex.GreeterBean" />
<bean id="CallerEJB"
class="pugtex.ejb.CallerEJB">
<property name="greeter">
<ref local="GreeterBean" />
</property>
</bean>
</beans>
Let's now move on to SLSB code, starting with the Remote interface. In a typical scenario, you don't have to declare your business methods here, courtesy the Spring framework :). [1] provides details for this aspect.
/**
* CallerEJBRemote.java
*/
package pugtex.ejb;
import java.rmi.RemoteException;
import pugtex.IGreet;
public class CallerEJBRemote extends EJBObject, IGreet {
// empty :)
}
The home interface:
/**
* CallerEJBHome.java
*/
package pugtex.ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public class CallerEJBHome extends EJBHome {
public CallerEJBRemote create() throws CreateException, RemoteException;
}
Now for the actual SLS bean:
/**
* CallerEJB.java
*/
package pugtex.ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.ejb.support.AbstractStatelessSessionBean;
import pugtex.IGreeter;
public class CallerEJB extends AbstractStatelessSessionBean implements IGreeter {
private static final long serialVersionUID = 1L;
private static final String SERVICE_BEAN_ID = "GreeterBean";
// the Greeter interface
private IGreeter greeter;
protected void onEjbCreate() throws CreateException {
greeter = (IGreeter) getBeanFactory().getBean(SERVICE_BEAN_ID);
}
public CallerEJB() {
}
/**
* Override default BeanFactoryLocator implementation
*/
public void setSessionContext(SessionContext sessionContext) {
super.setSessionContext(sessionContext);
setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance());
setBeanFactoryLocatorKey("beanContext");
}
public String greetMe(String someName) throws EJBException, RemoteException {
return greeter.greetMe(someName);
}
public void setGreeter(IGreet greeter) {
this.greeter = greeter;
}
public IGreet getGreeter() {
return greeter;
}
}
And now the usual ejb configurations...
ejb-jar.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN' 'http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd'>
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>CallerEJB</ejb-name>
<home>
pugtex.ejb.CallerEJBHome
</home>
<remote>
pugtex.ejb.CallerEJBRemote
</remote>
<ejb-class>
pugtex.ejb.CallerEJB
</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<env-entry>
<env-entry-name>ejb/BeanFactoryPath</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>beanRefContext.xml</env-entry-value>
</env-entry>
</session>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>CallerEJB</ejb-name>
<method-intf>Remote</method-intf>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
and Weblogic-specific:
weblogic-ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE weblogic-ejb-jar PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN" "http://www.bea.com/servers/wls600/dtd/weblogic-ejb-jar.dtd">
<weblogic-ejb-jar>
<description></description>
<weblogic-enterprise-bean>
<ejb-name>CallerEJB</ejb-name>
<stateless-session-descriptor></stateless-session-descriptor>
<reference-descriptor></reference-descriptor>
<jndi-name>ejb/CallerEJB</jndi-name>
</weblogic-enterprise-bean>
</weblogic-ejb-jar>
So that's about it.
Let me now present the structue of my build folder. This is where we ended up spending most of our time ;), because we were having classpath issues. Note the location of the (Spring) bean configuration files.
BUILD
+---classes
¦ +---pugtex
¦ ¦ GreeterBean.class
¦ ¦ IGreet.class
¦ ¦
¦ +---ejb
¦ ¦ CallerEJB.class
¦ ¦ CallerEJBHome.class
¦ ¦ CallerEJBRemote.class
¦ ¦
¦ +---servlets
¦ TestServlet.class
¦
+---ear
¦ +---META-INF
¦ application.xml
¦
+---jar
¦ ¦ applicationContext.xml
¦ ¦ beanRefContext.xml
¦ ¦
¦ +---META-INF
¦ ¦ ejb-jar.xml
¦ ¦ weblogic-ejb-jar.xml
¦ ¦
¦ +---pugtex
¦ ¦ GreeterBean.class
¦ ¦ IGreet.class
¦ ¦
¦ +---ejb
¦ ¦ CallerEJB.class
¦ ¦ CallerEJBHome.class
¦ ¦ CallerEJBRemote.class
¦ ¦
¦ +---servlets
¦ SessionTestServlet.class
¦
+---war
+---WEB-INF
¦ web.xml
¦
+---classes
¦ +---pugtex
¦ ¦ GreeterBean.class
¦ ¦ IGreet.class
¦ ¦
¦ +---ejb
¦ ¦ CallerEJB.class
¦ ¦ CallerEJBHome.class
¦ ¦ CallerEJBRemote.class
¦ ¦
¦ +---servlets
¦ TestServlet.class
¦
+---lib
commons-logging-1.0.4.jar (*)
spring.jar (*)
If you find this confusing, please note that the files mentioned below a directory name belong to that directory.
Also, ensure that your jars (*) are in the server classpath.
What follows is the code to access your (hopefully :) ) deployed EJB. I got this text from [2], and used it for Weblogic. Visit [2] for the original JBoss configuration files.
Okay, the client config file looks like this:
BeanConfig.xml
<?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="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<value>
java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory
java.naming.provider.url=t3://localhost:7001
</value>
</property>
</bean>
<bean id="greeterService" class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
<property name="jndiName">
<value>ejb/CallerEJB</value>
</property>
<property name="resourceRef">
<value>false</value>
</property>
<property name="jndiTemplate">
<ref local="jndiTemplate"/>
</property>
<property name="businessInterface">
<value>pugtex.IGreet</value>
</property>
</bean>
</beans>
And finally, the client:
/**
* CallerEJB.java
*/
package pugtex.ejb;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import pugtex.IGreeter;
public class Executor {
public static void main(String[] arg) {
try {
Resource beanConfigFile = new FileSystemResource("BeanConfig.xml");
BeanFactory factory = (BeanFactory) new XmlBeanFactory(beanConfigFile);
ISavvionProcessService remoteBean =(ISavvionProcessService)factory.getBean("greeterService");
String greeting = remoteBean.greetMe("Gromit");
System.out.println(greeting);
} catch (Exception e) {
e.printStackTrace();
}
}
}
And we're done. Woohoo!
Okay, I lifted the code from the following locations (aka References) :D :
[1]
http://www.springframework.org/docs/reference/ejb.html
[2]
http://forum.springframework.org/showthread.php?t=18988
Do go through [1] to know why we've done what we've done; and do let me know if you could figure out a better approach.