Technical Article

Getting Started With JavaSpaces Technology: Beyond Conventional Distributed Programming Paradigms

By Qusay H. Mamoud, 12, 2005

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;
       }
    }
    

MessageEntryspace



    JavaSpace space = getSpace();
    MessageEntry msg = new MessageEntry();
    msg.content = "Hello there";
    space.write(msg, null, Lease.FOREVER);
    

nullTransaction

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);
    
    
    write()Lease
    

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);
    null MessageEntry Long.MAX_VALUE read() take() readIfExists()
    

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.

TransientOutriggerImplPersistentOutriggerImpl

Compiling and Running the SpaceClient Application

To compile and run the sample application in this article, do the following:

  1. 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.

  2. 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.
  3. 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

  4. 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.