WebLogic provides a rich framework for the development and deployment of web services. Here is a brief outline of the capabilities of WebLogic's web services framework:
There are many orthogonal and overlapping aspects to designing and implementing web services. Before we take a closer look at WebLogic's web services framework, we need to understand how your deployed web services operate at runtime.
19.1.1 Web Services Architecture
A web service is composed of one or more operations, whereby each operation may be implemented using different backend components. You even can associate a separate set of message handlers for each operation. For example, a web service operation may be implemented by a single method of a standard Java object, or perhaps by a combination of SOAP message handlers and a remote method of a stateless session EJB. Because web services extend the capabilities of your web applications, WebLogic requires that you define a web-services.xml deployment descriptor that captures the vital information describing a web service. This XML-formatted descriptor file is located under the /WEB-INF folder of a web application and includes the following information on each web service:
The web-services.xml descriptor file is crucial to properly configuring your web services. Even though WebLogic provides various Ant tasks for automatically generating the web-services deployment descriptor, often you will need to return to the web-services.xml descriptor file and manually tweak its configuration settings. During the course of this chapter, we'll cover the different configuration elements of the web services deployment descriptor. WebLogic can use the web-services.xml descriptor file to automatically generate the WSDL document for the deployed web service.
As illustrated in Figure 19-1, WebLogic's web services are packaged into standard J2EE enterprise applications (EAR).
Figure 19-1. Package structure of a typical web service
The EAR file includes the web application (WAR) that contains the web-services.xml descriptor file and any Java classes that implement the web service, the message handlers, and support classes for handling custom datatypes. It also packages any EJB JAR files for any stateless session EJBs or JMS consumers and producers that implement the web service operations. Fortunately, WebLogic provides several Ant tasks for assembling the various components of the web service into an EAR file.
Because WebLogic's web services are packaged within enterprise applications, they can integrate easily with the rest of the J2EE framework. Your web services can automatically benefit from WebLogic's support for various J2EE features: access to JDBC connection pools and JTA transactions, the business objects within the enterprise application, and a simple and unified security model.
19.1.1.1 Typical behavior of a web service
Like EJB interfaces, a web service exposes a set of operations that can be invoked by remote clients. Instead of using RMI-IIOP to invoke the EJB's methods, the operations are accessed through SOAP calls passed over standard HTTP. Instead of the traditional marshalling of Java objects to a bit stream, client-side stubs now serialize the method arguments to an XML stream. On the server side, WebLogic parses the incoming XML stream and deserializes the method arguments. WebLogic uses this information to invoke an operation on a backend component that implements the particular web service. If the operation generates a response for the client, the client-side stubs deserialize it to extract the return values before passing them on to the client.
Along the way, the incoming SOAP request or the outgoing SOAP response may be processed by a number of message handlers that transform the message body. In fact, a web service operation may simply go through a chain of message handlers, without actually invoking a backend component! Figure 19-2 depicts the activity surrounding a client application that invokes an operation on a web service. You can think of this diagram as representing the runtime behavior of a JAX-RPC call.
Figure 19-2. Architecture of a typical web service operation
When a client invokes an operation exposed by a web service, the following actions occur:
How the backend component is invoked depends on the web-services.xml descriptor file. You can configure the "invocation style" for individual operations on a web service, and enable clients to invoke an operation without waiting for a SOAP response. Alternatively, you can mark a web service as being "document-centric," in which case each operation accepts a single XML document as an incoming parameter. Later in this chapter, we discuss similar design considerations in more detail once we've looked at how to build a web service.
Figure 19-2 describes the typical behavior of a web service that is implemented by some backend component. However, a SOAP message handler chain need not be associated with the web service at all. In that case, all incoming SOAP requests from the client and any outgoing SOAP responses to the client remain unaltered because no message handlers are configured to intercept the SOAP messages. Thus, if no SOAP interceptors are configured for a web service operation, you can disregard steps 3 and 7 depicted in Figure 19-2.
19.1.1.2 Web service operations without a backend
Just as the SOAP handler chain is optional for a web service operation, so too is the backend component. This means that a web service may implement an operation simply through a chain of SOAP interceptors. Figure 19-3 illustrates the behavior of a web service operation implemented purely through a chain of SOAP message handlers.
Figure 19-3. Behavior of an operation without a backend component
After the last SOAP interceptor in the chain has processed the incoming SOAP request, the SOAP message then continues directly with the "response chain" of SOAP handlers, but this time in reverse order of the chain. Each SOAP handler in the chain has direct access to the SOAP message and may alter the message before passing it to the next handler in the chain. A common use of SOAP message handlers is to decrypt and encrypt SOAP messages that enter and leave a web service operation.
19.1.2 Building Your First Web Service
Let's now survey the web services framework by constructing a simple web service that wraps a standard Java class. Example 19-1 provides the definition of the Java class that will implement the web service.
Example 19-1. A simple backend Java component for a web service
public class Simple { public String makeUpper(String arg) { return arg.toUpperCase( ); } }
It exposes a single method, makeUpper( ), that simply returns an uppercase equivalent of the input string. Recall that all web services in WebLogic are packaged within an EAR file that includes the web application (WAR), which holds the web-services.xml descriptor file, and any Java classes that implement the web service. If your web service is implemented using EJBs, then the EAR file also packages the required EJB JARs. In the case of our example, the EAR file will simply package the web application (myWar.war) that hosts the actual web services:
META-INF/MANIFEST.MF META-INF/application.xml myWar.war
Our web application will be a standard J2EE web archive that can hold the static content (HTML, images, text files, JARs, etc.), JSP pages, and compiled Java classes for any servlets, JSP tags, and filters. In addition, the web application will include the following files under the document root:
WEB-INF/web-services.xml WEB-INF/classes/com/oreilly/wlguide/webservices/pojo/Simple.class WEB-INF/web.xml
So, simple Java backends are packaged with the web application. The web-service.xml descriptor file is essential because it completely describes all the web services hosted by the web application. Example 19-2 describes the web service that wraps our Java class.
Example 19-2. The web-services.xml descriptor file
Notice how the web-services.xml descriptor file defines the operations that are supported by the web service and the components that are used to implement the web service. Our Simple web service exposes a single operation, makeUpper, that is mapped to the makeUpper( ) method on the standard Java class. The operation accepts a single in parameter of type xsd:string and returns a single out parameter, again of type xsd:string. In this way, the deployment descriptor specifies the parameters and return values of each operation and maps it to a particular method on one of the backend components. The web-services.xml descriptor file is quite similar to the WSDL document for a web service, though it provides additional deployment information. WebLogic lets you automatically generate the web-services.xml deployment descriptor in several ways:
All of these tools have their limitations, which become even more debilitating when you need to build multiple web services that are supported by different backend components or are associated with a chain of SOAP handlers. For now, we can safely rely on the servicegen Ant task to generate both the deployment descriptor and the actual EAR file that packages our web service. Example 19-3 shows how to invoke the servicegen task from within an Ant build script.
Example 19-3. Ant script for invoking the servicegen task
In order to invoke the servicegen task, we need to specify the name of the deployable EAR file that is generated and the name of the WAR file that hosts the web service and classes for the backend component. By running the preceding build script, you manufacture a deployable EAR file, myPOJOEar.ear, which in turn contains the WAR, myWar.war, which contains the web service and the Java implementation class. By deploying the EAR file to WebLogic Server, you also deploy all web services that are packaged within its web applications.
Note that the serviceURI attribute for the web service is set to /Simple, and the contextURI attribute for the EAR is set to pojoService. This means that you can access the web service by pointing a browser to the following address: http://hostname:port/pojoService/Simple, where hostname and port refer to the listen address and port number of a running WebLogic instance to which the web service has been successfully deployed. Here you can view the home page for the web service and also access the automatically generated WSDL by clicking the Service Description link. Example 19-4 lists the WSDL that describes our web service.
Example 19-4. Autogenerated WSDL for our web service
The home page also provides a customized environment where you can test the operations of the web service. If you click the makeUpper link, you navigate to a screen where you can invoke the makeUpper operation of the web service. Enter some random string value in the Value text box and then hit the Invoke button. The web service will return the uppercase equivalent of the same string. You also can view the SOAP request and response envelopes exchanged during the invocation. Here's a sample request envelope used to invoke the web service operation:
everything here should be in uppercase really!
Finally, the home page provides a link to the client JAR that you can download to build your own static JAX-RPC clients for the web service. In fact, you also could invoke the clientgen Ant task to manufacture a client JAR that can be used by JAX-RPC clients. Example 19-5 shows how to use the clientgen task to generate a client JAR that includes the required JAX-RPC interfaces and client-side stubs for the web service.
Example 19-5. Invoking the clientgen Ant task
Using myClient.jar, which is generated by invoking the previous Ant target, you then can write a Java client that invokes the web service. Example 19-6 lists the code for a JAX-RPC client that invokes the makeUpper operation exposed by the web service.
Example 19-6. Invoking a web service
public class Invoke { public static void main(String[] argv) throws Exception { // Set up the global JAXM message factory System.setProperty("javax.xml.soap.MessageFactory", "weblogic.webservice.core.soap.MessageFactoryImpl"); // Set up the global JAX-RPC service factory System.setProperty( "javax.xml.rpc.ServiceFactory", "weblogic.webservice.core.rpc.ServiceFactoryImpl"); Simple ws = new Simple_Impl("http://10.0.10.10:8001/pojoService/Simple?WSDL"); SimplePort port = ws.getSimplePort( ); String returnVal = port.makeUpper("Hello There"); System.out.println("The service returned: " + returnVal); } }
As you can see, the web service client needs to do very little to contact the web service. The client JAR packages the interface and implementation for each SOAP port defined in the WSDL. The Simple_Impl stub implements the JAX-RPC Service interface and is created using the URI /pojoService/Simple?WSDL, which fetches the WSDL for the target web service. The getSimplePort( ) method relies on the Service.getPort( ) method to then return an instance of the SimplePort stub implementation. Once you create the client-side port, you can use the local methods on the SimplePort interface to invoke the operations of the web service. In this case, the makeUpper( ) method is invoked. You can expect the following output:
java com.oreilly.wlguide.webservices.pojo.client.Invoke The service returned: HELLO THERE
We've just looked at the important aspects of WebLogic's web services framework. It is essentially a tool-driven environment that enables you to rapidly build deployable web services using different starting points the backend implementation of the web service or, as we shall see later in this chapter, the WSDL document itself.
19.1.2.1 Using the Administration Console
The Administration Console lets you view any web services deployed to WebLogic Server. Web services typically are deployed as a part of another application, either a WAR or EAR. To view the web service, expand the Deployments/Applications node and click the name of the container component in the left pane of the Administration Console. For example, if the web service was part of the EAR called demo, clicking demo will expand the EAR to show you the components contained within the EAR. Clicking the web service then will provide you with a number of tabs with which to manage the service. WebLogic 7.0 provides a Deployments/Web Service Components node with similar functionality.
19.1.2.2 Mapping your web service to an alternative URL
Notice how we used the URL http://10.0.10.10:8001/pojoService/Simple in order to access our web service. By default, any web service deployed to WebLogic Server is accessible through the URL http://server:port//. However, you may also modify the default endpoint for the web service. Because all requests to a web service deployed to WebLogic Server are handled by an internal servlet, weblogic.webservice.server.servlet.WebServiceServlet, this means that you can use the servlet-mapping element in the web.xml descriptor file to expose your web services via an alternative URL scheme.
The following portion from the web.xml descriptor shows how to map the Web Service servlet to a custom URL pattern:
InternalWebServiceServlet weblogic.webservice.server.servlet.WebServiceServlet ... InternalWebServiceServlet /CustomersComeHither/*
Any web service packaged within the web application then is accessible through the URL http://server:port//CustomersComeHither/. If you modify the web.xml descriptor file for myWar.war to include a similar URL mapping, clients need to use http://10.0.10.10:8001/pojoService/CustomersComeHither/Simple in order to access the web service.
19.1.3 Using the WSDL to Create a Web Service
Recall how the WSDL document describing your web service is generated automatically from the web-services.xml descriptor file and then made available as a download link on the home page for the web service. In the example, the WSDL is accessible through the URL http://10.0.10.10:8001/pojoService/Simple?WSDL. You can, however, reverse the roles and generate the web service from an existing WSDL document using the wsdl2service task. This generates a web-services.xml descriptor file and a template for the backend that implements the operations of the web service. You then can modify the Java source template and include the business logic that supports each web service operation.
The wsdl2service Ant task generates the support files for only a single web service described by the WSDL file. By default, the task chooses the first web service it finds in the WSDL document. You can use the serviceName attribute to specify a particular web service. The following Ant target shows how to generate a web service from a WSDL file:
wsdl="myWSDL.wsdl" serviceName="Simple" destDir="myEarWSDL" typeMappingFile="types.xml" packageName="com.oreilly.wlguide.webservices" />
The typeMappingFile attribute is needed only if the web service defines operations that use custom types for parameters and return values. If so, you must use the autotype task to generate the type mapping file for all custom types from the WSDL file. On invoking this Ant target, you get a web-services.xml descriptor that describes the web service Simple found in the WSDL file myWSDL.wsdl. In addition, the task creates a Java class located under the com.oreilly.wlguide.webservices package that implements the web service. This Java class contains skeleton code, with empty methods that correspond to the operations exposed by the web service. You can now build on the web-services.xml descriptor and Java class backend as required.
19.1.3.1 Publishing a static WSDL file
Instead of using the automatically generated WSDL that WebLogic provides for each web service, you could make a static WSDL file available through an alternate URL, perhaps apply different security constraints, bundle it with a J2ME client, or even extend the documentation in the WSDL file. While the automatically generated WSDL for a web service is always in sync with the deployed web service, you need to explicitly ensure that the static WSDL file also is kept up-to-date with changes in the web service. Of course, you could always use the automatically generated WSDL as a starting point for the static WSDL file, modify it as appropriate, and then publish the WSDL document.
To publish the WSDL file, you need to include it in the EAR file that packages the web service and define a suitable MIME type mapping. Thus, the WSDL file (say, myWSDL.wsdl) can be placed anywhere under the document root of the web application that hosts the web service. Then you should edit the web.xml descriptor file for the web application and map all documents with the suffix .wsdl to the text/xml MIME type:
wsdl text/xml
The static WSDL file for the web service will now be accessible through the URL http://host:port/webAppContextRoot/myWSDL.wsdl.
19.1.4 Using Ant Tasks to Build Web Services
In order to assemble a web service, you need to collect all the different components of the web service: the backend Java classes, any EJB JARs used to implement one or more operations of the web service, possible SOAP message handlers, datatypes and their support classes, and the web-services.xml descriptor file that completely describes the web service. Only then can the web service be packaged into a standard J2EE enterprise application. If you intend to assemble the web service manually, you need to execute the following steps:
In general, you would not assemble the web service manually because the process can be quite time-consuming and error-prone. Instead, you should rely on the various Ant tasks WebLogic provides to generate all the necessary components of the web service and then assemble them into a deployable EAR file. By automating many of the tasks required to assemble the web service, WebLogic removes some of the tedium and allows you to focus on the task of implementing the web service. In some cases, it can even eliminate the need to look inside the web-services.xml descriptor file. Let's now review how the various Ant tasks automate the job of assembling a web service:
servicegen
This Ant task takes an input EJB JAR (or a list of Java classes), generates all the necessary web service components, and packages them into a deployable EAR. servicegen actually subsumes the functionality of several other Ant tasks that focus on smaller aspects of assembling the web service. This Ant task introspects the Java code and looks for public methods that can be converted into web service operations, and for any custom datatypes that are used as parameters and return values. Based on the attributes passed to the Ant task and the information gleaned from the introspected code, it generates the web-services.xml descriptor file.
For any custom datatypes, it also generates the support classes for converting the datatypes between their XML and Java representations. Finally, it packages all the generated web services into a web application (WAR), and then packages the WAR and any EJB JARs into a deployable EAR file. Example 19-3 showed how to invoke the servicegen task to build a web service. Later, we'll examine other important attributes of the servicegen Ant task.
source2wsdd
Use the source2wsdd task to generate the web-services.xml descriptor file for a web service that wraps a standard Java class. The same process also occurs when the servicegen Ant task is invoked. This Ant task provides a quick and easy way to generate the deployment descriptor for a web service whose Java implementation class is prepared already. The following build script shows how to generate the web-services.xml descriptor from the Java source file MyService.java:
Here, c:autotype ypes.xml refers to a file that includes the XML Schema definitions for any custom datatypes used as parameters and return values, and maps these types to appropriate serialization classes. You often can generate this file by using the autotype Ant task.
autotype
If your web service defines operations whose parameters and/or return values use custom datatypes, you may use this Ant task to generate the Java representation of the type, the XML Schema and type mappings, and the serialization classes that convert the data between the XML and Java representations. Note that you can also invoke the servicegen Ant task so that the same support for custom datatypes is generated during the assembly of the web service.
The following build script shows one way of using the autotype task to generate the required support for handling custom datatypes:
Here mytypes.xsd holds the XML Schema definitions for any custom datatypes, and the value of the packageName attribute allows you to specify the package name for the generated serialization classes. The input for this Ant task can come from either a schema file representing the custom datatypes, a URL to a WSDL file containing a description of the datatypes, a Java class that represents the datatypes, or a Java class file that implements a web service. In the latter case, the Ant task will inspect the Java code for custom datatypes used in parameters or return values.
wsdl2service
Use this Ant task to generate a partial implementation of a web service from an existing WSDL file. This task generates the web-services.xml descriptor file and the Java source file for one of the web services it finds in the specified WSDL file. The output Java source provides a template upon which you can build the implementation for the web service. The following example shows how to use the wsdl2service task to generate the Java implementation from the specified WSDL file:
Once again, this task does not generate any type information or any serialization classes for any custom datatypes used to implement the web service operations. Instead, you need to use the autotype task first, and use the generated type mappings as input for this task.
wspackage
This is a useful Ant task for packaging the various components of a web service into a deployable EAR. Typically, you will invoke this Ant task if you have chosen to manually assemble all the pieces of the web service. It assumes that you've already generated the web-services.xml descriptor, the Java classes and any EJB JARs that implement the web service, a client JAR that users can download, SOAP handler classes, and necessary support for handling custom datatypes.
The following script shows how to package all the components of a web service implemented by a standard Java class:
Here the Ant task creates an EAR called c:outputmyEar.ear that packages the web service whose context URI is now CustomersComeHither. The webAppClasses attribute is used to specify the list of Java classes that are placed under the WEB-INF/classes folder of the web application. In addition, we've specified the location of the deployment descriptor and the folder that holds all the required serialization classes for handling any custom datatypes. This task can also be used with the overwrite attribute set to false to add additional components to an existing EAR. This will attempt to merge the contents of the EAR with the web-services.xml file.
clientgen
This Ant task generates a client JAR that clients can use to invoke both WebLogic and non-WebLogic web services. The clientgen task generates the client JAR either from an EAR file that packages the web service, or from the WSDL document of an existing web service, not necessarily running on WebLogic Server. The client JAR includes the JAX-RPC client API and the necessary stubs needed to statically invoke the web service. It also includes serialization classes for any custom datatypes used by the web service and a client-side copy of the WSDL file.
Example 19-5 showed how to generate the client JAR from an existing EAR file that hosts one or more web services. The following script shows how to generate the client JAR from the WSDL file of an existing web service:
Here the task generates the client JAR simple_client.jar from the WSDL file located at http://foobar.com/myapps/simple.wsdl. The packageName attribute specifies the name of the package used for the generated client interface and stub files.
wsdlgen
This Ant task generates a WSDL file from the EAR and WAR files that make up a web service. The following example generates a WSDL file for the web service myWebService, which should be in the referenced WAR:
This task is not available in WebLogic 7.0.
Remember, in order to invoke these Ant tasks, you need to first establish the appropriate shell environment by invoking the setEnv command-line script located under the root directory of your domain. You also can run the servicegen and clientgen tasks from the command line:
java weblogic.webservice.clientgen java weblogic.webservice.servicegen
The documentation for these tools is available if you simply run these tasks without supplying any additional command-line options.
The Ant tasks supplied by WebLogic will go a long way toward easing the process of building your web services. However, many of these Ant tasks are limited in scope. For instance, the source2wsdd task can generate the web-services.xml descriptor file only for a web service that wraps a standard Java class. The wsdl2service task generates the skeleton code for a Java backend that implements only one of the web services described in the WSDL file. Neither of these Ant tasks provides support for web services that can wrap stateless session EJBs or JMS destinations. Moreover, the servicegen task creates a deployable EAR for a web service that simply wraps the supplied backend component. If you need to build rich web services in which individual operations are implemented by separate backend components, then you have no choice but to manually author the web-services.xml deployment descriptor and ensure it accurately describes your web services. The same holds true if you need to associate specific operations of the web service with a chain of SOAP message handlers. None of the Ant tasks that we've examined provides any support for SOAP interceptors.
Still, it is important to be aware of the capabilities of these Ant tasks, and to be able to use them as the situation demands. We cover many scenarios in which the Ant tasks prove to be quite effective and circumvent the need to even look at the layout of the web-services.xml descriptor file. Depending on your needs, you can rely on Ant tasks such as servicegen and clientgen to generate a deployable EAR and client-side JAR files. Alternatively, you could use some combination of other available Ant tasks: autotype, wspackage, wsdl2service, and source2wsdd. WebLogic also offers a visual approach to building web services in the form of WebLogic Workshop. The Workshop IDE abstracts away many of the underlying details and greatly simplifies web service construction.
We believe that a proper understanding of the innards of the web-services.xml descriptor file and a judicious application of these Ant tasks will go a long way toward the development of your web services. In the rest of this chapter, you'll learn more about how to use the web-services.xml descriptor file to configure the different aspects of your web services. For instance, we examine how the web-services.xml descriptor file lets you configure a particular backend component for a specific operation. This information is intended to further your understanding of the web-services.xml descriptor file, which is crucial when you need to configure real-world web services that wrap diverse backend components.
Introduction
Web Applications
Managing the Web Server
Using JNDI and RMI
JDBC
Transactions
J2EE Connectors
JMS
JavaMail
Using EJBs
Using CMP and EJB QL
Packaging and Deployment
Managing Domains
Clustering
Performance, Monitoring, and Tuning
SSL
Security
XML
Web Services
JMX
Logging and Internationalization
SNMP