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