I recently have add the opportunity to migrate an historical JEE web application from JBoss 4 to JBoss 7.1. One of the more complicated point I faced was the migration of the EJB2 components. The old xml configuration was not correct anymore and it was very difficult to find documentation, tutorials or concrete examples, even on JBoss forums. Of course this is understandable because this old version of EJB is not used anymore (hopefully!), but it still exists in several historical web applications.
The goal of this new thread is not to explain all the EJB 2 mechanisms but to share what I really missed during the migration, that is a working example of EJB2 sessions and EJB2 entities on JBoss 7.1. Since JEE 6, it is possible to package the EJB directly in a war archive, so this example will be based on that.
The source code is available
here.
EJB 2 session java code
I will create an EJB 2 session called HelloWorld with a simple method returning a String object. Three classes must be created to do that :
package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface HelloWorldEJB extends EJBObject {
public String helloWorld() throws RemoteException;
}
package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloWorldEJBBean implements SessionBean {
private static final long serialVersionUID = 1L;
public String helloWorld() throws RemoteException {
return "Hello world, I am an EJB2 session";
}
public void ejbActivate() throws EJBException, RemoteException {}
public void ejbPassivate() throws EJBException, RemoteException {}
public void ejbRemove() throws EJBException, RemoteException {}
public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException {}
}
package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface HelloWorldEJBHome extends EJBHome {
public HelloWorldEJB create() throws RemoteException, CreateException;
}
EJB 2 entity java code
For the EJB 2 entity example, I will create an object called Member with two fields : an id and a login. Three java objects are also needed to do that :
package com.jsebfranck.jboss.ejb2.entity;
import javax.ejb.EJBLocalObject;
public interface Member extends EJBLocalObject {
public Long getId();
public String getLogin();
}
package com.jsebfranck.jboss.ejb2.entity;
import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
public abstract class MemberBean implements EntityBean {
private static final long serialVersionUID = 1L;
private transient EntityContext ctx;
public MemberBean() {
}
public Long ejbCreate(Long id, String login) throws CreateException {
setId(id);
setLogin(login);
return id;
}
public void ejbPostCreate(Long id, String login) {
}
public abstract Long getId();
public abstract void setId(Long id);
public abstract String getLogin();
public abstract void setLogin(String login);
public void setEntityContext(EntityContext ctx) {
this.ctx = ctx;
}
public void unsetEntityContext() {
this.ctx = null;
}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbLoad() {}
public void ejbStore() {}
public void ejbRemove() {}
}
package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface HelloWorldEJBHome extends EJBHome {
public HelloWorldEJB create() throws RemoteException, CreateException;
}
XML configuration
Now we have to declare the EJB entity and the EJB session in the xml configuration. A first file is needed for that, ejb-jar.xml. This file must be put in the /WEB-INF folder of the war archive.
<ejb-jar version="3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
<enterprise-beans>
<session>
<ejb-name>HelloWorldEJB</ejb-name>
<home>com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome</home>
<remote>com.jsebfranck.jboss.ejb2.session.HelloWorldEJB</remote>
<ejb-class>com.jsebfranck.jboss.ejb2.session.HelloWorldEJBBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
<entity>
<ejb-name>MemberEJB</ejb-name>
<local-home>com.jsebfranck.jboss.ejb2.entity.MemberHome</local-home>
<local>com.jsebfranck.jboss.ejb2.entity.Member</local>
<ejb-class>com.jsebfranck.jboss.ejb2.entity.MemberBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Long</prim-key-class>
<reentrant>false</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>member</abstract-schema-name>
<cmp-field>
<field-name>id</field-name>
</cmp-field>
<cmp-field>
<field-name>login</field-name>
</cmp-field>
<primkey-field>id</primkey-field>
</entity>
</enterprise-beans>
</ejb-jar>
Now let's configure the mapping of the EJB entity. This is done in the jbosscmp-jdbc.xml file which must be put in the META-INF folder of the war archive.
<jbosscmp-jdbc>
<defaults>
<datasource>java:jboss/datasources/hsqldbDS</datasource>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>MemberEJB</ejb-name>
<row-locking>false</row-locking>
<table-name>simple</table-name>
<cmp-field>
<field-name>id</field-name>
<column-name>id</column-name>
</cmp-field>
<cmp-field>
<field-name>login</field-name>
<column-name>login</column-name>
</cmp-field>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
JBoss configuration
As you noticed in the jbosscmp-jdbc.xml file, a datasource is required for the entity. This datasource is configured directly in the jboss configuration. As I launch JBoss 7.1 in a standalone mode, I put the following configuration in the $JBOSS_HOME/standalone/configuration/standalone-full.xml file.
In this example, a use a hsql database :
<subsystem xmlns="urn:jboss:domain:datasources:1.0">
<datasources>
<datasource enabled="true" jndi-name="java:jboss/datasources/hsqldbDS" jta="true" pool-name="hsqldbDS" use-ccm="true" use-java-context="true">
<connection-url>jdbc:hsqldb:file:/Users/jsebfranck/Documents/database/standaloneHsqldb</connection-url>
<driver>hsqldb</driver>
<pool>
<prefill>false</prefill>
<use-strict-min>false</use-strict-min>
<flush-strategy>FailingConnectionOnly</flush-strategy>
</pool>
<security>
<user-name>sa</user-name>
</security>
</datasource>
Deployment in jboss
When you deploy the web application on jboss (
standalone.sh --server-config=standalone-full.xml), you can see the JNDI names of both EJB in the logs. This will help us to do our lookups in the client.
19:07:56,689 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015876: Starting deployment of "WarWithEJB2.war"
19:07:57,069 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named MemberEJB in deployment unit deployment "WarWithEJB2.war" are as follows:
java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
java:app/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
java:module/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
java:app/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
java:module/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
19:07:57,073 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named HelloWorldEJB in deployment unit deployment "WarWithEJB2.war" are as follows:
java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:app/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:module/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:jboss/exported/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
java:app/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
java:module/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
java:jboss/exported/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
Simple servlet client
We are now able to test our EJBs. The following code calls the helloWorld method of the EJB session, then it creates a new line in the Member table.
package com.jsebfranck.jboss.servlet;
import java.io.IOException;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jsebfranck.jboss.ejb2.entity.Member;
import com.jsebfranck.jboss.ejb2.entity.MemberHome;
import com.jsebfranck.jboss.ejb2.session.HelloWorldEJB;
import com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome;
@WebServlet("/Ejb2Servlet")
public class Ejb2Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// EJB 2 session
HelloWorldEJBHome helloWorldEJBHome = (HelloWorldEJBHome) new InitialContext().lookup("java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome");
HelloWorldEJB helloWorldEjb = helloWorldEJBHome.create();
response.getWriter().println("EJB session test : " + helloWorldEjb.helloWorld());
// EJB 2 entity
MemberHome memberHome = (MemberHome) new InitialContext().lookup("java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome");
Member member = memberHome.create(25L, "jsebfranck");
response.getWriter().println("EJB entity test : " + member.getId() + " - " + member.getLogin());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
And we have the prove that this example is working :-)
Conclusion
I hope this example helped you to migrate your EJB2 to a newer version of your application server. Don't hesitate to contact me if you need further details to advance in your migration.