Creating J2SE and J2EE Web Services Clients


Now let's create a Java client for the PlantsByWebSphereCatalog web service, following the JSR-109 and JAX-RPC specifications.

Client Programming Model

The programming model for web services consists of APIs for the client-side programmer specified by JAX-RPC and contained in the Java package javax.xml.rpc.*. JAX-RPC provides an abstraction of the WSDL service definition so that the client programmer can interact with a service using late/dynamic binding or early/static binding.

The following abstractions are specified by JAX-RPC:

  • ServiceFactory
    An abstract class used to obtain a reference to an implementation of the service when the client is running outside a J2EE container and can't use JNDI to look up the service.

  • Service Interface
    A Java interface that represents the WSDL service description. There is a generic service interface, javax.xml.rpc.Service, as well as a generated service-specific interface that has methods to directly access the ports in the service. The generated service interface extends javax.xml.rpc.Service indicating that it is a JAX-RPC Service.

  • Service Endpoint Interface
    The Java representation of a specific WSDL portType. This interface extends java.rmi.Remote indicating that it can be called remotely over a network.

To use a web service, first use either the ServiceFactory or a JNDI lookup to get a reference to the Service interface. Then use the service to select the port and obtain a reference to the stub that implements the Service Endpoint Interface. Finally, invoke the operation on the service using the stub.

In the following sections, we examine each of these in more detail.

Locating a Service using JNDI Lookup

In the J2EE environment, JNDI is used to look up the service reference. J2EE components that intend to reference a web service are deployed with a JSR-109 deployment descriptor named webservicesclient.xml. This deployment descriptor follows the java:comp/env pattern established by J2EE. It contains a mapping of a logical name to the service, so that the calling component does not have to know the actual location of the service in the JNDI namespace. Here's a sample of the client JNDI lookup for the PlantsByWebSphereCatalog Service:

 Context ctx = new InitialContext(); PlantsByWebSphereCatalogService cs = (PlantsByWebSphereCatalogService)   ctx.lookup("java:comp/env/service/PlantsByWebSphereCatalogService"); 

Locating a Service using ServiceFactory

In the J2SE environment, the client program can't use JNDI to look up the service. In order to accommodate standalone Java clients, JAX-RPC has introduced an abstract class called ServiceFactory that provides access to JAX-RPC Service implementations, allowing vendor-neutral client code to be written:

package javax.xml.rpc; public abstract class ServiceFactory {   public static ServiceFactory newInstance() throws ServiceException {...}   public abstract Service createService(URL wsdlDocumentLocation,         QName serviceName) throws ServiceException; ... }

The newInstance() method creates an instance of the ServiceFactory. The ServiceFactory implementation class is provided by the JAX-RPC runtime vendor, and extends ServiceFactory. Once the client has an instance of ServiceFactory, they can create a JAX-RPC Service instance using the createService() method. Once the client has a reference to the service, the programming model is identical to the J2EE example above. Here's an example using the ServiceFactory to create an implementation of the PlantsByWebSphereCatalogService:

 Service cs = ServiceFactory.newInstance().createService(   new URL("file", "", "PlantsByWebSphereCatalog.wsdl"),   new QName("http://ejb.pbw.ibm.com", "PlantsByWebSphereCatalogService"));  

You call createService() with a URL for the WSDL file. In the example code above, the URL file://PlantsByWebSphereCatalog.wsdl identifies the WSDL file. Therefore, when the example client is run, the WSDL file must be in the current directory.

You will normally want to specify a more complete URL for the WSDL file. For example, the ?wsdl URL could have been used to dynamically access the WSDL from the server as follows:

Service cs = ServiceFactory.newInstance().createService(   new URL ("http://myhost:9080/PBW/services/PlantsByWebSphereCatalog?wsdl"),   new QName("http://ejb.pbw.ibm.com", "PlantsByWebSphereCatalogService"));

Using the Generic and Generated Service Interfaces

The result of the JNDI lookup or calling ServiceFactory.createService() is a reference to an object that implements the javax.xml.rpc.Service interface, which in turn is an abstraction of the <wsdl:service> element in the WSDL document. The service in turn serves as a factory for creating stubs connected to specific ports. The Service interface also contains a method that returns the location of the WSDL document used to create the Service. The JAX-RPC specification defines the Service interface as follows:

package javax.xml.rpc; public interface Service {   java.rmi.Remote getPort(QName portName, Class serviceEndpointInterface)     throws ServiceException;   java.rmi.Remote getPort(Class serviceEndpointInterface)     throws ServiceException;   ...   java.net.URL getWSDLDocumentLocation();   QName getServiceName();   java.util.Iterator getPorts() throws ServiceException; ... }

