Bank Application using Servant Manager

In the previous example, the BankImpl pre-created all the Account objects. This is of course going to be a problem if this bank ever gets a large number of accounts. In this section we will restructure the Bank application to use the Servant Manager instead of the Active Object Map for activating the AccountImpl servants on demand.

1 Bank Interface in IDL

The same interface as before.
module poaBank
{
    interface Account 
    {
    |   exception InsufficientFunds {};
    |                                                                      
    |   float deposit(in float sum);
    |                                                                      
    |   float withdraw(in float sum) raises (InsufficientFunds);
    |                                                                      
    |   readonly attribute float balance;
    };
                                                                           
    interface Bank
    {
    |   exception noSuchAccount {};
    |                                                                      
    |   Account getAccount(in long accountNum) raises (noSuchAccount);
    };
};

2 Bank Application Server

Same server as before except that it uses a different BankImpl and publishes object using a different name.
package poaBankSrvntMgr;
                                                                           
import util.Util;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Policy;
                                                                           
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
import org.omg.CosNaming.NamingContextPackage.NotFound;
                                                                           
import org.omg.PortableServer.POA;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.LifespanPolicyValue;
import org.omg.PortableServer.IdAssignmentPolicyValue;
                                                                           
import poaBank.Bank;
import poaBank.BankHelper;
                                                                           
public class Server
{
    public static void main(String[] args)
    {
    |   try {
    |   |                                                                  
    |   |   // create the jBroker ORB
    |   |   ORB orb = ORB.init(args, null);
    |   |                                                                  
    |   |   // get the root POA
    |   |   POA rootPOA = (POA) orb.resolve_initial_references("RootPOA");
    |   |                                                                  
    |   |   // create a persistent user assigned ids POA
    |   |   POA bankPOA = rootPOA.create_POA("bankPOA", 
    |   |       rootPOA.the_POAManager(),
    |   |       new Policy[] {
    |   |       |   rootPOA.create_id_assignment_policy(
    |   |       |       IdAssignmentPolicyValue.USER_ID),
    |   |       |   rootPOA.create_lifespan_policy(
    |   |       |       LifespanPolicyValue.PERSISTENT)
    |   |       });
    |   |                                                                  
    |   |   // create and activate the bank servant
    |   |   Servant bank = new BankImpl(bankPOA, args[0]);
    |   |   bankPOA.activate_object_with_id("bank".getBytes(), bank);
    |   |                                                                  
    |   |   // get the root of the NameService
    |   |   NamingContext root = NamingContextHelper.narrow(
    |   |       orb.resolve_initial_references("NameService"));
    |   |                                                                  
    |   |   // if not done before, publish the bank objref
    |   |   NameComponent nc = new NameComponent("bank2", "");
    |   |   NameComponent[] name = new NameComponent[] { nc };
    |   |   try {
    |   |   |   root.resolve(name);
    |   |   } catch (NotFound ex) {
    |   |   |   // create the bank objref
    |   |   |   Bank bankObj = BankHelper.narrow(bankPOA.
    |   |   |       create_reference_with_id("bank".getBytes(),
    |   |   |           bank._all_interfaces(null, null)[0]));
    |   |   |   System.out.println(orb.object_to_string(bankObj));
    |   |   |   root.rebind(name, bankObj);
    |   |   }
    |   |                                                                  
    |   |   // activate bankPOA
    |   |   bankPOA.the_POAManager().activate();
    |   |                                                                  
    |   |   // wait for invocations
    |   |   System.out.println("waiting for invocations ...");
    |   |   orb.run();
    |   |                                                                  
    |   } catch (Exception ex) {
    |   |   ex.printStackTrace();
    |   }
    }
}

3 Bank Implementation

The BankImpl constructor just creates a POA with USER_IDS, NON_RETAIN, and USE_SERVANT_MANAGER policies for Account objects and sets the Servant Manager. When it is asked for an account, it manufactures a new object reference (with the given account number as the object id) using create_reference_with_id.
package poaBankSrvntMgr;
                                                                           
