Home > Articles > Programming > Java

Why Should You Be Using JavaSpaces?

  • Print
  • + Share This
Find out how this technology and Jini can provide, improve, and simplify your shared distributing computing needs.
This chapter is from the book

This chapter is from the book

  • Introduction To Javaspaces

  • Reasons To Use Javaspaces

This chapter covers two topics in general. First, you take a look at what Java-Spaces are. You look at this from the very concrete (and simple) interface definition to the more abstract questions of what place JavaSpaces have in the world.

After you have this high-level understanding, you initially explore some high-level descriptions of the uses of JavaSpaces that will be expanded in later chapters. Examples include using JavaSpaces as a mechanism for decoupled communication, application structuring, and parallel computing.

What Is a JavaSpace?

There are several ways to answer the question, "What is a JavaSpace?" That's because there are really several answers, depending on the standpoint of where you are asking the question.

Some of the viewpoints from which to look at JavaSpaces are:

  • Strictly in an object manner

  • As a Jini service

  • As a mechanism for shared distributed communication

  • As a mechanism for object storage

From the standpoint of an object purist, all JavaSpaces are implementations of the JavaSpace interface which you take a look at, in a moment. It's really a very small interface to have such interesting consequences.

This is a good point to interject a note on the terminology of JavaSpaces. In this book, I will use the phrase "JavaSpaces technology" or JavaSpaces to refer to the implementation of JavaSpaces in general. When you see just JavaSpace or space, you will most commonly be reading about a concrete running instance of JavaSpaces.

From the standpoint of Jini, a JavaSpace is a Jini service that makes use of Jini infrastructure and provides its capabilities to other Jini clients and services. You'll be looking a lot more at how JavaSpaces fit into the world of Jini.

JavaSpaces provide a mechanism for performing shared distributed computing. This is probably the single most important functional aspect that JavaSpaces provide.

