[ Team LiB ] Previous Section Next Section

Creating Web Services with Java Code

In this section, we build a complete working Web Service using Java code and Ant tasks. We go through the steps involved in designing, implementing, deploying, and testing this Web Service.

Choosing and Implementing a Backend Component

Although there are many decisions to be made in designing a Web Service, the most important one is choosing a J2EE backend component. Although not required for a Web Service, a common purpose of Web Services is to allow clients to connect with existing J2EE components. WebLogic Server allows three types of backend components to be wrapped in a Web Service. After you've chosen which type of component to utilize, you must implement the component if it has not already been created.

Stateless Session Bean

A Web Service is wrapped around a method of a Stateless Session Bean when the focus of the Web Service is to perform self-contained operations, where each operation is accomplished in a single step. This is what is called a stateless component because no state is kept on behalf of the client. Examples of stateless operations are

  • Computing mortgage rates based on credit and home data

  • Querying for weather conditions for a given ZIP Code

  • Requesting the current price of a stock

The two components that we talk about next are also stateless. However, a Web Service implemented as a Stateless Session EJB enables you to leverage the capabilities of Stateless Session Beans, such as scalability, life cycle, security, and transaction management.

Java Class

Web Services that are wrapped around methods of Java classes are also stateless. However, unlike Stateless Session Beans, WebLogic Server creates only one instance of the Java class. That one instance is required to service all client requests, which is not a scalable solution.

On the flip side, one argument for using a Java class to implement Web Services is the simplicity involved. Writing, deploying, debugging and testing a Stateless Session EJB implementation of a Web Service is more time-consuming than using a vanilla Java class. Another advantage of using a Java class is that you do not require an EJB container to run the Web Service. A Web Service implemented using a Java class could run on WebLogic Server Express (which is not an EJB container).

JMS Listeners

Web Services can also act as listeners for JMS queues and topics. For more information about JMS, please refer to Chapter 12, "Enterprise Messaging with JMS." A Web Service is wrapped around a JMS listener when the Web Service needs to be asynchronous. You might need this in the following cases:

  • Processing a delayed event, such as an order

  • Working on a loan application

Other Web Service Features

In addition to the topics discussed so far, Web Services built with Java can also have the following attributes:

Packaging Web Services

A Web Service in WebLogic Server is deployed as a Web application in a WAR file that is wrapped in an EAR file, as shown in Figure 30.3. The WAR file contains backend components such as a Web service client JAR file, Java class files, EJB JAR files, and deployment descriptors.

Figure 30.3. Web Services are WAR files packaged inside an EAR file.

graphics/30fig03.gif

The easiest way to generate this packaging is to use the Ant servicegen task. These are the steps to using servicegen:

  1. Create a staging directory to build your Web Service.

  2. Compile and package Web Service backend components into this staging directory. For Stateless Session Beans, copy the JAR to this directory. For Java classes, copy the class file here in the proper directory reflecting its package.

  3. Create a build.xml Ant script in the staging directory.

  4. Open a command prompt and execute the setWLSEnv.cmd file from the WL_HOME\server\bin directory. You should now be able to execute the Ant tool.

  5. Make sure that the webservices.jar file that is located in %BEA_HOME%\weblogic81\server\lib is in your classpath.

  6. Type ant at the command prompt to execute the contents of build.xml.

Building a Web Service with a Java Class Back End

Now we're going to build a Web Service from scratch based on a Java class. The Java class we're going to use is shown in Listing 30.2. It has one public method, called getGreeting(), that takes a String and returns a String. This method will become an operation in our Web Service.

Listing 30.2 A Sample Java Class to Be Used as a Web Service
import java.util.Date;

public class JavaWebService {
 public JavaWebService() {
 }
 public String getGreeting(String name) {
  return "Hello, " + name + " the local time is " + new Date();
 }
}

When using a Java class to build a Web Service, the Java class must follow certain rules:

  • Must have a no-argument constructor.

  • Public methods will be translated into Web Service operations.

  • Must not start any threads because all code runs within WebLogic Server.

  • Must be thread-safe because WebLogic Server creates only one instance of this class and shares it among all Web Service client requests.

Creating the build.xml Ant Script

Creating Ant scripts is not a simple thing, but it does get easier over time. Listing 30.3 illustrates an Ant script that invokes the servicegen task.

