ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Business Logic, Part 3
Pages: 1, 2, 3


Example 8-9: The AccountManager Implementation Class



package com.forethought.ejb.account;
 
import java.rmi.RemoteException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.EJBHome;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
 
import com.forethought.ejb.util.SessionAdapter;
 
// Account bean
import com.forethought.ejb.account.Account;
import com.forethought.ejb.account.AccountHome;
import com.forethought.ejb.account.AccountInfo;
 
// AccountType bean
import com.forethought.ejb.accountType.UnknownAccountTypeException;
 
// User bean
import com.forethought.ejb.user.User;
import com.forethought.ejb.user.UserHome;
 
// LDAPManager (for utility method)
import com.forethought.ldap.LDAPManager;
 
public class AccountManagerBean extends SessionAdapter {
    
    /** Username related to this account */
    private String username;
    
    /** User bean for this account's user */
    private User user;
    
    /** <p> Required method for allowing bean lookups. </p> */
    public void ejbCreate(String username) 
        throws CreateException, RemoteException {
            
        this.username = username;
        
        try {
            // Get an InitialContext
            Context context = new InitialContext(  );
 
            // Look up the Account bean
            UserHome userHome = (UserHome) 
                context.lookup("java:comp/env/ejb/UserHome");
            this.user = userHome.findByUserDn(LDAPManager.getUserDN(username));
        } catch (NamingException e) {
            throw new CreateException("Could not load underlying User bean.");
        } catch (FinderException e) {
            throw new CreateException("Could not locate specified user.");
        }
    }
    
    public AccountInfo add(String type, float balance)
        throws UnknownAccountTypeException {
     
        try {
            // Get an InitialContext
            Context context = new InitialContext(  );
 
            // Look up the Account bean
            AccountHome accountHome = (AccountHome) 
                context.lookup("java:comp/env/ejb/AccountHome");
            Account account = accountHome.create(type, balance, user);
            return account.getInfo(  );
        } catch (RemoteException e) {
            return null;
        } catch (CreateException e) {
            return null;
        } catch (NamingException e) {
            return null;
        }
    }
 
    public AccountInfo get(int accountId) throws RemoteException {
        return getAccount(accountId).getInfo(  );
    }
 
    public List getAll(  ) {
        List accounts = new LinkedList(  );
        
        try {
            Integer userId = user.getId(  );
            
            // Get an InitialContext
            Context context = new InitialContext(  );
 
            // Look up the Account bean
            AccountHome accountHome = (AccountHome) 
                context.lookup("java:comp/env/ejb/AccountHome");
            Collection userAccounts = accountHome.findByUserId(userId);
            for (Iterator i = userAccounts.iterator(); i.hasNext(  ); ) {
                Account account = (Account)i.next(  );
                accounts.add(account.getInfo(  ));
            }
        } catch (Exception e) {
            // Let fall through to the return statement
        }
        return accounts;
    }
 
    public AccountInfo deposit(AccountInfo accountInfo, float amount) 
        throws RemoteException {
            
        // Look up bean, to ensure most current view of data
        Account account = getAccount(accountInfo.getId(  ));
        AccountInfo info = account.getInfo(  );
        
        // Update balance
        info.setBalance(info.getBalance(  ) + amount);
        try {
            account.setInfo(info);
        } catch (UnknownAccountTypeException neverHappens) { }
        return info;
    }
 
    public AccountInfo withdraw(AccountInfo accountInfo, float amount)
        throws RemoteException {
            
        // Look up bean, to ensure most current view of data
        Account account = getAccount(accountInfo.getId(  ));
        AccountInfo info = account.getInfo(  );
        
        // Update balance
        info.setBalance(info.getBalance(  ) - amount);
        try {
            account.setInfo(info);
        } catch (UnknownAccountTypeException neverHappens) { }
        return info;
    }
    
    public boolean delete(int accountId) {
        try {
            Account account = getAccount(accountId);
            account.remove(  );
            return true;
        } catch (Exception e) {
            return false;
        }
    }
 
    public float getBalance(int accountId) throws RemoteException {
        return getAccount(accountId).getBalance(  );
    }
 
    private Account getAccount(int id) {
	
            // Get an InitialContext
            Context context = new InitialContext(  );
 
            // Look up the Account bean
            AccountHome accountHome = (AccountHome) 
                context.lookup("java:comp/env/ejb/AccountHome");
            Account account = accountHome.findByPrimaryKey(new Integer(id));
            
            return account;
        } catch (Exception e) {
            // Any problems - just return null
            return null;
        }
    }
}

This is all basic EJB material, and shouldn't cause you any problems. You'll notice that this class also uses a new finder method on the Account bean:

public Collection findByUserId(Integer userId)
    throws FinderException, RemoteException;

The accompanying query element in the Account bean's entry in the ejb-jar.xml descriptor would look like this:

