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.
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);
};
};
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();
| }
}
}
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 the
create_reference_with_id method.
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;
}
}
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();
}
}
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();
| }
}
}
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
The same client as before.
package poaBank;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
import poaBank.AccountPackage.InsufficientFunds;
public class Client
{
ORB _orb;
Bank _bank;
Account _account;
public static void main(String[] args)
{
| try {
| | new Client().run(args);
| } catch (Exception ex) {
| | ex.printStackTrace();
| }
}
public void run(String[] args) throws Exception
{
| // create the jBroker ORB
| _orb = ORB.init(args, null);
|
| // get the root of the NameService
| NamingContext root = NamingContextHelper.narrow(
| _orb.resolve_initial_references("NameService"));
|
| // get the bank object reference
| NameComponent nc = new NameComponent(args[0], "");
| NameComponent[] name = new NameComponent[] { nc };
| _bank = BankHelper.narrow(root.resolve(name));
|
| // enter the request processing loop
| processCommands();
}
void processCommands() throws Exception
{
| // create a input stream reader
| BufferedReader reader = new BufferedReader(new InputStreamReader(
| System.in));
|
| // get the account number
| System.out.print("Enter Account number: ");
| System.out.flush();
| _account = _bank.getAccount(new Integer(reader.readLine()).intValue());
|
| // enter the read-execute loop
| while (true) {
| | System.out.print("bank > ");
| | executeCommand(readCommandLine(reader));
| }
}
void executeCommand(String[] command) throws Exception
{
| if (command.length == 0)
| printHelp();
|
| // get balance
| else if (command[0].equals("b"))
| System.out.println("\tbalance: $" + _account.balance());
|
| // check the command for addition parameter
| else if (command[0].equals("d")) {
| | if (command.length != 2) printHelp();
| | else System.out.println("\tbalance: $"+ _account.deposit(new Float(
| | command[1]).floatValue()));
| }
|
| // deposit money
| else if (command[0].equals("w")) {
| | if (command.length != 2) printHelp();
| | else {
| | | try {
| | | | System.out.println("\tbalance: $"+ _account.withdraw(
| | | | new Float(command[1]).floatValue()));
| | | } catch (InsufficientFunds ex) {
| | | | System.out.println("\tInsufficient Funds!");
| | | }
| | }
| }
|
| // quit
| else if (command[0].equals("q")) {
| | System.exit(0);
| }
|
| // print help
| else printHelp();
}
void printHelp()
{
| System.out.println();
| System.out.println("\tCommands:");
| System.out.println("\t b // get balance");
| System.out.println("\t d <sum> // deposit sum");
| System.out.println("\t w <sum> // withdraw sum");
| System.out.println("\t q // quit");
| System.out.println();
}
String[] readCommandLine(BufferedReader reader) throws Exception
{
| int i=0;
|
| String line = reader.readLine();
|
| if (line == null) System.exit(0);
|
| StringTokenizer tokenizer = new StringTokenizer(line);
| String[] command = new String[tokenizer.countTokens()];
| while (tokenizer.hasMoreTokens()) {
| | command[i++] = tokenizer.nextToken();
| }
|
| return command;
}
}
Copyright © 2003, 2004 Novell, Inc. All rights reserved. Copyright © 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.