The Artima Developer Community
Sponsored Link

Cleaning Up After Jini Services
Standardize the Way Jini Clients Release Resources
by Bill Venners
First Published in JavaWorld, March 2000

<<  Page 5 of 8  >>

Advertisement

How to use Terminatable
Here's what I wrote in the Service UI proposal about Terminatable:

Terminatable is an interface implemented by service objects that need to prepare themselves for garbage collection.

Service objects may be pulled into Jini client programs that were written by programmers who did not have (or did not use) full prior knowledge about the interface of the service object. Eventually, such clients may wish to offer up such service objects to their garbage collector by releasing all references to the service object.

If a service object, during the course of its lifetime on the client, creates and holds onto no finite nonmemory resources (such as file handles or sockets) and starts no threads, the client can safely release all references to the service. The garbage collector will, at its leisure, reclaim the memory occupied by the service object[...] But if a service object opens a socket connection back to the host from which it came, or opens a file locally on the client, or fires off a thread, then the client can't just let go of all references to the service object. The client must first "terminate" the service -- tell the service to prepare itself for garbage collection.

Any service object that needs to be prepared for garbage collection for any reason should implement Terminatable. Client programs, before they release the last reference to a service object, should check to see if it implements Terminatable. If so, it should invoke terminateService() on the service object before releasing the last reference to the object.

If a service object need not be prepared for garbage collection, it should not implement Terminatable. Thus, Terminatable should never be mentioned in Jini Service APIs. Terminatable should be implemented by individual service object implementation classes, if their instances need to be prepared for garbage collection.

To be client-friendly, service objects should not "start" (for example, fire off threads, allocate big chunks of memory, open a socket connection, and so forth) when they are deserialized into the client's address space. Services should in general use a lazy starting approach, in which they don't start until they are first used. If a client never uses a particular service object, then it won't ever start. This in turn implies that if a client never used a Terminatable service object, then that client wouldn't actually need to invoke terminateService() on the object before releasing the last reference to the object. However, given the possibility that some service objects may decide to ignore the rules of good citizenship and start when they are deserialized, client programs should always invoke terminateService() on Terminatable service objects before releasing the last reference.

In a different part of the Service UI proposal, I described the behavior of clients with regard to Terminatable:

If the client is finished with the service object, it can also release all references to the service object. Before releasing the last reference to the service object, however, a client should check to see whether the service object's class implements net.jini.service.Terminatable. If so, the client should invoke terminateService on the service object before releasing the last reference.

Once terminateService() has been invoked on a Terminatable service, the service object should be considered defunct. [...] Thus, if a client wants to grant a user access to the same service again after invoking terminateService() on the service object, or even just use the service directly, the client program will have to retrieve a fresh copy of the service item from the lookup service. (Put another way, terminateService() should only be called on a service object just before releasing all references to the service object.)

Concerns about Terminatable
At the design review, I mentioned how a Jini client could ensure that a network-mobile object released any nonmemory resources it held at the client, before the client released the last reference to the object. When I presented the Terminatable interface, the Jini architects didn't like it. Their reaction didn't surprise me. I brought up the issue primarily because I wanted to hear the architects' discussion about it, a discussion I found quite interesting.

First, the Jini architects believed an interface such as Terminatable didn't belong in the Service UI API, because cleaning up nonmemory resources used by network-mobile objects wasn't a problem specific to Jini Service UIs. In fact, as Jim Waldo said, "Resource reclamation is not even a problem specific to Jini programs, it's a general problem of Java programs." As a result, they concluded the Jini Service API was not the proper place to address the issue. Terminatable was given a thumbs down.

What I also found interesting, however, was that I heard no consensus on another potential solution. Various ideas were tossed about, including having finite nonmemory resources leased on the client side, or extending the garbage collector's contract so that it would promise to run if file handles or sockets ran out. But in the end, it was merely decided that since the solution was unclear, we shouldn't attempt to define something like Terminatable in the Service UI API.

John McClain, a member of Sun's Jini team and participant in the ServiceUI design review, summarized this portion of the design review this way:

Resource recovery is a hard problem. To come up with a good solution would require both many people-hours, much calendar time, and probably require changes to software and specifications the Jini team does not control (for example, the Java spec and/or the JDK). Providing a partial solution could well be worse than providing no solution at all, especially since in many circumstances there are existing ways to avoid the problems Terminatable addresses. Given a choice between holding up the service UI spec to work up a solution (a task that may well take years), providing a partial solution, and doing nothing, it seemed that discretion was the better part of valor.

One concern I had about Terminatable that the Jini architects did not bring up was the "defunct" state that invoking terminateService() could create. I mentioned the defunct state in my description of the interface contract:

Once terminateService() has been invoked on a Terminatable service, the service object should be considered defunct.

My concern was that this defunct state could render invalid other contracts offered by other interfaces and classes that make up the object. This behavior violates the principle that all the supertypes of an object's class should be compatible with each other. For example, imagine you have an object whose class implements Laughable and Toastable. Laughable declares two methods, laugh() and lastLaugh(). Toastable declares one method, toast(). You should be able to pass this object to a method that expects a Toastable, and that code should be able to operate on the object knowing only about Toastable.

Now imagine that invoking laugh() interfered with the object's ability to fulfill the Toastable contract when toast() is invoked. And that invoking lastLaugh() would enable the object to once again fulfill the Toastable contract. In effect, invoking laugh() places the object in an "untoastable" state, and invoking lastLaugh() places the object back into a "toastable" state. Given this behavior, code that knows only about Toastable would only be able to use the object as a Toastable when the object happened to be in a "toastable" state. Another way to look at this is: if the object isn't always toastable, its class really shouldn't implement Toastable. Thus, Laughable and Toastable aren't compatible interfaces, and both shouldn't be implemented by the same class.

Now consider the following language from my description of Terminatable's contract:

If a service object need not be prepared for garbage collection, it should not implement Terminatable. Thus, Terminatable should never be mentioned in Jini Service APIs. Terminatable should be implemented by individual service object implementation classes, if their instances need to be prepared for garbage collection.

Thus, terminateService() is a bit like the laugh() method of the previous example, in that laugh() was not part of the contract of Toastable, but laugh() nevertheless affected the object's ability to fulfill the Toastable contract. As soon as you invoke terminateService on a Terminatable object, it can break the contracts of all the other classes and interfaces that make up the object. Invoking terminateService() puts an object in a "defunct" state, and the rest of the object is guaranteed to work as advertised only if it's in the "nondefunct" state. In my mind, this feature of Terminatable makes it incompatible with most other interfaces and classes, so I was very uneasy about actually adopting Terminatable as the standard way to deal with mobile objects holding onto finite nonmemory resources at the client.

<<  Page 5 of 8  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2017 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us