Version 2

    Client access of a Document style service endpoint

     

    In this tutorial we demonstrate how to create a WS4EE compliant web service client accessing a remote document style

    service endpoint.

     

     

     

     

    Again, we use a J2EE client application to connect to the service we developed in Document style service endpoint. In real life you are more likely to connect to some web service not deployed

    on JBoss, probably not even running on Java.

     

    Obtaining the remote WSDL

     

    The WSDL is often the starting point for a web service client. You normally obtain it from some URL like this

     

       http://TDDELL:8080/ws4ee-samples2-doc/OrderProcess?wsdl
    

     

    The WSDL returned from the URL is the one we generated with wscompile and in the previous tutorial. There is one important difference however, the service element contains the proper location of the service

     

       <service name="OrderProcessService">
        <port binding="tns:OrderProcessBinding" name="OrderProcessPort">
         <soap:address location="http://TDDELL:8080/ws4ee-samples2-doc/OrderProcess"></soap:address>
        </port>
       </service>
    

     

    Generating the required deployment artifacts

     

    From the WSDL we can generate the service endpoint interface (SEI), the service interface (SI), the Java/XML mapping descriptor (jaxrpc-mapping.xml) and possibly the complex types that are used in the SEI. Again we use wscompile to do that.

     

       wscompile -gen:client -f:documentliteral -f:wsi -mapping jaxrpc-mapping-client.xml -keep config-client.xml
    

     

    wscompile needs a configuration file for its operation, we use

     

    <configuration
      xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
    
      <wsdl location="http://localhost:8080/ws4ee-samples2-doc/OrderProcess?wsdl"
        packageName="org.jboss.test.webservice.samples2docclient">
      </wsdl>
    
    </configuration>
    

     

    Having read about Document style service endpoint you know that we prefer to wrap all

    request/response parameters in a single java object that represents the entire SOAP payload (i.e. the SOAP body element). The -f:wsi feature instructs wscompile to do that.

     

     

     

     

    The options to wscompile are described as part

    of the jaxrpc documentation that comes with the JWSDP.

     

     

    The generated service endpoint interface (SEI)

     

    First let's have a look at the generated SEI

     

    public interface OrderProcess extends java.rmi.Remote {
    
        public org.jboss.test.webservice.samples2docclient.ProcessOrderResponse processOrder(org.jboss.test.webservice.samples2docclient.ProcessOrder parameters) throws 
            org.jboss.test.webservice.samples2docclient.OrderException,  java.rmi.RemoteException;
    }
    

     

    We notice that wscompile uses a different naming schema for the request/respose structure. What was called

    OrderProcess_proccessOrder_RequestStruct is now called ProcessOrder and what was called

    OrderProcess_proccessOrder_ResponseStruct is now called ProcessOrderResponse .

     

     

     

     

    It does not really matter, the generated objects are semantically equivalent.

     

    public class ProcessOrder {
        protected org.jboss.test.webservice.samples2docclient.OrderItem[] arrayOfOrderItem_1;
        protected org.jboss.test.webservice.samples2docclient.Person person_2;
        
        public ProcessOrder() {
        }
        
        public ProcessOrder(org.jboss.test.webservice.samples2docclient.OrderItem[] arrayOfOrderItem_1, org.jboss.test.webservice.samples2docclient.Person person_2) {
            this.arrayOfOrderItem_1 = arrayOfOrderItem_1;
            this.person_2 = person_2;
        }
        
        public org.jboss.test.webservice.samples2docclient.OrderItem[] getArrayOfOrderItem_1() {
            return arrayOfOrderItem_1;
        }
        
        public void setArrayOfOrderItem_1(org.jboss.test.webservice.samples2docclient.OrderItem[] arrayOfOrderItem_1) {
            this.arrayOfOrderItem_1 = arrayOfOrderItem_1;
        }
        
        public org.jboss.test.webservice.samples2docclient.Person getPerson_2() {
            return person_2;
        }
        
        public void setPerson_2(org.jboss.test.webservice.samples2docclient.Person person_2) {
            this.person_2 = person_2;
        }
    }
    
    
    public class ProcessOrderResponse {
        protected org.jboss.test.webservice.samples2docclient.OrderResponse result;
        
        public ProcessOrderResponse() {
        }
        
        public ProcessOrderResponse(org.jboss.test.webservice.samples2docclient.OrderResponse result) {
            this.result = result;
        }
        
        public org.jboss.test.webservice.samples2docclient.OrderResponse getResult() {
            return result;
        }
        
        public void setResult(org.jboss.test.webservice.samples2docclient.OrderResponse result) {
            this.result = result;
        }
    }
    

     

    The generated service interface (SI)

     

    This interface is later bound into JNDI and the client uses it to obtain a dynamic proxy to the (SEI). It looks like this

     

    public interface OrderProcessService extends javax.xml.rpc.Service {
    
        public org.jboss.test.webservice.samples2docclient.OrderProcess getOrderProcessPort() throws ServiceException;
    }
    

     

    The SI is optional because the client might choose to lookup an instance of the less typed javax.xml.rpc.Service

    from JNDI. We see how this is done in a bit.

     

    The generated complex types

     

    The generated complex types are

     

    • OrderException

    • OrderItem

    • Person

     

    all of which are defined by the schema that was contained in the WSDL. For readability reasons I'll only show the generated exception that maps to the operation fault message part.

     

    public class OrderException extends Exception {
        private java.lang.String message;
        
        
        public OrderException(java.lang.String message) {
            super(message);
            this.message = message;
        }
        
        public java.lang.String getMessage() {
            return message;
        }
    }
    

     

    Java to XML mapping

     

    Let's have a look how the mapping of the request/response structures are defined in jaxrpc-mapping.xml. For readability resons, I won't show the entire mapping file.

     

      <java-xml-type-mapping>
        <java-type>org.jboss.test.webservice.samples2docclient.ProcessOrder</java-type>
        <root-type-qname xmlns:typeNS="http://org.jboss.test.webservice/samples2/types">typeNS:processOrder</root-type-qname>
        <qname-scope>complexType</qname-scope>
        <variable-mapping>
          <java-variable-name>arrayOfOrderItem_1</java-variable-name>
          <xml-element-name>arrayOfOrderItem_1</xml-element-name>
        </variable-mapping>
        <variable-mapping>
          <java-variable-name>person_2</java-variable-name>
          <xml-element-name>Person_2</xml-element-name>
        </variable-mapping>
      </java-xml-type-mapping>
    
      <java-xml-type-mapping>
        <java-type>org.jboss.test.webservice.samples2docclient.ProcessOrderResponse</java-type>
        <root-type-qname xmlns:typeNS="http://org.jboss.test.webservice/samples2/types">typeNS:processOrderResponse</root-type-qname>
        <qname-scope>complexType</qname-scope>
        <variable-mapping>
          <java-variable-name>result</java-variable-name>
          <xml-element-name>result</xml-element-name>
        </variable-mapping>
      </java-xml-type-mapping>
    

     

    What about all the other stuff

     

    wscompile generates a lot of other java files that are not relevant to WS4EE. If you opt to use JWSDP as your

    WS client, then you would need these proprietary implementations. But remember, WS4EE is about portable J2EE

    compliant web services. We should not need implementation specific artifacts as this would render our WS client unportable to other J2EE compliant app servers.

     

    The client deployment descriptor

     

    An application client uses a descriptor called application-client.xml. What is described here, equally applies

    to ejb-jar.xml if an EJB is the WS client or web.xml if a servlet/jsp is the WS client.

     

    <application-client xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/application-client_1_4.xsd"
      version="1.4">
    
      <display-name>webservice client app</display-name>
    
      <service-ref>
        <service-ref-name>service/OrderProcess</service-ref-name>
        <service-interface>org.jboss.test.webservice.samples2docclient.OrderProcessService</service-interface>
        <wsdl-file>USE_JBOSS_CLIENT_XML_OVERRIDE</wsdl-file>
        <jaxrpc-mapping-file>META-INF/jaxrpc-mapping-client.xml</jaxrpc-mapping-file>
      </service-ref>
    
    </application-client>
    

     

    The <service-ref-name> is the JNDI name that the client uses to lookup the SI from it's environment context.

     

    jboss-client.xml deployment descriptor

     

    Here we use a feature where it is possible to specify the URL to the remote WSDL in jboss-client.xml,

    rather than packaging the WSDL with the client deployment

     

    <!DOCTYPE jboss-client PUBLIC
       "-//JBoss//DTD Application Client 4.0//EN"
       "http://www.jboss.org/j2ee/dtd/jboss-client_4_0.dtd">
    
    <jboss-client>
      <jndi-name>ws4ee-client</jndi-name>
      <service-ref>
        <service-ref-name>service/OrderProcess</service-ref-name>
        <wsdl-override>http://localhost:8080/ws4ee-samples2-doc/OrderProcess?wsdl</wsdl-override>
      </service-ref>
    </jboss-client>
    

     

    Accessing the remote web service

     

    First the client gets an implementation of the SI from JNDI, from that it gets an implementation of the SEI.

    Then it uses the proxy that implements the SEI like a local java object.

     

       /** Test a valid access */
       public void testValidAccess() throws Exception
       {
          InitialContext iniCtx = getInitialContext();
          OrderProcessService service = (OrderProcessService)iniCtx.lookup("java:comp/env/service/OrderProcess");
          OrderProcess endpoint = service.getOrderProcessPort();
    
          Person p = new Person(3, "Tom");
          OrderItem i0 = new OrderItem("Ferrari", 1);
          OrderItem i1 = new OrderItem("Twix", 10);
          OrderItem i2 = new OrderItem("IceCream", 3);
    
          OrderResponse res = endpoint.processOrder(new OrderItem[]{i0, i1, i2}, p);
          assertEquals(3, res.getItems().length);
    
          assertEquals(i0.getName(), res.getItems()[0].getName());
          assertEquals(i0.getQuantity(), res.getItems()[0].getQuantity());
    
          assertEquals(i1.getName(), res.getItems()[1].getName());
          assertEquals(i1.getQuantity(), res.getItems()[1].getQuantity());
    
          assertEquals(i2.getName(), res.getItems()[2].getName());
          assertEquals(i2.getQuantity(), res.getItems()[2].getQuantity());
    
          assertEquals("aproved", res.getMessage());
       }
    

     

    Specifing the JNDI properties

     

    Again specific to J2EE application clients, you need to set the j2ee.clientName property, like this

     

       /**
        * Get the client's env context, see source forge tracker [840598] for details
        */
       protected InitialContext getInitialContext() throws NamingException
       {
          Properties env = new Properties();
          env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
          env.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming.client");
          env.setProperty(Context.PROVIDER_URL, "jnp://localhost:1099");
          env.setProperty("j2ee.clientName", "ws4ee-client");
          return new InitialContext(env);
       }