[ Team LiB ] |
Creating Web Services with Java CodeIn 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 ComponentAlthough 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 BeanA 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
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 ClassWeb 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 ListenersWeb 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: Other Web Service FeaturesIn addition to the topics discussed so far, Web Services built with Java can also have the following attributes:
Packaging Web ServicesA 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.The easiest way to generate this packaging is to use the Ant servicegen task. These are the steps to using servicegen:
Building a Web Service with a Java Class Back EndNow 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 Serviceimport 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:
Creating the build.xml Ant ScriptCreating 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:
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
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 ScriptTo create the testWebService.ear file in the staging directory, do the following:
If everything goes correctly, you will have created the testWebService.ear file in the staging directory. Deploying the Web Services As an Exploded DirectoryWhile 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 FileWeb 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.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.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.The Web Service Home PageEvery 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:
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.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 WSDLGo 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:
A Web Service Test ClientWeb 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:
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.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.
Writing a Static Web Service ClientIn 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:
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 Servicepackage 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 ClientIf 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 Serviceimport 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-RPCThe 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 FileTo 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:
In general, servicegen creates the following files:
The WebLogic Server Client JAR FilesIn 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:
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 ] |