New York
University
Computer
Science Department
Courant
Institute of Mathematical Sciences
Session 14:
The Architecture of Aglets
Course Title: Extreme Java Course Number: g22.3033-007
Instructor: Jean-Claude Franchitti Session:
14
Summary
Mobile agents have been around for many years, but they haven't yet entered the
mainstream. Aglets is a mobile-agent technology built on top of Java.
Aglets: Not just for shoelaces anymore
According to Webster's Ninth New Collegiate
Dictionary, an aglet is:
1.
aglet
1: the plain or ornamental tag covering the ends of a lace
2.
aglet
2: any of various ornamental studs, cords, or pins worn on clothing
In other words, aglets are those little plastic
tubes on the ends of your shoelaces. Now, however, there is a new definition of
the word aglet: a Java-based autonomous software agent.
As used here, a software agent is a program
that can halt itself, ship itself to another computer on the network, and
continue execution at the new computer. An agent doesn't restart execution from
the beginning at the new computer; it continues where it left off. For example,
imagine an agent that increments a counter starting with zero. If that agent
counts from zero to ten, then halts and ships itself to another computer, it
will not start counting again at zero. It will continue counting starting with
ten, because that was where it left off when it halted at its previous computer.
Agents are autonomous because they decide
where they will go and what they will do. They control their lifetimes. They
can receive requests from external sources, such as other agents, but each
individual agent decides whether or not to comply with external requests. Also,
agents can decide to perform actions, such as travel across a network to a new
computer, independent of any external request.
Aglets versus applets
The Java aglet extends the model of network-mobile
code made famous by Java applets. Like an applet, the class files for an aglet
can migrate across a network. But unlike applets, when an aglet migrates it
also carries its state. An applet is code that can move across a network from a
server to a client. An aglet is a running Java program (code and state) that
can move from one host to another on a network. In addition, because an aglet
carries its state wherever it goes, it can travel sequentially to many
destinations on a network, including eventually returning back to its original
host.
A Java aglet is similar to an applet in that it runs
as a thread (or multiple threads) inside the context of a host Java
application. To run applets, a Web browser fires off a Java application to host
any applets it may encounter as the user browses from page to page. That
application installs a security manager to enforce restrictions on the
activities of any untrusted applets. To download an applet's class files, the
application creates class loaders that know how to request class files from an
HTTP server.
Likewise, an aglet requires a host Java application,
an "aglet host," to be running on a computer before it can visit that
computer. When aglets travel across a network, they migrate from one aglet host
to another. Each aglet host installs a security manager to enforce restrictions
on the activities of untrusted aglets. Hosts upload aglets through class
loaders that know how to retrieve the class files and state of an aglet from a
remote aglet host.
The aglet lifestyle
An aglet can experience many events in its life. It
can be:
Created: a brand new aglet is born
-- its state is initialized, its main thread starts executing
Cloned: a twin aglet is born -- the current state of the
original is duplicated in the clone
Dispatched: an aglet travels to a new host -- the state goes
with it
Retracted: an aglet, previously dispatched, is brought back
from a remote host -- its state comes back with it
Deactivated: an aglet is put to sleep -- its state is stored
on a disk somewhere
Activated: a deactivated aglet is
brought back to life -- its state is restored from disk
Disposed of: an aglet dies -- its state
is lost forever
Note that every activity besides creation and
disposal involve either duplication, transmission across a network, or persistent
storage of the aglet's state. Each of these activities uses the same process to
get the state out of an aglet: serialization.
Serializing the state...
Aglet hosts use object serialization, available in
JDK 1.1 or with the RMI (remote method invocation) add-on to JDK 1.0.2, to
export the state of an aglet object to a stream of bytes.
Through this process, the aglet object and the tree
of serializable objects reachable from it, are written to a stream. An object
is serializable if it implements either the Serializable or the Externalizable interface. In a reverse
process, the state of the aglet can be reconstructed from the stream of bytes.
Serialization allows an image of the heap (the heap's state) to be exported to
a byte stream (such as a file) and then reconstructed from that byte stream.
...but not all of the state
The state of the execution stacks and program
counters of the threads owned by the aglet are not serialized. Object
serialization touches only data on the heap, not the stacks or the program
counters. Thus when an aglet is dispatched, cloned, or deactivated, any
relevant state sitting on any stack of a running aglet, as well as the current
program counter for any thread, is lost.
In theory, a software agent should be able to migrate
with all its state: heap, execution stack, and registers. Some will likely
consider the inability of aglets to do this as a flaw in the aglet's
implementation of mobile-agent theory. This feature of aglets arises out of the
architecture of the JVM, which doesn't allow a program to directly access and
manipulate execution stacks. This is part of the JVM's built-in security model.
Unless there is a change to the JVM, aglets and any other mobile Java-based
agent will be unable to carry the state of their execution stacks with them as
they migrate.
Before it is serialized, an aglet must place on the
heap everything it will need to know to be resurrected properly as a newly
activated aglet, a freshly dispatched aglet, or a clone. It can't leave any of
this information on the stack, because the stacks won't be reproduced in the
aglet's new life. As a result, the aglet host informs an aglet that it is about
to be serialized so that the aglet can prepare itself. When the aglet is
informed of an impending serialization, it must place onto the heap any
information it will need to continue its execution properly when it is
resurrected.
From a practical standpoint, the inability of an
aglet to migrate with its execution stacks is not an unreasonable limitation.
It simply forces you to think a certain way when you write aglets. You can look
at an aglet as a finite state machine with the heap as the sole repository of
the machine's state. If at any point in an aglet's life you can know what state
it is in by looking at its heap, then it can be serialized at any time. If not,
then you must have a way to record sufficient information on the heap just
prior to serialization such that you can continue properly when the aglet is
resurrected.
Also, even though the inability to serialize
execution stacks necessitates giving aglets a warning prior to serialization,
such warnings probably are a good idea anyway. It is difficult to think of a
case in which an aglet wouldn't want to know it was about to be serialized and
why. It may need to finish some incomplete process before allowing the
serialization, or it may want to refuse the action that requires the
serialization. For example, if an agent is told it is about to be serialized
and dispatched to an aglet host in Silicon Valley, it may refuse and decide
instead to dispatch itself to a host on an island in the South Pacific.
How to write an aglet
The process of writing an aglet is in many ways
similar to the process of writing an applet. To create an applet, you subclass
class Applet. To initialize an applet,
you override the init() method, the starting point
for any applet. You can use init() to build the user interface of the applet. If you
wish, you can fire off other threads from init(). If you do this, you also
may override stop() and start() to stop and restart your
threads when the browser leaves and returns to the Web page. If you don't
create any threads in init(), your applet likely will
get at least one thread just because class Applet descends from class Panel. The AWT user-interface
library of which Panel is a part will provide
whatever threads are needed to run the user interface you create in init().
The aglet development and run-time environments
provide a library of Java classes that support the creation and running of
aglets. To create an aglet, you must subclass class Aglet, which includes several
methods you can override to customize the behavior of your aglet. The aglet's
counterpart to the init() method of applets is the onCreation() method. To initialize an
aglet, you override onCreation(). The onCreation() method is invoked only once
in an aglet's lifetime and should be used only for initialization.
The aglet also has a run() method, which represents
the entry point for the aglet's main thread. This is similar to the main() method of a Java
application, except that run() is invoked each time an
aglet arrives at a new aglet host. For example, if you designed a CatAglet that visits nine different
aglet hosts looking for MouseAglets, onCreation() would be invoked only once,
when the CatAglet was first instantiated at
its first host. Once onCreation() completed, run() would be invoked. Each time
the CatAglet arrived at a new host, a
method called onArrival() would be invoked to perform
any initialization. Once onArrival() completed, run() would be invoked to get the
aglet started again at the new host.
Starting run() again each time an aglet is
brought to life illustrates the inability of aglets to transmit the state of
their execution stacks. For example, imagine a HealthyAglet whose run() method periodically invokes
a method named walk(). If, as it is walking, the HealthyAglet is serialized and
transmitted to another host, it wouldn't by default continue executing where it
left off in walk(). It would start over again
at the beginning of run(). Thus, when the aglet is
informed that it is about to be serialized, it would need to record on the heap
that it is walking -- perhaps in an instance variable of HealthyAglet. That instance variable
would be serialized and would migrate with the aglet. When run() is invoked to start the
aglet's new life, the run() method would check the
instance variable, see it was walking beforehand, and call walk().
The callback model
Before any major event in an aglet's life, a "callback"
method is invoked to allow the aglet to prepare for (or refuse to partake in)
the event. This is how an aglet learns that it is about to be serialized. For
example, before an aglet is dispatched to a new location, the aglet's onDispatch() is invoked. This method
indicates to an aglet that it is about to be sent to a new host, the URL of
which is specified as a parameter to onDispatch(). In the body of onDispatch(), the aglet must decide
whether or not to go. If the aglet decides it doesn't want to go, it throws an
exception. If it decides to go, it must complete any unfinished business and
prepare its state for serialization. When it returns from onDispatch(), its state will be
serialized and all its threads terminated. The class files and serialized state
will then be sent to the new host, where the aglet will be resurrected.
The method onDispatch() is a "callback"
method because the aglet host invokes it some time after another method, dispatch(), is invoked. An aglet can
invoke dispatch() on itself or on another
aglet. This callback model for aglets is similar to that of windowing user
interfaces. To repaint an AWT component, for example, you invoke the
component's repaint() method. At some point
later, the system calls back the component's update() method, which in turn calls
paint().
The Aglet class defines these five callback methods, which you can override to
customize the behavior of your aglet:
onCloning() -- called before a clone
operation
onDispatch() -- called before a dispatch
onReverting() -- called before a
retraction
onDeactivating() -- called before a
deactivation
onDisposing() -- called before a dispose
operation (Unlike real life, an aglet can throw an exception if it doesn't want
to die.)
For each of these processes, the Aglet class has a corresponding
method that triggers the action: clone(), dispatch(), retract(), deactivate(), and dispose(). Some time after these are
called, the aglet host will invoke the appropriate callback method.
Each time an aglet begins execution at a host, the
host invokes an initialization method on the aglet. When the initialization
method returns, the host invokes run(). Depending on the event
that precipitated the aglet's new life, the aglet host will choose to invoke
one of these four initialization methods:
onCreation() -- called the first time an
aglet springs to life
onClone() -- called on a clone after
a clone operation
onArrival() -- called after a dispatch
or a retraction
onActivation() -- called after an
activation
Interaction between aglet and host
An aglet interacts with its environment (its aglet host) through an AgletContext object. An aglet can obtain
a handle to its context by invoking getAgletContext(), a method it inherits from
base class Aglet. The aglet context has methods
such as createAglet() and retractAglet(), which allow an aglet to
add new aglets (or get an old aglet back) to its local host.
Interaction between aglets
To interact with each other, aglets do not normally
invoke each other's methods directly. Instead they go through AgletProxy objects, which serve as
aglet representatives. For example, if a BossAglet wishes to make a request of
an EmployeeAglet, the BossAglet obtains a handle to a proxy
object that "represents" the EmployeeAglet. The BossAglet then makes a request by
invoking a method in the EmployeeAglet's proxy, which in turn
forwards the request to the actual EmployeeAglet.
The AgletProxy class contains methods that allow aglets to request
other aglets to take actions, such as dispatch(), clone(), deactivate(), and dispose(). The aglet that has been
requested to take an action can comply, refuse to comply, or decide to comply
later.
The proxy also allows an aglet to send a message,
either synchronously or asynchronously, to another aglet. A Message object is supplied for this
purpose; it carries a String to indicate the kind of
message plus one other optional piece of data, either a String or one of Java's primitive
types. To send a message you create a Message object and pass it as a
parameter to the sendMessage() or sendAsynchMessage() method of the proxy object.
An aglet must go through a proxy object to interact
with an aglet, even if both aglets are in the same aglet host. The reason
aglets aren't allowed to directly interact with one another is that the aglet's
callback and initialization methods are public. These methods should be
invoked only by the aglet host, but if an aglet could get a handle to another
aglet, it could invoke that aglet's callback or initialization methods. An aglet
could become very confused if another aglet inadvertently or maliciously
invoked these methods directly.
The aglet being represented by a proxy might be
local or remote, but the proxy object is always local. For example, if a BossAglet in Silicon Valley wants to
communicate with an EmployeeAglet on a South Pacific island,
the BossAglet gets a local AgletProxy object, which represents
the remote EmployeeAglet. The BossAglet merely invokes methods in
the local proxy, which in turn communicates across the network to the EmployeeAglet. Only aglets, not proxies,
migrate across the network. A proxy communicates with a remote aglet that it
represents by sending data across the network.
You get a proxy to an aglet in one of three ways,
each of which involves invoking a method in the context object:
1.
By
creating the aglet in the first place with createAglet(). (This returns a proxy
object.)
2.
By
searching through an enumeration of local proxies returned by getAgletProxies().
3.
By
supplying an "aglet identifier" and, if remote, an aglet location as
parameters to getAgletProxy(). (Every aglet, upon
creation or cloning, is assigned a globally unique aglet identifier.)
Security
Mobile-agent systems, such as aglets, require high
levels of security, because they represent yet another way to transmit a
malicious program. Before aglets can be used in practice, there must be an
infrastructure of aglet hosts that prevent untrusted aglets from doing damage
but provide trusted aglets with useful access to the host's resources. Security
is amply provided for in Java's intrinsic architecture and in the extra
security features of JDK 1.1, but as with applets, some attacks (such as denial
of service by allocating memory until the host crashes) are still possible.
Currently, the aglet hosts from IBM (named Tahiti
and Fiji) place very severe security restrictions on the activities of any
aglet that didn't originate locally.
Conclusion
Will aglets become as ubiquitous as their plastic cousins, which quietly perch
on the ends of everyone's shoelaces? Aglets represent a good example of
innovation on top of Java's network-oriented architecture, but what new
benefits do they offer developers and end users that client/server, applets,
and servlets don't already offer?