Both the getPort() methods return a stub that accesses a WSDL port. The stub implements the Service Endpoint Interface and delegates method calls to the remote service. The first getPort() variant, passing the portName in addition to the Service Endpoint Interface class, associates a specific WSDL port with the service. The second form, passing only the Service Endpoint Interface class, permits the JAX-RPC runtime to select an appropriate port (that is, one whose portType matches the Service Endpoint Interface) and configure it for the client's use.

The JAX-RPC specification also requires that tooling generate a service-specific Java interface for the web service. This generated service interface provides additional port-specific methods that explicitly expose each port in the WSDL document. For example, running the WSDL2Java command on the PlantsByWebSphereCatalog WSDL produces the following service-specific interface:

 package com.ibm.pbw; public interface PlantsByWebSphereCatalogService extends      javax.xml.rpc.Service {   public PlantsByWebSphereCatalog getPlantsByWebSphereCatalog() throws     javax.xml.rpc.ServiceException;   public PlantsByWebSphereCatalog getPlantsByWebSphereCatalog(     java.net.URL portAddress) throws javax.xml.rpc.ServiceException; } 

These methods return a stub for a specific port without passing any parameters, and without casting the result to a specific Service Endpoint Interface type. The first method uses the soap:location URL in the WSDL to access the service. The second form lets the client override the soap:location URL. If you know at development time that your client program must use a specific WSDL port, then using one of these methods to get a reference to the web service implementation may be faster or more convenient at run time than using the more dynamic binding available in the generic Service interface methods.

Important

If you're using the Technology Preview, there are some restrictions. JNDI lookup of a service always returns an implementation of the generated service interface that can be cast to the generic service interface if desired. The ServiceFactory mechanism for J2SE always returns an implementation of the generic service interface. An implementation of the generated service interface cannot be obtained using the ServiceFactory. If you require access to a generated service interface implementation in a J2SE environment, consult the Technology Preview documentation for a workaround.

So far, all we've seen of JAX-RPC is a collection of Java interfaces. You're probably wondering who is supposed to implement all those interfaces. The JAX-RPC standard requires vendors to provide tooling that generates implementations for the generic Service interface, the service-specific interface and the stub for the Service Endpoint Interface. These generated implementation classes are vendor specific and not portable to other JAX-RPC runtime implementations.

Client Programming Model Summary

To summarize, there are three steps the client performs to get a stub that accesses a Web service:

  1. Obtain a reference to the service implementation.

  2. Select a port from the service. This selection results in a reference to a stub.

  3. Invoke the operation on the stub.

Following is an example of obtaining a reference to the PlantsByWebSphereCatalog service and invoking the getItems() method:

 Context ctx = new InitialContext(); PlantsByWebSphereCatalogService cs = (PlantsByWebSphereCatalogService)   ctx.lookup("java:comp/env/service/PlantsByWebSphereCatalogService"); PlantsByWebSphereCatalog c = cs.getPlantsByWebSphereCatalog(); StoreItem[] items = c.getItems(); 

Client Development Process

Now that you're familiar with the JAX-RPC client programming model for web services, let's see how we put all the pieces together. To begin, you must have the WSDL file for the service the client is to access. Then follow these steps to create the client:

  1. Run the WSDL2Java tool. This will produce the following items:

    • The generated service interface

    • The Service Endpoint Interface

    • Implementations of the Service Endpoint Interface (the stub) and the generated service interface

    • The JSR-109 client deployment descriptor template webservicesclient.xml, used when running the client in a container

    • Other runtime support classes used to marshal and demarshal values between Java and XML.

  2. Write and compile the client code using the javax.xml.rpc.* interfaces, the generated service interface, and the Service Endpoint Interface

  3. Compile the classes and assemble the client JAR including all the generated classes.

  4. If you're not configuring for a J2EE container, you're done. Otherwise configure the webservicesclient.xml deployment descriptor and add it to the JAR.

The following diagram illustrates this process:

click to expand

Accessing the PlantsByWebSphere Catalog Service

Now let's work through these steps in more detail to provide access to the PlantsByWebSphereCatalog web service. All client development begins with a WSDL document.

Generate the Web Service Interfaces and Bindings