<query>
  <query-method>
    <method-name>findByUserId</method-name>
    <method-params>
      <method-param>java.lang.Integer</method-param>
    </method-params>
  </query-method>
  <ejb-ql>
    <![CDATA[WHERE userLocal.id = ?1]]>
  </ejb-ql>
</query>

To deploy the AccountManager bean, you would use this (additional) XML entry in your ejb-jar.xml deployment descriptor:

<session>
  <description>
    This AccountManager bean allows administration of Forethought accounts.
  </description>
  <ejb-name>AccountManagerBean</ejb-name>
  <home>com.forethought.ejb.account.AccountManagerHome</home>
  <remote>com.forethought.ejb.account.AccountManager</remote>
  <ejb-class>com.forethought.ejb.account.AccountManagerBean</ejb-class>
  <session-type>Stateful</session-type>
  <transaction-type>Container</transaction-type>
  <ejb-ref>
    <ejb-ref-name>ejb/AccountHome</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <home>com.forethought.ejb.account.AccountHome</home>
    <remote>com.forethought.ejb.account.Account</remote>
    <ejb-link>AccountBean</ejb-link>
  </ejb-ref>
</session>

Additions to your application server's vendor-specific descriptors should be equally simple. With this bean in stateful form ready for use, it's time to see how it can be turned into a better-performing stateless session bean.

Going Stateless

To move this bean into stateless territory, you first need to change the home interface's create( ) signature. Since stateless beans can't maintain any information between method calls, passing in a username (or any other data) to the create( ) method is useless. Make the following change:

    public AccountManager create(  ) 
        throws CreateException, RemoteException;

Once this is done, you will need to determine which methods advertised by the bean require a username for operation. In other words, browse through your bean's implementation class and note any method that uses the username or user method variable. Once you've determined the methods in this category, you will need to change the signature for those methods in the remote interface:

public interface AccountManager extends EJBObject {
 
    public AccountInfo add(String username, String type, float balance)
        throws RemoteException, UnknownAccountTypeException;
        
    public AccountInfo get(int accountId) throws RemoteException;
    
    public List getAll(String username) throws RemoteException;
    
    public AccountInfo deposit(AccountInfo accountInfo, float amount)
        throws RemoteException;
        
    public AccountInfo withdraw(AccountInfo accountInfo, float amount)
        throws RemoteException;
        
    public float getBalance(int accountId) throws RemoteException;
 
    public boolean delete(int accountId) throws RemoteException;  
}

In this case, only two methods require this information, so it's not terribly inconvenient. However, in many cases conversion from stateful to stateless requires a parameter to be added to ten, twenty, or more methods. Even though this example is somewhat trivial, I want to continue the discussion assuming that it is a major issue to have to keep the username around for these multiple method calls. Before getting to the solution, though, you'll need to update your bean implementation class to operate without maintaining state. First, add a utility method to the end of the class:

private User getUser(String username) throws RemoteException {
    try {
        // Get an InitialContext
        Context context = new InitialContext(  );
 
        // Look up the Account bean
        UserHome userHome = (UserHome) 
            context.lookup("java:comp/env/ejb/UserHome");
        User user = userHome.findByUserDn(LDAPManager.getUserDN(username));
        return user;
    } catch (NamingException e) {
        throw new RemoteException("Could not load underlying User bean.");
    } catch (FinderException e) {
        throw new RemoteException("Could not locate specified user.");
    }
}

Then remove the username and user member variables, and modify three methods (those affected by the change to stateless):

public void ejbCreate(  ) throws CreateException {
    // Nothing to be done for stateless beans
}
 
public AccountInfo add(String username, String type, float balance)
    throws UnknownAccountTypeException {
  
    try {
        // Get an InitialContext
        Context context = new InitialContext(  );
        
        // Get the correct user
        User user = getUser(username);
 
        // Look up the Account bean
        AccountHome accountHome = (AccountHome) 
            context.lookup("java:comp/env/ejb/AccountHome");
        Account account = accountHome.create(type, balance, user);
        return account.getInfo(  );
    } catch (RemoteException e) {
        return null;
    } catch (CreateException e) {
        return null;
    } catch (NamingException e) {
        return null;
    }
}
 
public List getAll(String username) {
    List accounts = new LinkedList(  );
    
    try {
        User user = getUser(username);
        Integer userId = user.getId(  );
        
        // Get an InitialContext
        Context context = new InitialContext(  );
 
        // Look up the Account bean
        AccountHome accountHome = (AccountHome) 
            context.lookup("java:comp/env/ejb/AccountHome");
        Collection userAccounts = accountHome.findByUserId(userId);
        for (Iterator i = userAccounts.iterator(); i.hasNext(  ); ) {
            Account account = (Account)i.next(  );
            accounts.add(account.getInfo(  ));
        }
    } catch (Exception e) {
        // Let fall through to the return statement
    }
    return accounts;
}

Pages: 1, 2, 3

Next Pagearrow