HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

At the end of last year, I was given a rather unpleasant assignment. This company had several Java Remote Method Invocations (RMI) services that were interacting with the legacies of the organization and I needed to open up an XML interface for them.

My first idea was to use the Simple Object Access Protocol (SOAP). But the current W3C specification and implementation of the SOAP API from Apache presented only the HTTP binding for SOAP. Because of this, the SOAP services had to be deployed in a Web server container. The possibility of opening up a SOAP-based interface to the existing RMI services was limited by the following factors:

  • If we convert the existing RMI services as SOAP-based and deploy them in a Web server container, the existing clients to those services have to be rewritten or reconfigured in order to be able to talk to the SOAP server in a SOAP-recommended way. In the worst-case scenario, we can maintain two interfaces (RMI and SOAP) for the same set of services, which is not desirable.
  • The services were interacting with many legacies and in turn with the native programs, which could not be accessed from the Web server system for various security reasons.

    We could have implemented a proprietary XML-based solution to overcome the problem. I chose to use SOAP because it's slowly emerging as the new standard for XML-based distributed computing. Moreover, by following a standard, it's easier for other applications to use the services without any particular knowledge of their internals.

    In this article, we want to develop a framework that soap-enables the existing RMI services by keeping the interfaces intact so they can still be used as normal RMI services. In addition, the framework will demonstrate how to write a SOAP message handler that will receive a SOAP request document, parse it to invoke the requested service, and construct a SOAP response document. We'll also develop a mechanism to specify, deploy, and configure the services with the help of a deployment descriptor file. Then we'll build a client program to access these services.

    The framework will be developed by defining a very simple RMI service; we'll be using the Apache SOAP implementation (version 2.2) to build a SOAP client to use the RMI services. Note: This framework is just one approach that can be used to talk SOAP over RMI. (The source code can be downloaded from below. )

    Anatomy of SOAP
    SOAP provides a simple and lightweight mechanism to exchange structured information in a decentralized and distributed environment. In essence, it's a model for encoding data in a standardized XML format for use in a variety of situations, such as messaging and remote procedure calls (RPC). SOAP consists of three essential parts:

    1.   Envelope: This is the top-level XML element or the root element in an XML-encoded SOAP message. The Envelope may contain any or all of the following information: the recipient of the message, the content of the message, and the processing instructions for the message.

    2.   Encoding rules: These specify the way the application-defined data-type instances will be exchanged.

    3.   RPC: These define a convention for representing the remote procedure calls and the responses to them.

    To understand the three parts of the SOAP message let's look at Listing 1. This listing shows a typical SOAP message in an HTTP request binding. The Envelope element is namespace qualified (SOAP-ENV) in order to separate it from any other application-specific identifier. It also contains information about namespace encoding. The immediate child element of the envelope in this example is the Body element. This element must be present in a SOAP message; it holds the information about the name of the remote procedure (GetLastTraderPrice) to be invoked and also encodes the parameters required by the remote procedure. In this particular message the remote procedure requires the name of the company (DIS) from which to retrieve the last trade price. Optionally, the Body element contains information about any error that occurred during the RPC and is encoded in a Fault element. The Fault element contains the error/status information with a SOAP message and, if present, can appear only once as a child element of the Body element.

    In complex cases, the Envelope element may contain another child element named Header. If this element is present in a SOAP Envelope, it must be the immediate child of the Envelope element. The Header element can typically hold information regarding authentication, transaction management, and more. In our particular example, we would not be using this header information.

    A SOAP response document contains a similar structure (see Listing 2). The Envelope element contains the Body element, which in turn contains the requested information, or a fault string, should there be any fault generated during the service call.

    For the purpose of this article we'll develop a remote procedure call framework to invoke RMI services, and we'll be using and dissecting the three parts of the SOAP specification.

    Conceptual Framework for Sending SOAP Messages over RMI
    The basic idea of how to achieve SOAP over RMI relies on the fact that if we can send and receive SOAP request and SOAP response documents over the wire using RMI protocol, we'll be able to achieve our goal. In the process, we'll create a SOAP Envelope document, serialize it, and send it to an RMI service that will decode the SOAP Envelope message, invoke the specified method on the specified Remote object, construct a Response object containing the return value or a fault string in case there's a problem, and again serialize it back to the caller (see Figure 1). The caller then parses the received SOAP response document and processes the result or the fault string accordingly. As the Apache SOAP implementation defines and implements the SOAP-HTTP binding, the objects provided in the API are not serializable in the context of Java RMI, and there lies the challenge - to overcome the present limitation until a more standard implementation of SOAP-RMI comes up.

    Figure 1

    The Components
    Since the requirement is to continue using RMI services and to add a SOAP interface around them, we need to build a SOAP wrapper around the existing RMI services. Also, we need to build a SOAP client following the Apache SOAP implementation. But at the moment the Apache SOAP implementation handles HTTP binding only, so we need to extend it so it can call RMI services. Also, on the RMI server side, we need some mechanism to configure, deploy, and load these SOAP-aware services. Remember, not all the RMI services may need a SOAP interface, so you should be able to specify and configure the services that are required to be SOAP-enabled. Thus, the following components need to be developed:

    • SOAP wrapper
    • SOAP service manager
    • Configuration file - the deployment descriptor
    • Apache SOAP extension (this will be a custom Call object, as we will see)
    • SOAP client
    The SOAP Client
    It's unusual to start with the last component specified in the list, but it helps me explain the flow better. As mentioned earlier, we'll be using the Apache SOAP API version 2.2 to write a SOAP client. It's beyond the scope of this article to provide a complete introduction and tutorial of the API, but I'll scheme through the necessary sections as and when required.

    First, it's important to understand that to invoke a remote service deployed as a Web service over HTTP or to invoke a remote service over RMI protocol, as we'll be doing, we're essentially making a remote procedure call (RPC). In an RPC, we make a call to the remote procedure and receive a response back. The API provides us with a call and a response object. Also, we often need to pass a few parameters to invoke the remote procedure, and these parameters can be encapsulated in the Parameter object.

    The main task is to provide the Call object with the Universal Resource Locator or the URI of the remote service, the name of the method to be invoked as a part of the remote call, and the parameters required by the remote method. The parameters may frequently be complex user-defined data types and objects such as a Person or an Address object. It's also required to define a serialization and deserialization mechanism for these complex objects. By default, the API provides default serializers and deserializers for data types such as Boolean, Double, Float, and Vector. If the data type is complex and user-defined, as an implementer of the SOAP client we have to define our own serializer and deserializer and register it with SOAP by setting a SOAPMappingRegistry for the particular data type.

    The term serialization/deserialization as used in the SOAP context is different from the serialization notion of Java RMI. In the SOAP context, serialization in a broader term means the XML encoding and decoding of the data types. Again, our example focuses on how to implement these remote procedure calls over RMI and use simple data types to avoid extra work in defining serializers and deserializers for complex data types.

    The Call object implemented in the API is tailored to the HTTP binding of SOAP. However, as explained earlier, we want to perform the RPC over RMI. Thus it's required to customize the Call object and extend it as RMICallObject and build the mechanism to achieve the RPC over RMI.

    The Envelope object in the API is not serializable to perform an RMI operation. Hence the first step in our framework will be to reproduce a SOAP Envelope element in the form of an XML Document object, which is serializable in the RMI context.

    public class RMICallObject extends Call
    {
    public Response invoke(URL url, String s) throws SOAPException
    private Document buildDocument()throws IllegalAddException
    private Response buildResponse
    (Document doc) throws Exception
    }

    This class structure represents the RMICallObject class that's designed to handle the RMI call. The class overrides the invoke() method from the Call class and defines two private helper methods to construct the Document object, which will be serialized over the RMI call, and another method to construct a Response object out of the returned Document object from the RMI call. For ease of use, I've used the JDOM API from Apache for XML data handling.

    The invoke() method takes the URL of the configuration file where the location and name of the RMI server is specified and looks up and obtains a remote reference of the same. As we'll see later, this RMI service will act as an entry point to all other RMI services we want to access (see Listing 3).

    SOAPRMIInterface is the remote interface that the RMI service implements in order to parse the Document object that's passed as a result of the SOAP call. The implementation of the SOAPRMIInterface is given a binding in the RMI registry under the name assigned through the variable server-Name.

    A Document object is constructed out of the generated Call object, which represents the SOAP Envelope.

    //construct a Document object from the Call object entries
    doc = buildDocument();

    After obtaining a remote reference of the RMI service, execute the remote method named "invokeMethod()" on this reference by passing the Document object to it. This method is responsible for parsing the Document object and acts as the gateway to all the SOAP RMI services.

    //calling the invoke method
    responseDoc = obj.invokeMethod(doc);

    Once the Document object is handed over to the remote service, the client-side process waits for a return Document object from the RMI server. The remote server object processes the passed Document object.

    Once we've extended the Call object to the RMICallObject and incorporated the functionality to perform an RMI call, the rest of the client programming follows what we do for a normal SOAP client (RMIClient.java) (see Listing 4).

    This listing is an example of the SOAP client. First, we obtain the reference to an RMICallObject, then pass the target remote service name as it's bound to the RMI registry, pass the name of the remote method to be invoked, create the Parameter object to pass the required parameters to the remote method, and, finally, invoke the remote method by calling the invoke() method on the RMICallObject. It's important to note that we pass the URL as the address of the configuration file (conf.txt) that holds the name and location of the SOAP server manager. As shown in Listing 4, the conf.txt file should be placed under the current working directory.

    Once this URL is passed to the invoke() method of the RMICallObject class, it constructs a Properties file out of the given configuration file conf.txt and an RMI lookup URL with the server name and the location.

    Once the returned XML Document object is obtained from the RPC, we can analyze the entries of the Document to obtain the returned result of the method call or the fault string associated with this method call, and construct the Response object out of that to return it to the Caller RMI client.

    SOAP Wrapper
    A major part of building this framework is to develop a SOAP wrapper around RMI services, which can parse and handle incoming SOAP requests. So, the basic idea of this framework is to serialize the SOAP request over RMI in the form of an XML Document object, and the SOAP wrapper is required to be an RMI service that can accept the serialized Document object. As a matter of fact, this is an RMI service acting as an entry point to all other RMI services deployed as SOAP-enabled, so it's vital to ensure that this service is up and running so other services can be accessed and used.

    First, declare a Remote interface for this RMI service.

    public interface SoapRMIInterface extends Remote
    {
    public Document
    invokeMethod(Document doc) throws RemoteException,
    RMISoapException;
    }

    Our SOAP wrapper RMI service will implement this interface, and, as a result, define the invokeMethod() method. As we can see, apart from throwing the usual RemoteException, this service also throws a customized RMISoapException to differentiate between normal RMI failures and any SOAP-related ones. This design is guided by the fact that to build a Response object, we need to pass the proper fault string information back to the caller object.

    The implementation of the invokeMethod() method must perform the following tasks:

  • Parse the received document and retrieve the Body element of the SOAP request.
  • Identify the name of the remote service, the name of the remote method, and the parameters passed to it.
  • Locate the remote service from the registry by analyzing the deployment descriptor (to be discussed in the following section).
  • Check whether the method specified is exposed as a part of the given service and if the service is defined in the deployment descriptor. If it can't locate the specified service, it throws an exception and eventually propagates it as a SOAP fault string back to the caller.
  • Create a new instance of the specified service implementation class and invoke the specified method by passing the parameters obtained as a part of the SOAP request, if the method specified is a part of the deployed service. If the method name could not be located, it throws an exception and propagates it back to the caller as a SOAP fault string.
  • Create and return a response XML document to the caller as an RPC response once the service is successfully invoked. After receiving this XML document, the RMICallObject then constructs the Response object out of it and passes it to the SOAP client object.

    This process is large in scope; the source code for SoapRMIServer is available on www.sys-con.com/java/sourcec.cfm. To check if the method exists for a given service, the service uses the Java reflection mechanism. It also uses this mechanism when it constructs a Response XML Document object out of the returned value of the service, in case the returned value is an object. While creating a response XML document to maintain the SOAP response specification, it uses the received SOAP request document to maintain the integrity of the namespace and encoding style used.

    The Deployment Descriptor
    SOAP utilizes an XML file called deployment descriptor to supply information to the SOAP runtime environment. The deployment descriptor contains the Universal Resource Name (URN) for the service, the name of the method, the Java implementation class if the SOAP service is deployed as a Java class, and more regarding serialization and deserialization (in a SOAP context and not in an RMI context). The amount of information that can and should be supplied depends on the SOAP runtime requirements. In the example we're following, which is a simplistic model, we need information regarding the RMI services that are deployed as SOAP-enabled services.

    Typically, our deployment descriptor(s) will contain the following information:

    • The URN of the service as the name that binds it to the RMI registry
    • The name of the Java class deployed as the specified service
    • The method that's exposed as the interface to the service
    • Whether or not the method is implemented as static
    The other bits of information that normally become a part of the SOAP service, such as the scope of the service (application/session, etc.), are not relevant to the framework we're developing, because in this particular framework I've tried to avoid the complications of session management.

    The deployment descriptor is typically encoded within a top-level element <isd:service>, which is namespace-qualified to avoid conflict with any other user-defined entity. An example of a deployment descriptor is as follows:

    <isd:service xmlns:isd=
    "http://xml.apache.org/xml-soap/deployment" id="urn:greetingserver">
    <isd:provider type="java" scope="Application" methods="sayHello">
    <isd:java class="Greeting" static="false"/>
    </isd:provider>
    </isd:service>

    This descriptor particularly defines a service named "greetingserver" with an exposed method "sayHello", and the implementation Java class of the service is "Greeting" and the method is nonstatic.

    The basic idea is to provide an XML file in which we can describe the deployed services. To deploy all the services in a single file, I've put the individual descriptors under a root element called "<soapservices>" (soap.xml).

    The Apache SOAP implementation provides a utility class called DeploymentDescriptor to load and parse the deployment descriptor files. I've used the same to load and access my consolidated deployment descriptor file, along with a ConfigDescriptor class designed to parse the file and retrieve everything or a descriptor for a particular service against its service ID.

    The Service Manager
    So far on the server side we've created the SOAP wrapper RMI service, which is the gateway to all other SOAP-enabled RMI services, and have also devised a deployment descriptor to specify and describe all the services that are to be deployed and loaded as SOAP-enabled. As a precondition to these specific services working, we need to load all the services specified in the deployment descriptor file along with the wrapper RMI service and bind them to the RMI registry. Binding is straightforward. The only difficulty I faced was in using the DeploymentDescriptor class of the Apache SOAP API, as it doesn't accept the fact that more than one service can be specified within the same descriptor file.

    I designed a ConfigDescriptor class (ConfigDescriptor.java) that parses the deployment descriptor XML file and creates an individual DeploymentDescriptor object by taking each child node of the <soapservices> root element. I added a few convenient methods to retrieve all or one particular DeploymentDescriptor object given the service ID (the URN) of the service. The SoapServerManager class uses the ConfigDescriptor object and retrieves all the DeploymentDescriptor objects specified in the descriptor file. It then finds all the Java classes specified as the service implementation and binds them to the RMI registry with the names specified in the ID attribute of the <isd:service> element.

    This brings all the specified RMI services up and running, with all the methods specified in the deployment descriptor file exposed as SOAP-accessible remote service methods. Once this is done, the last job of the SoapServerManager class is to load the SOAP wrapper RMI service. The additional functionality of starting up an individual service or taking down one or all of the services can be provided through the SoapServerManager class.

    A Simple RMI Service
    Consider a simple RMI service. This service is named "addressserver" and the remote service method is "getAddress", which takes two parameters - the name and surname of a person. Given the name and surname, the service returns the person's address. To make it a very small example service, I've hard-coded the address inside the code, although in real life this service might interact with a database and fetch the address. I've created an Address object that holds the name, city, and zip code. The service returns the Address object in response to a SOAP call (AddressService.java) (see Listing 5).

    This listing explains the structure of the addressserver service, which is also designed as an RMI service.

    Running the Server
    To run the server, we need to ensure the following:

  • Place all the .jar files (mail.jar, xerces.jar, activation.jar, soap.jar, and jdom.jar) under the lib directory of your install directory.
  • Set the CLASSPATH to include the current working directory and the lib directory under it.
  • Generate the stubs and skeletons of all the RMI services to be deployed (included in the source code). For the SoapRMIServer, it's important to provide all the .jar files in the rmic -classpath option.

    rmic -classpath .;
    c:\your-directory\lib\jdom.jar;
    c:\your-directory\lib\soap.jar;
    c:\your-directory\lib\xerces.jar;
    c:\your-directory\lib\mail.jar;
    c:\your-directory\lib\activation.jar
    rmi.server.SoapRMIServer

  • Run the Service Manager (SoapServerManager). This will read the deployment descriptor file, load all the specified RMI services there, and bind them to the RMI registry under the name specified as the service ID.
  • Provide the deployment descriptor in the CLASSPATH.
  • Check your security policy for problems starting up or accessing the RMI services. For this example, I created and supplied a policy file (myPolicy.txt) that grants all permissions. If you create/use this policy file, pass it to the runtime with the following command:

    java - Djava.security.policy=myPolicy.txt
    rmi.server.SoapServerManager

    Running the Client
    The client program, as explained, is ready to call the deployed addressservice. We can run the client program using the following command from a DOS prompt - java rmi.client.RMIClient.

    The address of the rmi server: rmi://localhost:1099/soapserver
    Target obj: urn:addressserver
    The return value:
    person...Samudra Gupta
    city...London
    post...SS1 2RQ

    This shows the result of the client call and we receive the address of the specified person.

    To find out how the framework behaves in case of erroneous inputs, I changed the method name to "getLocation" instead of the correct exposed method "getAddress, and received the following error message:

    The address of the rmi server: rmi://localhost:1099/soapserver
    Target obj: urn:addressserver
    The fault code: SOAP-ENV:Server.BadTargetObjectURI
    The return value:
    faultcode...SOAP-ENV:Server.BadTargetObjectURI
    faultstring...Could not invoke the specified method

    It's important to note that the client will also need all the .jar files to be set in the CLASSPATH as they're used by the Apache SOAP implementation as well as our own extended RMICallObject class.

    Conclusion
    This framework served my purpose very well, and I could use the existing RMI services both as normal RMI as well as SOAP deployed over RMI. This framework is tailored to meet my requirements, so it may not meet yours. The ability to handle a user-defined data type as a parameter to the remote services is surely one of the main areas to be incorporated; although it's used in the real project, I didn't explain it here due to space constraints. I strongly believe that more experimentation with SOAP will eventually help it become a mature and robust technology.

    Resources

  • Apache SOAP API for an API download, mailing list, and FAQ: http://xml.apache.org/soap/index.html
  • An introduction to RMI: http://java.sun.com/docs/books/tutorial/rmi/index.html

    Author Bio
    Samudra Gupta is an independent Java consultant in the UK, providing solutions for e-commerce projects. He has five years of experience in Java and Web technology. [email protected]

    	
    
    
    Listing 1
    
    POST /StockQuote HTTP/1.1
    Host: www.stockquoteserver.com
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    SOAPAction: "Some-URI"
    
    <SOAP-ENV:Envelope
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
      SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
       <SOAP-ENV:Body>
           <m:GetLastTradePrice xmlns:m="Some-URI">
               <symbol>DIS</symbol>
           </m:GetLastTradePrice>
       </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    
    Listing 2
    
    HTTP/1.1 200 OK
    Content-Type: text/xml; charset="utf-8"
    Content-Length: nnnn
    <SOAP-ENV:Envelope
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
      SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
       <SOAP-ENV:Body>
           <m:GetLastTradePriceResponse xmlns:m="Some-URI">
               <Price>34.5</Price>
           </m:GetLastTradePriceResponse>
       </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    
    Listing 3
    
    //construct a Properties object and load with it the properties file specified by the URL
                Properties prop = new Properties();
                prop.load(url.openConnection().getInputStream());
                //construct a Document objet from the Call object entries
                doc = buildDocument();
                //obtain the name and address of the rmi server frm the properties file
                serverURI = prop.getProperty("serverLocation");
                serverName = prop.getProperty("serverName");
                System.out.println("The address of the rmi server:"+"
                rmi://"+serverURI+"/"+serverName);
                //get the rmi server URI
                System.out.println("Target obj:	"+this.getTargetObjectURI());
                //obtain the reference to the rmi server object by the target object URI
                SoapRMIInterface obj =(SoapRMIInterface)java.rmi.Naming.lookup
    			("rmi://"+serverURI+"/urn:+serverName);
    
    
    Listing 4
    
    //create a RMICallObject
                RMICallObject call = new RMICallObject();
                //set the target remote service name
                call.setTargetObjectURI("urn:addressserver");
                //set the name of the remote method to be invoked
                call.setMethodName("getAddress");
                //set the encoding style to be used
                call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
                //creating the parameters to be passed to the remote method
                Vector params = new Vector();
                params.addElement(new Parameter("name1", String.class,"Samudra", null));
                params.addElement(new Parameter("name2", String.class,"Gupta", null));
                call.setParams(params);
                //obtaining the current working directory
                String workingDir = System.getProperty("user.dir");
                String url ="file://localhost/"+workingDir+"/conf.txt";
                System.out.println("Invoking..."+url);
                
                //invoking the remote service
                Response res = call.invoke(new URL(url), "");
    
    
    Listing 5
    
    public Address getAddress(String personName, String surName)
        {
            Address add = new Address();
            StringBuffer buffer = new StringBuffer(personName);
            buffer.append(" ");
            buffer.append(surName);
            add.setPerson(buffer.toString());
            add.setCity("London");
            add.setPost("SS1 2RQ");
            System.out.println("Returning address for:"+add.getPerson()+" "+surName);
            return add;
        }
    
    	
    	
    

    Additional Source Code Listing 1: RMICallObject.java public class RMICallObject extends Call { /** Creates new RMICallObject */ public RMICallObject() { super(); } public Response invoke(URL url, String s) throws SOAPException { try { //construct a Properties object and load with it the properties file specified by the URL Properties prop = new Properties(); prop.load(url.openConnection().getInputStream()); System.out.println ("LOADED THE PROPERTIES AND THE SERVER NAME: "+prop.getProperty("serverName")); //construct a Document objet from the Call object entries doc = buildDocument(); //obtain the name and address of the rmi server frm the properties file serverURI = prop.getProperty("serverLocation"); serverName = prop.getProperty("serverName"); System.out.println ("The address of the rmi server: "+"rmi://"+serverURI+"/"+serverName); //get the rmi server URI System.out.println("Target obj: "+this.getTargetObjectURI()); //obtain the reference to the rmi server object by the target object URI SoapRMIInterface obj = (SoapRMIInterface)java.rmi.Naming.lookup ("rmi://"+serverURI+"/urn:"+serverName); //SoapRMIInterface obj = (SoapRMIInterface)java.rmi.Naming.lookup(serverName); //calling the invoke method responseDoc = obj.invokeMethod(doc); //construct the response object res = buildResponse(responseDoc); if(res.getFault()!=null) System.out.println ("The fault code: "+res.getFault().getFaultCode()); }catch(java.rmi.RemoteException e) { throw new SOAPException(Constants.FAULT_CODE_SERVER, "Could not invoke the server. Please try later."); }catch(Exception gene) { throw new SOAPException(Constants.FAULT_CODE_CLIENT, gene.toString()); } return res; } } //end of class Listing 2: SoapRMIServer.java /** * This is the class managing all the soap services deployed as RMI. This class reads all the deployed services from the deployment *descriptor and make the exposed methods available for any SOAP client to use them. This class is also responsible for parsing the SOAP *request and sending back a SOAP response back to the client along with any fault-code generated. * @author default * @version 1.0 */ public class SoapRMIServer extends UnicastRemoteObject implements SoapRMIInterface { /** This method receives the SOAP Document and parse it, invokes the requried * remote method, constructs a SOAP response Document and sends it back to * the caller * @param doc the SOAP request Document * @throws RemoteException the RemoteException * @throws RMISoapException The Custom RMISOAPException * @return the SOAP response Document */ public Document invokeMethod(Document doc) throws RemoteException, RMISoapException { try { soapDoc = doc; //finding the body element envElement = doc.getRootElement(); bodyElement = (Element)envElement.getChildren().get(0); System.out.println("Body element: "+bodyElement); //finding the method element and method name and service id out of the method child of the body element methodElement = (Element)(bodyElement.getChildren().get(0)); methodName = methodElement.getName(); serviceID = methodElement.getNamespaceURI(); System.out.println("The method name is: "+methodName); System.out.println("The service id..."+ serviceID); //obtaining the paramteres passed String[] params = new String[methodElement.getChildren().size()]; String[] paramValues = new String[methodElement.getChildren().size()]; int size = methodElement.getChildren().size(); System.out.println ("The number of paramteres to the method: "+methodElement.getChildren().size()); for(int i=0; i<size; i++) { Element el = (Element)(methodElement.getChildren().get(i)); params[i] = el.getName(); paramValues[i] = el.getText(); } //obtain the deployment descriptor for the service desc = ConfigDescriptor.getDescriptor(serviceID); //now call the specified method if(checkMethod(desc, methodName)) { Class c = Class.forName(desc.getProviderClass()); Method[] methods = c.getMethods(); for(int i=0; i<methods.length; i++) { if(methods[i].getName().equals(methodName)) { System.out.println ("Invoking method.."+methodName+" and the declaring class: "+c.getName()); System.out.println ("The parameters getting passed: "+paramValues.toString()+" and the no of params: "+paramValues.length); Object obj = methods[i].invoke(c.newInstance(), paramValues); //construct the response out of the return value response = constructResponse(doc, obj); } } }else { System.out.println("Throwing soap exception..."); //throw a RMISoapException //throw new RMISoapException (Constants.FAULT_CODE_SERVER_BAD_TARGET_OBJECT_URI, "Could not invoke the specified method. Please check if it is deployed correctly."); //throw new RemoteException("Problem...", new RMISoapException("faultcode" , "faultstring")); response = constructFaultDocument (doc, Constants.FAULT_CODE_SERVER_BAD_TARGET_OBJECT_URI, "Could not invoke the specified method"); } }catch(RemoteException re) { re.printStackTrace(); throw new RemoteException("Exception "); }catch(InvocationTargetException ite) { //RMISoapException se = new RMISoapException (Constants.FAULT_CODE_SERVER_BAD_TARGET_OBJECT_URI, "Could not invoke the specified method. Please check if it is deployed correctly."); response = constructFaultDocument (doc, Constants.FAULT_CODE_SERVER_BAD_TARGET_OBJECT_URI, "Could not invoke the specified method"); //throw se; }catch(IllegalAccessException iae) { }catch(ClassNotFoundException cnfe) { //RMISoapException se = new RMISoapException (Constants.FAULT_CODE_SERVER_BAD_TARGET_OBJECT_URI, "Could not match the target object. Please check if it is deployed correctly."); response = constructFaultDocument (doc,Constants.FAULT_CODE_SERVER_BAD_TARGET_OBJECT_URI, "Could not match the target object. Please ensure it is deployed correctly."); }catch(InstantiationException inste) { //RMISoapException se = new RMISoapException (Constants.FAULT_CODE_SERVER, "Could not instantiate the specified target object.."); response = constructFaultDocument (doc, Constants.FAULT_CODE_SERVER, "Could not instantiate the specified target object."); }catch(Exception e) { e.printStackTrace(); //RMISoapException se = new RMISoapException (Constants.FAULT_CODE_SERVER, "General problem encountered in the server. Please restart the server."); response = constructFaultDocument (doc, Constants.FAULT_CODE_SERVER, General problem in the server. Please restart the server."); } return response; } } Listing 3: SoapServerManager.java package rmi.server; /** This class is the manager for reading all the SOAP exposed RMI services from the * deploymnet descriptor file and binds all the available services to the rmiregistry * against the name specified in the deployment descriptor file. * * This class also facilitates starting and stopping of individual services or all the services. */ public class SoapServerManager extends Object { /** This is the main method which loads the services and bind them to the registry. * @param args the command line arguments */ public static void main (String args[]) { String serverName = null; String action = null; //create an instance of the manager SoapServerManager soapManager = new SoapServerManager(); System.out.println("Args: "+args.length); try { if(args.length==0) { //load all the available services soapManager.loadAllServices(); }else { System.out.println("Usage: java SoapServerManager"); } }catch(RemoteException re) { System.out.println("There is an exception: "+re.toString()); } } /** This method reads the deployment descriptor file and loads all the specified * services and bind them to the registry for the RMI use * @throws RemoteException RemoteException */ public void loadAllServices() throws RemoteException { ConfigDescriptor configManager = null; //the configmanager List ddList = null; //list of DeploymentDescriptors Iterator iterator = null; //iterator for the list DeploymentDescriptor descriptor = null; //the deployment descriptor String serviceName = null; //the name of the service String providerClass = null; //the provider class String configFileName = "c:\\dev\\src\\lib\\soap.xml"; try { //intialising the config manager configManager = ConfigDescriptor.getInstance(); //obtain all the descriptors ddList = configManager.getAllDeploymentDescriptor(); iterator = ddList.iterator(); while(iterator.hasNext()) { descriptor = (DeploymentDescriptor)iterator.next(); serviceName = descriptor.getID(); providerClass = descriptor.getProviderClass(); startServer(serviceName,providerClass); } }catch(Exception e) { e.printStackTrace(); } } /** This method binds the specified RMI service to the registry * @param serverName the name of the service as to be bound with the RMI reg. * @param providerClass the implmentation class * @throws RemoteException RemoteException */ public void startServer(String serverName, String providerClass) throws RemoteException { System.out.println("About to start the service with the name: "+serverName); try { //installing a new security manager System.setSecurityManager(new RMISecurityManager()); //creating an instance of the service specified Naming.rebind(serverName,(Remote)Class.forName(providerClass).newInstance()); }catch(Exception e) { e.printStackTrace(); throw new RemoteException("There is a problem in starting up the server"); } System.out.println("Successfully started the server : "+serverName); } }

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.
      E-mail: [email protected]

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.