Listing 30.3 An Ant Script to Create a Web Service from a Java Class
<project name="buildWebservice" default="ear">
 <target name="ear">
  <servicegen
    destEar="testWebService.ear"
    contextURI="WebServices" >
   <classpath>
     <pathelement path="${java.class.path}" />
     <pathelement path="." />
   </classpath>
   <service
    javaClassComponents="JavaWebService"
     targetNamespace="http://www.getgamma.com/webservices/javaclass"
     serviceName="TestWS"
     serviceURI="/TestWS"
     generateTypes="True"
     expandMethods="True"
     style="rpc" >
     <client
      packageName="com.wls8unleashed.webservices.javaclass"
     />
    </service>
   </servicegen>
 </target>
</project>

We assume that you compile your Java classes using some script or by hand. This script file does the following:

  • Look at JavaWebService.class and convert all public methods (getGreeting) into a Web Service operation

  • Create a deployment descriptor called web-services.xml with information from build.xml and JavaWebService.class

  • Create a Web Service client JAR file that contains a proxy to call the Web Service from a Java client

  • Create a file called web-services.war that contains the front-end Web application

  • Create a file called testWebService.ear that contains all the preceding files

In Listing 30.3, lines 6–9 add the current directory to the classpath so that the Java components in the staging directory will be found. The build script contains elements that will determine the URL of the Web Service. The URL that is used to invoke a Web Service has the following form:


protocol://host:port/contextURI/serviceURI
  • protocol defaults to HTTP but can be set to HTTPS

  • host is the machine that WebLogic Server is running on

  • port is the port that WebLogic Server is listening on

  • contextURI is set in the servicegen Ant task in build.xml

  • serviceURI is set in the servicegen Ant task in build.xml

So, given a host of www.mydomain.com and a port of 7001, the URL of the Web Service in Listing 30.2 is http://www.mydomain.com:7001/WebServices/TestWS.

In depth information about the servicegen Ant task can be found at http://e-docs.bea.com/wls/docs81/webserv/anttasks.html#1063540.

Executing the build.xml Ant Script

To create the testWebService.ear file in the staging directory, do the following:

  1. Copy the JavaWebService.class to the staging directory and create the build.xml file in the same directory.

  2. Copy the setWLSEnv.cmd from a user-defined WebLogic Server domain to the staging directory.

  3. Execute the setWLSEnv.cmd script to set environmental variables to work with WebLogic tools. Remember, you need to ensure that the webservices.jar file is in the classpath.

  4. Now execute build.xml by typing ant on the command line. The Ant command will execute a script called build.xml in the current directory by default. You can specify another script by using the -buildfile option.

If everything goes correctly, you will have created the testWebService.ear file in the staging directory.

Deploying the Web Services As an Exploded Directory

While you're developing your Web Service, it's much easier to debug if you deploy it as an exploded directory rather than as an EAR file. If you're using servicegen, change the destEar attribute to a name without an extension. servicegen will use this as a directory name rather than an EAR file. You can extract the files out of web-services.war (including web-services.xml) and place them in the WebLogic Server directory structure as a Web application. You can then modify the backend Java class (the business logic, not the interface) and the Web Service will pick up the changes. If the interface has to be changed, you must re-create the web-services.xml file.

Deploying the Web Service As an EAR File

Web Service EAR files are deployed just like other EAR files. Start WebLogic Server, if it is not already started, and open the Administrator Console. Expand the Deployments node in the left panel and then click on the Applications node. Click on the Deploy a New Application link in the right panel. This is shown in Figure 30.4. Locate the Web Services EAR file that you want to deploy and then click Deploy.

Figure 30.4. Select the Web servers you want to deploy the Web Service on.

graphics/30fig04.gif

The next screen attempts to deploy the EAR file to your selected servers. If all goes well, you'll see a message indicating that the embedded web-services.war file was deployed correctly, as shown in Figure 30.5.

Figure 30.5. A successful deployment of the Web Service.

graphics/30fig05.gif

After the Web Service has been successfully deployed, you'll see it listed under the Web Service Components node and the Applications node, as shown in Figure 30.6.

Figure 30.6. Deployed Web Services show up under the Applications node.

graphics/30fig06.gif

The Web Service Home Page

Every Web Service deployed under WebLogic Server has a home page. The URL for a Web Service home page is protocol://host:port/contextURI/serviceURI. The home page enables us to do the following:

  • Test the operations of the Web Service

  • Download the Web Service client JAR file

  • View and download the WSDL for the Web Service

Click the Testing tab to see the link to the home page for the Web Service. This is shown in Figure 30.7.

Figure 30.7. The Web Service home page enables us to test the deployed Web Service.

graphics/30fig07.gif

