Planning, Development and Deployment of a Portal Gear Using
Dynamo 5.5
Part 2 - EJBs and Business Logic
Part 1 is here : Part
3 is here
July 18, 2001 : Version 1.0
Introduction
This document describes the process of adding business logic to our gear. In
this case a stateful session bean which actually calculates the lotto numbers
and retains them for the user for the duration of the session. This is done to
ensure that the users' lotto numbers don't change each time they view the page.
This section assumes the setup as described in part 1.
Contents
Problem Statement
Design Overview
Implementation
Testing the New EJB Enabled Gear
Where Next?
Add a business logic component which calculates sets of lotto numbers based
on the following parameters:
- numberCount - the number of numbers in each set
- minNumber - the lowest integer in the random space (inclusive)
- maxNumber - the highest integer in the random space (inclusive)
- setCount - the number of sets to generate
The component should maintain the same set of results for the duration of the
session.
The component should be able to regenerate a new set of sets on demand and
discard any existing sets.
Lotto numbers are random batches of 'numberCount' non-repeating numbers
between 'minNumber' and 'maxNumber'. To allow people to play multiple sets of
numbers we generate batches of 'batchCount'. Each batch is should also be
different from each other batch, hence the need to group the generation of
batches together.
The logic and lotto numbers will be encapsulated in a stateful session bean
called /lotto/Generator.
The bean will have the following interface
// Properties
void setNumberCount(int n);
int getNumberCount();
void setMinNumber(int n);
int getMinNumber();
void setMaxNumber(int n);
int getMaxNumber();
void setBatchCount(int n);
int getBatchCount();
boolean verifyConfiguration(); // returns true if the current configuration is valid
void regenerateNumberSets(); // Replaces any existing numbers with new ones.
java.util.Iterator getNumbers(); // An iterator over int[batchCount][numberCount]
// Generates the numbers if we don't have any yet
If you are using an IDE now is the time to set up your development project
for the EJBs. If you are not using an IDE. You should still follow exactly
the same the file layout, at least for now.
- Create a folder called 'src' underneath the LottoGear folder for your java
files:
C:/ATG/Dynamo5.5/PortalTraining/j2ee-apps/LottoGear/src
- Create a folder called 'ejb_jar' underneath the LottoGear folder for your
class files:
C:/ATG/Dynamo5.5/PortalTraining/j2ee-apps/LottoGear/ejb_jar
- Whenever you compile your java code ensure that the class files are placed
in the ejb_jar folder using the -d javac directive or using your IDE. After
you have set up your classpath to include the j2ee classes, the compiler
command line would look something like this.
C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear>javac -d ejbs_jar src\lotto\*.java
- To set up your environment for command line development the easiest thing
to do is to write a batch file like the following and run it in a fresh
command window:
c:
cd c:\atg\Dynamo5.5\home
set DYNAMO_HOME=c:\atg\Dynamo5.5\home
bin\DynamoEnv.bat
rem Optionally go directly to your project directory
cd C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear
In this example everything goes into the lotto package. I recommend always
using packages when you are developing code. We need three java files:
| lotto\Generator.java |
The Remote interface, pretty much defined in the design
stage |
| lotto\GeneratorHome.java |
The Home interface used to create our beans as required |
| lotto\GeneratorBean.java |
The actual implementation of our bean, (where the math goes) |
- Create a file called lotto/Generator.java under your src folder, this is
the remote interface for our Generator bean, notice that each of our methods
declares throws RemoteException
package lotto;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Generator extends EJBObject
{
// Properties
public void setNumberCount(int n) throws RemoteException;
public int getNumberCount() throws RemoteException;
public void setMinNumber(int n) throws RemoteException;
public int getMinNumber() throws RemoteException;
public void setMaxNumber(int n) throws RemoteException;
public int getMaxNumber() throws RemoteException;
public void setBatchCount(int n) throws RemoteException;
public int getBatchCount() throws RemoteException;
public boolean verifyConfiguration() throws RemoteException; // returns true if the current configuration is valid
public void regenerateNumbers() throws RemoteException; // Replaces any existing numbers with new ones.
public int[][] getNumbers() throws RemoteException; // int[batchCount][numberCount]
// Generates the numbers if we don't have any yet
}
- Create a file called lotto/GeneratorHome.java under your src folder, this
is the home interface for our Generator bean:
package lotto;
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface GeneratorHome extends EJBHome
{
Generator create() throws RemoteException, CreateException;
}
- Create a file called lotto/GeneratorBean.java under your src folder, this
is the implementation of our Generator bean:
package lotto;
import javax.ejb.*;
import java.rmi.RemoteException;
public class GeneratorBean implements javax.ejb.SessionBean
{
public GeneratorBean(){ }
///////////////////////////////////////////////////////
// Session Bean Implementation
public void ejbCreate() { printMessage("ejbCreate"); }
public void ejbActivate() { printMessage("ejbActivate"); }
public void ejbPassivate() { printMessage("ejbPassivate"); }
public void ejbRemove() { printMessage("ejbRemove"); }
javax.ejb.SessionContext mCtx;
javax.ejb.SessionContext getSessionContext(){ return mCtx; }
public void setSessionContext(javax.ejb.SessionContext ctx)
throws javax.ejb.EJBException,
java.rmi.RemoteException
{
printMessage("setSessionContext(" + ctx + ")");
mCtx = ctx;
}
///////////////////////////////////////////////////////
// Properties
int mNumberCount;
int mMinNumber;
int mMaxNumber;
int mBatchCount;
int mNumbers[][];
java.util.Random mRandom;
public void setNumberCount(int n){ mNumberCount = n; }
public int getNumberCount(){ return mNumberCount; }
public void setMinNumber(int n){ mMinNumber = n; }
public int getMinNumber(){ return mMinNumber; }
public void setMaxNumber(int n){ mMaxNumber = n; }
public int getMaxNumber(){ return mMaxNumber; }
public void setBatchCount(int n){ mBatchCount = n; }
public int getBatchCount(){ return mBatchCount; }
///////////////////////////////////////////////////////
// High Protein Bean Meat
// Replaces any existing numbers with new ones.
public void regenerateNumbers() throws java.rmi.RemoteException
{
mRandom = new java.util.Random(System.currentTimeMillis());
printMessage("regenerateNumbers");
mNumbers = null;
if(verifyConfiguration())
{
mNumbers = new int[mBatchCount][mNumberCount];
printMessage("mBatchCount= " + mBatchCount);
printMessage("mNumbers.length = " + mNumbers.length);
for(int i=0; i < mNumbers.length; i++)
{
// don't infinite loop on this
for(int nRetry = 2; (nRetry > 0); nRetry--)
{
doBatch(mNumbers[i]);
if(isUniqueInBatch(i,mNumbers))
{
break;
}
}
}
}
else
{
throwRemoteException("Config Verification failed");
}
}
// int[batchCount][numberCount]
// Generates the numbers if we don't have any yet
public int[][] getNumbers() throws RemoteException
{
if(mNumbers == null)
{
regenerateNumbers();
}
return mNumbers;
}
public boolean verifyConfiguration()
{ // returns true if the current configuration is valid
boolean bResult = false;
// Minimum random space needed
int nNeeded = (int)( mBatchCount * Math.sqrt(mNumberCount) );
int nSpaceSize = spaceSize();
bResult = ((mNumberCount > 0)
&& (mBatchCount > 0)
&&(nSpaceSize >= nNeeded));
printMessage("verifyConfiguration=" + bResult);
return bResult;
}
///////////////////////////////////////////////////////
// Helpers
boolean batchMatch(int[] a, int[]b)
{
boolean match = true;
for(int i=0; match && (i < a.length);i++)
{
match = inBatch(a[i],b,b.length - 1);
}
return match;
}
boolean isUniqueInBatch(int pos, int batches[][])
{
boolean unique = true;
for(int i=0;unique && (i < pos); i++)
{
unique = !batchMatch(batches[i],batches[pos]);
if(!unique)
{
printMessage("duplicate batch found");
}
}
return unique;
}
boolean inBatch(int num, int[]numbers, int iPos)
{
int limit = iPos > numbers.length ? numbers.length : iPos;
for(int i=0; i < limit; i++)
{
if(num == numbers[i])
{
printMessage("duplicate number found");
return true;
}
}
return false;
}
int spaceSize()
{
return mMaxNumber - mMinNumber + 1;
}
int randomNumber(java.util.Random rand)
{
double dSpaceSize = spaceSize();
double d = rand.nextDouble();
int x = mMinNumber + (int)(d * dSpaceSize);
printMessage("randomNumber =" + x);
return x;
}
void doBatch(int numbers[])
{
for(int i=0; i < numbers.length; i++)
{
int num;
do{
num = randomNumber(mRandom);
}while(inBatch(num, numbers, i));
numbers[i] = num;
}
java.util.Arrays.sort(numbers);
}
void throwRemoteException(String s)
throws RemoteException
{
printMessage("EXCEPTION:" + s);
throw new RemoteException(instanceDescriptor() + " : " + s);
}
///////////////////////////////////////////////////////
// Logging Helpers
static boolean sVerbose = true;
public boolean getVerbose(){ return sVerbose; }
public void setVerbose(boolean b){ sVerbose = b; }
String instanceDescriptor(){ return getClass().getName(); }
void printMessage(String str) {
if(sVerbose) {
System.out.println(instanceDescriptor() + " : " + str);
}
}
}
- Compile these files using your IDE or:
C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear>javac -d ejbs_jar src\lotto\*.java
- Start Dynamo 5.5 (with Portal.Gears and the PortalTraining application
from part 1) and the ACC
- In the ACC select Tools->J2EEDeployment from the blue on screen menu
- Double-click the PortalTraining.LottoGear application to launch the
application editor
- Select EJB Modules and create a new module called ejbs