JavaSpaces also provide a very interesting and easy mechanism for object storage. This is not to say that they are an object database (they aren't), but it is another very useful feature.

In the following sections, you take a look at JavaSpaces from each of these viewpoints. Combined, they should give you a good start at understanding Java-Spaces at a conceptual level.

JavaSpace Interface

The actual interface definition for JavaSpace is short and compact, and can be seen in Listing 1–1.

Listing 1–1 JavaSpace.java

package net.jini.space; 

import net.jini.core.entry.*;
import net.jini.entry.*; 
import net.jini.core.transaction.*; 
import net.jini.core.event.*; 
import net.jini.core.lease.*; 
import java.rmi.*; 

 public interface JavaSpace { 
       Lease write(Entry entry, Transaction txn, long lease)
              throws TransactionException, RemoteException; 

       long NO_WAIT = 0; 

Entry read(Entry tmpl, Transaction txn, long timeout)
       throws UnusableEntryException, TransactionException,
                   InterruptedException, RemoteException; 

Entry readIfExists(Entry tmpl, Transaction txn,
                               long timeout)
       throws UnusableEntryException, TransactionException,
                   InterruptedException, RemoteException; 

Entry take(Entry tmpl, Transaction txn, long timeout) 
       throws UnusableEntryException, TransactionException, 
                   InterruptedException, RemoteException; 

Entry takeIfExists(Entry tmpl, Transaction txn, 
                               long timeout)
       throws UnusableEntryException, TransactionException, 
                   InterruptedException, RemoteException; 

EventRegistration notify(Entry tmpl, Transaction txn, 
                                         RemoteEventListener listener, 
                                         long lease, 
                                         MarshalledObject handback)
       throws TransactionException, RemoteException; 

Entry snapshot(Entry e) throws RemoteException; 

As you will see as you progress though this book, these seven methods in Listing 1–1 can be used to provide the mechanisms for some very complex behavior. However, before you get into complexity, it is best to start simple. Luckily, the interface lends itself to starting simply.

Entries. Before you get into the actual methods, the class Entry deserves some special attention. Notice that every method takes an entry as a parameter and that five of the seven methods return an entry. Clearly, there is a pretty big link between entries and JavaSpaces. The link is that entries are what you put into (or read from) a JavaSpace.

Here is the interface definition for net.jini.core.entry. Entry:

package net.jini.core.entry; 

public interface Entry extends java.io.Serializable { 

This is even simpler than the JavaSpace interface—no methods at all. The Entry interface is an example of a marker interface. It doesn't add any particular direct functionality itself. What it provides is the indication that a particular class can be put into a space.

Note that the Entry interface is in the package net.jini.core.entry, but the Java-Space interface is in net.jini.space. The Entry interface is not just used as a marker for JavaSpace usage. Entry really provides the common interface that can be used by any Jini service (you learn about Jini and services in the section "Jini and Java-Spaces" as a searchable building block).

In addition to providing the indication as to which classes may be put into spaces, an entry defines some of the semantics of how a JavaSpace implementation will use its entry instances.

When you produce a class that implements entry, you are required to follow several rules:

  • Each field of the Entry subclass must be public. (Fields can be nonpublic, but they won't get saved in the space.)

  • Fields cannot be primitives. They must be objects.

  • Fields need to be serializable.

  • A public, no-argument constructor must be provided.

You hear more about Entry objects in Chapter 3, but, briefly, these rules are there to allow simple, efficient mechanisms for searching through large groups of entries. That is, in effect, what a JavaSpace is—a collection of instances of classes that implement the Entry interface.

Now, back to the methods in the JavaSpace interface itself.

Read. The method read allows entries to be found within a JavaSpace. In essence, it provides a way to search a JavaSpace.

Entry read(Entry tmpl, Transaction txn, long timeout) 
      throws UnusableEntryException, TransactionException, 
                  InterruptedException, RemoteException; 

The first parameter is an entry that is used as a template to perform the search against. If a field of the entry is null, the same field within any entry of the same type within the space will match. The word "type" here is used to mean that matching entries can be either the same class as the template or a subclass.

If a field within the template is not null, fields within other entries of the same class have to match exactly. In Chapter 3, you go over more of the details of what matching precisely means. If a match is found, the matching entry is returned. If more than one matching entry is in the space, the space can return any of the matching entries. There is no guarantee as to which entry will be returned. This means you should not count on anything, such as the order of arrival, to have any correspondence with the order in which you might read entries.

The second parameter provides a Transaction instance under which the read should be performed. In Chapter 4, you look into how to use transactions with JavaSpaces.

The final parameter is a long that gives a value in milliseconds, which says how long to wait in the read method for a matching entry to appear in the space. This means that, if a matching entry is not in the space at the time that the read method is first invoked, the method waits for the timeout value number of milliseconds for the entry to be added to the space. If, after that many milliseconds, no entry has been matched, null is returned.

ReadIfExists. The readIfExists method is very similar to the read method. It has exactly the same parameters and return value. It uses them in a slightly different way, however. It is also used for searching the space and uses the same rules for matching the template instance. Where read and readIfExists differ is in their treatment of the timeout value.

The readIfExists method attempts to match the template entry passed in as the first parameter. If there is no match, it immediately returns, rather than waiting around for a matching entry like the read method. If it returns immediately, what is its timeout parameter for?

The answer has to do with how JavaSpaces work with transactions. A matching entry may be in the space, but may have been written under a transaction that has not yet completed. This means that the matching entry is not really visible to the readIfExists method. The timeout parameter specifies how long readIfExists waits for the unfinished transaction to complete.

Therefore, the read method waits until a matching entry is found or the timeout expires. The readIfExists method waits only if there is no other matching entry other than one that is under a nonfinished transaction.

You look into the interaction of JavaSpaces and transactions in more depth in Chapter 4.

Take. The take method again has the same parameters and return value as read. It uses the same matching rules for the entry template, and its timeout value means the same thing as the timeout for read—wait until a matching entry appears. The important difference is that, if a matching entry is found, not only is it returned to the method's caller, but it is removed from the space.

It is also guaranteed that, if multiple clients call the take method and they match the same entry in the space, only one of those clients gets the entry, and the rest get a null return value.

TakeIfExists. The takeIfExists method exactly corresponds to take like the read-IfExists method corresponds to the read method. That is, its timeout parameter specifies how long to wait for an unfinished transaction with a matching entry to complete.

Write. The write method is what you call to put entries into a space in the first place.

Lease write(Entry entry, Transaction txn, long lease) throws TransactionException, RemoteException;

A write takes the entry that you want to have placed into the space as its first parameter. Note that any kind of entry can be written to a space. The write method also takes the Transaction instance that the write should belong to and a lease parameter.

You go into depth on what the lease parameter is in Chapter 4, but, briefly, the lease indicates how long, in milliseconds, the caller of the write likes the entry to stay within the space.

The return value of the write method is a Lease instance. This allows the caller to have some control over keeping the entry in the space.

Notify. The notify method provides an asynchronous mechanism for being informed when interesting entries are written into a space.

EventRegistration notify(Entry tmpl, Transaction txn, 
                                         RemoteEventListener listener, 
                                         long lease, 
                                         MarshalledObject handback) 
                throws TransactionException, RemoteException; 

The first Entry parameter again specifies a template to use in matching against entries in the space. The matching rules are the same as in the case of the read method. The difference from the read method is that a notify indicates that the caller is interested in being informed whenever a matching entry is written, rather than looking at entries that are there at the time of the call.

The RemoteEventListener parameter tells the space who to send events back to. When a new entry is written to the space that matches the template, the space sends an event to the RemoteEventListener so that it may deal with it.

The Handback parameter is sent to the listener as part of the event data. This provides a way for the requestor of the notify method to communicate with the Listener instance.

The Lease parameter specifies how long, in milliseconds, the caller is interested in receiving notifications.

The EventRegistration return value provides some information for the caller of the notify method to manage its registration—such as a Lease instance.

In Chapter 4, you look into notifications and how they can be used.

Snapshot. The snapshot method is provided as a way to help optimize the performance of your interaction with a space.

Entry snapshot(Entry e) throws RemoteException;

The snapshot method can help the performance of your code in cases where you are repeatedly calling methods upon a space with the same template entry. The way snapshot works is you call it upon a space passing the template that you want to optimize performance for.

You get back an Entry instance that represents the entry you passed in. Essentially, the space remembers that this new entry is really the old entry. When this new representative entry that is passed on calls to the space, much of the overhead of the serialization process is avoided. This can improve your performance dramatically in cases where your template entry is large and costly to serialize.

One important thing to notice is that this only works with the space that you called the snapshot method upon. If you call a method upon a different space and pass the snapshot entry, the new space will not recognize the snapshot entry as representing the original template entry.

Another important point is that the entry you get back is not the same as the entry you passed in at all. You should not compare the new entry with any entries you may already have.

Jini and JavaSpaces

Jini is the technology infrastructure upon which JavaSpaces is built. It really isn't possible to program within the JavaSpace world without a decent understanding of how the JavaSpaces technology fits within the Jini universe.

Jini provides a foundation upon which distributed computing systems can be built. This may sound familiar because earlier you learned that JavaSpaces are a mechanism for distributed computing.

A thorough discussion of Jini is beyond the scope of this book. If you want a good solid understanding of Jini, the book Core Jini1 by W. Keith Edwards is an excellent place to start. The Web site, www.jini.org, is also an excellent place to look for more information on Jini itself.

The aim of this book is to provide enough information and background to show where JavaSpaces fit within Jini and specific Jini mechanisms that are either required or will prove to be very useful to the JavaSpace programmer.

In Figure 1–1, you see a conceptual view of how JavaSpaces relate to Jini.

Figure 1-1Figure 1–1 JavaSpaces and Jini

A JavaSpace is a Jini service. A Jini service provides functionality to other Jini services and clients. It makes itself available to potential users through the Jini lookup mechanisms. (You see more detail on lookup in Chapter 3.)

Because a JavaSpace is a Jini service, clients can combine the functionality available from JavaSpaces with other Jini services and base mechanisms. You can also use all the rest of Java as well—of course. The advantage of being part of the Jini world lies in the leverage provided by the base functions of Jini itself and the additional services available, such as JavaSpaces itself.

The base functions available within Jini can be broken into five areas:

  • Discovery
  • Lookup
  • Leasing
  • Events
  • Transactions

From a very high level, these functions have well-defined roles. Discovery and Lookup are provided as means of finding things. The Discovery protocols provided in Jini are what you use to find Lookup services. A Lookup service provides the mechanisms you use to find other Jini services.

Leasing, Events, and Transactions are provided as support mechanisms for distributed programming. Leasing was briefly mentioned in the previous section on JavaSpaces. The write method of JavaSpaces returns a net.jini.core.lease.Lease instance. The Lease interface is provided as part of Jini. Leasing is the concept that things (including program services) have a lifetime. By allowing things that are not being actively maintained the ability to go away, Jini allows for the removal of potential garbage.

Events (as mentioned previously in the discussion of the "Notify" section) provide a means for the asynchronous notification of interested parties. Basically, a client registers interest in receiving an event when something of interest happens in a service that sends out events. The client program can then proceed with other tasks rather than waiting for the event to occur.

Transactions provide the mechanisms to prevent partial failures. By participating (correctly) in a transaction, either all of the operations done with that transaction will succeed, or they will all fail. This aids in stopping inconsistent states from becoming a major problem.

Another important mechanism that is heavily leveraged with JavaSpaces applications is dynamic code downloading. This allows services and clients to make use of classes that they have not encountered until they are actually running.

Shared Distributed Computing

The need for leasing, events, and transactions is driven by the nature of distributed programming. Much more so than the normal world of local computing, distributed computing is an environment in which the fallibility of the underlying environment should be expected.

Thus, rather than assuming services always exist, you plan for the inevitable problems inherent in a distributed environment. The "standard" method for distributed processes to communicate is for them to make contact with each other and then directly pass messages back and forth. The messages may appear to the programmer as remote-method calls or packets of data, but the essential point is that a direct link is established between processes. Figure 1–2 shows two processes communicating directly.

Figure 1-2Figure 1–2 Process-to-process communication

JavaSpaces introduce a different model. A JavaSpace provides an intermediary point for communication. In Figure 1–3, you see this model.

Figure 1-3Figure 1–3 Using a JavaSpace for process communication

At first glance, it may seem that all that is done here is to introduce another potential point of failure into your distributed system. However, what you have really accomplished is to decouple the processes. Rather than worrying about the details of communicating with a specific process, all Process 1 (in Figure 1–3) has to worry about is writing an entry to the JavaSpace. Process 2 does not have to be concerned with how the entries got into the JavaSpace; it just has to read them and then do its own task.

Decoupling the process provides several benefits. If Process 2 were to fail, this does not affect Process 1; it can continue with its tasks. If you need to add another process into the picture, in the tightly coupled model in Figure 1–2, you would have to change the code of one or more of the programs or would have to have written complex code for dealing with multiple processes from the beginning. In Figure 1–4, you see that adding another process is literally as easy as drawing it into the picture.

Figure 1-4Figure 1-4 Adding another process

Now, Process 3 can quite happily start reading entries from the space. Because Process 1 did not need to know details about Process 2, it did not need to be changed to allow Process 3 to enter the picture. Process 2 does not care (in this case) where the entries in the space come from; it just consumes them as they appear.

This loosely coupled model of computing is particularly useful for dealing with the complexities of distributed programming. In Part 2, you take a close look into how this model affects the design of programs and what sorts of benefits can be reaped.

Persistent Object Repository

The final view of JavaSpaces that you consider in this chapter is as a repository of object data. The entries that are written into a space are full-fledged Java objects.

Notice, however, that JavaSpaces are not an object database. The Entry instances are not active while they are within the JavaSpace and are only accessible as copies. This means that you cannot directly change an entry within a space. For example, if you were to write the same Entry instance twice in a row to a space, two entries would now be within that space. Thus, there is no notion of maintaining object identity for the entries within a space.

The implementation of JavaSpaces that is supplied from Sun Microsystems comes in two flavors. There is a transient version and a persistent version. The difference between the two is that, in the transient version, when the space goes away, the entries that have been written to it also go away. In the persistent version, the entries continue to exist when a space starts and stops multiple times.

The purpose of the persistent JavaSpace is for robustness. If the machine upon which the space were running were to crash, it could be restarted, and the entries that had been successfully written to it would still exist. In a production environment, knowing that your data will not disappear is a very useful thing. This is not to imply that the transient version cannot be used in a production environment. If your environment is such that recovering entries after a crash is not important, the transient version may be very well suited to your needs.

  • + Share This
  • 🔖 Save To Your Account