Notice that the input parameter from the SOAP request was converted into a Java String object and sent into the JavaWebServices getGreeting() method. The JavaWebServices getGreeting() then output a string that was placed in the SOAP response message. This conversion from XML to Java and back again was taken care of for us by WebLogic Server.

Viewing a WSDL

Go back to the Web Service home page and click the Service Description link. This will display the WSDL for the Web Service, as shown in Listing 30.4. Notice that the WSDL is quite verbose and was generated for us by WebLogic Server.

Listing 30.4 The Generated WSDL File for Our Simple Web Service Is Quite Verbose
<?xml version="1.0" encoding="utf-8"?>

<definitions xmlns:s="http://www.w3.org/2001/XMLSchema"
  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:tns="http://www.getgamma.com/webservices/javaclass"
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
  targetNamespace="http://www.getgamma.com/webservices/javaclass"
  xmlns="http://schemas.xmlsoap.org/wsdl/">
 <message name="getGreeting" >
  <part name="string" xmlns:partns="http://www.w3.org/2001/XMLSchema"
    type="partns:string" />
 </message>
 <message name="getGreetingResponse" >
  <part name="result" xmlns:partns="http://www.w3.org/2001/XMLSchema"
    type="partns:string" />
 </message>
 <portType name="TestWSPort" >
  <operation name="getGreeting" >
   <input message="tns:getGreeting" />
   <output message="tns:getGreetingResponse" />
  </operation>
 </portType>
 <binding name="TestWSPortSoapBinding" type="tns:TestWSPort" >
 <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
  <operation name="getGreeting" >
   <soap:operation soapAction="" style="rpc" />
   <input>
    <soap:body use="encoded"
      namespace="http://www.getgamma.com/webservices/javaclass"
     encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
   </input>
   <output>
    <soap:body use="encoded"
       namespace="http://www.getgamma.com/webservices/javaclass"
      encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
   </output>
  </operation>
 </binding>
 <service name="TestWS" >
  <documentation>todo: add your documentation here</documentation>
  <port name="TestWSPort"
   binding="tns:TestWSPortSoapBinding">
   <soap:address location="http://localhost:7001/WebServices/TestWS"/>
  </port>
 </service>
</definitions>

Although a full explanation of WSDL is beyond the scope of this book, understanding the basics will serve us well as our Web Services grow more complex and we are forced to modify WSDL files by hand. The following is a list of major sections with WSDL files:

  • definitions— Defines namespaces for the rest of the document, including SOAP, WSDL, and the namespace for this Web Service. A namespace is a way of referencing XML elements from multiple sources without having their names conflict with each other. For example, line 5 associates the prefix tns with elements from our Web Service, and line 18 refers to the getGreetingResponse XML element defined within this WSDL file. Line 10 refers to partns:string, which is the XML element named string in the partns namespace. Line 10 also associates the prefix partns with the URI http://www.w3.org/2001/XMLSchema. It will be used to prefix tags from the W3C XML Schema definition. You can learn more about XML namespaces at http://www.w3.org/TR/1999/REC-xml-names-19990114/Overview.html. Alternatively, doing a Google search for xml namespaces should lead you to several good resources on this topic.

  • message— Defines the SOAP messages used with the Web Service. Each message can be composed of one or more parts, each representing a parameter with a specific data type. There are two messages in this WSDL file—getGreeting and getGreetingResponse—and each is composed of a single parameter that is a String.

  • types— Indicates any custom data types used by the Web Service. Although WSDL attempts to define a data type specification, it defaults to the W3C XML Schema specification. This specification defines many commonly used data types, such as string, integer, float, time, and date. If you're using a data type that isn't supported, such as an array of strings, you have to include a types section in the WSDL file that clearly indicates the composition of the user-defined data type.

  • soap:binding— Defines what type of SOAP messages are used with this Web Service. This can have one of two values:

    • Document— The body and response of SOAP messages will simply consist of an XML document indicating the Web Service operation and its parameters. An analogy for a Document Web Service is sending an email. You do not need to wait for the person to receive and read the email. You can move on to do other things.

    • RPC— The body and response of SOAP messages will include XML wrappers around the Web Service operation and its parameters. RPC Web Services are more of the request-response style. For example, if you're calling someone who does not have a voicemail, you expect the person to respond. This is a good analogy of a RPC Web Service.

    NOTE

    A good article that compares document-style Web Services versus RPC-style Web Services is

    http://www.devx.com/enterprise/Article/10397/0/page/3


    The method of calling the Web Service is also defined. The statement transport=http://schemas.xmlsoap.org/soap/http indicates that SOAP over HTTP will be used.

  • portType— Combines message elements to form one-way or round-trip operations. Within this element, you'll find the defined operations for the Web Service. In this case, there is one operation named getGreeting that accepts a getGreeting message and returns a getGreetingResponse message.

  • service— Contains documentation on the Web Service and the URL for invoking it.