- Select Session Beans and add a new bean 'NumberGenerator' and fill in the
EJB classes and description as follows:
- EJB Name : NumberGenerator
- Display Name: Number Generator
- Description : Generates Batches of Lotto Numbers
- EJB Class : lotto.GeneratorBean
- Home Interface : lotto.GeneratorHome
- Remote Interface : lotto.Generator
- Session Type : Stateful
- Transaction Type : Container

- Save and validate the application (Tools->validate) you should see no
messages. The compiled classes were successfully loaded from the
C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto folder
- Build the application (Tools->Build) and you'll see a lot of messages
pop up
Info: Stage directory: C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear
Info: Validating configuration
Info: Beginning build of "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear"
Info: Building EJB client jar for "ejbs_jar"
Info: Generating java source file "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_GeneratorHome_WCC.java"
Info: Generating java source file "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_Generator_WCC.java"
Info: Generating java source file "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_GeneratorHome_Impl_Stub.java"
Info: Generating java source file "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_Generator_Impl_Stub.java"
Info: Generating java source file "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_GeneratorHome_Impl.java"
Info: Generating java source file "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_Generator_Impl.java"
Info: Compiling "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_GeneratorHome_Impl.java"
Info: Compiling "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_GeneratorHome_Impl_Stub.java"
Info: Compiling "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_Generator_Impl.java"
Info: Compiling "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear\ejbs_jar\lotto\_Generator_Impl_Stub.java"
Info: Building module "ejbs_jar"
Info: Building module "web-app_war"
Info: Merging specifier data into base J2EE configuration.
Info: Generating EJB classes for "ejbs_jar"
Info: Finished build of "C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear"
This is when the application server generates the stub classes necessary to
handle all the J2ee container jobs. These classes are transparent to users
of the EJB. If you have errors here among other things it may be due to one
of these factors:
- Not declaring throws RemoteException on your remote interface methods
- Not sub-classing the correct classes, EJBHome (home Interface),
EJBObject (RemoteInterface) or SessionBean (the Bean implementation)
- Not declaring implementation methods as public
- Do not pass Go, do not collect $200 until your application compiles, the
Start the application (Tools->Start Application)
In order to use the bean in the JSP page gears we have to create a reference
between the two applications. That is tell the app server that our JSP pages
need the Generator Bean.
- In the ACC open up the LottoGear application in the J2ee application
editor as before
- Select web-app_war in the WebModules node and click the EJB References
tab.
- Add a reference to 'LottoGenerator' and fill in the fields as follows:

