Modifying our launch code

RemoteException { superid, 0; try { _balance = Money data.get ; } catch Exception e { Both ClassNotFoundException and IOException can be thrown. } } This calls the superclass constructor and then immediately attempts to initialize itself from the MarshalledObject by getting an instance of Money and setting it as the account balance. The activatable constructor takes two arguments: an instance of ActivationID and an instance of MarshalledObject . This constructor must be present in order for the Activation Framework to work. That is, when the Activation Framework tries to launch a server, it looks for a two- argument constructor with precisely this signature and throws an exception if the constructor is not present. Our constructor calls a similar superclass constructor that takes two arguments: an instance of ActivationId and a port. The port argument serves the same role as it does with UnicastRemoteObject : a value of 0 means that the associated server socket will listen on whatever port the RMI runtime finds convenient. Our code simply passes along the instance of ActivationID and lets RMI choose the port. This is by far the most common way of writing an activatable server. Unless you have a good reason to do otherwise, extending Activatable and using a port with a value of 0 is convenient. The only problem arises when you need to pass a nonserializable argument to your server for initialization. However, that problem arises in any system that delays launching a server™you must find a way to store the initialization parameters until the server is launched. The second most common way uses the second constructor, which adds socket factories. Well discuss reasons to use this second constructor in Chapt er 18 . Apart from the constructor, our server code is exactly the same as it was before.

17.6.1.2 Modifying our launch code

The server itself isnt complex. However, our launch code is substantially more complicated than it used to be. The reason for this is simple: instead of simply launching our servers, we need to tell the Activation Framework how to launch our servers. This boils down to two things: Describing a JVM The first thing the Activation Framework will do when it needs to launch a server is check to see whether a suitable JVM has already been created. This is done by creating an instance of ActivationGroup . Describing the actual server After the Activation Framework creates the JVM the server will run in, it needs to create the server. In order to do so, it needs a complete description of the server object. This is why we include an instance of MarshalledObject in our constructor; it makes things simpler for the Activation Framework. This is done by creating an instance of ActivationDesc . The first part of our launch code looks very familiar: public static void mainString[] args { try { ActivationGroup activationGroup = createActivationGroup ; createBankAccounts ; } catch Exception e { System.out.printlnUtter Failure.; e.printStackTrace ; System.exit0; } createBankAccounts ; } private static void createBankAccounts { createBankAccountBob, getRandomMoney ; createBankAccountTom, getRandomMoney ; createBankAccountHans, getRandomMoney ; createBankAccountBill, getRandomMoney ; createBankAccountYolanda, getRandomMoney ; createBankAccountDave, getRandomMoney ; } private static Money getRandomMoney { int cents = int Math.random 100000; return new Moneycents; } All the interesting things happen in createActivationGroup and createBankAccount . Heres createActivationGroup : private static ActivationGroup createActivationGroup throws ActivationException, RemoteException { Properties pList = new Properties ; pList.putjava.security.policy, d: \\java.policy; ActivationGroupDesc.CommandEnvironment configInfo = null; ActivationGroupDesc description = new ActivationGroupDescpList, configInfo; ActivationGroupID id = ActivationGroup.getSystem . registerGroupdescription; return ActivationGroup.createGroupid, description, 0; } Not surprisingly, this creates an instance of ActivationGroup , which corresponds to a set of activatable servers. That is, you can think of that ActivationGroup as a container that contains a set of ActivatableServer s. Each ActivationGroup is associated with an instance of ActivationGroupDesc , which corresponds to a JVM that the activation daemon will launch when required. One of the most common ways to configure the JVM that the activation daemon will launch is to pass it an instance of java.lang.Properties . The instance of Properties can set any system parameters that you may want to pass to the JVM, including, for example, all of the parameters we defined in Chapt er 16 . The most important parameter given to an ActivationGroupDesc , or at least the one most commonly given, is the security policy of the JVM that will be created. This code snippet creates a simple JVM description i.e., setting a security policy, but nothing else and an instance of ActivationGroup . It then associates the two. When the activation daemon needs to do something with this particular ActivationGroup , it will use ActivationGroupDesc to create a JVM. Now that we have a description of the JVM, we need to describe the individual servers. We do this in createBankAccount , as follows: private static void createBankAccountString owner, Money money { try { ActivationDesc aD = createActivationDescowner, money; Account account = Account Account_Impl.registeraD; Naming.rebindowner, account; } catch Exception e { System.out.printlnFailed to create account for + owner; e.printStackTrace ; } } private static ActivationDesc createActivationDescString owner, Money moneyToStart throws ActivationException, RemoteException, IOException { return new ActivationDesccom.ora.rmibook.chapter17.activation. Account_Impl, file:D:Classes, new MarshalledObjectmoneyToStart; } The URL for finding classes in this example, file:D:Classes specifies a directory or an HTTP path and must, therefore, end with a . If it doesnt, the class files wont be found. If we unroll the code a little, we see that, for each activatable server we wish to create, the following lines of code are executed: new ActivationDesccom.ora.rmibook.chapter17.activation.Account_Impl, file:D:Classes, new Mars halledObjectmoneyToStart; Account account = Account Account_Impl.registeraD; Naming.rebindowner, account; Each of these lines accomplishes a different task. The first line creates an object that contains enough information to create a server. In particular, when we examine the arguments passed into the constructor of ActivationDesc , we see: • The complete name, including the package, of the server class • A URL where the class file can be found or downloaded • An instance of MarshalledObject containing all the initialization information that our instance of Account requires This is enough information to create an instance of Account_Impl . Recall that the Activation Framework insists that any activatable server has a two-argument constructor that takes an instance of ActivationID and an instance of MarshalledObject . The Activation Framework will create an instance of ActivationID at launch time, invoke the constructor using the URL and classname to find the .class file, and then pass in a copy of the instance of MarshalledObject . The second line of code calls a static method, register , defined on the Activatable class. register actually makes a remote call to the activation daemon, passing it ActivationDesc ActivationDesc implements Serializable , and getting a stub in return. Since the activation daemon received a copy of the ActivationDesc , it has enough information to launch the server when necessary. Note that the returned stub is an instance of Account_Impl_Stub , which is the stub class generated by rmic . You still need to run rmic on your servers in order to use activation. The third line of code binds the stub returned by the activation daemon into the RMI registry so that the server is available to clients. This all works with our naming service as well. Nothing in the Activation Framework assumes that the naming service from which the client retrieves stubs is the RMI registry. In fact, nothing in the Activation Framework assumes that a naming service is involved at all. Theres a very nice synergy between our second implementation of factories and the Activation Framework. The application-specific factory provides client- friendly methods for lookup and adds things such as persistence and lock management. However, it delegates the actual launching of the servers to the activation daemon.

17.6.2 Deploying an Activatable System