import java.io.File;
                                                                           
import org.omg.CORBA.Policy;
                                                                           
import org.omg.PortableServer.POA;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.IdAssignmentPolicyValue;
import org.omg.PortableServer.ServantRetentionPolicyValue;
import org.omg.PortableServer.RequestProcessingPolicyValue;
                                                                           
import poaBank.BankPOA;
import poaBank.Account;
import poaBank.AccountImpl;
import poaBank.AccountHelper;
import poaBank.BankPackage.noSuchAccount;
                                                                           
public class BankImpl extends BankPOA
{
    POA       _acctPOA;
    String    _acctRepId; // repository Id for Account Objects
                                                                           
    BankImpl(POA bankPOA, String dir) throws Exception
    {
    |   // make sure that the DB dir exists
    |   File dbDir = new File (dir);
    |   if (!dbDir.exists()) dbDir.mkdir();
    |                                                                      
    |   // create the POA for Transient Account Objects
    |   _acctPOA = bankPOA.create_POA("acctPOA", bankPOA.the_POAManager(),
    |       new Policy[] {
    |       |   bankPOA.create_id_assignment_policy(
    |       |       IdAssignmentPolicyValue.USER_ID),
    |       |   bankPOA.create_servant_retention_policy(
    |       |       ServantRetentionPolicyValue.NON_RETAIN),
    |       |   bankPOA.create_request_processing_policy(
    |       |       RequestProcessingPolicyValue.USE_SERVANT_MANAGER)
    |       });
    |                                                                      
    |   // set the servant manager in Acct POA
    |   _acctPOA.set_servant_manager(new AccountServantMgr(dbDir));
    |                                                                      
    |   // compute the repository id for Account objects
    |   _acctRepId = new AccountImpl()._all_interfaces(null, null)[0];
    }
                                                                           
    public Account getAccount(int acctNum) throws noSuchAccount
    {
    |   Account account = null;
    |                                                                      
    |   if ((acctNum >= 100) && (acctNum < 110)) {
    |   |                                                                  
    |   |   // construct the object id for account
    |   |   byte objectId[] = new byte[1];
    |   |   objectId[0] = (byte) ( acctNum & 0xFF);
    |   |                                                                  
    |   |   try {
    |   |   |   account = AccountHelper.narrow(_acctPOA.
    |   |   |      create_reference_with_id(objectId, _acctRepId));
    |   |   } catch (Exception ex) {
    |   |   |   // org.omg.PortableServer.POAPackage.WrongPolicy
    |   |   |   // thrown on JDK 1.2 and 1.3
    |   |   |   ex.printStackTrace();
    |   |   }
    |   }
    |                                                                      
    |   if (account == null) throw new noSuchAccount();
    |                                                                      
    |   return account;
    }
}

4 Account Servant Manager

The Account Servant Manager maintains a table of active servants. When the ORB calls the preinvoke method on the servant manager, it looks up in the table. If the servant is not in the table it is created.

The Servant Manager could also implement a cache instead of a table where it would time out servants. Using this approach you can literally support a very large number of objects.

package poaBankSrvntMgr;
                                                                           
import java.io.File;
import java.util.Hashtable;
                                                                           
import org.omg.CORBA.Policy;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.portable.ObjectImpl;
                                                                           
import org.omg.PortableServer.POA;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.ForwardRequest;
import org.omg.PortableServer.ServantLocator;
import org.omg.PortableServer.POAPackage.WrongPolicy;
import org.omg.PortableServer.IdAssignmentPolicyValue;
import org.omg.PortableServer.RequestProcessingPolicyValue;
import org.omg.PortableServer.ServantLocatorPackage.CookieHolder;
                                                                           
import poaBank.Account;
import poaBank.AccountImpl;
                                                                           
