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
http://your_machine:port/your_jar_file.jar
.
To configure what your code is allowed to do, you must set
a security policy file. Make sure that your policy file allows
DiscoveryPermission
for the groups you're searching
for, or AllPermission
if you want to turn off all
security (which is a bad idea for a production environment).
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 renewUntil()
with
renewFor()
. The renewFor()
method does
not have this continuous renewal behavior, and passing
Lease.ANY
to it does essentially nothing.
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 RemoteExceptions
--you have
the responsibility to do the clean up in your own code. The way you
do this is by calling discard()
on your
LookupDiscovery
object. This causes the
discarded()
method on your DiscoveryListener
to be invoked.
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 renewUntil()
with
renewFor()
. The renewFor()
method does
not have this continuous renewal behavior, and passing
Lease.ANY
to it does essentially nothing.
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 equals()
and hashCode()
implementations on stubs will already
have the correct behavior. If your proxy is a "smart proxy" that
"wraps" an RMI stub, you can likely just delegate calls to
equals()
and hashCode()
to your internal
RMI stub.
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 -Dnet.jini.discovery.interface=127.0.0.1
on the command line. Also, use the loopback IP address (127.0.0.1)
instead of the hostname everywhere you have the opportunity to do so.
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