Troubleshooting Jini Applications
Class Not Found (especially a Stub class)
ClassNotFoundException
when your application receives a serialized Java object from another
program, but the implementation (class files) for this object aren't
available locally.
There are three common causes:
Codebase problems
You haven't installed a security manager
java.rmi.RMISecurityManager.
The HTTP server isn't correctly exporting code
root/foo/bar/MyClass.class, where root
is the web server's root directory. (The codebase in this case would
be simply http://your_machine:port/; be sure to note the
trailing slash.)
Alternatively, if you bundle your code into a JAR file, you'd typically
put this JAR in the web server's root and set the URL to
To configure what your code is allowed to do, you must set
a security policy file. Make sure that your policy file allows
lookup() method
(the one that doesn't take a maximum number of "hits"), the method
may return a null proxy simply because there is no registered service
matching the search criteria you provided.
Code isn't being downloaded properly
lookup() that take the maximum number
of matches, and you get back a ServiceItem object but
the proxy is null, then you know something is misconfigured.
What this means is that your client is not able to download the code needed to reconstitute the proxy. This can be because you haven't installed a security manager (or have set a bogus policy), or the service that registered the proxy didn't set its codebase correctly, or codebase was set but is pointing to a bogus location on a web server. See the "Class Not Found" hint at the top of this page for more details.
public to it.
Lookup proxy not properly exported
reggie-dl.jar file
in Sun's sample implementation).
rmid program must be running before
you start the lookup service. It can take a while for rmid
to get cranking, so give it a few seconds.
Security problems prevent lookup proxy from being downloaded
You're being bitten by the StartServices GUI bug
LeaseRenewalManager and calling renewUtil()
with the parameter Lease.ANY. This means to continue
to renew the lease indefinitely until told otherwise (or when the
LeaseRenewalManager goes away, which will happen when
the client exits).
One very common problem is confusing
RemoteEventListener (subclassing
UnicastRemoteObject, etc.) but you never get called with
any remote events, chances are you're not properly exporting your
listener class.
Make sure that your program sets codebase properly (see above) and puts the listener class and any supporting classes in an accessible location. Usually this means a web server. What's typically going on in this case is that the service you're talking to is going through the "Class Not Found" issues described above.
So when you detect that a lookup service has gone awry--because attempts
to communicate with it cause
Call remote methods in a separate thread
RemoteEventListener in a client--this method
invocation will block until the client returns. If the client hangs,
or just takes a long time to complete, then the service will be hung
as well.
The solution to problems like this is to have remote methods run in separate, short-lived threads. You can create a small "wrapper" thread just to invoke the remote method.
setSerialForm() method on
the Lease interface to control this.
LeaseRenewalManager and calling renewUtil()
with the parameter Lease.ANY. This means to continue
to renew the lease indefinitely until told otherwise (or when the
LeaseRenewalManager goes away, which will happen when
the client exits).
One very common problem is confusing
rmid. Starting with 1.3, you need
to provide some additional security guidelines to rmid if you want
something other than the default behavior. And, since reggie is
basically just a "wrapper" program that registers with rmid, it
bumps into this new behavior when you're running under 1.3. To
work around the problem by simply getting the "old" 1.2-style behavior
of rmid, you need to launch rmid with the following option:
rmid -J-Dsun.rmi.activation.execPolicy=none
ServiceDiscoveryManager. There
are two likely causes of this behavior:
You need to make sure you're correctly exporting the SDM's event listener
ServiceDiscoveryManager registers remote event listeners
with the lookup services with which it is interacting. It is
your responsibility to make sure that this event listener is
correctly exported. Typically, you will do this by bundling up a
JAR file containing the necessary classes (net.jini.lookup.ServiceDiscoveryManager$LookupCacheImpl$LookupListener_Stub.class and
net.jini.core.event.RemoteListener), place this
JAR file in the filespace of an HTTP server that can export it
to callers, and set a codebase URL that points to this JAR file
on the web server. Without doing this, the
ServiceDiscoveryManager will be unable to correctly
monitor the services in the community.
ServiceDiscoveryManager considers two services to
be "equal" if their proxies equals() methods return
true. This is so that the ServiceDiscoveryManager can
determine when a service's proxy implementation changes, and
report that a new version of the service has appeared. It is
your responsibility to correctly implement equals()
on your service's proxy. Typically, you will override the method
to return true if two proxies refer to the same back-end service.
Be sure to note that the Java libraries expect that objects that
are equals() to one another to return the same values
from hashCode(). So you will have to override this
method too.
If your proxy is simply an RMI stub object, then the
Having said that, though, alert reader Frank Kmiec reports that it's
possible to get things running by ensuring that all parties (rmid,
the lookup service, and of your clients and services, the web server,
etc.) all are using the loopback interface. To set this up, pass
the property Be aware that in general, using the loopback address is a terrible idea when you actually have a network, and especially when you're deploying. But this may help for those folks developing sans network. No guarantees, your mileage may vary. |
Keith Edwards
kedwards@kedwards.com