Chapter 4.
Creating a Stateful Session BeanThis chapter covers how to create a stateful session EJB component. Unlike stateless beans, stateful bean instances are associated with a particular client session and can therefore maintain information useful during the lifetime of that session. A classic and familiar example of this in a web e-commerce context is that of a shopping cart.
We'll try something a little less ambitious for the time being! This bean is similar to the StoreAccess Bean created in the previous chapter except that it will store the userID of the client, after authentication is successful.
Note : This bean will not be used further in tutorial, as this chapter demonstrates how stateful beans are created.
Tasks :Create a Stateful Session Bean named StoreAccessState.
Add a business method in the bean named loginUser with the signature
public String loginUser (String username, String password)
Add a call back method ejbCreate with the signature
public void ejbCreate (String userID)
Generate the DAO interface.
Access the DAO named StoreAccesDAOImpl under package au.com.tusc.dao, created and implemented in the previous chapter. This DAO has a method named loginUser, called by loginUser in the bean with the signature
public String loginUser (String username, String password)
Deploy the StoreAccessState bean.
Create your test client named SessionStateClient under package au.com.tusc.client.
Run your client and test the bean.
Create Stateless Bean :
Go To Package Explorer > Expand Mystore (project) node > select src, right click and a menu will pop up.
On pop up menu > New > Lomboz EJB Creation Wizard.
Enter the package name au.com.tusc.sessionState, bean name StoreAccessState and select bean type as stateful > Finish.
This will create package named au.com.tusc.sessionState under src and StoreAccessStateBean under that package as shown below.
As we can see from the figure above it has created a class level tag @ejb.bean, which has assigned the bean type, name and its JNDI name which will be generated in the Home interface. This tag will also generate deployment descriptors in ejb-jar.xml and jboss.xml file as well once you generate your EJB classes, which is covered later on in this chapter.
Note: It will generate the bean name, jndi-name and type of bean in file. Also, the name of file is appended with word 'Bean' as you gave the name of the bean as StoreAccess only. So be careful with your naming convention, as you only need specify the bean name in the wizard (ie. without a 'Bean' suffix as the wizard will append that for you).
Create DAO Interface :
Since we are going to use a DAO to access the database for this Stateful Bean, we will use the StoreAccesDAOImpl class which was created and implemented in the previous chapter. This will provide the implementation for the generated DAO interface.
Now go to your Bean class and declare this tag at class level (i:e; at top), as described below to generate the DAO interface.
@ejb.dao class="au.com.tusc.sessionState.StoreAccessStateDAO"
impl-class="au.com.tusc.dao.StoreAccessDAOImpl"
Expand the StoreAccessStateBean node under Package Explorer. Right click and a pop up menu will appear.
On that menu go to Lomboz J2EE > Add EJB to module. Select EJB MyStoreMgr > OK.
Note : This step is covered in previous chapter.
Expand MyStoreMgr node under MyStore Project in Package Explorer. Right click and a pop up menu will appear.
Go to Lomboz J2EE > Generate EJB Classes as shown in figure at right.
EJB interfaces and helper classes are generated under 'ejbsrc' directory as shown on the right.
Seven files are generated.
StoreAccessState is our remote object interface.
StoreAccessLocal is our local object interface.
StoreAccessStateSession extends our bean class named StoreAccesStateBean.
StoreAccessStateHome is our remote home interface.
StoreAcessStateLocalHome is our local home interface.
StoreAccessStateUtil is a helper class which has methods for accessing Home and LocalHome interface along with generating GUID.
StoreAccesStateDAO is the DAO interface, which be implemented by StoreAccessDAOImpl under au.com.tusc.dao.
StoreAccessStateDAO is generated by this tag declared in StoreAccesStateBean as shown below. If you don't declare this tag in that file it won't generate this interface.
@ejb.dao class=au.com.tusc.sessionState.StoreAccessStateDAO
impl-class=au.com.tusc.dao.StoreAccessDAOImpl
Other files of interest which are generated are ejb-jar.xml' and jboss.xml' under MyStoreMgr/META-INF.
As shown in figure at right, a few descriptors are generated in the 'ejb-jar.xml' file.
These descriptors are generated by the following tag declared in the StoreAccessBean file, which was generated by Lomboz.
@ejb.bean name =StoreAccessSate
jndi-name=StoreAccessStateBean
type=Stateful
This tag also generates the following descriptors in jboss.xml as shown below.
Add Business Method :The next step is to add a business method to the bean.
Go to StoreAccesStateBean node > right click > select New on pop up menu.
Select Lomboz EJB Method Wizard.
Note : This step is covered in the previous chapter.
Add a business method with the following signature..
public String loginUser (String username, String password).
Select Method Type as Business and Interface as Remote as shown below..
This wizard generates a loginUser method in our bean class, with the method level tag' @ejb.interface' shown below.
This tag is responsible for generating this method in the Remote Interface (in this case it is StoreAccessSate which will be created once you generate your classes).
Now, this business method needs to invoke a method on the DAO, which encapsulates access to the database.
So, add another tag on this method so that a method with this signature is generated in the DAO interface. We can then implement that method in our DAOImpl class so that our business method can get the desired result.
@dao.call name=loginUser
Add this tag at the method level as shown on the right.
Now generate your EJB classes again as shown in the steps earlier.
OK, for reference these are the steps you have to follow:
Expand 'MyStoreMgr' node under 'MyStore' Project in Package Explorer.
Right click > pop up menu.
Go to Lomboz J2EE > Generate EJB Classes.
Add Callback Methods :In contrast to the Stateless bean, the ejbCreate method will have an argument. This will be used to initialize a persistent field in the bean.
Add that field, and add accessor and mutator methods to access that field as shown below.
public abstract class StoreAccessStateBean implements SessionBean {
private String userID;
/**
* @ejb.interface-method
* view-type="remote"
*/
public void setUserID(String userID) {
this.userID = userID;
}
/**
* @ejb.interface-method
* view-type="remote"
*/
public String getUserID() {
return userID;
}
Now add the ejbCreate method with the signature
public void ejbCreate (String userID)
Assign userID to the persistent field userID we created above as shown.
Now, the other two callback methods required to complete this bean are
1. setSessionContext.
2. unsetSessionContext.
Add a field to store sessionContext.
protected SessionContext ctx;
Add a method setSessionContext with sessionContext as its parameter and assign that to the sessionContext variable as shown below.
Similarly, add a method unsetSessionContext which sets the context variable to null as shown above.
Note : The StoreAccessStateSession class inherits the StoreAccessStateBean abstract class and implements SessionBean, which will override all methods of the interface SessionBean. So, after completing the methods in your bean class, generate your EJB classes again. The SessionContext methods will be overridden, as discussed in previous chapter.
Generate EJB classes.
Note : The steps to generate the EJB classes are discussed above and in the previous chapter.
Implement DAO Interface :We don't have to implement the DAO interface as we are using the StoreAccessDAOImpl class created and implemented in the previous chapter.
So go into the StoreAccessDAOImpl class and change the class declaration statement, so that it implements StoreAccessStateDAO interface, as shown below in code snippet.
Now, all methods are implemented, and only deployment descriptors remain to be done.
Deploy Bean :
In order to deploy our bean we have to declare a few tags in StoreAccessStateBean class as shown below.
Add the tag shown below in StoreAccessStateBean at class level (at the top).
@ejb.resource-ref res-ref-name="jdbc/DefaultDS"
res-type="javax.sql.Datasource"
res-auth="Container"
This tag will generate deployment descriptors in ejb-jar.xml, as the bean has to know which datasource you are going to connect, what is its type, etc. This will generate these descriptors as shown below.
Add this tag shown below in StoreAccessStateBean at class level (at top).
@jboss.resource-ref res-ref-name="jdbc/DefaultDS" jndi-name="java:/DefaultDS"
This tag will generate deployment descriptors in jboss.xml, as the application server has to know with what jndi-name the datasource has been registered with. This will generate these descriptors as shown below.
Add this tag shown below in StoreAccessStateBean at class level (at top).
@jboss.container-configuration name ="Standard Stateful SessionBean"
This tag will generate deployment descriptors in jboss.xml, as the application server has to know which configuartion is to be read for Stateful Bean from standardjboss.xml under /opt/jboss/jboss-3.2.1/server/all/conf/. This will generate these descriptors in jboss.xml as shown below.
<session>
<ejb-name>StoreAccessState</ejb-name>
<jndi-name>StoreAccessStateBean</jndi-name>
<configuration-name>Standard Stateful SessionBean</configuration-name>
<resource-ref>
<res-ref-name>jdbc/DefaultDS</res-ref-name>
<jndi-name>java:/DefaultDS</jndi-name>
</resource-ref>
</session>
Note : This configuration is for container in Jboss to read for Stateful Session Bean. Jboss comes with timeout of 60 mins for stateful session bean. Change the <max-bean-life> attribute to 120 secs (i:e2 mins) and restart your server as shown below in code snippet from /opt/jboss/jboss-3.2.1/servaer/all/conf/standardjboss.xml.
<container-configuration>
<container-name>Standard Stateful SessionBean</container-name>
<call-logging>false</call-logging>
<invoker-proxy-binding-name>stateful-rmi-invoker</invoker-proxy-binding-name>
<container-interceptors>
-------------------------------------------------------------------
----------------------------------------------------------------------
---------------------------------------------------------------------
<container-cache-conf>
<cache-policy>org.jboss.ejb.plugins.LRUStatefulContextCachePolicy</cache-policy>
<cache-policy-conf>
<min-capacity>50</min-capacity>
<max-capacity>1000000</max-capacity>
<!--remover-period>1800</remover-period-->
<remover-period>120</remover-period>
<max-bean-life>120</max-bean-life>
<!-- By default Jboss comes withh 60 mins time out for stateful bean,change it
to 120 secs (2 mins) and restart the server to read this config.
-->
<!--max-bean-life>1800</max-bean-life-->
<!-- 1800 secs = 30 mins -->
<overager-period>300</overager-period>
<max-bean-age>600</max-bean-age>
<resizer-period>400</resizer-period>
<max-cache-miss-period>60</max-cache-miss-period>
<min-cache-miss-period>1</min-cache-miss-period>
<cache-load-factor>0.75</cache-load-factor>
</cache-policy-conf>
</container-cache-conf>
----------------------------------------------------------------------
---------------------------------------------------------------------
Now, everything is complete, and it's time to deploy the bean.
First, regenerate your EJB classes as shown in the steps above for the final time.
Note : We have regenerated the classes again and again in order to explain every step and its result. Once you are familiar with these steps you will only need to generate your classes a few times as you go. Either way, it doesn't affect your implementation, so you can (re)generate your classes however you prefer to work.
Go to Lomboz J2EE View > expand node MyStore > expand MyStoreMgr > select 'Jboss 3.2.1 ALL' .
Right click > select 'Debug Sever' on the pop up menu.
Go to MyStoreMgr node in LombozJ2EE view > right click > select Deploy on the pop up menu.
Note : These steps are covered in the previous chapter.
And now wait for your deployment result.
If everything goes fine, you will have this message under your console as shown below.
So, now your bean is deployed successfully, let's create a test client, which will invoke the loginUser method on StoreAccessStateBean.
Create your Test Client :
Go to Project MyStore node > select src node > right click.
Select New on pop up menu > select Lomboz EJB Test Client Wizard as shown below.
Select package name au.com.tusc.client, with name SessionStateClient and select Ejb Home as au.com.tusc.sessionState.StoreAccessStateHome and Ejb Interface as au.com.tusc.sessionState.StoreAccessState as shown below.
This will generate the necessary helper methods for you in your SessionStateClient class and you simply have to invoke the loginUser method on bean.
So add the lines of code shown below in the testBean method.
public void testBean() {
String userID = null;
try {
au.com.tusc.sessionState.StoreAccessState myBean =
getHome().create(userID);
//--------------------------------------
//This is the place you make your calls.
//System.out.println(myBean.callYourMethod());
System.out.println("Request from client : ");
userID = myBean.loginUser("ANDY","PASSWD");
System.out.println("Reply from Server: Your userid is " + userID);
myBean.setUserID(userID);
System.out.println("Going to Sleep for 1 min ......................... " );
Thread.sleep(60000); // sleep for 1 minute.
System.out.println("Reply from bean after 1min " + myBean.getUserID());
System.out.println("Again going to Sleep for 3 min ......................... " );
Thread.sleep(180000); // sleep for 3 minute.
System.out.println("Resuming after 3 mins ......................... " );
//Next statement will not be executed as bean will timeout on the server side
//resulting in exception.
System.out.println("Reply from bean after 2mins " + myBean.getUserID());
}catch (InterruptedException e) {
}catch (NoSuchObjectException e) {
System.out.println("No Such Object Exception caught ");
} catch (RemoteException e) {
System.out.println("Remote Exception caught ");
} catch (CreateException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
} catch (EJBException e) {
System.out.println("EJB Exception caught ");
}
}
public static void main(String[] args) {
SessionStateClient test = new SessionStateClient();
test.testBean();
}
Test your Client :Now, in order to test your client, Select SessionStateClient node > Go at top level menu and select the icon with the 'Man running'.
On that select 'Run as' > select 'Java Application'.
Now under your console, if you get your reply for 'ANDY' as 'U2', event after 1 min but after 3 mins NoSuchObjectException is caught then your stateful bean is successful as shown below.
Note : So our Stateful Session Bean is deployed and tested successfully. This particular Bean is not used again in this tutorial.