Run the WSDL2Java command to generate the client interfaces and bindings files for the client:

 WSDL2Java -verbose PlantsByWebSphereCatalog.wsdl Parsing XML file: PlantsByWebSphereCatalog.wsdl Generating com\ibm\pbw\ejb\StoreItem.java Generating com\ibm\pbw\ejb\StoreItem_Helper.java Generating com\ibm\pbw\ejb\StoreItem_Ser.java Generating com\ibm\pbw\ejb\StoreItem_Deser.java Generating com\ibm\pbw\ejb\PlantsByWebSphereCatalog.java Generating com\ibm\pbw\ejb\PlantsByWebSphereCatalogSoapBindingStub.java Generating com\ibm\pbw\ejb\PlantsByWebSphereCatalogService.java Generating com\ibm\pbw\ejb\PlantsByWebSphereCatalogServiceLocator.java Generating META-INF\webservicesclient.xml Generating META-INF\PlantsByWebSphereCatalog_mapping.xml Generating META-INF\ibm-webservicesclient-bnd.xml

The -verbose option displays the files generated by WSDL2Java. The purpose of each is shown in the following table:

File

Purpose

PlantsByWebSphereCatalog.java

The Service Endpoint Interface, used by the client to invoke operations on the service.

PlantsByWebSphereCatalogService.java

The generated service interface, used by the client to access ports of the service.

StoreItem.java

A generated JavaBean to provide access to the fields of the StoreItem XML complex type.

webservicesclient.xml

The template for the JSR-109 J2EE application client deployment descriptor.

PlantsByWebSphereCatalog_mapping.xml

The JSR-109 mapping deployment descriptor to be packaged with a J2EE application client.

ibm-webservicesclient-bnd.xml

The template for the WebSphere specific binding information for the J2EE application client. Initially used for security configuration.

PlantsByWebSphereCatalogServiceLocator.java

The generated implementation of the Service interface. Client code should not use this class directly.

PlantsByWebSphereCatalogSoap
BindingStub.java

The generated implementation of the Service Endpoint Interface. Client code should not use this class directly.

StoreItem_Helper.java

StoreItem_Ser.java

StoreItem_Deser.java

Runtime helper classes for converting StoreItem bean to and from XML. Client code should not use these classes directly.

Write the Web Services J2SE Client

Let's start simply with the J2SE client that uses the ServiceFactory interface to access the service. Here's the complete code for the client that calls the Catalog.getItem() operation to find out more about a wheelbarrow. All of the code to get a reference to a port and call getItem() using it is in the try block in the main() method:

 import javax.naming.InitialContext; import javax.xml.rpc.ServiceFactory; import javax.xml.namespace.QName; import java.net.URL; import javax.xml.rpc.Service; import com.ibm.pbw.ejb.PlantsByWebSphereCatalog; import com.ibm.pbw.ejb.PlantsByWebSphereCatalogService; import com.ibm.pbw.ejb.StoreItem; //Web services J2SE client for PlantsByWebSphereCatalog Web service. public class PlantsClient {   static String wheelbarrowID = "A0011";       private static void printItem (StoreItem item) {     System.out.println(item.getName() + "\t $" + item.getPrice());     System.out.println(item.getDescr ());     System.out.println("\tQuantity:\t" + item.getQuantity());   }        public static void main (String[] args) throws Exception {     try {       Service catService = ServiceFactory.newInstance().createService(         new URL("file", "", "PlantsByWebSphereCatalog.wsdl"),         new QName("http://ejb.pbw.ibm.com",          "PlantsByWebSphereCatalogService"));       PlantsByWebSphereCatalog cat = (PlantsByWebSphereCatalog)          catService.getPort(new QName("http://ejb.pbw.ibm.com",          "PlantsByWebSphereCatalog"), PlantsByWebSphereCatalog.class);       StoreItem item = cat.getItem(wheelbarrowID);       printItem(item);     } catch (Exception e) {       e.printStackTrace();     }   } } 

The example client has been simplified by telling it the item ID of the wheelbarrow in advance. It's easy to extend the example to call getItems() and browse the entire catalog contents.

Compile and Test the J2SE Client

For the J2SE client, no assembly is required; you can just configure the classpath and compile the client code. The classpath needs to contain the following JAR files:

JAR file

Contents

WAS_HOME\lib\j2ee.jar

J2EE classes, including XML processing APIs

WAS_HOME\lib\jaxrpc.jar

JAX-RPC APIs (used by web services clients and client bindings)

WAS_HOME\lib\xerces.jar

XML manipulation classes, needed by client runtime.

WAS_HOME\lib\axis.jar

Client runtime (used by web services client bindings)

WAS_HOME\lib\ws-commons-logging.jar

Client runtime logging support

WAS_HOME\lib\commons-discovery.jar

Client runtime configuration support

WAS_HOME\lib\qname.jar

Contains javax.xml.namespace.QName

WAS_HOME\lib\wsdl4j.jar

Client runtime WSDL utilities

WAS_HOME\lib\webservices.jar