This tell Dynamo to make the home interface available to our JSP pages
throught the InitialContent as more on that later.
- Save and validate the application (Tools->validate)
- In order to gain access to the bean from JSP we need to create an
InitialContext, get the home interface and then create a reference to the
Generator bean. Here's the code which does that.
<%
ServletContext cx = getServletContext();
javax.naming.InitialContext initCtx = new javax.naming.InitialContext( );
try
{
lotto.Generator generator = null;
Object ref = initCtx.lookup("java:comp/env/LottoGenerator");
lotto.GeneratorHome home = (lotto.GeneratorHome) javax.rmi.PortableRemoteObject.narrow(ref, lotto.GeneratorHome.class);
generator = home.create();
// Use the bean here!
}
catch(Exception ex)
{
}
>
- So how do we use the bean, here's the complete code to test the EJB, this
is our new version of shared/html/content.jsp. Save this new version of the
file to your application.
<%@ taglib uri="/paf-taglib" prefix="paf" %>
<%@ taglib uri="/core-taglib" prefix="core" %>
<%@ taglib uri="/dsp" prefix="dsp" %>
<paf:InitializeGearEnvironment id="pafEnv">
<dsp:page>
<%-- set a parameter so we can get to it without JSP escaping --%>
<dsp:setvalue param="paf" value="<%= pafEnv %>"/>
<P class="small">Your Lucky Numbers are:</P>
<%
javax.naming.InitialContext initCtx = new javax.naming.InitialContext( );
try
{
lotto.Generator generator = null;
Object ref = initCtx.lookup("java:comp/env/LottoGenerator");
lotto.GeneratorHome home = (lotto.GeneratorHome) javax.rmi.PortableRemoteObject.narrow(ref, lotto.GeneratorHome.class);
generator = home.create();
generator.setNumberCount(6);
generator.setMinNumber(1);
generator.setMaxNumber(35);
generator.setBatchCount(10);
boolean bOK = generator.verifyConfiguration();
if(bOK)
{
int numbers[][] = generator.getNumbers();
out.println("<table>");
for(int i=0; i < numbers.length; i++)
{
out.println(" <tr><td class=small><b>" + (i + 1)+ "</b></td>");
for(int j=0; j < numbers[i].length; j++)
{
out.println(" <td class=small align=center>");
out.println(numbers[i][j]);
out.println(" </td>");
}
out.println(" </tr>");
}
out.println("</table>");
}
else
{
out.println("<p>Verification Failed</p>");
}
}
catch(Exception ex)
{
out.println("<p>Exception :" + ex.getMessage() + "</p>");
}
%>
</dsp:page>
</paf:InitializeGearEnvironment>
- Point your browser at http://localhost:8840/portal/lotto_test
to test your new page

- Play around with the parameters in the JSP page to show different batches
of numbers
Now we have our basics up and running we want to deal with configuration. How
can the administrator set some of the properties, how can users set their own
preferences for the LottoGear?
We'll tackle these issues in Part 3
|