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 :- The EJB interface
package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface HelloWorldEJB extends EJBObject {
public String helloWorld() throws RemoteException;
}
- The implementation
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 {}
}
- And the EJB Home
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 :
- The EJBLocalObject
package com.jsebfranck.jboss.ejb2.entity;
import javax.ejb.EJBLocalObject;
public interface Member extends EJBLocalObject {
public Long getId();
public String getLogin();
}
- The EntityBean
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() {}
}
- And the EJBHome
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 :-)