|
The Java programming language continues to be the language of choice for
implementing enterprise and distributed applications. This can be attributed to
its platform independence, simple object-oriented model, clean syntax, and
built-in support for building blocks (such as threads and sockets) for
distributed application development.
Building distributed applications is difficult because you must take into
account several issues, such as partial failure, increased latency, distributed
persistence, and language compatibility.
The JavaSpaces technology is a simple and powerful high-level tool for
building distributed and collaborative applications. Based on the concept of
shared network-based space that serves as both object storage and exchange area,
it provides a simple API that is easy to learn and yet expressive for building
sophisticated distributed applications.
This article provides a fast-track tutorial to the JavaSpaces technology,
including
- An introduction to distributed computing
- An introduction to the JavaSpaces technology
- A comparison of JavaSpaces technology and databases
- A description of JavaSpaces services and operations
- The JavaSpaces technology application model
- The JavaSpaces technology programming model
- A flavor of the effort involved in developing applications using
JavaSpaces technology
Distributed Computing
Distributed computing is about building network-based applications as a set
of processes that are distributed across a network of computing nodes (or hosts)
that work together to solve a problem. The advantages of building applications
using this approach are many, including performance, resource sharing,
scalability, and fault tolerance. But using distributed technologies does not
guarantee these advantages. The developer must take special care in the design
and implementation or distributed applications in order to achieve such
benefits.
The network environment on top of which you build distributed applications
introduce complexities that are not of concern when you write stand-alone
applications. The most obvious complexity is the varied architecture of
machines. However, Java technology's platform independence and its virtual
machine allow for applications that you write once and run anywhere. Other
issues that have significant impact on designing and implementing distributed
applications include latency, synchronization, and partial failure.
Several technologies can be used to build distributed applications, including
low-level sockets, message passing, and remote method invocation (RMI). The
JavaSpaces technology model is different in that it provides persistent object
exchange areas (or spaces) through which remote Java technology processes
coordinate actions and exchange data. Such an approach can simplify the design
and implementation of sophisticated distributed applications, and it enables you
to deal with the challenges of designing and implementing distributed
applications.
The JavaSpaces Technology
The JavaSpaces technology is a high-level tool for building distributed
applications, and it can also be used as a coordination tool. A marked departure
from classic distributed models that rely on message passing or RMI, the
JavaSpaces model views a distributed application as a collection of processes
that cooperate through the flow of objects into and out of one or more spaces.
This programming model has its roots in Linda, a coordination language developed
by Dr. David Gelernter at Yale University. However, no knowledge of Linda is
required to understand and use JavaSpaces technology.
The JavaSpaces service specification lists the following design goals for the
JavaSpaces technology:
- It should provide a platform that simplifies the design and implementation
of distributed computing systems.
- The client side should have few classes, both to keep the client simple
and to speed the downloading of client classes.
- The client side should have a small footprint because it will run on
computers with limited local memory.
- A variety of implementations should be possible.
- It should be possible to create a replicated JavaSpaces service.
JavaSpaces Technology vs. Databases
As mentioned earlier, a space is a shared network-accessible
repository for objects: The data you can store there is persistent and later
searchable. But a JavaSpaces service is not a relational or object database.
JavaSpaces services are not used primarily as data repositories. They are
designed for a different purpose than either relational or object databases.
Although a JavaSpaces service functions somewhat like a file system and
somewhat like a database, it is neither. The key differences between JavaSpaces
technology and databases are the following:
- Relational databases understand the data they store and manipulate
it directly through query languages such as SQL. JavaSpaces services, on the
other hand, store entries that they understand only by type and the serialized
form of each field. As a result, there are no general queries in the
JavaSpaces application design, only "exact match" or "don't care" for a given
field.
- Object databases provide an object-oriented image of stored data
that can be modified and used, almost as if it were transient memory.
JavaSpaces systems do not provide a nearly transparent persistent or transient
layer, and they work only on copies of entries.
JavaSpaces Services and Operations
Application components (or processes) use the persistent storage of a space
to store objects and to communicate. The components coordinate actions by
exchanging objects through spaces; the objects do not communicate directly.
Processes interact with a space through a simple set of operations.
You can invoke four primary operations on a JavaSpaces service:
write(): Writes new objects into a space
take(): Retrieves objects from a space
read(): Makes a copy of objects in a space
notify: Notifies a specified object when entries that match
the given template are written into a space
Both the read() and take() methods have variants:
readIfExists() and takeIfExists(). If they are called
with a zero timeout, then they are equivalent to their counterpart. The timeout
parameter comes into effect only when a transaction is used.
Each operation has parameters that are entries. Some are templates, which are
a kind of entry. The write() operation is a store operation. The
read() and take() operations are a combination of
search and fetch operations. The notify method sets up repeated
search operations as entries are written to the space. If a take()
or read() operation doesn't find an object, the process can wait
until an object arrives.
Unlike conventional object stores, objects are passive data. Therefore,
processes do not modify objects in the space or invoke their methods directly.
In order to modify an object, a process must explicitly remove, update, and
reinsert it into the space.
How can we build sophisticated distributed applications with only a handful
of operations? The space itself provides a set of key features.
The JavaSpaces Technology Application Model
A JavaSpaces service holds entries, each of which is a typed group of objects
expressed in a class that implements the interface
net.jini.core.entry.Entry. Once an entry is written into a
JavaSpaces service, it can be used in future look-up operations. Looking up
entries is performed using templates, which are entry objects that have some or
all of their fields set to specified values that must be matched exactly. All
remaining fields, which are not used in the lookup, are left as wildcards.
There are two look-up operations: read() and
take(). The read() method returns either an entry that
matches the template or an indication that no match was found. The
take() method operates like read(), but if a match is
found, the entry is removed from the space. Distributed events can be used by
requesting a JavaSpaces service to notify you when an entry that matches the
specified template is written into the space. Note that each entry in the space
can be taken at most once, but two or more entries may have the exact same
values.
Using JavaSpaces technology, distributed applications are modeled as a flow
of objects between participants, which is different from classic distributed
models such as RMIs. Figure 1 indicates what a JavaSpaces technology-based
application looks like.
 |