A Web Service Test Client

Web Services running in WebLogic Server can be invoked from many types of clients, including Microsoft .NET, Cape Clear, Apache SOAP, Microsoft SOAP Toolkit, and Java (via JAXM and JAX-RPC). There are many companies involved in Web Services. For a good overview of the industry, visit http://www.webservices.org/.

JAX-RPC is a specification for building Web Service clients written in Java and includes several classes that we'll use in this section. Java clients can call Web Services in one of two ways:

  • Static clients use the Web Service–specific client JAR file and the strongly typed Java interface of that Web Service.

  • Dynamic clients retrieve a WSDL file from the service at runtime and use this to identify Web Service operations and the parameters they take. This is similar to using Java reflection to interrogate and invoke methods.

INTRODUCTION TO JAX-RPC API

JAX-RPC is a specification for Java clients that want to access Web Services. The JAX-RPC API allows Java clients to connect to Web Services without having to worry about the underlying protocols. In fact, Web Services created with WebLogic Server tools take care of most of the effort in converting XML to and from Java classes and parameters. Web Services are invoked as if they were local objects, and the JAX-RPC implementation classes take care the complexities of serialization of parameters and communication with the remote Web Service. The architecture is shown in Figure 30.8.

Figure 30.8. How JAX-RPC links clients to Web Services.

graphics/30fig08.gif

The JAX-RPC specification defines a set of APIs in many packages. The classes and interfaces focused on the client programming model are found in the javax.xml.rpc package. Table 30.1 lists the most commonly used classes and interfaces from that package.


Table 30.1. JAX-RPC Interfaces and Classes

Interface or Class

Description

Service

Main point of communication to a Web Service

ServiceFactory

A factory class for creating Service instances; used with dynamic clients

Stub

A local object that represents the remote Web Service

Call

Used with dynamic clients to invoke a Web Service operation

JAXRPCException

Exception related to the JAX-RPC runtime mechanisms

Writing a Static Web Service Client

In this section, we write a static client to our Web Service using the client JAR files. Before we attempt to invoke our Web Service, we must set the following system properties:

  • javax.xml.soap.MessageFactory— The name of class used as a factory for creating SOAPMessage objects

  • javax.xml.rpc.ServiceFactory— The name of class used as a factory for the creation of instances of the type javax.xml.rpc.Service

In both cases, WebLogic Server supplies classes for these two purposes and they are included in the webserviceclient.jar and associated files. The main() method in Listing 30.5 illustrates this. When we compile and execute this code, we need TestWS_client.jar and webserviceclient.jar in our CLASSPATH.

Listing 30.5 A Static Java Client for the TestWS Web Service
package com.wls8unleashed.webservices.javaclass;

public class WebServiceClient {
  public static void main(String[] args) {
    // Setup the global JAXM message factory
    System.setProperty("javax.xml.soap.MessageFactory",
      "weblogic.webservice.core.soap.MessageFactoryImpl");
    // Setup the global JAX-RPC service factory
    System.setProperty( "javax.xml.rpc.ServiceFactory",
      "weblogic.webservice.core.rpc.ServiceFactoryImpl");

    // Parse the argument list
    WebServiceClient client = new WebServiceClient();
    String wsdl = (args.length > 0? args[0] : null);
    client.callWS(wsdl);
  }

  public void callWS(String wsdlURI) {
    try {
      TestWSPort testWS = null;
      if (wsdlURI == null) {
        testWS = new TestWS_Impl().getTestWSPort();
      } else {
        testWS = new TestWS_Impl(wsdlURI).getTestWSPort();
      }

      String str = testWS.getGreeting("Jessie Marin");
      System.out.println(str);

    } catch (Exception e) {
      e.printStackTrace();
    }

  }
}

The callWS method instantiates a TestWS_Impl object either using a WSDL file or without using one. We then use this object to get an instance of TestWSPort. TestWSPort is the local stub that enables us to invoke Web Service operations as demonstrated with the code testWS.getGreeting("Jessie Marin").

Writing a Dynamic Web Service Client

If the Web Service you're invoking is not written in Java or you do not have access to the client JAR file, you can invoke the Web Service by simply utilizing its WSDL as shown in Listing 30.6. We use a Java client to invoke a Web Service. The Web Service itself might or might not be implemented in Java. However, for the client, you still need the WebLogic JAX-RPC implementation JAR file in your CLASSPATH.

