|
Calling the SessionBean from the Servlet
By now you've most likely seen this a couple of times, so I'll just show you the code that you'll need to add to your servlet so you can use the EJB.
Make modifications to the servlet's init method:
public void init( ServletConfig config ) throws ServletException
{
super.init( config );
String msg = null;
try
{
Context ctx = new InitialContext();
Object obj = ctx.lookup("java:comp/env/beanrefs/ejb1");
home = (SBEjb1Home)PortableRemoteObject.narrow(obj,SBEjb1Home.class);
}
catch (Exception e)
{
e.printStackTrace();
}
}
Make modifications to the servlet's doGet method:
public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException
{
response.setContentType( CONTENT_TYPE );
PrintWriter out = response.getWriter();
String msg = null;
try
{
SBEjb1 ejb1 = home.create();
msg = ejb1.callMe("give me my message back!");
ejb1.remove();
}
catch (Exception e)
{
msg = e.getMessage();
e.printStackTrace();
}
out.println( "<html><head><title>Servlet1</title></head><body>" );
out.println("<P>the bean wants to tell us this: " + msg + "</P>");
out.println( "</body></html>" );
}
Add this instance variable:
private SBEjb1Home home = null;
Add the following import statements:
import ejb1.*;
import javax.naming.*;
import javax.rmi.*;
Getting the Compile Time Class Path Right
You guessed it. This is cool, but it doesn't compile! We need to make the used ejb classes available in the project's class path. What bean classes are we using? The home and remote interface classes, nothing else. So all we need is the EJB1-client.jar. Add this to WAR1's class path as follows:
Project -> Project Settings
Classpath tab
Add Archive... and point to the "EJB1-client.spf" file
Note that EJB1-client.spf is not a typo. When you add the spf (the project definition) to the class path, XWB checks the project for changes and builds them if necessary before building the WAR project. And, if you modify the class path entry, e.g., "..\ejb1\EJB1-client.spf", you'll make your project independent from fixed directory structures.
Now, try to build and deploy the WAR. If you are lucky like I was, it will work. Cool, we're done. Or are we? You guessed it. We're not! Try to call the servlet. When you point your browser to "http://server/db/WAR1/servlet1", you'll get something like "an unexpected server exception occurred while processing...".
So what's wrong?
Actually, a couple of things:
- We called a bean reference in our servlet to lookup the bean in the servlet's environment, but we never mapped that bean reference.
- We told the WAR project where to get the bean classes at compile time, but didn't for runtime.
Now let's fix it.
Getting the Runtime Class Path Right
Here's where the spec writers remembered how useful applets are in loading classes from outside the jar in which they're packaged. The mechanism is called virtual class path, or manifest class path. How does it work? The servlet container introspects the manifest file of the WAR and checks for class path entries. In case there are archives defined in that file, the container extends the runtime class path of the WAR by those mentioned archives. Currently, the various J2EE servers differ quite a bit. To be cross-vendor compatible, the safest approach is probably to package the WAR and EJB jar in an Enterprise Archive (EAR). I'll show you how this can be accomplished later. (See "Packaging the Two as an EAR".) But for now we'll deploy the two archives separately. In this case, we'll need to make sure that resources in the WAR can access the EJB resources at runtime. To accomplish this, we'll extend the WAR's manifest class path to include the EJB's remote jar. This jar is created by the containers deployment tool when you deploy the ejb jar. It's needed to communicate with the enterprise bean, since it contains the RMI stubs that talk to the bean remotely. But check your particular server (and version) to make sure this works.
At this point, let's assume we'll deploy to a SilverStream Application Server (>= 3.7.4), and we'll use XWB 1.1 or SilverCmd to deploy the archives to SilverStream. Note that if you don't specify a name for the remote jar, the name will be generated for you. The naming pattern for this is jar name + "Remote.jar" (e.g., EJB1.jar becomes EJB1Remote.jar). You can check SilverStream's Objectstore for the names of all deployed objects (http://server/db/SilverStream/Objectstore/Jars or Jars in the SilverStream Designer).
Adding the Manifest
Currently, XWB 1.1 does not support creating and adding the manifest. Adding that capability to XWB is on SilverStream's to do list, but for now the fastest way I know of to create and add the manifest is to go to your Explorer and create a text file called "MANIFEST.MF" in "c:\work\war1\META-INF", then enter the following two lines:
Manifest-Version: 1.0
Class-Path: EJB1Remote.jar
Note that it's a good thing to make sure you have a line break after the class path line as some containers read the content of this file via BufferedReader.readln().
Now we can go back to XWB and add the file to the WAR project.
Right click on WAR.spf and choose "Add File to Project..."
Point to META-INF/MANIFEST.MF
Add the file to the WAR1 project at this location: "META-INF/MANIFEST.MF"
And that's it. We've just extended the virtual class path. Now we only have to add the bean reference and we'll be done.
Map the SessionBean as a Bean Reference
- Open the WAR's deployment descriptor and scroll all the way down to the EJB references
- Right click on EJB references and choose "Add"
- Right click on "UntitledBeanReference" and choose "Properties"
- Name the bean reference "beanrefs/ejb1"
- Leave the referenced bean name empty
- Enter "ejb1.SBEjb1Home" for the home interface
- Enter "ejb1.SBEjb1" for the remote interface
You can make the link here by filling in the referenced bean name, or you can do it later in the deployment plan. I prefer to create the reference here and link the bean later to the JNDI name I've selected for it. (I've had trouble in the past when entering the bean's name here. In most cases the lookup couldn't find the bean.)
Now to deploy our modified WAR.
- Open the deployment plan. (In case you left the referenced bean name empty in the deployment descriptor, you'll see a "beanrefs/ejb1" EJB reference. Just right click on it and choose "Properties". Fill in the JNDI name of your SessionBean "ejb/ejb1".)
- Save and deploy.
Yes, it still deploys successfully. No change there. But now, do the ultimate test: Call the servlet.
|