Hi all: I'm confused by the solution to the "What goes where?" exercise on page 109. How does the home stub get into the client application? Why is the home stub class on the client side instead of the home class itself. The following code comes from the AdviceClient.java on page 57.
Is the Object o the home stub? In the second line the object is being cast into AdviceHome. Doesn't this require that the client has access to the AdviceHome class? Is the advisor that is returned in the third line the EJBObject stub? Thanks for your help.
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
Joined: Nov 25, 2003
Hey Kathy: Thanks a million. My cloud of confusion has lifted. I suggest that you put that explanation in your next printing of HF. Just a couple minor questions. You said:
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)
Was there a jndi.properties file for the advisor application exercise? I suppose we didn't one because the client and the bean were on the same machine. You said:
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.
How does the client usually get the client JAR file. I suppose it is often the case that the application assembler creates the client and has access to the client JAR file. What if the client app is created by someone other than the application assembler? Is there some kind of protocol for this person to get a hold of the client JAR? Also, if someone changes one of methods in the component interface as you suggested, who is responsible for ensuring that all the clients get an updated version of the client JAR file? Thanks again. [ January 16, 2004: Message edited by: Keith Rosenfield ]
thanks Kathy, That was really an interesting explanation...Most of the EJB books in the market have plenty of pages explaining how to code EJBs, but fail to explain the fundamental concepts clearly. But in this matter, Javaranch rocks! Cheers, Vish
I was having the java.lang.ClassCastException: when I was invoking the EJB AdviceApp.ear with its client (the example of the Head First EJB book). I deployed the EAR with deploytool (for Sun Java System Application Server 8.2). An important thing I realized was the generated EAR was not having the Stubs inside the EAR file. The modifications are in the following order:
1.- Change the client from this:
The reason is that in the previous code there is an Exception “Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/corba/se/org/omg/CORBA/ORB” and it is better to specify the characteristics of connection.
2.- Deploy the application again “with the .java files” (the ones that inherit from EJBObject, EJBHome, SessionBean) in the deploytool. When this is done you can get the EAR file in the path you selected.
3.- Use the Sun Java System Application Server 8.2 (usually at http://localhost:4848 or http://localhost:4848/admingui/TopFrameset) and log in with your user and password. Look for Application Server -> Applications -> Enterprise Applications and undeploy your recent deployed App.
4.- Look for the EAR generated by deploytool and Deploy it. It is very important to select “Generate: RMIStubs ”
5.- Log out from the Sun Java System Application Server 8.2 and go back to the Deploytool. In Server -> Localhost… -> Applications, select the App and click on the Client Jar, now type the path for the Client Jar and Ok. You will be able to see the Stubs generated inside the Jar.
6.- Add the new client jar to your classpath where you have your client java (step 1) and run it. Be sure the server is running. You should see the EJB working without errors.