![]() |
Sponsored Link •
|
Advertisement
|
Summary
This article teaches you about a simple, yet powerful, new network and distributed programming tool: JavaSpaces. It explains the basic concepts of the JavaSpaces programming model and introduces the compact JavaSpaces API.
This article begins a second thread of the Jiniology series. In June, Bill Venners launched Jiniology with an overview of Jini technology -- a powerful new infrastructure for building and deploying distributed systems that are organized as federations of services. This thread, which will be featured every other month in this column, focuses on JavaSpaces, a core Jini service from Sun Microsystems that provides a high-level means of creating collaborative and distributed applications. If you're building applications with Jini, you'll want to know how to use JavaSpaces to coordinate the participants in a Jini federation. But it's also important to remember that you can use JavaSpaces separately from Jini, as a tool for building general distributed systems in Java. In either case, JavaSpaces is worth a look, because it can significantly ease the design and coding of distributed applications.
In this series, we will begin by introducing you to the unique JavaSpaces programming model, which is quite different from other network and distributed tools with which you might be familiar. In subsequent articles, we will cover the details of the JavaSpaces API and how you can use it to glue processes together into a distributed application, and describe how JavaSpaces interacts with other components of Jini. Throughout the series, you'll see that JavaSpaces is simple (the API consists of only a handful of operations), expressive (a large number of problems can be solved using JavaSpaces), and powerful (you can build sophisticated distributed systems with small amounts of JavaSpaces code).
Let's get started.
write
new
objects into a space, take
objects from a
space, or read
(make a copy of) objects in
a space; Figure 1 depicts several processes (represented by
Dukes) interacting with spaces using these operations. When
taking or reading objects, processes use simple matching, based
on the values of fields, to find the objects that matter to them.
If a matching object isn't found immediately, then a process can
wait until one arrives. In JavaSpaces, unlike conventional object
stores, processes don't modify objects in the space or invoke
their methods directly -- while there, objects are just passive
data. To modify an object, a process must explicitly remove it,
update it, and reinsert it into the space.
Figure 1. Processes use
spaces and simple operations to coordinate
activities |
Spaces are object stores with several important properties that contribute to making JavaSpaces a powerful, expressive tool. Let's take a closer look:
null
to act as wildcards). An
object in the space matches a template if it matches the
template's specified fields exactly. You'll see that, with
associative lookup, you can easily express queries for objects
such as "Are there any tasks to compute?" or "Are there any
answers to the prime factor I asked for?"As this series progresses, we will show you how these properties play a key part in letting you create distributed applications that work well in the Jini environment, where networking is often spontaneous, and processes join and leave the computation dynamically, sometimes because of device or network failure.
We've described JavaSpaces as a new distributed computing model, but its origins can be traced back to Yale University in the early 1980s. There, Dr. David Gelernter developed a tool called Linda for creating distributed applications. Linda consists of a small number of operations combined with a persistent store called a tuple space. These operations are orthogonal to any particular programming language; they are part of a coordination language that can be added to any other computation language. The result of the Linda research was surprising: by using an object store along with a small number of simple operations, you can easily implement a large class of parallel and distributed problems using techniques that alleviate many of the pitfalls of building networked systems. In other words, space-based systems are not only simple (requiring only a few operations), but also expressive (lending themselves well to solving many distributed problems).
Dr. Gelernter's work inspired Sun's JavaSpaces service, and also influenced the design of the lookup and discovery components of the core Jini technology (which you'll see as the Jiniology series progresses). While JavaSpaces inherited the space model from Linda, the designers of JavaSpaces have updated the model in significant ways, leveraging the power of Java objects, Jini, RMI, and object serialization.
Our description so far has been a little abstract, so let's consider a few examples of real distributed applications that you can model as processes exchanging objects through spaces.
Chat systems
Consider a simple multiuser chat system, in which a space serves
as a chat area that holds all the messages making up a
discussion. To talk, a participant deposits message objects into
the space. All chat members wait for new message objects to
appear, read them, and display their contents. Late arrivals can
examine the existing message objects in the space to review
previous discussion. In fact, since the space is persistent, a
new participant can view the discussion long after everyone else
has gone away, and participants can even come back much later to
pick up the conversation where they left off. The list of chat
participants can also be kept in the space and updated whenever
someone joins or leaves the conversation.
Compute servers
Now consider analyzing realtime radio telescope data for signs of
extraterrestrial life (much as the SETI@home project does). Such data
is voluminous, and analyzing it is a computationally intensive
job that is well suited to parallel computation by a network of
computers -- in other words, a "compute server." Using the
JavaSpaces technology, a series of tasks -- for instance, one
task per chunk of data that needs to be analyzed -- is written
into the space. Each participating computer searches the space
for a task, removes it, completes the necessary computational
work, drops the result back into the space, and then continues to
look for more tasks. This approach scales naturally: it works the
same way whether there are 10 computers available or 1,000. The
approach also provides natural load balancing, since
each worker picks up exactly as much work as it can handle in a
given time, with slow computers doing less work and fast
computers doing more.
Broker systems
As a third example, consider an online auction system that brings
buyers and sellers of goods and services together. Suppose you,
as a potential buyer, describe the item (such as a car) you'd
like to buy and the price you're willing to pay, wrap the
information in an entry, and write the resulting wanted-to-buy
entry to a space. At the same time, potential sellers continually
monitor the space for the arrival of wanted-to-buy entries that
match items in their inventory. For example, Mazda dealers
monitor the space for entries that describe Mazdas, while
used-car dealers monitor the space for all used-car requests.
When a matching request is found and read, a potential seller
writes a bid entry into the space, stating an offering price. As
a potential buyer, you continually monitor the space for bids on
your outstanding requests, and, when you find one that's
acceptable, you remove the bids and contact the seller (possibly
through the space via another entry).
Now it's time to introduce the JavaSpaces API. As we've
already said, it's simple; in fact, in the remainder of this
article we will cover everything that you need to know (barring
some minor details) about it. However, before we describe the
JavaSpace
interface and its methods, we first need
to talk about entries.
Entries
An object that is stored in a space is called an entry.
To be an entry, an object just needs to implement the
Entry
interface. As an example, let's define a
message entry that you can write into a space:
import net.jini.core.entry.Entry;
public class Message implements Entry
{
public String
content;
// a no-arg
constructor
public Message()
{
}
}
Here we've defined a Message
class with a string
field that will hold the content of the message. Because we want
to use this class with spaces, we need to implement the interface
net.jini.core.entry.Entry
, which is found in the
package net.jini.core.entry
. It's important to point
out that Entry
is a marker interface; in
other words, the interface contains no constants or methods and
therefore requires no special work to implement, other than
adding implements Entry
to your class
definition.
Besides implementing the Entry
interface, there
are a few other conventions that our entries must follow. We'll
have more to say about the reasons in later articles, but for now
we'll just look at the broad outlines. An entry must have a
public constructor that takes no arguments (a so-called
no-arg constructor); this requirement stems from the
underlying serialization that occurs when entries are transferred
in to and out of spaces. Note that our definition of
Message
contains a no-arg constructor. Another
convention is that fields of an entry should be declared
public
; this lets other processes find your entries
in spaces via associative lookup, based on the values of those
fields. A third convention is that fields of an entry must
contain references to objects, rather than primitive types (that
is, if you need to define a primitive type field such as
int
, you should use the corresponding wrapper class
Integer
instead). To make sure you are covering all
your bases in defining entries, we recommend that you refer to
JavaSpaces
Principles, Patterns, and Practice,or to the Sun Microsystems
JavaSpaces Specification for details. We will also, as
mentioned, touch on some of the finer points in later
articles.
Other than these requirements, an entry is like any other Java
class; you can instantiate it, invoke its methods, and assign
values to its public fields. Now that we've defined a
Message
entry class, let's see what operations are
available for interacting with entries in spaces.
The JavaSpace interface
To interact with a space, you need to obtain access to an object
that implements the JavaSpace
interface. There are
many ways of obtaining access to such an object (you can, for
example, use the Jini lookup or the RMI registry) and we will
cover the details of doing so in the next article. For now, we
will concentrate on the JavaSpace
interface
itself.
The following methods are the bread and butter of the
JavaSpaces API and are supplied by any object that implements the
JavaSpace
interface:
write
: Places one copy of an
entry into a space. If called multiple times with the same
entry, then multiple copies of the entry are written into the
space.read
: Takes an entry that is
used as a template and returns a copy of an object in the space
that matches the template. If no matching objects are in the
space, then read
may wait a user-specified amount
of time until a matching entry arrives in the space.take
: Works like
read
, except that the matching entry is removed
from the space and returned as the result of the
take
.In addition, the JavaSpace
interface supplies the
following methods, which can be useful in many applications:
notify
: Takes a template and
an object and asks the space to notify the object whenever
entries that match the template are added to the space. This
notification mechanism, which is built on Jini's distributed
event model, is useful when you want to interact with a space
using a reactive style of programming.snapshot
: Provides a method
of minimizing the serialization that occurs whenever entries or
templates are used; you can use snapshot
to
optimize certain entry usage patterns in your applications. We
will cover this method in detail in a later article.That, in a nutshell, is the entire JavaSpaces API. Of course, there are a lot of details that we haven't covered here, and we will dive into them in the next article. For now, let's see how to code the obligatory "Hello World" example with JavaSpaces, and then wrap things up.
We'll implement our "Hello World" as an applet, using a
convenience class called SpaceApplet
, which is
provided in Sun's Jini distribution. SpaceApplet
handles the details of gaining access to a space for us via its
space
method. Here is our "Hello World" program:
import net.jini.core.lease.Lease;
import net.jini.space.JavaSpace;
import com.sun.jini.example.spaceApplet.SpaceApplet;
public class HelloWorld extends SpaceApplet {
private JavaSpace space;
public void init() {
super.init();
space =
(JavaSpace)space();
try {
Message
msg = new Message();
msg.content
= "Hello World";
space.write(msg,
null, Lease.FOREVER);
Message
template = new Message();
Message
result =
(Message)space.take(template,
null, Long.MAX_VALUE);
msg(result.content);
} catch
(Exception e) {
e.printStackTrace();
}
}
public void msg(String str) {
System.out.println(str);
}
}
In the applet's init
method, we first call
super.init
(the SpaceApplet
's
init
method), which ensures that a
SpaceApplet
has access to a space. Then we call the
space
method, which returns an instance of an object
that implements the JavaSpace
interface (which we
will refer to as a space object or space
throughout this series).
With a space object in hand, we instantiate a
Message
entry (using the Message
class
we defined earlier in this article) and then assign "Hello
World"
to its content
field. Now we are ready
to write the entry into the space. To do so, we call the
write
method of the space, and pass the
Message
entry, null
(the second
parameter is used for specifying a transaction, and here we
aren't using one), and a lease time of Lease.FOREVER
(which asks the space to store this object indefinitely) to the
method. The call to write
results in placing one
copy of the entry into the space; when the call completes, it's
guaranteed that our entry is there.
Now that a copy of the entry is in the space, we can do two
things with it: read
it or take
it out
of the space. If we read
it, we just obtain a copy
of the entry in the space. If we take
it, then we
obtain a copy, but also remove the entry from the space so that
no other process can obtain it. To do either, we need to
construct a template that will match the entry. We will
cover the specifics of matching in our next article, but it is an
intuitive concept: a template matches an entry in the space if
the objects have the same type and if, for every field the
template specifies, that field matches the corresponding field of
the entry. Unspecified fields (those with a value of
null
in the template) act as wildcards and will
automatically match the corresponding fields of an entry in the
space.
Continuing with our code, we next instantiate a template,
which is another Message
entry. We do not specify
any fields (they are null
by default), so our
template will match any Message
entry in the space.
Alternately, we could specify "Hello World"
in the
content
field, and our template would still match
the one Message
entry we stored in the space. If, on
the other hand, we specify a content
field of
"Goodbye Cruel World"
in our template, the template
will not match our entry in the space.
Now that we have a template, we call take
on the
space, passing it the template, another null
value
for a transaction, and a length of time for the call to wait
before returning. In this case, we specify a value of
Long.MAX_VALUE
, so the take
will not
return until a matching entry is found. At that point, the
matching entry is removed from the space and returned as the
result of the call, and we assign the value to the variable
result
. With this object in hand, we print out its
content
field and see this message:
Hello World
In this article we've gotten our feet wet with a brief look at the JavaSpaces model and API. The real power of JavaSpaces will become apparent as we dive deeper into the programming model, which uses entries combined with the key properties of the space to create communication and synchronization between processes. We will see that, when processes communicate and synchronize their activities through spaces rather than communicating with each other directly, they become loosely coupled. This loosely coupled nature of space-based programs is significant and powerful, and leads to flexible, powerful, scalable distributed applications -- and that is the whole point of Jini!
To get up and running with JavaSpaces programming, you will need to compile and run your code against the following packages from Sun Microsystems:
Dr. Eric Freeman is currently a director of engineering at Disney's Go.com and a fellow at Yale's Center for Internet Studies. Eric recently coauthored JavaSpaces Principles, Patterns, and Practice, the official Sun Microsystems Jini Series book on JavaSpaces, along with Susanne Hupfer and Ken Arnold. Previously, Eric was CTO at Mirror Worlds Technologies, which builds products based on his work with David Gelernter at Yale University.
Dr. Susanne Hupfer is director of product development for Mirror Worlds Technologies, a Java- and Jini-based software applications company, and a research affiliate in the Department of Computer Science at Yale University, where she completed her PhD in space-based systems and distributed computing. Previously, she taught Java network programming as an assistant professor of computer science at Trinity College. Susanne coauthored the Sun Microsystems book JavaSpaces Principles, Patterns, and Practice.
"Make Room for JavaSpaces, Part I" by Eric Freeman and Susan
Hupfer was originally published by JavaWorld (www.javaworld.com), copyright IDG,
November 1999. Reprinted with permission.
http://www.javaworld.com/jw-11-1999/jw-11-jiniology.html
Sponsored Links
|