A 20 minute crash-course in Voyager 3.3

advertisement
A 20 minute crash-course in Voyager 3.3
By G.D. Georgovassilis
Introduction
During our search for JAVA agent platforms we made an encounter with
Objectspace’s Voyager (www.objectspace.com/voyager.html) , which has been
discussed extensively in [1]. In this document I’ll investigate Voyager’s technical
nature by discussing a simple demonstration.
What do you need?
We tested the Voyager ORB platform on Windows 95, 98 and 2000 with JDK
1.2.x and 1.3. The manufacturer claims it works with JDK 1.1.8 and later versions
under Windows, SunOS and Linux. Voyager is shipped in two bundles (ORB, ORB
Professional) none of which is free-of-cost for commercial use. However they all
contain documentation as well as programming examples.
Voyager’s installation is simple; yet the CLASSPATH environment variable
has to be completed, as usual. Information on necessary settings is provided in the
environment.txt in the installation directory.
How does programming with Voyager work?
The entire Voyager functionality is encapsulated into a handful of classes. So
we don’t need a precompiler or an extra utility, like we would utilize it with RMI.
Usually, one would import a com.objectspace.voyager.*. Moreover you don’t
have to extend (although you can if you want to) a standard Voyager class to obtain
it’s functionality. Voyager provides the programmer with a few global objects
(Voyager, Proxy, Agent etc) which encapsulate the entire platform functionality.
Voyager does not really distinguish between local and remote objects.
Instead, an object has various facets :