Web services client runtime

WAS_HOME\lib\saaj.jar

Client runtime attachments support

WAS_HOME refers to the WebSphere Application Server 5.0 installation directory. You'll also need to add the path to the files generated by WSDL2Java to the classpath (remember to compile them). You can then compile the J2SE client.

Test the Web Services Client

After checking that the server is running and that the WSDL file is in the current directory, you can execute the stand alone Java client:

 java PlantsClient Wheelbarrow      $29.0 Shiny red wheelbarrow with epoxy coated steel bin and wooden handles. Tire is solid with thick treads that grip rough, wet surfaces. Large capacity - 3 Cu.Ft. capacity, 150 Lb. maximum load         Quantity:       100

Monitor the Web Services Messages

Both WebSphere Studio and WebSphere Application Server include tools to monitor SOAP messages being exchanged between client and server. The monitor listens on one TCP/IP port, displays the messages in a window, and forwards to another TCP/IP port.

In order to use the monitor, the client needs to be reconfigured to access the Web service through a port other than 9080. Let's pick port 9088 instead. The easiest way to do this is to edit the client's WSDL document and change the wsdlsoap:address location attribute URL to use port 9088 instead of 9080. Then run WSDL2Java again and recompile the generated bindings.

To run the monitor included with WebSphere Application Server, configure the classpath as shown in the table above and run:

 java org.apache.axis.utils.tcpmon 

The TCPMon window opens:

click to expand

Configure the monitor to listen on port 9088 and forward messages to localhost port 9080 (the port the application server is listening on). Press the Add button to add the Port 9088 tab to the monitor. Select this tab to see the messages. Run the client application again to see the SOAP request and response in the monitor window:

click to expand

The top pane contains the list of all messages sent and their responses. It shows that one message has been monitored at 06:21:06PM. When there are multiple messages displayed, you can select the one you want to view. The middle pane shows the SOAP request sent by the client. You can see the getItem element containing the inventoryID part value. The bottom pane shows the response received. You can see the getItemResponse message element containing (via a multi-ref), the elements of the StoreItem complex type.

While debugging and testing your service, you can modify the text in the request pane and click the Resend button. The will transmit the modified request to the server and show the new response in the bottom pane.

Writing and Assembling the J2EE Client

Now let's adapt the client code to run in a J2EE container. This code will work in an application client, or in the web or EJB containers to access web services on other servers. The following example constructs an EAR for the J2EE application client container. The code changes required are to use the JNDI lookup code we saw earlier to get a reference to the service implementation instead of using the ServiceFactory.

Start by copying PlantsClient.java to a new file called PlantsClientJNDI.java and change the client code in the main() method from:

Service catService = ServiceFactory.newInstance().createService(    new URL("file", "", "PlantsByWebSphere.wsdl"),    new QName("http://ejb.pbw.ibm.com", "PlantsByWebSphereCatalogService")); PlantsByWebSphereCatalog cat = (PlantsByWebSphereCatalog)     catService.getPort(new QName("http://ejb.pbw.ibm.com",    "PlantsByWebSphereCatalog"), PlantsByWebSphereCatalog.class);    

to:

 InitialContext ctx = new InitialContext(); Service catService = (Service)   ctx.lookup("java:comp/env/service/PlantsByWebSphereCatalogService"); PlantsByWebSphereCatalog cat = (PlantsByWebSphereCatalog)   catService.getPort(PlantsByWebSphereCatalog.class); 

Notice that the J2EE client doesn't refer to any WSDL elements, just the JNDI name for the service and the class for the Service Endpoint Interface.

Compile PlantsClientJNDI.java

Configuring the J2EE Client Deployment Descriptor

When you ran WSDL2Java, it generated the META-INF/webservicesclient.xml file. This deployment descriptor is specified by JSR-109:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE webservicesclient PUBLIC "-//IBM Corporation, Inc.//   DTD J2EE Web services client 1.0//EN" "http://www.ibm.com/webservices/dtd/j2ee_web_services_client_1_0.dtd">  <webservicesclient>    <service-ref>       <description>          WSDL Service PlantsByWebSphereCatalogService       </description>       <service-ref-name>          service/PlantsByWebSphereCatalogService       </service-ref-name>          <service-interface>          com.ibm.pbw.ejb.PlantsByWebSphereCatalogService       </service-interface>       <port-component-ref>          <service-endpoint-interface>              com.ibm.pbw.ejb.PlantsByWebSphereCatalog          </service-endpoint-interface>       </port-component-ref>    </service-ref> </webservicesclient> 

This deployment descriptor does not require any configuration unless you want to change the JNDI name in the java:comp/env namespace used to look up the service. This name is specified in the service-ref-name element.

