Introduction—First principles—How it works—Writing an activatable server—Registration—Building an activatable server—Runtime setup—the Unreferenced
interface—Which servers should be activatable—Activation system as a registry—Debugging—Activation Groups in Win32—Activation Clients—Remarks—Exercises
In Chapter 7 we discussed the “unicast” server—the simplest form of RMI server. This chapter builds on the information provided there and describes how to write activatable servers—servers that can be activated by the activation system if they are not already running. You can find out more about other server types in Chapter 14 and Chapter 17.
As we have seen, “unicast” RMI servers are based on the UnicastRemoteObject
class, or exported by its static exportObjec
t method. A client's remote reference to such an RMI server remains valid as long as the server itself remains running, and no longer. Once the server has exited, or has been unexported, the reference is no longer of any use. It no longer refers to anything. It will fail if the client uses it to invoke a remote method. It cannot usefully be saved and restored across a system shutdown. A client of such a server has to go through the process of obtaining a remote reference (a stub) every time it runs, starting with a registry lookup.
Activation removes this restriction. A remote reference to an “activatable” RMI server remains valid for as long as the server it refers to remains registered with the RMI activation system. Any time the reference to the server is used, the server will be automatically restarted by the activation system if it is not currently running. The remote reference itself can usefully be saved and restored across system shutdowns.
You can think of a remote reference to an activatable object as a “ persistent” reference.
An activatable server is one which the activation system can activate—create—on demand. To be activatable, an RMI server must:
be registered with the activation system
have an accessible constructor of a particular form for invocation by the activation system
export itself when so constructed, either by calling the appropriate constructor in Activatable
, if the class is derived from Activatable
, otherwise by calling Activatable.exportObject
.
An activatable server is registered by calling Activatable.register
. When registered, it is associated with an activation group, a codebase, and a (possibly null) initial argument.
We deliberately use the term “activatable server” rather than “activatable class”. An activatable server is specified by its activation descriptor, i.e. by the tuple of {activation group ID, codebase, class, initialization argument}. The activatable class is only one element of the tuple.
An activatable server is registered in a specific activation group.
An activation group is a group of zero or more activatable servers. Each activation group runs in a separate Java virtual machine. An activation group is registered with the activation system. When required, it is created by the activation system in a its own JVM.
The RMI activation system is an instance of the interface
ActivationSystem
provided by the implementation. In the Sun JDK it is provided in the form of the rmid program. A reference to the activation system is obtained via the static methodActivationGroup.getSystem
.
The codebase of an activatable server is the URL at which its class files can be found. This is the same concept as the normal RMI codebase.
The initialization argument of an activatable server is a MarshalledObject
which is supplied to the constructor invoked by the activation system during activation. This MarshalledObject
is initially constructed by the program which registers the server. It may contain anything you like as long as it is serializable; it may contain nothing, or it may be null.
An activatable server can change its own initialization argument, by obtaining its own activation descriptor—via
ActivationSystem.getActivationDesc
—and then resetting the activation descriptor with a new value for the initialization argument—viaActivationSystem.setActivationDesc
. This technique is useful for activatable servers which require progressively different initialization arguments for each activation. In other words, this mechanism provides a simple way for an activatable server to save and restore its own status. The initialization argument can benull
at any stage of this process, including the initial stage.
Figure 10.1 shows the major components of the activation system.
When a client receives a stub for an activatable server, the stub contains a special form of RemoteRef
which initially contains a null
“real” RemoteRef
and an ActivationID
for the remote object. When the stub is first used for a remote method invocation the RemoteRef
is null
, so the stub engages in a protocol interaction with the activation system daemon rmid in the remote host.[1] The result of this protocol interaction is a live RemoteRef
which the stub can then use to invoke the remote method.
Behind the scenes, the activation system has taken the following steps:
If any of these steps fails, an ActivateFailedException
is thrown to the client.
As we saw when discussing unicast servers in §7.3, there are essentially three ways to write a server using the Activatable
class:
The discussions in §7.3 and §7.4 apply equally to activatable servers, with the exception that an activatable server must export itself on construction. In addition, the following extra steps are required when defining an activatable server:
These steps are discussed in detail below.
An activatable server must provide a public constructor of the form
ActivatableXXX(ActivationID id,MarshalledObject data)
where ActivatableXXX
should be replaced by the name of the class being constructed. This constructor is used by the activation system to construct the server on demand.
This constructor is required to do one of two things, depending on whether or not the server is derived from Activatable
. These two possibilities are discussed separately below.
If the activatable server we are writing is derived from Activatable
, the constructor described above (the “Activation” constructor) must call one of the following constructors for its Activatable
base class:
Activatable(ActivationID id, int port) Activatable(ActivationID id, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
depending on whether it does or does not want to specify client and server socket factories. The call will be via one of the forms:
super(id, port); super(id, port, csf, ssf); // csf/ssf are the client & server socket factories
By the rules of Java, if the server we are writing extends Activatable
directly, this invocation will be in the server class itself; otherwise it will be in that class in the server's inheritance chain which does extend Activatable
directly.
If the server we are writing is not derived from Activatable
, the constructor we have just described (the “activation” constructor) must call—directly or indirectly—one of the following methods:
Activatable.exportObject(Remote remote,ActivationID id,int port) Activatable.exportObject( Remote remote, ActivationID id, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf)
depending, again, on whether it does or does not want to specify client and server socket factories. These methods are public and static in Activatable
and so are accessible to all classes.
Unlike the export step for UnicastRemoteObject
, which can be deferred, the export step for Activatable
s must be complete when the constructor exits.
An activatable server can extend RemoteServer
or RemoteObject
. In this case, the server inherits remote object semantics from RemoteObject
. Its behaviour under cloning and non-RMI serialization is up to you. Other than inheriting remote object semantics and various public static methods, such a server is identical to a server which extends a class other than Activatable
, RemoteServer
, and RemoteObject
.
Registration is the process of making an activatable server known to the activation system as an entity which can be created and activated on demand.
This should not be confused with the functions of the RMI registry, which provides a name binding/lookup service.
The process of registration is rather complex. This is mainly because of the complications added by activation groups and initialization arguments. Further reasons are discussed in §10.15.
The basic registration steps are illustrated in Example 10.1.
Example 10.1. Registering an activatable server
String file = ...; // URL of your security policy file String location = ...; // Codebase URL MarshalledObject data = ...; // initialization argument for activatable Properties props = new Properties(); props.put("java.security.policy",file); // At this point you may set other system properties for the group ActivationGroupDesc.CommandEnvironmentace = null; ActivationGroupDesc groupDesc = new ActivationGroupDesc(props,ace); ActivationGroupID gid = ActivationGroup.getSystem().registerGroup(groupDesc); ActivationDesc desc = new ActivationDesc (gid,MyActivatable.getName(),location,data); MyRemote rx = (MyRemote)Activatable.register(desc);
In this example:
MyRemote
is the name of a remote interface
MyActivatable
is the name of an activatable class which implements the remote interface
initialization of the variables file
, location
, and data
is up to you
data
may be null
.
These registration steps are discussed in detail below.
An activatable server is registered in an activation group, so we must first consider registration of activation groups.
To register an activation group, create an ActivationGroupDesc
, using one of its public constructors, and register it using ActivationSystem.registerGroup
. This returns an ActivationGroupID
which you then supply to the object registration method discussed below.
Let's consider the process step by step.
Construct an instance of Properties
, in which you should specify the security manager settings, including the absolute location of the security policy file for the group.
Construct an instance of ActivationGroupDesc.CommandEnvironment
—usually null
, unless you want to use a non-standard path or options for the java command executed when the group is activated.
Register this group and obtains its group ID. This returns an ActivationGroupID
, which you can subsequently use to register objects within the group, and to unregister the group.
Save the group ID. This group ID is a precious thing. If you don't want to keep creating groups ad infinitum, you should save the group ID somehow—typically in a file—via serialization.
Sun's activation tutorials used to state that you should now explicitly create the group in the current JVM by calling ActivationGroup.createGroup
. This is unnecessary and undesirable: instead, just use the constructor for ActivationDesc
that takes a groupID
parameter. See the discussion in §10.15.2.
The process of registering an activatable server is the same whether or not the object is derived from Activatable
.
To register an activatable server, you need an activation descriptor (ActivationDesc)
; to create an ActivationDesc
you need the group identifier (an ActivationGroupID
) obtained in the previous step, and the activatable server's class and initial data if any.
Create an activation descriptor (an ActivationDesc)
, specifying the activation group ID;[2] the object's class name as a String
, including package qualifiers—typically the result of Class.getName
; a location—a URL from whence the class definition can be loaded when the object is activated; and either a null
or a MarshalledObject
containing initial data for the object.
Register this activation descriptor. The result of this method is an object of type Remote
: it is a remote reference—a remote stub for the activatable erver—which you can use in the same way as the result of a registry lookup.[3]
Save the remote reference. Like the activation group ID, this remote reference is also a precious thing.
You can save it in a file (via serialization), or in a persistent naming service if one is available.
You must ensure that the reference is serialized somewhere, possibly by a client, otherwise the registration is effectively lost. Registration gave you a persistent remote reference, but you must make it persist somewhere, otherwise the whole exercise has been pointless.
The activation system lacks
lookup
andlist
methods for group IDs, activation IDs, and remote references to activatable servers; otherwise it would be possible to use the activation system's own database rather than have to savegroupIDs
and activatable references yourself. See also §10.12.1.
The saved reference can be retrieved and used, behaving as a “faulting” reference to the activatable server. The saved reference remains valid after system reboots, and on different host computers connected to the same network.
You will probably also bind the reference to a name in the RMI registry: perhaps immediately, to make the activatable server immediately available to clients; or you can arrange a registry initialization program to “prime” the registry each time it starts up, using serialized remote references saved as described above. You could also use the persistent registry technique discussed in the Exercises of Chapter 6.
The activation system provides two other means of registering activatable servers: during construction and during export. These are briefly discussed in §10.15.
It's rather difficult to unregister an activatable server. The Activatable.getID
method has protected member access, so only the activatable object can unregister itself, unless you add a public accessor method for the ID to your activatable server. In the case of an activatable server not derived from Activatable
, you must also save the activation ID provided on construction, so that this accessor can return it.
The result of a single registration is a “singleton” server. Only one instance of the server will be activated, regardless of the number of client connections. If that's not what you want, you should review whether the server in question needs to be activatable. See the discussion in §12.10. Alternatively, you should register the server multiple times and arrange to distribute the resulting stubs according to your requirements, e.g. to a load-balancing scheme.
This process is no different from the process of compiling a non-activatable server, described in §7.7.
The only run-time setup required for activatable servers is to ensure that the activation daemon rmid is running, and perhaps to bind the activatable stub(s) into the RMI registry. You can accomplish the latter by a variant of the registry load program described in Chapter 6.
You must specify a codebase at least once, perhaps twice, when setting up activatable servers:
These actions occur at different phases of execution, so the codebase may have to be specified twice.
Activatable RMI systems which worked under JDK 1.2.2 do not work unchanged under JDK 1.3. Lurking deep in the “Summary of New Features and Enhancements” for JDK 1.3 is the harmless-looking statement “rmid now requires a security policy file”.
This change was introduced because the
ActivationGroupDesc
structure provides the ability to register and activate an arbitrary executable, not just a Java program. Obviously this constitutes a security hole. Prior to JDK 1.3 (actually prior to JDK 1.2.2_006), this was addressed by the restriction that activatables can only be registered from programs running in the same host as the activation daemon, and the assumption that unprivileged code is unable to connect to port 1098. A stronger and more fine-grained security model was required.
To satisfy the new activation security semantics from JDK 1.3 onwards, you must specify one of the following in the command line for rmid:
no security, by starting rmid with the command-line argument -J-Dsun.rmi.activation.execPolicy = none
; this replicates the JDK 1.2 behaviour (not recommended, except for temporary development situations)
a security policy file, by starting rmid with the command-line argument -J-Djava.security.policy =
file
, where file is the name of an rmid policy file as described in the JDK documentation for rmid
an alternate security policy, by starting rmid with the command-line argument -J-Dsun.rmi.activation.execPolicy =
classname
, where class-name is the name of an alternate rmid policy class as described in the JDK documentation for rmid.
If you don't specify one of the above security solutions to rmid, or if the security solution doesn't grant all the required permissions, attempts to use activatable stubs in clients will fail with the exception java.security.AccessControlException
for a missing ExecPermission
or ExecOptionPermission
.
Note that
AccessControlException
extendsSecurityException
which in turn extendsRuntimeException
, so this exception will only be caught if there is a handler in the client for one of those exceptions.
During development, specifying an activation execPolicy
of none may be used as a stopgap. In production, you normally would supply an rmid policy file. This must contain entries granting ExecPermission
s for all the executables used by the activation daemon, normally just java (and perhaps java_g during development), and ExecOptionPermissions
for all the system properties you have specified when defining activation groups, including the java.security.policy
file for the group.
The rmid security policy file of Example 10.2 can be used as a starting point.
Example 10.2. Activation security policy file
// Security policy file for rmid (activation daemon) grant { // permission to execute the specified program(s) permission com.sun.rmi.rmid.ExecPermission "d:\jdk1.3\bin\java"; // adjust to suit your installation permission com.sun.rmi.rmid.ExecPermission "d:\jdk1.3\bin\java_g"; // adjust to suit your installation // permission to set the specified file as the security policy file for the group permission com.sun.rmi.rmid.ExecOptionPermission "-Djava.security.policy=d:\projects\RMIBook\rmidgroup.policy"; // permission to set the following properties permission com.sun.rmi.rmid.ExecOptionPermission "-Djava.security.debug=*"; permission com.sun.rmi.rmid.ExecOptionPermission "-Djava.rmi.*"; permission com.sun.rmi.rmid.ExecOptionPermission "-Dsun.rmi.*"; };
The file supports a wild-card syntax as shown. Sadly, the Java policytool doesn't yet know about ExecPermission
and ExecOptionPermission
, but you can type in the permission names yourself, so you can still use this tool to manage the file. See the JDK documentation for rmid for further information.
An activatable remote object may implement the Unreferenced
interface. This is a reasonable and recommendable thing for activatable remote objects to do.
In §7.6 we saw that the Unreferenced.unreferenced
method is invoked by the RMI system whenever the number of remote clients of a remote object falls to zero. We saw that the callback can occur when remote clients still exist. We also saw that for “unicast” remote objects, it is quite dangerous to use this opportunity to unexport the object, because this causes any future uses of current remote references to it to fail with a NoSuchObjectException
in the client.
However, if the remote object is activatable, this difficulty disappears. All remote references to such remote objects remain valid and usable until the object is actually unregistered from the activation system.
When an activatable object receives an unreferenced
callback, it is perfectly in order to de-activate the object. This implements an “exit when idle” strategy:
If on the other hand you never unexport the remote object, it will never exit while the activation system (i.e. rmid) remains running. Both of these strategies are reasonable: which of them you want is up to you.
You should call Activatable.inactive
rather than Activatable.unexportObject
. The inactive
method first unexports the object if necessary, then tells the activation system that the object is inactive; i.e. that if further references to it are used, it needs to be reactivated first. Don't just unexport the object yourself without telling the activation system.
The unexport/inactivation process also clears any local references to the object held by the RMI system: as soon as all other local references held by the application code are released the object can be garbage-collected.
The return value of inactive
is true
if the object could be unexported successfully or was already unexported, otherwise it is false
. This is similar to the return value of the various unexportObject
methods.
An implementation of the “exit when idle” strategy is shown in Example 10.3.
Note that the object is not actually garbage-collected until the JVM's garbage collector decides to do so, which may be never, and which may vary from platform to platform, JDK to JDK, and JVM to JVM. You can help things along by calling System.gc
and System.runFinalization
in the unreferenced
method, but this doesn't guarantee anything. On the other hand, the activation group itself exits when there are no more active activatables in it: this causes the entire JVM devoted to the group to exit. This is garbage-collection with a vengeance.
Example 10.3. Implementing "exit when idle" in activatable server
// Unreferenced interface method // Strategy is "exit when idle" public void unreferenced() { try { boolean exiting = Activatable.inactive(getID()); if (exiting) RemoteServer.getLog().println("unreferenced and inactive"); } catch (RemoteException e) { // If this fails for any reason, // we are either still active or already inactive ... } }
As a general rule, most of your RMI servers should be transient, i.e. exported by UnicastRemoteObject.exportObject
. The criterion for choosing which servers should be activatable is often the same as for choosing which servers are bound into the registry: the “outermost” servers, the ones which clients will encounter first.
Remember, “activatable” implies “persistence” of the stub, and “persistent” and “transient” are opposites. The more transient your server, the less likely it is to be useful as an Activatable.
The activation system—the rmid program—contains a standard RMI registry running on port 1098. It is possible to use this as the registry for your entire RMI application: this saves you from running the RMI registry program. You can verify this for yourself by starting rmid, and then running the ListRegistry
program of §6.11.1, with the command-line argument rmi://localhost:1098
. You will see that the program succeeds, meaning that it has located a registry, and that the activation system has registered itself with the name “java.rmi.activation.ActivationSystem”.
In fact this is how ActivationGroup.getSystem
works. Essentially, it returns the result of:
Naming.lookup("rmi://localhost:1098/java.rmi.activation.ActivationSystem")
cast to ActivationSystem
, throwing an ActivationException
if the activation system is not bound in the local registry at port 1098.
You may find it useful to run rmid with the system property sun.rmi.server.activation.debugExec
set to true
and monitor its output. This setting causes the activation system to log all attempts to activate groups and servers, and all exceptions arising from those attempts.
You may also find it useful to run rmid with -C-Djava.rmi.server.logCalls = true
. The -C mechanism transmits the argument following to all activation-group processes started by rmid. In this case it enables remote call logging in all activation groups, which you probably want at all times.
Sun has made an activation debugging utility available on a non-supported basis.[4] This utility is a tool to read and print the contents of an rmid log directory, i.e. the Activation database. The output is a list of all the registered activation groups and activatable servers, and a log of actions taken by rmid.
On Win32 platforms you might expect to see rmid, which runs in a command window, spawn other command windows to execute the JVMs of activation groups. This does not happen; instead, the command window of rmid contains the output of all the JVMs of all the active activation groups. Multiple JVMs are started as separate processes, but their inputs and outputs are all piped to the input and output of the rmid process, which appear in the rmid command window.
Clients of activatable servers are largely indistinguishable from clients of unicast remote object servers. The only differences are that they may encounter activation delays and activation exceptions.
Clients of activatable servers need to allow for time for activation before they think about timing-out. When a client invokes a remote method in an activatable server which is not currently running, the activation system at the server host must activate the server. This is generally pretty quick, as it's not much more than the instantiation of a Java object: the client generally doesn't need to be too aware of this issue.
However, if the activation group in which the server was registered isn't running, the group itself must be activated and then the server activated by the group. Activating a group involves the creation of a new process, which is a heavyweight activity by comparison with creating a Java object, especially if the host is heavily loaded.
An invocation of a remote method in an activatable server may throw an ActivationException
. Now, you are already obliged to catch RemoteException
by the signature of the remote method, and ActivationException
extends RemoteException
, so it is therefore perfectly possible not to catch it explicitly, as illustrated in Example 10.4.
Example 10.4. Simplistic exception-handling for activatable call
try { myRemote.remoteMethod(); } catch (RemoteException e) { // some RMI exception caught }
However, activation exceptions generally indicate a serious problem at the other end, and you should give careful consideration to catching them explicitly. In particular you should treat UnknownGroupException
and UnknownObjectException
, which both extend ActivationException
, as extremely serious conditions. ActivateFailedException
indicates that the object couldn't be activated for some reason, which may be transient; UnknownGroupException
and UnknownObjectException
indicates that the client is using an invalid activatable reference: one which doesn't refer to any known activatable service at the host. This is illustrated in Example 10.5.
Example 10.5. Improved exception handling for activatable call
try { myRemote.remoteMethod(); } catch (ActivationException e) { // unknown group or object: permanent error with stub } catch (ActivateFailedException e) { // some other activation problem, possibly transient } catch (RemoteException e) { // some other general RMI problem }
The java.rmi.activation
package is the most complex part of the RMI specification. We have already discussed those parts of it which really constitute its external API and which are of practical use. The rest of it is of no practical interest to the programmer and can be ignored. The following discussion takes you “under the hood” of activation, for those who are interested.
Activation is itself a set of remote services, built “on top of” RMI. Just like any other RMI application, it is built out of remote interfaces and implementations of remote services. Most of these are part of the internal design, not part of the activation API used by Java programmers (other than those implementing Activation systems).
Figure 10.2 shows all the components of the Activation system. In the diagram, single-ended arrows indicate RMI client/server relationships, with the arrow-head located at the server end.
You don't need to know much about this detailed version of the block diagram, or about most of the Activation package. The following notes discuss the remaining interfaces, classes, and methods. In general they are of no concern to RMI programmers unless they are implementing Activation themselves.
ActivationGroup.createGroup
is called by the activation system. Calling it yourself is not generally appropriate. The setup programs in Sun's activation tutorial used to create a group, but only because they used the wrong constructor for ActivationDesc
, the one which doesn't take a groupID parameter, but uses the default activation group—which must exist, otherwise an ActivationException
is thrown.[5] Creating the group in the setup program makes no sense; using the wrong constructor and then working around its behaviour is the wrong solution. Use the constructor which does take an activation group ID.
Activatable
has constructors and exportObject
methods which implement a simultaneous registration/export facility. Activatable servers can simultaneously register and export themselves, or be simultaneously registered and exported by other code. You must be running in an activation group, otherwise an ActivationException
is thrown.
It is difficult to see the point of this feature. It is simpler to keep registration code—setup programs—separate from activatable code. No doubt there are times when you continually want to register new activatable servers, but consider the risk and costs of combinatorially exploding the number of activatable servers. If you need to register and export a server immediately, you can already do it yourself with the rest of the activation API.
All the non-static ActivationGroup
methods—activeObject
, inactiveGroup
, inactiveObject
, and newInstance
—implement the ActivationInstantiator
interface. Ignore them.
ActivationGroupDesc
has a constructor with which you can specify an alternate implementation class for the associated ActivationGroup
. Ignore it. “Rolling your own” implementation of ActivationGroup
is impractical and unnecessary.
ActivationGroupID
and ActivationID
both have public constructors. Ignore them. These items are obtained, not constructed:
ActivationInstantiator
, ActivationMonitor
, and Activator
are remote interfaces implemented by ActivationGroup
and two hidden internal classes respectively. Ignore them.
ActivationSystem
exports quite a few methods. The only ones of interest are:
The registerObject
method looks interesting, but in fact it is not much use, as it doesn't return a remote stub: use Activatable.register
instead.[6]
1: | Modify the remote date/time server exercise of Chapter 7 to be activatable. Test the system and show the output. |
2: | Modify this server to implement the “exit when idle” strategy. |
3: | Modify this server to not implement the “exit when idle” strategy, but instead to be forced into existence each time rmid starts. |
4: | Study §10.15. Write a public final class called |
5: | Modify the server created in the exercises above to use the class of the previous exercise rather than call the activation API directly. Re-test the system and show the output. |
6: | Modify the “exit when idle” version of the activatable remote date/time server, to save its date of last inactivation by resetting its ActivationDesc, and to display this information when activated. |
[1] To be specific, it calls ActivationID.active
, a local method which calls the remote method Activator.activate
in the target host's activation daemon, using a bootstrap technique similar to that used to contact the RMI registry.
[2] Do not omit the groupID
—do not use the constructor for ActivationDesc
which omits this parameter. Otherwise, you must do the ActivationGroup.createGroup
step described above.
[3] Note this asymmetry: registering a group returns its ID, which can be used to register objects in it, or unregister the group; registering an activatable server returns a Remote
, which can be used to activate it. In another asymmetry, Activatable.register
returns a Remote
, but ActivationSystem.registerObject
returns an ActivationID
. The former is called by users, the latter is called by the system. (See also the footnote for §10.15.6.)
[4] RMI Utilities (reg, rmipm), posting to the RMI Mailing List, 29 November 1999. It was also at http://developer.jini.org/exchange/users/aecolley/rmiutil, under the name rmipm.jar
at the time of writing, but search for the mailing list item. You should also read the associated document. At the time of writing, you had to be a member of the Jini Sun Community Source Licence programme to access this URL. You can join this programme at no charge; see directions for joining elsewhere at the Jini developer site http://developer.jini.org.
[5] According to the implementation and the online JDK documentation. This disagrees with several statements in the RMI specification, §7.4.8. The behaviour described in the specification was implemented in beta releases of JDK 1.2, but was changed prior to the release of JDK 1.2FCS. This is probably the source of the confusion in the activation tutorial.
[6] This method calls ActivationSystem.registerObject
and then constructs an activatable stub. If you prefer complication, you could use ActivationSystem.registerObject
and then ActivationID.activate
.
3.141.30.210