Version 2

    Client access of an RPC style service endpoint

     

    This tutorial is about the WS4EE client programming model i.e. the component deployed on JBoss acts as a client to a remote web service. There are three types of possible WS clients in J2EE: application client, EJB, servlet/jsp.

     

     

     

     

    Application clients are rarely used by folks developing J2EE applications. One possible reason might be, that they don't

    realize what kind of services an application server can provide for a standalone java application. Among others, there

    is certainly the JNDI service that java applications could use to look up datasources for example or for the sake of

    this tutorial deploy a J2EE compatible web service client. Remember, JBoss can easily be scaled down to only provide

    minimal services for your application, so using JBoss as a platform for your client applications is not at all unreasonable.

     

     

     

     

    In this tutorial we use a J2EE client application to connect to the service we developed in RPC 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-samples-server-jse/Organization?wsdl
    

     

    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:rpcliteral -mapping jaxrpc-mapping.xml -keep config.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-samples-server-jse/Organization?wsdl"
        packageName="org.jboss.test.webservice.samples">
      </wsdl>
    
    </configuration>
    

     

    The generated service endpoint interface (SEI)

     

    First let's have a look at the generated SEI

     

    public interface Organization extends java.rmi.Remote {
    
        public java.lang.String getContactInfo(java.lang.String string_1) throws java.rmi.RemoteException;
    
        public org.jboss.test.webservice.samples.Person jobRequest(org.jboss.test.webservice.samples.Person person_1) throws java.rmi.RemoteException;
    }
    

     

    Knowing the server side from the previous tutorial, we can confirm that this is what we expect.

     

    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 OrganizationService extends javax.xml.rpc.Service {
    
        public org.jboss.test.webservice.samples.Organization getOrganizationPort() 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 SEI uses a Person object that was described in XML schema in the WSDL. wscompile generated the following Java

    representation for it

     

    public class Person {
        protected int age;
        protected java.lang.String name;
        
        public Person() {
        }
        
        public Person(int age, java.lang.String name) {
            this.age = age;
            this.name = name;
        }
        
        public int getAge() {
            return age;
        }
        
        public void setAge(int age) {
            this.age = age;
        }
        
        public java.lang.String getName() {
            return name;
        }
        
        public void setName(java.lang.String name) {
            this.name = name;
        }
    }
    

     

    Note, unfortunately it does not generate equals/hashCode for it.

     

    Java to XML mapping

     

    There is nothing Java specific in the WSDL. How XML namespaces map to Java packages and other mapping concerns are described in jaxrpc-mapping.xml. Funnily enough, the jaxrpc-mapping.xml generated from the wsdl is identical to the

    one we generated from the SEI in RPC style service endpoint.

     

    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 implementation. 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 an 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/OrganizationServiceJSE</service-ref-name>
        <service-interface>org.jboss.test.webservice.samples.OrganizationService</service-interface>
        <wsdl-file>META-INF/OrganizationService.wsdl</wsdl-file>
        <jaxrpc-mapping-file>META-INF/jaxrpc-mapping.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.

     

    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.

     

       public void testApplClientAccessJSE() throws Exception
       {
          InitialContext iniCtx = getInitialContext();
          OrganizationService service = (OrganizationService)iniCtx.lookup("java:comp/env/service/OrganizationServiceJSE");
          Organization endpoint = service.getOrganizationPort();
          String info = endpoint.getContactInfo("mafia");
          assertEquals("The 'mafia' boss is currently out of office, please call again.", info);
          
          Person bill = new Person("Bill", 56);
          Person ret = endpoint.jobRequest(bill);
          assertEquals(bill, ret);
       }
    

     

    The application client's environment

     

    In order to provide an environment context for the application client, we must give it a name. This is done in jboss-client.xml. You do not have to do this for EJB or WEB clients.

     

    <!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>
    </jboss-client>
    

     

    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);
       }