|
Figure 1: A Typical JavaSpaces Technology Application
|
|
As you can see, a client can interact with as many JavaSpaces services as
needed. Clients perform operations that map entries to templates onto JavaSpaces
services. Such operations can be singleton or contained in a transaction so that
all or none of the operations take place. Notifications go to event catches,
which can be either clients or proxies for clients.
To get a flavor of how to implement distributed applications using a handful
of JavaSpaces operations, consider a multiuser chat system. All the messages
that make up the discussion are written to a space that acts as a chat area.
Participants write message objects into the space, while other members wait for
new message objects to appear, then read them out and display their contents.
The list of participants can be kept in the space and updated whenever someone
joins or leaves the discussion. Because the space is persistent, a new member
can read and view the entire discussion.
You can implement such a multiuser chat system in RMI by creating remote
interfaces for the interactions discussed. But by using JavaSpaces technology,
you need only one interface.
The JavaSpaces Technology Programming Model
All operations are invoked on an object that implements the
net.jini.space.JavaSpace interface. A space stores entries, each of
which is a collection of typed objects that implements the Entry
interface. Code Sample 1 shows a MessageEntry that contains one
field: content, which is null by default. Information
on how to compile and run the sample application appears later in this article.
Code Sample 1: MessageEntry.java
import net.jini.core.entry.*;
public class MessageEntry implements Entry {
public String content;
public MessageEntry() {
}
public MessageEntry(String content) {
this.content = content;
}
public String toString() {
return "MessageContent: " + content;
}
}
|
The following code segment writes an entry of MessageEntry into
the JavaSpace service referred to by space:
JavaSpace space = getSpace();
MessageEntry msg = new MessageEntry();
msg.content = "Hello there";
space.write(msg, null, Lease.FOREVER);
|
The null field indicates that no Transaction object
is managing the operation.
The write() operation places a copy of an entry into the given
JavaSpace service, and the Entry passed is not affected by the
operation. Each write() operation places a new Entry
into the space even if the same Entry object is used in more than
one write().
Entries written in a space are governed by a renewable lease. If you like,
you can change the lease (when the write() operation is invoked) to
one hour as follows:
space.write(msg, null, 60 * 60 * 1000);
|
A write() operation returns a Lease object that is
milliseconds long. If the requested time is longer than the space is willing to
grant, you will get a lease with a reduced time. In addition, when the lease
expires, the entry is removed from the space.
Once the entry exists in the space, any process with access to the space can
perform a read() on it. To read an entry, a template is used, which
is an entry that may have one or more of its fields set to null. An
entry matches a template if (a) the entry has the same type as or is a subtype
of the template and (b) if for every specified non-null field in
the template, their fields match exactly. The null fields act as
wildcards and match any value. The following code segment shows how to create a
template and perform a read() on the space:
MessageEntry template = new MessageEntry();
MessageEntry output = (MessageEntry) space.read(template, null, Long.MAX_VALUE);
|
Now, because the template's content field is null, the template
will match any MessageEntry entry in the space regardless of the
content. The Long.MAX_VALUE is a timeout value that specifies how
long the client is willing to wait for a matching entry to appear, even if there
is no hint of an entry appearing. The read() and
take() operations would block on a completely empty space. But if
the timeout value was readIfExists(), then it would be how long the
client was willing to wait for interfering transactions to settle.
Code Sample 2 shows the client that discovers the JavaSpace service, writes a
message into the space, and then reads it. Instructions on how to compile and
run this sample application appear later in this article.
Code Sample 2: SpaceClient.java
import net.jini.space.JavaSpace;
public class SpaceClient {
public static void main(String argv[]) {
try {
MessageEntry msg = new MessageEntry();
msg.content = "Hello there";
System.out.println("Searching for a JavaSpace...");
Lookup finder = new Lookup(JavaSpace.class);
JavaSpace space = (JavaSpace) finder.getService();
System.out.println("A JavaSpace has been discovered.");
System.out.println("Writing a message into the space...");
space.write(msg, null, 60*60*1000);
MessageEntry template = new MessageEntry();
System.out.println("Reading a message from the space...");
MessageEntry result = (MessageEntry) space.read(template, null, Long.MAX_VALUE);
System.out.println("The message read is: "+result.content);
} catch(Exception e) {
e.printStackTrace();
}
}
}
|
Transactions
The JavaSpaces API uses the package net.jini.core.transaction to
provide basic atomic transactions that group multiple operations across multiple
JavaSpaces services into a bundle that acts as a single atomic operation. Either
all modifications within the transactions will be applied or none will,
regardless of whether the transaction spans one or more operations or one or
more JavaSpaces services. Note that transactions can span multiple spaces and
participants in general.
A read(), write(), or take() operation
that has a null transaction acts as if it were in a committed
transaction that contained that operation. As an example, a take()
with a null transaction parameter performs as if a transaction was
created, the take() was performed under that transaction, and then
the transaction was committed.
The Jini Outrigger JavaSpaces Service
The Jini Technology Starter Kit comes with the package
com.sun.jini.outrigger, which provides an implementation of a
JavaSpaces technology-enabled service. You can run it two ways:
- As a transient space that loses its state between executions: Use
com.sun.jini.outrigger.TransientOutriggerImpl.
- As a persistent space that maintains state between executions: Use
com.sun.jini.outrigger.PersistentOutriggerImpl. The
TransientOutriggerImpl can be run only as a nonactivatable server,
but the PersistentOutriggerImpl can be run as either an activatable
or nonactivatable server.
Compiling and Running the SpaceClient Application
To compile and run the sample application in this article, do the following:
- Download and install the Jini Technology Starter
Kit. I am using the 2.1 Beta release.
- Compile the code in Code Sample 1 (
MessageEntry.java) using
javac as follows:
prompt> javac -classpath <pathToJiniInstallation\lib\jini-ext.jar> MessageEntry.java
|
Note that you need to include the JAR file jini-ext.jar in
your classpath. This JAR file comes with the starter kit and is
in the lib directory of your installation.
- Compile the code in Code Sample 2 (
SpaceClient.java). Note
that this code makes use of a utility class called Lookup
to locate or discover a JavaSpace space. Therefore, before you
compile SpaceClient.java, you should download
Lookup.java and then compile both classes using
javac as shown in step 2. Note that you should include the
directory that contains MessageEntry.class in your
classpath when compiling SpaceClient.java.
- Run
Launch-All, which is in the installverify
directory of your Jini installation directory. This will start a service
browser (as shown in Figure 2) and six contributed Jini network technology
services, one of which is the JavaSpace service.
 |
|
Figure 2: Jini Network Technology Service
Browser
|
|
- Finally, run the
SpaceClient application using the
java command as follows. Here I assume that your Jini
installation directory is C:\Jini2_1beta and that the classes you
compiled earlier are at C:\Jini2_1beta\myclasses.
C:\Jini2_1beta\myclasses> java -classpath .\;..\lib\jini-ext.jar;..\lib\reggie.jar;..\lib\outrigger.jar SpaceClient
|
If all goes well, you will see the output shown in Figure 3.
 |
|
Figure 3: SpaceClient Sample Output
|
|
Conclusion
The JavaSpaces technology provides services and tools for building
sophisticated distributed applications. This technology is designed to work with
applications that can model themselves as flow objects through one or more
servers. If your application can be modeled this way, JavaSpaces technology will
provide you with many benefits, such as a reliable distributed storage system
for the objects. In addition, JavaSpaces technology handles concurrent access,
storing and retrieving entries atomically.
For More Information
Acknowledgments
Special thanks to John McClain of Sun Microsystems, whose feedback helped me
improve this article.
|
|