Integrating Apache with an EJB Server
This first part deals mainly with getting a working VAJ 3.5 SOAP configuration for development. But, even though a lot of it is related to WebSphere and Visual Age, it covers issues such as security integration between SOAP and EJB servers that apply equally well to any J2EE server.
Setup the development environment.
First, download SOAP from the Apache SOAP homepage. I downloaded the 2.0 binary zip and the 2.0 source zip. Unzip both in to a common directory. Make sure you get the source version. Apache SOAP, by default, uses the servlet 2.2 API. VAJ doesn't support this right now so we get the source version and modify a couple of lines to make it work with servlet V2.1. WAS 3.5.2 does support servlet 2.2 but it's not much good when the development tool does not.
Now, get the xerces.jar file from your WebSphere/appserver/lib directory. Import this in the IBM XML Parser project in VAJ. Next, we import the SOAP sources in to a new project called SOAP. There will be a couple of problems, thats OK. Now, switch to the Problems pane in VAJ. You will see that your SOAP project has some problems. These are the methods that are using the servlet 2.2 API. There are two types of changes to make. Any session.getAttribute calls need to be changed to session.getValue. Change any session.setAttribute methods to session.putValue. At this point you should have no problems in the SOAP project.
It's important to use the xerces.jar (and xalan.jar) from the WAS appserver/lib directory. If you use a different version for your server application then you run a good chance of runtime problems as WAS was developed with a different version of xerces/xalan that your code. So, from now on, you should get used to the fact that you have to use the same version as your WAS runtime. If you play around with custom class loaders etc, then you could load both the WAS and your version concurrent in the same VM but this is only for the brave.
Now, we need to add the SOAP rpc router servlet to our VAJ servlet test environment. Find the default_app.webapp file. It should be in your VAJ/ide/project_resources/IBM WebSphere Test Environment/hosts/default_host/default_app/servlets directory. Add the following xml fragment to the file before the final </webapp> tag.
<servlet> <name>rpcrouter</name> <description>SOAP rpcrouter</description> <code>org.apache.soap.server.http.RPCRouterServlet</code> <servlet-path>/rpcrouter</servlet-path> <init-parameter> <name>ServicesStore</name> <value>c:/temp/DeployedServices.ds</value> </init-parameter> <autostart>true</autostart> </servlet>This declares a new servlet at the /rpcrouter uri. Now, copy from the directory where you unziped the soap binary zip all the files at this path 'soap-2_0webappssoap' to a directory in VAJ/ide/project_resources/IBM WebSphere Test Environment/hosts/default_host/default_app/web. So, you should have a soap directory in the web directory after this action. This copies the web application for administering the SOAP services to the WTE directories. At this point, the environment should be ready to go. You'll see that the servlet has an initial parameter ServicesStore. This should specify where the list of deployed services is persisted. This is ignored in V2.0 of SOAP as far as I can see. The .ds file is persisted in the C:ibmvjavaidetoolscom-ibm-ivj-ui-webcontrolcenter directory on my machine.
Verifying the test environmentCreate a new project called SOAP Samples. Import the java file in the soap/samples directory to this project. You should have no errors. We will now deploy and launch the address book sample that come with the product to verify the environment is good. Launch the WebSphere test environment tools and select the servlet engine. Click the 'Edit Class Path' button and select all projects for the classpath. This adds the SOAP project and XML etc to the path the WTE servlet engine uses. Click OK, and hit the start servlet engine button. Now, launch a browser and enter the url 'http://localhost:8080/soap'. This should display a welcome to SOAP page. Click the admin link. You should see the SOAP admin page with List/Deploy and undeploy buttons. Click on LIST and you should see a message saying that no services are deployed. Now click on DEPLOY. A form appears where we describe the new service. Each sample SOAP service comes with a DeploymentDescriptor.xml file. This file contains all the information needed for deployment but you can't deploy automatically using this file for now (as far as I know!). So I just copied the data from this file to the form manually to deploy the address book sample. Here is the information I entered in the form to deploy the service:
Right, click Deploy and it should say it was successful. You can verify that the DeployedServices.ds file was created after this step. If you restart the servlet engine then it should read the old deployed services from this file. The file contains some binary serialized objects so you can't edit it manually or construct it with an editor.
Now, we'll run the addressbook sample client. The package samples.addressbook has the code for this sample. The service is the class AddressBook. The test client we will use is the class GetAddress. Right click on the class GetAddress and click properties. Click the Program Tab and enter:
http://localhost:8080/rpcrouter "Bob Q. Public"for the command line arguments. The first argument is the URL of the servlet we just added to the WTE. This is the SOAP HTTP dispatcher. It accepts the XML encoded RPC from a HTTP request. It then makes a call to our service class (samples.addressbook.AddressBook) and invokes the appropriate method. Only the methods declared when we deployed the service may be called. The dispatcher then encodes the result of the method call and returns it as the HTTP response. The second argument is the name of the address we require from the service. Now switch to class path and click the compute button. Click OK to display the properties dialog. Run it. You should see the following output in the console:
456 North Whatever Notown, ME 12424 (987) 444-5566So, if you get this far then it's working fine in VAJ.
Gotchas with Clustering and Apache SOAP V2.0.
Apache SOAP is completely cluster dumb for the moment. They store the deployed services in a text file and overwrites it when-ever you deploy a new service to that box. This means that even if you nfs share the file between WebSphere nodes in a cluster you will trash the file when you deploy a service to box A and then deploy a service to box B. Basically, the first service will be erased by deploying B. They should really use a database for this store. This is acknowledged in the source so if you can wait fine. Otherwise, just deploy all services using a single box and then copy/replicate/share the file to the other boxes. If you do this then you should be fine with regards to clustering. Just make the SOAP web app a model and clone it out to your cluster.
Once you do this then it should be fine in a cluster. Each SOAP dispatcher in each VM is independant from all others in the cluster.
Next, remember it uses the 2.2 servlet API. We had to change it so that it used the V2.1 servlet API to get it to work in VAJ. Lastly, the ServicesStore attribute for the dispatcher servlet is ignored so it stores the services registry in the current directory your servlet engine has.
We can put a WebSphere ACL on the rpc dispatcher. Unfortunately this allows us to merely say who can or cannot make any SOAP calls. We can't stop someone making SOAP calls to specific services using this approach. Our java service class also has no access to the servlet context so it cannot use J2EE security methods (getCallerIdentity or isCallerInRole) to implement any security checks.
But, what we can do to workaround this is to put the code for the service in a stateless session bean. This means our service class is merely a java bean that delegates the call to the session bean. So, the SOAP service simply wraps the session bean. So, it would get an initial context, find the home interface, create the bean and make the method call. Now, we make a SOAPROLE method group in WAS. We only allow people contained in this SOAP role to make calls to the rpcrouter servlet. The SOAP dispatcher will then forward the RPC to our service. We then get the IC, the home and make the call to the session bean.
We should specify method groups on the methods of the session bean. Clients that need to access to the methods should be placed in these method groups. Clients that need to make any SOAP calls to any bean should also be members of the SOAPROLE. This works around the security problem. Now, even though any one in the SOAPROLE method group can make SOAP RPCs, the WebSphere container will stop the servlet from making calls to a session bean method unless the client that authenticated to the servlet is authorized to do this. This security check happens even though this is all taking place in the same thread. So, as long as we use a Session bean for our business logic and just make the service a wrapper to the bean, then we can leverage WebSphere's security to limit SOAP RPCs to specific users albeit a little indirectly. You will have a problem limiting access to specific RPCs if you just put business logic code in a normal java bean.
ConclusionSo, you should have a working VAJ SOAP setup at this stage. You should know how to integrate WebSphere security with the SOAP runtime. The next installment will show how to write a service that talks with stateless session beans and how to deploy this to WAS 3.5 although it should be pretty clear how to deploy it now.