} public void setWithdrawAccountID(String withdrawAccountID) { this.withdrawAccountID = withdrawAccountID; } public void setDepositAccountID(String depositAccountID) { this.depositAccountID = depositAccountID; } public void setTransferAmount(double transferAmount) { this.transferAmount = transferAmount; } public double getDepositAccountBalance() { return depositAccountBalance; } public double getWithdrawAccountBalance() { return withdrawAccountBalance; } public TransferFundsCommand() {} } Command Superclass package examples.command; import java.io.Serializable; public abstract class Command implements Serializable { public abstract void execute() throws CommandException; } CommandServer Session Bean package examples.command; import javax.ejb.*; import java.rmi.RemoteException; import javax.naming.*; public class CommandServerBean implements SessionBean { SessionContext ctx; Pattern Code Listing 209 public void CommandServer() {} public Command executeCommand(Command aCommand) throws CommandException { try { aCommand.execute(); } catch (CommandException e) { ctx.setRollbackOnly(); throw e; } return aCommand; } public void ejbActivate() throws EJBException, java.rmi.RemoteException {} public void ejbCreate() throws CreateException {} public void ejbPassivate() throws EJBException, java.rmi.RemoteException {} public void ejbRemove() throws EJBException, java.rmi.RemoteException {} public void setSessionContext(final SessionContext p1) throws EJBException, java.rmi.RemoteException { this.ctx = p1; } } CommandException package examples.command; public class CommandException extends Exception { Exception wrappedException; public CommandException(){} public CommandException(Exception e) { this.wrappedException = e; } 210 Appendix Exception getWrappedException() { return wrappedException; } public CommandException(String s) { super(s); } } CommandTarget Interface package examples.command; interface CommandTarget { Command executeCommand(Command aCommand) throws CommandException; } EJBCommandTarget package examples.command; import javax.rmi.PortableRemoteObject; import javax.ejb.CreateException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import java.rmi.RemoteException; public class EJBCommandTarget implements CommandTarget { private CommandServerHome serverHome; public EJBCommandTarget() { try { Context ctx = new InitialContext(System.getProperties()); Object obj = ctx.lookup(“CommandServer”); System.out.println(obj); this.serverHome = (CommandServerHome) PortableRemoteObject.narrow(obj, CommandServerHome.class ); } catch (NamingException e) { e.printStackTrace(); } Pattern Code Listing 211 catch (ClassCastException e) { e.printStackTrace(); } } public Command executeCommand(Command aCommand) throws CommandException { try { CommandServer aCommandServer = serverHome.create(); aCommand = aCommandServer.executeCommand(aCommand); return aCommand; } catch (Exception e) { throw new CommandException(e); } } } CommandExecutor package examples.command; public class CommandExecutor { private static EJBCommandTarget ejbTarget = new EJBCommandTarget(); //execute command, overwriting memory reference of the passed //in command to that of the new one public static Command execute(Command aCommand) throws CommandException { //at this point, a real implementation would use a properties file //to determine which command target (EJB, Local, Corba, etc) to //use for this particular command, as well as which deployed //CommandServer to use (in order to run commands //under different transaction configurations) return ejbTarget.executeCommand(aCommand); } } 212 Appendix Data Access Command Bean The implementations of the abstract super classes BaseReadCommand and BaseUpdateCommand, as well as the InsertEmployeeCommand and QueryEmployeeByNameCommand classes are provided. Note that the BaseReadCommand uses a RowSet to simplify its implementation. RowSets are part of the JDBC 2.0 optional package, and joined core JDBC as of JDBC 3.0. The example here uses Sun’s free CachedRowSet implementation of the RowSet interface. BaseReadCommand.java package examples.datacommands; import javax.sql.*; import javax.naming.InitialContext; import javax.naming.NamingException; import java.sql.*; import sun.jdbc.rowset.CachedRowSet; /** * The Super class for any data command beans Read from the * database. */ abstract class BaseReadCommand { protected PreparedStatement pstmt; protected CachedRowSet rowSet = null; private Connection con; protected BaseReadCommand ( String jndiName, String statement ) throws DataCommandException { InitialContext ctx = null; try { ctx = new InitialContext(); DataSource ds = (javax.sql.DataSource) ctx.lookup(jndiName); con = ds.getConnection(); pstmt = con.prepareStatement(statement); } catch (NamingException e) { throw new DataCommandException(e.getMessage()); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); Pattern Code Listing 213 } } public void execute() throws DataCommandException { try { rowSet = new CachedRowSet(); rowSet.populate(pstmt.executeQuery()); rowSet.beforeFirst(); this.release(); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } public boolean next() throws DataCommandException { try { return rowSet.next(); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } private void release() throws SQLException { if (pstmt != null) pstmt.close(); if (con != null) con.close(); } } BaseUpdateCommand.java package examples.datacommands; import javax.sql.*; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.ejb.EJBException; import java.sql.*; /** * The Super class for any data command beans that Create, Update or * Delete. This class is reusable across projects, all proj. specific data * (Datasource JDNI and SQl String) are left to the subclasses */ abstract class BaseUpdateCommand { 214 Appendix protected PreparedStatement pstmt; private Connection con; protected BaseUpdateCommand ( String jndiName, String statement ) throws DataCommandException { InitialContext ctx = null; try { ctx = new InitialContext(); DataSource ds = (javax.sql.DataSource) ctx.lookup(jndiName); con = ds.getConnection(); pstmt = con.prepareStatement(statement); } catch (NamingException e) { throw new DataCommandException(e.getMessage()); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } public int execute() throws DataCommandException { try { //execute update, return the rowcount int updateCount = pstmt.executeUpdate(); this.release(); return updateCount; } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } private void release() throws SQLException { if (pstmt != null) pstmt.close(); if (con != null) con.close(); } } InsertEmployeeCommand.java package examples.datacommands; import java.sql.*; import javax.sql.*; /** * InsertEmployeeCommand, this class Pattern Code Listing 215 * is the usecase specific Command bean that * an application developer would write. */ public class InsertEmployeeCommand extends BaseUpdateCommand { static String statement = “insert into Employees (EMPLOYEEID, NAME, EMAIL) values (?,?,?)”; static final String dataSourceJNDI = “bookPool”; /** * Passes parent class the usecase specific sql statement to use */ protected InsertEmployeeCommand() throws DataCommandException { super(dataSourceJNDI, statement); } public void setEmail(String anEmail) throws DataCommandException { try { pstmt.setString(3, anEmail); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } public void setId(int id) throws DataCommandException { try { pstmt.setInt(1, id); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } public void setName(String aName) throws DataCommandException { try { pstmt.setString(2, aName); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } } 216 Appendix QueryEmployeeByName.java package examples.datacommands; import java.sql.*; import javax.sql.*; /** * A usecase specific querying object **/ public class QueryEmployeeByNameCommand extends BaseReadCommand { static final String statement = “select EMPLOYEEID, NAME, EMAIL from Employees where NAME = ?”; static final String dataSourceJNDI = “bookPool”; protected QueryEmployeeByNameCommand() throws DataCommandException { super(dataSourceJNDI, statement); } public String getEmail() throws DataCommandException { try { return rowSet.getString(3); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } public int getId() throws DataCommandException { try { return rowSet.getInt(1); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } public String getName() throws DataCommandException { try { return rowSet.getString(2); } catch (SQLException e) { Pattern Code Listing 217 throw new DataCommandException(e.getMessage()); } } public void setName(String aName) throws DataCommandException { try { pstmt.setString(1, aName); } catch (SQLException e) { throw new DataCommandException(e.getMessage()); } } } Dual Persistent Entity Bean Included is the code example of the bank account entity bean inheritance rela- tionship and deployment descriptors. These classes can be compiled and then deployed in CMP or BMP by swapping the provided deployment descriptors. Account Deployment Descriptor for CMP <ejb-jar> <enterprise-beans> <entity> <ejb-name>dualPersistent</ejb-name> <home>examples.dualpersistent.AccountHome</home> <remote>examples.dualpersistent.Account</remote> <ejb-class>examples.dualpersistent.AccountCMPBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.String</prim-key-class> <reentrant>False</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>AccountBean</abstract-schema-name> <cmp-field> <field-name>accountId</field-name> </cmp-field> <cmp-field> <field-name>balance</field-name> </cmp-field> <primkey-field>accountId</primkey-field> <query> 218 Appendix [...]... HomeFactoryException { EJBHome anEJBHome; anEJBHome = (EJBHome) this.ejbHomes.get(homeClass); try { if(anEJBHome == null) { anEJBHome = (EJBHome) PortableRemoteObject.narrow (ctx.lookup (homeClass.getName()), homeClass); this.ejbHomes.put(homeClass, anEJBHome); } } catch (ClassCastException e) { throw new HomeFactoryException(e); } catch (NamingException e) { throw new HomeFactoryException(e); } return anEJBHome; } /**... cache an EJBHome object * This ‘alternate’ implementation delegates JNDI name knowledge * to the client It is included here for example only */ public EJBHome lookUpHome(Class homeClass, String jndiName) throws HomeFactoryException { EJBHome anEJBHome; anEJBHome = (EJBHome) this.ejbHomes.get(homeClass); try { if(anEJBHome == null) { System.out.println(“finding HOME for first time”); anEJBHome = (EJBHome)... catch(NamingException e) { throw new EJBException(e); } } public public public public void void void void ejbActivate() {} ejbCreate() {} ejbPassivate() {} ejbRemove() {} } Sequence Session and Entity EJBJAR.xml 237 ... setName(java.lang.String newName); ejbActivate() {} ejbLoad() {} ejbPassivate() {} ejbPostCreate(String name) {} ejbRemove() {} ejbStore() {} public void setEntityContext(EntityContext unused) {} public void unsetEntityContext() {} } Pattern Code Listing Sequence Session Remote Interface package examples.sequencegenerator; import java.rmi.*; public interface SequenceSession extends javax .ejb. EJBObject { public int... 2 19 220 Appendix dualPersistent< /ejb- name> Remote * Required < /ejb- jar> Account Remote Interface package examples.dualpersistent; import java.rmi.RemoteException; import javax .ejb. EJBObject; public interface Account extends EJBObject... futher simplify client class EJBHomeFactory private Map ejbHomes; private static EJBHomeFactory aFactorySingleton; Context ctx; Pattern Code Listing /** * EJBHomeFactory private constructor */ private EJBHomeFactory() throws NamingException { ctx = new InitialContext(); this.ejbHomes = Collections.synchronizedMap(new HashMap()); } /* * Returns the singleton instance of the EJBHomeFactory * The sychronized... ( EJBHomeFactory.aFactorySingleton == null ) { EJBHomeFactory.aFactorySingleton = new EJBHomeFactory(); } } catch (NamingException e) { throw new HomeFactoryException(e); } return EJBHomeFactory.aFactorySingleton; } /** * Lookup and cache an EJBHome object using a home class * Assumes that the JNDI name of the EJB Home being looked for * is the same as the fully qualified class name of the * same EJB. .. * If EJB- REF tags are being used externally, then the classname * of the EJB Home can be mapped to the actual JNDI name of the * deployed bean transaprently at deployment time * If EJB- REF tags are not used, then the EJB s must be deployed * with JNDI names equal to their fully qualified home interfaces 2 29 230 Appendix */ public EJBHome lookUpHome(Class homeClass) throws HomeFactoryException { EJBHome... ProcessingErrorException(String message) {super(message); } EJB Home Factory Here we present an example of an EJB Home Factory Simple EJB Home Factory package com.portal.util; import import import import import /** * EJB * For * can * the */ public { javax .ejb. *; java.rmi.*; javax.rmi.*; java.util.*; javax.naming.*; Home Factory, maintains a simple hashmap cache of EJBHomes a production implementations, exceptions... exception, so //try again continue; } Pattern Code Listing else { // we tried too many times, so fail throw new javax .ejb. EJBException(e); } } } } return entry.last++; } catch (javax .ejb. CreateException e) { throw new javax .ejb. EJBException(e); } } public void setSessionContext( javax .ejb. SessionContext sessionContext) { try { Context namingContext = new InitialContext(); _blockSize = ((Integer)namingContext.lookup . aCommand; } public void ejbActivate() throws EJBException, java.rmi.RemoteException {} public void ejbCreate() throws CreateException {} public void ejbPassivate() throws EJBException, java.rmi.RemoteException. AccountCMPBean() {} public void ejbActivate() {} public void ejbLoad() {} public void ejbPassivate() {} public void ejbPostCreate(String accountId,double initialBalance){} public void ejbRemove() throws RemoveException. 2 19 <method> < ;ejb- name>dualPersistent< /ejb- name> <method-intf>Remote</method-intf> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> < /ejb- jar> Account Remote Interface package examples.dualpersistent; import java.rmi.RemoteException; import javax .ejb. EJBObject; public interface Account extends EJBObject { public