public class AccountServantMgr extends ObjectImpl
    implements ServantLocator
{
    File      _dbDir;
    Hashtable _accounts;
                                                                           
    AccountServantMgr(File dbDir)
    {
    |   _dbDir    = dbDir;
    |   _accounts = new Hashtable(19);
    }
                                                                           
    public Servant preinvoke(byte[] oid, POA adapter, String operation,
        CookieHolder the_cookie) throws ForwardRequest
    {
    |   Servant servant = null;
    |                                                                      
    |   // get the account number
    |   int acctNum = oid[0];
    |   Integer AcctNum = new Integer(acctNum);
    |                                                                      
    |   // get the account servant
    |   synchronized (_accounts) {
    |   |   // see if the account is already active
    |   |   servant = (Servant) _accounts.get(AcctNum);
    |   |                                                                  
    |   |   // activate the servant
    |   |   if (servant == null) {
    |   |   |   servant = new AccountImpl(_dbDir, acctNum);
    |   |   |   _accounts.put(AcctNum, servant);
    |   |   }
    |   }
    |                                                                      
    |   return servant;
    }
                                                                           
    public void postinvoke(byte[] oid, POA adapter, String operation,
        java.lang.Object the_cookie, Servant the_servant)
    {
    }
                                                                           
    // servant manager is locality constrained
    public String[] _ids()
    {
    |   throw new BAD_OPERATION();
    }
}

5 Account Implementation

We use the same account implementation as before.
package poaBank;
                                                                           
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
                                                                           
import org.omg.CORBA.PERSIST_STORE;
                                                                           
import poaBank.AccountPackage.InsufficientFunds;
                                                                           
public class AccountImpl extends AccountPOA
{
    float _balance;
    File  _acctDb;
                                                                           
    public AccountImpl() {}
                                                                           
    public AccountImpl(File dir, int i)
    {
    |   _acctDb = new File(dir, new Integer(i).toString());
    |   if (_acctDb.exists()) _balance = readBalance();
    }
                                                                           
    public synchronized float deposit(float sum)
    {
    |   _balance += sum;
    |                                                                      
    |   writeBalance();
    |                                                                      
    |   return _balance;
    }
                                                                           
    public synchronized float withdraw(float sum) throws InsufficientFunds
    {
    |   if ((_balance - sum) < 0) throw new InsufficientFunds();
    |                                                                      
    |   _balance -= sum;
    |                                                                      
    |   writeBalance();
    |                                                                      
    |   return _balance;
    }
                                                                           
    public synchronized float balance()
    {
    |   return _balance;
    }
                                                                           
    void writeBalance()
    {
    |   try {
    |   |   FileOutputStream fos = new FileOutputStream(_acctDb);
    |   |   ObjectOutputStream oos = new ObjectOutputStream(fos);
    |   |                                                                  
    |   |   oos.writeObject(new Float(_balance));
    |   |                                                                  
    |   |   oos.close();
    |   } catch (Exception ex) {
    |   |   throw new PERSIST_STORE();
    |   }
    }
                                                                           
    float readBalance()
    {
    |   try {
    |   |   FileInputStream fis = new FileInputStream(_acctDb);
    |   |   ObjectInputStream ois = new ObjectInputStream(fis);
    |   |                                                                  
    |   |   float balance = ((Float) ois.readObject()).floatValue();
    |   |                                                                  
    |   |   ois.close();
    |   |                                                                  
    |   |   return balance;
    |   } catch (Exception ex) {
    |   |   throw new PERSIST_STORE();
    |   }
    }
}

6 Register the Server

The server deployment descriptor.
server.main=poaBankSrvntMgr.Server
server.alias=bank2
server.classpath=/usr/local/jbroker30/examples/lib
server.args=/usr/local/jbroker30/examples/src/poaBankSrvntMgr/db
server.vmflags=-Djava.compiler=NONE

7 Bank Client

The same client as before.
Copyright © 2000-2003, Novell, Inc. All rights reserved.