Assembling the J2EE Application Client

Next, we need to make a client JAR containing the following files:

  • The client application classes

  • The classes for all the generated Java files

  • The application-client.xml deployment descriptor

  • The MANIFEST.MF file containing a Main-Class: entry

  • The webservicesclient.xml deployment descriptor in the META-INF subdirectory of the JAR for an application or EJB client, (the WEB-INF subdirectory for a client in the WEB container)

  • The JAX-RPC mapping file, PlantsByWebSphereCatalog_mapping.xml

  • The WSDL file, which by convention is placed in the META-INF directory of the JAR

You can use your favorite assembly tool to create the application client JAR and EAR. The process described in the following sections works from the command line to do the assembly to make the process explicit.

The directory you're working in should currently have the following after running WSDL2Java, creating the client class, and compiling all the Java files:

.    PlantsClientJNDI.class    PlantsClientJNDI.java    PlantsByWebSphereCatalog.wsdl .\META-INF    PlantsByWebSphereCatalog_mapping.xml    ibm-webservicesclient-bnd.xml    webservicesclient.xml .\com\ibm\pbw\ejb     PlantsByWebSphereCatalog.class     PlantsByWebSphereCatalog.java     PlantsByWebSphereCatalogService.class     PlantsByWebSphereCatalogService.java     PlantsByWebSphereCatalogServiceLocator.class     PlantsByWebSphereCatalogServiceLocator.java     PlantsByWebSphereCatalogSoapBindingStub.class     PlantsByWebSphereCatalogSoapBindingStub.java     StoreItem.class     StoreItem.java     StoreItem_Deser.class     StoreItem_Deser.java     StoreItem_Helper.class     StoreItem_Helper.java     StoreItem_Ser.class     StoreItem_Ser.java

Now, continue the steps to build the client JAR and EAR.

Move the WSDL file to the META-INF subdirectory.

Create the META-INF\application-client.xml file with the following contents:

 <?xml version="1.0" encoding="UTF-8"?>  <!DOCTYPE application-client PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application Client 1.3//EN" "http://java.sun.com/dtd/application-client_1_3.dtd">     <application-client >       <display-name>PlantsClientJNDI</display-name>    </application-client> 

Create a manifest.txt file containing the following line in the current directory:

 Main-Class: PlantsClientJNDI 

This file will be merged into the META-INF\MANIFEST.MF file using the –m option of the jar command to instruct the client container which class has the main method for the application.

Now run the jar command (located in the java\bin directory under the WebSphere installation directory) to update the manifest and create the JAR file:

 jar –cvmf manifest.txt PlantsClient.jar META-INF\* com PlantsClientJNDI.* 

That completes the creation of the client JAR. The launchClient command requires an EAR file, so it will take a few more steps to assemble the JAR into an EAR.

Create a META-INF\application.xml file with the following contents:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd"> <application >       <display-name>WebServices Managed Client Sample</display-name>       <module >          <java>PlantsClient.jar</java>       </module> </application> 

The purpose of this descriptor is to indicate to the container which JAR in the EAR contains the main class. Finally, create the EAR:

 jar cvf PlantsClient.ear META-INF\application.xml PlantsClient.jar 

You now have a JSR-109 web services enabled application client EAR. Let's try it out.

Testing the Application Client

Run the application EAR with the launchClient command located in the WebSphere Application Service bin directory:

 launchClient PlantsClient.ear IBM WebSphere Application Server, Release 5.0 J2EE Application Client Tool Copyright IBM Corp., 1997-2002 WSCL0012I: Processing command line arguments WSCL0013I: Initializing the J2EE Application Client Environment WSCL0035I: Initialization of the J2EE Application Client Environment has completed WSCL0014I: Invoking the Application Client class PlantsClientJNDI Wheelbarrow      $29.0 Shiny red wheelbarrow with epoxy coated steel bin and wooden handles. Tire is solid with thick treads that grip rough, wet surfaces. Large capacity - 3 Cu.Ft. capacity, 150 Lb. maximum load         Quantity:       100

You have now created a web service from the Catalog EJB, and created both J2SE and J2EE clients that access the service. We won't develop the client sample further here, but you can imagine how you might expand the client code to automatically process Catalog entries in a variety of ways. You would probably also create web services to handle ordering, customer registration, and so on until Plants-By-WebSphere was a fully web service-enabled enterprise.

Now let's take a look at the rest of the WebSphere web services universe.




Professional IBM WebSphere 5. 0 Applicationa Server
Professional IBM WebSphere 5. 0 Applicationa Server
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 135

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net