Essential ATG Dynamo Training - Got atg Certified Relationship Management Developer?
This is not an official ATG site: ATG, Dynamo, Scenario Server and Personalization Server are trademarks or registered trademarks of Art Technology Group
Articles Exercises Resources Links Search

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?


Problem Statement

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.


Design Overview

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

Implementation

Setting up the Development Environment

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.

  1. Create a folder called 'src' underneath the LottoGear folder for your java files:
    C:/ATG/Dynamo5.5/PortalTraining/j2ee-apps/LottoGear/src
  2. Create a folder called 'ejb_jar' underneath the LottoGear folder for your class files:   
    C:/ATG/Dynamo5.5/PortalTraining/j2ee-apps/LottoGear/ejb_jar
  3. 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
  4. 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

Creating the EJB

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)
  1. 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
    }
  2. 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;
    }
  3. 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);
        }
      }
    }
    
    
  4. Compile these files using your IDE or:
    C:\ATG\Dynamo5.5\PortalTraining\j2ee-apps\LottoGear>javac -d ejbs_jar src\lotto\*.java

Adding the EJB to the Application

  1. Start Dynamo 5.5 (with Portal.Gears and the PortalTraining application from part 1) and the ACC
  2. In the ACC select Tools->J2EEDeployment from the blue on screen menu
  3. Double-click the PortalTraining.LottoGear application to launch the application editor
  4. Select EJB Modules and create a new module called ejbs
  5. 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
  6. 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
  7. 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
  8. Do not pass Go, do not collect $200 until your application compiles, the Start the application (Tools->Start Application)

Using the Bean in our JSP Page Gear

Creating an EJB Reference

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.

  1. In the ACC open up the LottoGear application in the J2ee application editor as before
  2. Select web-app_war in the WebModules node and click the EJB References tab.
  3. 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.
  4. Save and validate the application (Tools->validate)

Accessing the Generator Session Bean from JSP

  1. 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)
      {
      }
    >
  2. 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>

Testing the New EJB Enabled Gear

  1. Point your browser at http://localhost:8840/portal/lotto_test to test your new page
  2. Play around with the parameters in the JSP page to show different batches of numbers

Where Next?

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

 


Technical Training Advertise your Training Programs for Free! Los Angeles Web Design Shopping Cart Software  Form a Corporation