A Basic Factory Implementing a Generic Factory

17.3 Implementing a Generic Factory

In order to implement a factory for our bank example, we need to deal with three basic problems. The first, clearly, is actually implementing the factory. The second is adapting the existing code to use the factory. The third is revisiting some of the more esoteric functionality we implemented, such as the automatic locking mechanism in Chapt er 12 . We will implement a generic factory first, in order to show the basic idea. In this simple factory, the client will request a server from the factory and tell the factory when it is no longer interested in the server.

17.3.1 A Basic Factory

The interface to our factory consists of two methods: public interface BasicFactory extends Remote { public Account getAccountString accountName throws RemoteException; public void doneWithAccountString accountName throws RemoteException; } The idea is this: because the client explicitly signals interest in a server and announces when it is done with a server, the factory will be able to directly manage the servers, shutting down those that are no longer active. In order to implement this factory, we need to solve two basic problems: • We need to store enough information in the factory so it can create the servers when they are requested. • We need to make sure that, if the same account is simultaneously requested by two different clients, the factory doesnt create two different servers. The first problem is nothing new. We had to solve it before in our launch code. In the real world, the factory would probably query a database to get the information. For the purpose of this chapter, however, well simply hardwire in some values: public class BasicFactory_Impl extends UnicastRemoteObject implements Factory { private HashMap _namesToAccounts; private HashMap _accountsToSupport; private Integer _one = new Integer1; public Factory_Impl throws RemoteException { createServers ; _accountsToSupport = new HashMap ; } private void createServers { _namesToAccounts = new HashMap ; addAccount Bob, 10000; addAccount Alex, 1223; addAccount Trish, 1894; addAccount Pat, 3970; addAccount David, 120056; addAccount Mary, 21283; } private void addAccountString name, int cents { Integer Cents = new Integercents; try { Account_Impl newAccount = new Account_Implnew MoneyCents; _namesToAccounts.putname, newAccount; } catch Exception e {System.out.printlne;} } Whats going on here is simple. We create a HashMap of Account_Impl objects, which we will then query for the appropriate instance of Account_Impl . Its worth repeating that this is an ad hoc, hardwired solution to an application-specific problem. In reality, creating all the servers and storing them in a HashMap at launch time goes a long way towards defeating the whole purpose of a factory. After this, it may seem that getAccount doneWithAccount should be straightforward. Here, for example, is what getAccount must do: 1. Find the instance of Account_Impl associated with the request 2. Make this object available over the network, using UnicastRemoteObjects s exportObject method 3. Return a stub so the client can communicate with the newly exported server This is exactly what we do. The only complication comes from the objection I noted earlier: we dont want to export the same server more than once. We especially dont want to unexport it prematurely in the doneWithAccount method. This means we need to keep track of the number of clients that are currently using the server. We do this using an instance of Hashmap , which maps server instances to instances of java.lang.Integer . With that in mind, heres the implementation of getAccount and doneWithAccount : public synchronized Account getAccountString accountName throws RemoteException { Account account = Account _namesToAccounts.getaccountName; This should really create the server if null==account { return null; } Integer support = Integer _accountsToSupport.getaccount; if null==support { try { RemoteStub stub = RemoteStub UnicastRemoteObject. exportObjectaccount; System.out.printlnAccount + accountName + successfully exported.; support = _one; } catch Exception e { System.out.printlne; return null; } } else { support = new Integersupport.intValue +1; } _accountsToSupport.putaccount, support; return account; } public void doneWithAccountString accountName throws RemoteException { Account account = Account _namesToAccounts.getaccountName; if null==account { return; } Integer support = Integer _accountsToSupport.getaccount; if null==support { System.out.printlnAttempt to unexport non - supported account; return; } int newSupportValue = support.intValue - 1; if newSupportValue0 _accountsToSupport.putaccount, new IntegernewSupportValue; return; } try { UnicastRemoteObject.unexportObjectaccount, true; System.out.printlnAccount + accountName + successfully unexported.; _accountsToSupport.removeaccount; } catch Exception e { System.out.printlne; } } When a server is first requested, _accountsToSupport contains an instance of Integer with a value of 1 . Every additional client request for that server increments the value in accountsToSupport . When clients call doneWithAccount , the value is decremented. When the value reaches again, the server is unexported. We should point out, however, that in this last step, when the client is unexported, a factory typically takes further action, instead of simply leaving the server object in a HashMap . This is, in effect, a very crude version of garbage collection that relies on the clients good behavior. If clients dont call doneWithAccount , or call it too many times, bad things can happen. Also, second thoughts probably tell us that, for the bank example, reference counting in our application is probably a bad idea. We dont want to vend an account more than once.

17.3.2 Modifying the Existing Application