Stateless Session EJBs

I've written before that EJBs are really nothing new. Yes, you have to adhere to a published interface but we do that all the time by implementing Serializable or Comparable. I've put together a number of stateless session beans in the last year which interface with a back-end database. Why the customer wanted stateless session rather than entity beans is not for me to question; I'm sure they had their justifications. So how do you go about creating stateless session beans?

There are only three files which need to be generated:

I've written a number of tools which permit me to generate code from a variety of sources. The base code was written separately and tested extensively against multiple databases. The actual input to the generator is a flat file, tab-separated, containing information about the database table. I also have a tool which takes an SQL table create file and generates the flat file. For the sake of example, let's take a really simple table and follow the process through.

Here's the SQL for creating a table with only two fields, one of which is the primary key:

CREATE TABLE authentication {
    username VARCHAR(32) NOT NULL,
    password VARCHAR(16),
    PRIMARY KEY( username )
};
We have to support the typical database operations such as insert, delete and select. Since these are going to be stateless beans, we cannot retain information between method calls; all necessary information must be provided in each call. So here are the prototypes of the methods we'll provide:
public void create( String username, String password ) throws RemoteException, SQLException;
public void delete( String username ) throws RemoteException, SQLException;
public String list() throws RemoteException, SQLException;
public void getPassword( String username ) throws RemoteException, SQLException;
public void setPassword( String username, String password ) throws RemoteException, SQLException;
All methods have to be defined as throwing the RemoteException and, since I'm using JDBC, I also specify that an SQLException can also be thrown. And that's the gist of the remote file! Add the imports and the appropriate framework and you end up with the completed file:
import	java.sql.SQLException;
import	java.rmi.RemoteException;
import	javax.ejb.EJBObject;

public interface Authentication extends EJBObject {
	// method prototypes from above
}
The Home interface is even simpler:
import	javax.ejb.EJBHome;
import	javax.ejb.CreateException;
import	java.rmi.RemoteException;

public interface AuthenticationHome extends EJBHome {
	public Authentication create() throws RemoteException, CreateException;
}
Since we're creating stateless session beans, there is no argument to the constructor. Note that the create method returns an object which implements the remote interface, i.e. Authentication rather than AuthenticationHome. Now all we have to do is write the bean methods themselves. Here is some sanitized (no JDBC included) code:
import	java.sql.Connection;
import	java.sql.PreparedStatement;
import	java.sql.Statement;
import	java.sql.ResultSet;
import	java.sql.SQLException;
import	javax.ejb.SessionBean;
import	javax.ejb.SessionContext;

public class AuthenticationBean implements SessionBean {
	public SessionContext	context = null;

	public void create( String username, String password ) throws SQLException {
		// insert a new record into the database
	}

	public void delete( String username ) throws SQLException {
		// delete a record from the database
	}

	public String list() throws SQLException {
		// return a space-separated list of primary keys
	}

	public String getPassword( String username ) throws SQLException {
		// select a record from the database and return password field
	}

	public void setPassword( String username, String password ) throws SQLException {
		// update a record in the database with a new password
	}

	public void setSessionContext( SessionContext context ) {
		this.context = context;
	}

	public void ejbCreate() {
	}

	public void ejbRemove() {
	}

	public void ejbActivate() {
	}

	public void ejbPassivate() {
	}
}
Those last five methods are required but don't have to do anything in stateless session beans. You can put class initialization code in the ejbCreate method if you desire, perhaps expensive operations such as name lookups. You could even obtain a database connection, although I prefer to do that in the individual methods and then explicitly release it before returning. Just a personal preference.

Now that we've got the code written, how do we tie it all together? My tools automatically generate the primary file required by J2EE servers, namely ejb-jar.xml. This file must reside in the META-INF directory of the generated jar file. Here's what the file would look like in our example:

<?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 id="ejb-jar_ID">
   <enterprise-beans>
      <session id="Session_1">
         <ejb-name>Authentication</ejb-name>
         <home>AuthenticationHome</home>
         <remote>Authentication</remote>
         <ejb-class>AuthenticationBean</ejb-class>
         <session-type>Stateless</session-type>
         <transaction-type>Container</transaction-type>
      </session>
   </enterprise-beans>
   <assembly-descriptor>
   </assembly-descriptor>
</ejb-jar>
Now it should be noted that this file is typically generated by the application builder component of the J2EE server. I examined the files generated by my servers in order to determine the appropriate format. It's also why I use the IBM mechanism of naming the session beans sequentually in the form Session_#. So the ejb-jar.xml file ties the three files together and gives them a name. But that's only one part of the puzzle.

In order to deploy applications, more information is required, in particular the mapping to JNDI (Java Naming and Directory Interface.) Again, the tools supplied with the application servers can walk you through creating the appropriate mappings but I found that the manual method is just too time-consuming. So I once again resorted to looking at the files created by WebSphere and WebLogic in order to be able to generate them automatically.

IBM requires two extra XML files: ibm-ejb-jar-bnd.xmi (the bindings file) and ibm-ejb-jar-ext.xmi (extensions.) Here's the ibm-ejb-jar-bnd.xmi file for our example:

<ejbbnd:EJBJarBinding xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ejbbnd="ejbbnd.xmi" xmlns:ejb="ejb.xmi" xmi:id="ejb-jar_ID_Bnd">
  <ejbJar href="META-INF/ejb-jar.xml#ejb-jar_ID"/>
  <ejbBindings xmi:id="Session_1_Bnd" jndiName="ejbs/Authentication">
    <enterpriseBean xmi:type="ejb:Session" href="META-INF/ejb-jar.xml#Session_1"/>
  </ejbBindings>
</ejbbnd:EJBJarBinding>
And here's the ibm-ejb-jar-ext.xmi file:
<ejbext:EJBJarExtension xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ejbext="ejbext.xmi" xmlns:ejb="ejb.xmi" xmi:id="ejb-jar_ID_Ext">
  <ejbJar href="META-INF/ejb-jar.xml#ejb-jar_ID"/>
  <ejbExtensions xmi:type="ejbext:SessionExtension" xmi:id="Session_1_Ext" timeout="600">
    <enterpriseBean xmi:type="ejb:Session" href="META-INF/ejb-jar.xml#Session_1"/>
    <structure xmi:id="BeanStructure_1" inheritenceRoot="false"/>
    <beanCache xmi:id="BeanCache_1" activateAt="ONCE"/>
    <internationalization xmi:id="BeanInternationalization_1" invocationLocale="CALLER"/>
    <localTran xmi:id="LocalTran_1" boundary="BEAN_METHOD" unresolvedAction="ROLLBACK"/>
  </ejbExtensions>
</ejbext:EJBJarExtension>
It's not really necessary for me to explain these files: the meaning should be fairly self-evident. The important element is the ejbBindings and the jndiName parameter. Of course, WebLogic uses a completely different format, with all necessary deployment information stored in a file named weblogic-ejb-jar.xml. Here are the contents for our example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE weblogic-ejb-jar PUBLIC "-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN"
"http://www.bea.com/servers/wls510/dtd/weblogic-ejb-jar.dtd">
<weblogic-ejb-jar>
   <weblogic-enterprise-bean>
      <ejb-name>Authentication</ejb-name>
      <caching-descriptor>
         <max-beans-in-free-pool>200</max-beans-in-free-pool>
         <initial-beans-in-free-pool>10</initial-beans-in-free-pool>
      </caching-descriptor>
      <jndi-name>ejbs/Authentication</jndi-name>
   </weblogic-enterprise-bean>
</weblogic-ejb-jar>
Again, nothing particularly surprising here, just some deployment parameters and the all-important jndi-name element. Now it's just a matter of actually deploying the beans, a process which varies by server. Once you find where your server places the files you can usually just copy them over directly, especially when you're changing things frequently while developing your applications. I generally restart the server once I've copied new releases into the appropriate place. While WebSphere is good at recognizing when a JSP has been changed and will recompile it, I'm not so sure that it keeps track of timestamps on jar files.

So now that the EJBs are deployed you will want to do some local testing. No sense having your JSP developers trying to use your beans unless you know that they're functioning perfectly. The only difference in the code used for testing versus the beans themselves is in the lookups. The beans need only instantiate a new InitialContext and cast the results from the lookup method as necessary. A client has to do a bit more work, including using PortableRemoteObject.narrow to convert the result to the appropriate class. Here's the bean code:

InitialContext	ic = new InitialContext();
AuthenticationHome	home = (AuthenticationHome) ic.lookup( "ejbs/Authentication" );
Authentication	bean = home.create();
Here's the equivalent client code:
String	nsFactory = null;
String	urlString = null;

// if using WebLogic
nsName = "weblogic.jndi.WLInitialContextFactory";
urlString = "t3://localhost:7001";
// if using WebSphere
nsName = "com.ibm.websphere.naming.WsnInitialContextFactory";
urlString = "iiop://localhost";

Properties props = new Properties();
props.put( Context.INITIAL_CONTEXT_FACTORY, nsFactory );
props.put( Context.PROVIDER_URL, urlString );
Context context = new InitialContext( props );

Object obj = context.lookup( "ejbs/Authentication" );
AuthenticationHome home = (AuthenticationHome)
  javax.rmi.PortableRemoteObject.narrow( obj,
  AuthenticationHome.class );
Authentication bean = home.create();
It's interesting to note that WebLogic uses a proprietary protocol for access to JNDI (t3) while IBM uses iiop (Internet Inter-ORB Protocol.) Then again, BEA was in the application server business very early on. In either case, we create the InitialContext using the appropriate parameters and use PortableRemoteObject.narrow to convert the object returned by the lookup.

Now it's just a matter of making the method calls. To change the password of user 'joeblow' to 'password', for example, we'd only need the following line:

bean.setPassword( "joeblow", "password" );
As you can see, accessing EJBs from the client is very straightforward. See my article on JSPs for some demonstration code which accesses EJBs.

Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 by Phil Selby