Howdy!
This is a good one that gets almost everybody...
Remember that the client of a remote bean's home is using only two things: the bean's home interface and the home stub.
So the client needs three things total, one at compile time:
COMPILE TIME: the client needs the interface in order to compile (just plain old
Java rules).
RUNTIME: the client will be getting
A) the stub object (shipped over as a result of the JNDI lookup)
B) the stub object CLASS
In RMI, the stub is serialized and shipped to the client. When it reaches the client, the RMI subsystem tries to deserialize it and *reconstitute* the object. And since you can't have an object without a class... the class has be findable for deserialization to succeed.
Two things implement the Home interface:
1) the Home object, which lives ONLY on the server, and is implemented by the Container
2) The Home stub, which has to run on the client, and is implemented by the Container.
So, you have to make sure that your client programmer has the Home interface in order to write the client code, but the client will also need the Home STUB class file, in order to actually *run*. The client, of course, needs to have the same things for the component interface: the component interface and the CLASS of the component stub. The client does NOT need the class file of the bean class or the EJBObject.
The real question when you're working out your client, is to figure out HOW to get these things to your client programmer (and client application). The ideal way is to have dynamic code downloading (WARNING: this is NOT on the exam) (which really means dynamic CLASS downloading)where the stub object is shipped over, but the stub carries with it information (usually a URL) about *where* the class file can be retrieved using (usually) an HTTP get. So if your vendor supports that, and the client has appropriate security settings, then it works like this:
* Serialized stub comes over to the client
* RMI subsystem tries to deserialize the object
* RMI subsystem cannot find the class file in its classpath
* RMI subsystem inspects the object to see if it has been *stamped* with a URL for where its class file lives
* RMI subsystem does an HTTP get at that URL, for that class file.
* At that URL, there is a web server serving up class files
* The class file is downloaded to the client, and RMI subsystem finishes deserializing the object
OK, that's the cool and maintainable way.
But not all vendors support it, and your client has to be configured to accept the code (so it needs to install a security manager, blah, blah, blah)
So, the typical way today is to just GIVE the client the stub class, in advance, to put in the client's classpath. Your Container needs to give you the stub classes for the client, because YOU don't create them yourself. Most Containers will prepare some kind of a client JAR file that has everything (interfaces and stub classes) that the client needs.
(Note: some containers generate the stub on-the-fly with dynamic proxy creation, but we're not talking about that because you don't have to worry about it)
Does this help?
I would say that one of the biggest problems people have when they try to run a client (well, um, except for that little RI bug
) is that the stub class can't be found, so the client fails at the lookup. The stub object gets shipped over, but it can never be successfully deserialized.
In this code:
Object o = ic.lookup("Advisor");
AdviceHome home = (AdviceHome) PortableRemoteObject.narrow(o, AdviceHome.class);
If the lookup is successful (i.e. there is something with that name in JNDI), then Object o holds a reference to a stub object.
The next line does a narrow (to guarantee that the stub object is something which can in fact be cast to the home interface type).
It then does a cast to the home interface type. So, AdviceHome is the home interface type, and the stub is something which implements the home interface.
Several things can go wrong here...
1) A JNDI exception, for a variety of reasons including that the lookup name is wrong, or that there's no JNDI server at that IP address and port number (which you don't see in this code because these values are in a jndi.properties file)
2) A ClassNotFoundException because the stub class file is not on the client's classpath, so the deserialization can't complete. This is usually because the client just doesn't have it on their classpath. If you DO have a client JAR on the client, and you get this exception, you need to make sure the JAR is actually on the classpath when you run the client.
3) A ClassCastException because the stub came over, but can't be cast to the home interface type. (The lookup succeeds, but then it fails at the narrow/cast line). This could happen, for example, because you changed the interface at some point *after* you gave it to the client. Maybe you changed the arguments in one of your interface methods, and then redeployed, which caused a new stub object to be generated, and then that stub can't be cast to an older version of the interface.
OK, that's all I can think of for now...
cheers,
Kathy