Listing 30.6 A Dynamic Java Client for the TestWS Web Service
import java.net.URL;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.Service;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.namespace.QName;

public class DynamicWebServiceClient {
  public static void main(String[] args) {
    // Setup the global JAXM message factory
    System.setProperty("javax.xml.soap.MessageFactory",
      "weblogic.webservice.core.soap.MessageFactoryImpl");
    // Setup the global JAX-RPC service factory
    System.setProperty( "javax.xml.rpc.ServiceFactory",
      "weblogic.webservice.core.rpc.ServiceFactoryImpl");

    try {
      // create service factory
      ServiceFactory factory = ServiceFactory.newInstance();

      // define qnames
      String targetNamespace = "http://www.getgamma.com/webservices/javaclass";

      // create service
      QName serviceName = new QName(targetNamespace, "TestWS");
      URL wsdlLocation = new URL("http://localhost:7001/WebServices/TestWS?WSDL");
      Service service = factory.createService(wsdlLocation, serviceName);

      // create call
      QName portName = new QName(targetNamespace, "TestWSPort");
      QName operationName = new QName(targetNamespace, "getGreeting");
      Call call = service.createCall(portName, operationName);

      // invoke the remote web service
      String result = (String) call.invoke(new Object[] {"Jessie Marin"});

      System.out.println(result);
    } catch (Exception e) {
       e.printStackTrace();
    }
  }
}

This code utilizes ServiceFactory to generate a new instance of Service that is tied to the TestWS Web Service by associating it with the WSDL. In this code, we have attributes from the WSDL file, such as the target namespace, Web Service name, port name, and operation name.

NOTE

To retrieve the WSDL for a Web Service, you can end the URL with ?WSDL.

Example: http://localhost:7001/WebServices/TestWS?WSDL


NOTE

In this section, we introduced the JAX-RPC API. For more information on this, please refer to http://java.sun.com/xml/jaxrpc/.


Exception Handling with JAX-RPC

The JAX-RPC specification includes a SOAPFaultException exception to be thrown from the Java backend component of a Web Service. If these components throw another exception, WebLogic Server attempts to map it to a SOAP fault as closely as possible, but using SOAPFaultException ensures that clients receive the most useful exception information. The SOAPFaultException class contains very detailed information concerning the nature of the exception.

For a complete example using JAX-RPC, please refer to http://java.sun.com/webservices/docs/ea1/tutorial/doc/JAXRPC.html.

The Web Service–Specific Client JAR File

To build a static Web Service client in Java, you need the client JAR file for that Web Service. The client JAR can be downloaded from the Web Service home page, if the Web Service originator created one. If you cannot access the JAR file, you can create one from the Web Service WSDL file that you can also download from the Web Service home page. The Ant task clientgen creates a client JAR from a WSDL file or a Web Service EAR file. Please visit http://e-docs.bea.com/wls/docs81/webserv/anttasks.html#1080160 for more information about clientgen.

The client JAR file contains implementations of the classes and interfaces in Table 30.1 in the JAX_RPC sidebar. The client JAR for the previous example is named TestWS_client.jar. The contents include the following:

  • TestWS_Impl— Implements the JAX-RPC service interface. Web Service clients instantiate this class.

  • TestWSPort— A local interface that represents the Web Service. It contains one method: getGreeting.

In general, servicegen creates the following files:

  • Client JAR fileWebServiceName_client.jar

  • Service interfaceWebServiceName_Impl.java

  • Stub interfaceWebServiceNamePort.java

The WebLogic Server Client JAR Files

In addition to the specific client JAR file, you also need a WebLogic Server JAR file. These files are available at WL_HOME\server\lib. You will use one of the following files, depending on the situation:

  • webserviceclient.jar— Contains the runtime implementation of the JAX-RPC APIs

  • webserviceclient+ssl.jar— JAX-RPC APIs with implementation of SSL

  • webserviceclient+ssl_pj.jar— JAX-RPC APIs with implementation of SSL for the CDC profile of the Java 2 Micro Edition (J2ME)

DOWNLOADING THE WEB SERVICE CLIENT JAR FILE

To download this client JAR file, go to the Web Service home page. The Click here link downloads the client JAR file when you click it. By default, the name of this file is WebServiceName_client.jar. In our case, the name of the client JAR file is TestWS_client.jar.


    [ Team LiB ] Previous Section Next Section