Factory objects are the simplest and least functional type of objects. They
already have the ability of performing remote transactions. They can invoke
other remote objects’ methods, move to another host etc.
Exported objects are Factory Objects that have been bound to a TCP port.
Exported objects can receive names and thus can be accessed as remote
objects from other objects. So, again they are not different in nature from
Factory Objects, they just are Factory Objects with a name (more about
naming conventions later on).
Agents are Voyager-aware Factory Objects, meaning that somewhere inside
the JAVA code an import com.objectspace.voyager will appear and
that the class will make use of the corresponding abilities.
From a programming point of view, each class predestinated for use as a remote
object should implement it’s interface. This is not absolutely necessary, but the
programming techniques for non-interface classes are somewhat more complicated.
WIZE – an example
In the simple demo I’ve written we’ll see various ways of constructing and
working with remote objects.
Nikomachos is a librarian in Alexandria. He has found that his shelves are
empty and now the good man tries to find material in order to fill them. After a minor
consultation with the pharaoh, he decides to ask three wise men for a quote:
Heraklit of Ephesus
Demokrit of Abdera
Platon of Athens
: “Everything flows.”
: “In depth lies the truth.”
: “Every science separated from justice and virtue
resembles to cunning rather than wisdom.”
Our librarian first visits an oracle and asks Heraklit himself (apparently by
appearing in his dream) for a quote. As we will see, this corresponds to the direct
invocation of a remote object’s (exported object) method.
Demokritos is busy and politely declined seeing him, so Nekomachos will
have the pharaoh’s elite squad abduct Demokritos. This brutal method corresponds
to an attempt to relocate a remote object that has not been created by the process
attempting to perform the move.
After all these preparation Nekomachos has no time left to visit Plato himself,
hence he sends the light-footed Thrasybulos instead to Athens. This would be the
last and most complicated way, doing business via a mobile agent.
For the following discussion I propose to have handy a print-out (or another
window for the online fanatics among us) of the code.
Installing and running Wize
The demo is written for usage on a single system. However it shouldn’t be
difficult to adjust the MS-DOS batch files for usage on a network, just change the
corresponding parameters.
Unzip the contents of the attachment into an empty directory. Change into
that directory and compile the contents : javac *.java. It didn’t work? Did you
include all necessary libraries in the CLASSPATH?
Now run town.bat . Three DOS windows will open, each containing a Java
server process. After they have initialized, start the test application by running
lib.bat .
Keep in mind that only the test application terminates (under normal
conditions), not the other three server applications. They must be terminated
manually.
The servers (Town) expect four command line parameters: The town name,
the town’s philosopher, a quote (included inside “…”) and a TCP port number. You
can start manually various towns on different machines connected to the same
network.
The test application expects three parameters which represent the network
address of each one of the three servers. An address forms like :
//machine_name:port/Philosophers_name_who_lives_in_town
Philosopher.java – IPhilosopher.java
From the first two lines already one can tell: this class is not Voyager-aware.
It does not implement any networking functionality. It represents a philosopher, who
has a name, a town he lives in… and he knows a quote. Unlike the multi-talented
ancient paradigm, our simple philosopher of 1’s and 0’s can only print his name out
to the screen and return a quote – but that shall suffice for now. We will encounter
‘Philosopher’ mainly as a Factory or Exported object.
Town.java
The Town class has a simple function: it provides the Voyager framework to
Philosopher :
- By starting up the platform : Voyager.startup()
- By creating an instance of Philosopher and binding it to a TCP port
IPhilosopher ph=(IPhilosopher) Proxy.export(new Philosopher
(quote, name, town),port);
- By giving it a name through which it can be recognized and accessed by other
remote objects :
Namespace.bind(name,ph);
A few remarks: nearly every method of Voyager classes throw exceptions,
which should or even must be caught. The Town class execution will not terminate
under normal conditions, as no Voyager.shutdown() method is invoked. The
Philosopher object remains bound to the specified TCP port for ever…
It is possible to create and export an object directly on a remote host by
issuing a :
Factory.create(“MyObject”, ”//remote_host:port”)
The Town class has no interface (ITown) because a town serves here just
the purpose of exporting a philosopher. A Town is not a remote object and thus does
not qualify as anything of the three object categories.
Library.java
Again, a class without an interface. And indeed, it is just a server which starts
and hosts an agent while interacting at the same time for demonstration purposes
with ‘Philosopher’ objects that have been exported by ‘Town’s.
As usual, first thing is to make sure the Voyager platform has been started :
Voyager.startup().
The program will attempt to interact with three remote objects. Each of these
objects must be Exported Objects – how would it be possible to access objects that
are not exported? Naturally, when trying to access an object and invoke it’s methods
one has to know what kind of object it is – so the least we can do is import it’s
interface: import Philosopher;
Next we will link up to a remote object :
IPhilosopher ph1=(IPhilosopher) Namespace.lookup(town1);
Notice that when accessing a remote object the class-body is not required,
just it’s interface. From now on, all public methods can be accessed as if the object
would reside on the local machine. Only that it doesn’t. This allows us to perform
difficult and time consuming calculations on remote computers.
Although not demonstrated here, one can pass parameters to the invoked
methods as usual. Well, almost as usual, because any basic types (int etc) do not
apply to this rule - the object variant must be used instead.
In the second section I demonstrate the functionality of the Mobility object.
By creating a mobility facet of an object, it is possible to move it around the network.
The only reason why this attempt ends in a disgraceful exception thrown by
mobility.moveTo("//localhost:3000");
is that I tried to move a remote object created by somebody else. The Library
process did not create the Philosopher yet is trying to move it to it’s local computer
– a Town class created and exported the Philosopher on another computer … and
another process. Luckily it is impossible to remove a remote object one does not own
from another process. The only way to obtain ownership over that object is to
persuade the owner-process to loosen it’s grip on the object. In other words, the
owner process has to move the object away and free any references to it.
In the third section, a mobile agent is created and sent to the town (location)
of a philosopher. The agent is created in the usual way as an exported object :
IMessenger Thrasybulos=(IMessenger) Proxy.export (new
Messenger("Thrasybulos"));
Only the statement :
Agent.of(Thrasybulos).moveTo(ph3,"atTown");
exhibits the ‘mobile’ nature of the object. Again, Thrasybulos remains an object, we
just ‘look’ at it as if it was an agent, which is demonstrated pretty nicely with the
Agent.of() construct. The entire code + data is transferred over the network to the
location of ph3 (which is an exported Philosopher). Thrasybulos has thus left the
localities of our server.
What does the “atTown” string do? It instructs the Agent object to invoke
the Thrasybulos.atTown method once the transfer is complete and Thrasybulos
has reached his destination. This way an agent can resume execution after the
journey. There are more powerful ways of working with agents by extending the
Agent class and overriding it’s methods, as described in the manual.
Although a copy of the object still exists on the local machine, it is not
updated synchronously. This means, that one should not access this object anymore,
until the agent has returned home.
Should implies a form of moral obligation, but as there are no limits for
programmers and other immoral elements, we’ll have now a look at what can be
done with an agent’s lifeless hull as long as it’s spiritual incarnation travels through
the network. One must imagine the whole process like this: After Thrasybulos has left
the library, a copy of his remains there. That copy is completely autonomous until
Thrasybulos returns. The very moment he returns, the copy is replaced by the repatriated object. So don’t wonder if suddenly, after you have changed some values in
a departed object they change again :) Should Thrasybulos die on the way (network
error, server crash etc) and news of his death reaches the library, the copy will be
claimed by the garbage collector instantly. So a try {} … catch {} pair is quite
suitable here.
This is the reason why I decided to periodically poll the value of
Thrasybulos.hasReturned. The class’ constructor defaults this value to false.
However, the agent is programmed to set it to true on the event of his return. Thus,
the only way this method is every going to return a true value is in case of the
agent’s return. Nothing more, nothing less. A small note : Thread.sleep(1) makes
sure that we don’t waste too much processing time during the poll.
Messenger.java – IMessenger.java
As you will probably remember, Messenger implements a mobile agent. One
good thing Voyager has is that we won’t have to extend a predefined class - which
increases flexibility and leads to clean code. A quick glance at Library.java will be
helpful at this point.
BackHome() is called from outside, usually as the result of a successful
Agent.of(this).moveTo(…). After writing some info on the screen it sets the
backInTown property to true, signalising to the library that Thrasybulos has
returned.
The atTown(IPhilosopher ph) method is somewhat more interesting.
Again, it is called as the result of a successful moveTo() call. The parameter is a
remote object of the town’s local philosopher. As usual, ph’s methods are accessible
from here. However, the fact that the agent runs now at the same machine on the
same platform with a philosopher does not allow it to access ph’s private properties
or even force it to relocate…
After having spoken to the philosopher, Thrasybulos sails for Alexandria. The
Agent.of(this).getHome() method is quite handy at this point as it contains a
string representation of the home address. With a single line of code :
Agent.of(this).moveTo(Agent.of(this).getHome(),"backHome");
the agent has returned to the waiting library. Upon arrival, the agent’s backHome()
method is called, as discussed earlier.
Performance issues
Voyager is neither a weight watcher nor a sprinter. According to Objectspace,
the developers focused on flexibility, security and minimized network transfer which
might pay off during the development phase. Either way, the Wize demo terminates
on a local 450 MHz PC in 4 seconds (loading times not included) which is not
overwhelming.
[1] Java-based Agent Development Toolkits: A Survey
Thomi Pilioura, Isambo Karali, Michalis Hatzopoulos
Download