Web Services on XFire – Fast Forward to 2007

来源:百度文库 编辑:神马文学网 时间:2024/04/28 20:17:16
Over a decade after the emergence of the first implementations, the clouds of hype have fully dissipated, and Web Services are proving to be viable, relevant, and effective for the development of business-to-business and business-to-consumer applications all over the world.
With the SOA (Service Oriented Architecture) movement, enterprise level data-flow and communications architectures are now being built on top of the continually maturing web services foundation. Benefiting from a decade of the development community’s focus and attention, web services have become increasingly robust, interoperable, and easy to use. This easy to use aspect is poised to become the enabling element that ushers in a whole new generation of web services advocates and enthusiasts.
If you haven’t revisited web services since the initial hype, it’s time to take another look. Web services are here to stay.
This article provides an exploration of Java web services appropriate for 2007. It shows how new generation open source web services frameworks, such as XFire, combined with rich support from development environments, can make the development of non-trivial web services truly simple.
You will get hands-on guidance to building a web service in Java using XFire, then a Java client in the Eclipse 3.2 IDE. The finale is the creation of a .NET 2.0-based client for the web service, built within a few minutes, using Visual Studio 2005.All About Web Services
Web services’ claim to fame centres around its ability to ride over theHypertext Transport Protocol (HTTP). Since the World Wide Web is completely built over HTTP, conduits for information are guaranteed to be available as long as the web stays around. While many other transport protocols (such as telnet, ftp, and so on) are banned and blocked by IT departments, and viewed as potential security loopholes, HTTP stays wide open to enable the continual operations of web browsers. This ensures longevity for application protocols built on top of HTTP.
Building on top of HTTP,Simple Object Access Protocol (SOAP) provides the ability to: Access data and process services over the web Transfer data objects between clients and services over the web
Both are accomplished by passing XML messages between the client and the service. Formidable as it may seem, these two abilities are powerful enough to enable a wide variety of business-to-business and business-to-consumer transactions.
While SOAP on top of HTTP provides the mechanism for automated transactions and information flow over the web, interconnect code still needs to be written to take advantage of this ability. The difficulty in writing such code has been steadily declining over the past decade. Today, you are able to write web service clients in almost any programming language. By and large, however, most web services are written either in Java or C#.
Tools are available today to generate most of the glue code that is necessary to make web services work. The enabling technology in this area is the World Wide Web Consortium (W3C) standardization of Web Services Description Language (WSDL). See www.w3.org/TR/wsdl for information on the latest WSDL technical report.
A WSDL file fully describes the API available through a web service. With the assistance of anXML schema, the data types of return values and arguments can be fully specified. There is enough information in a WSDL, together with its associated schemas, to construct client code in any supporting languages for the web service. To make a web service available, then, all you need is to expose a copy of the service’s WSDL to your potential clients. Automated discovery of web services is specified asUniversal Description, Discovery, and Integration (UDDI). However, even without discovery capability, the WSDL information can be directly exposed via a specific URL. The example that we are coding in this article exposes the WSDL to the client via a specific URL.
In this example, we’ll be using a Java web services framework called XFire to expose Plain Old Java Objects (POJOs) as web service. Figure 1 shows the big picture.

Figure 1: A Java web services framework
In Figure 1, the XFire engine exposes our Java objects as a web service and generates a WSDL dynamically for us. The entire web service bundle is deployed as a web application on Tomcat 5.5.20 (any later version should also work). This web service exposes a method to retrieve information on orders placed through an ecommerce store. The Order object that is retrieved is a non-trivial Java object. In fact, the Order object contains references to other custom Java objects, including a collection of LineItems objects and a reference to a Customer object. Figure 2 shows the composition of an Order object.

Figure 2: The composition of an Order object
We’ll create client code to access this web service in both the Java environment and, using C#, the .NET environment. In both cases, most of the client code is created automatically by the IDE. In the case of Java, you will use Eclipse 3.2’s web service client generation support, based on Apache AXIS. In the case of .NET, you will use Visual Studio 2005’s support for web references via .NET 2.0.Using XFire
XFire is a leading open source web service creation framework that greatly simplifies the creation of non-trivial Java web services. You can find the latest downloads, and a lot of information on XFire, at http://xfire.codehaus.org. Technically, XFire can create ready-to-deploy WAR files of web services, directly from your POJOs, that can be deployed on any Servlet 2.4 compliant servlet container such as Tomcat 5 or Jetty. As well as servlets based on Tomcat, XFire supports other lightweight application container technologies, including Spring, PicoContainer, and Plexus. XFire can be easily embedded inside a Java application providing web service client or server functionality.XFire Bindings
Figure 3 illustrates how you can use XFire to create a web service; the blue components are what you need to supply. When you are using Java SE 5 and above, all you need is the annotated POJO code. If you are using JDK 1.4, which does not support annotations, you can use XFire’s support for attributes instead. (This article uses Java SE 6, see XFire documentation if you need to use JDK 1.4 compatible attributes.) Based on your annotations, XFire performs the Java to XML mapping. This mapping process is called data binding.

Figure 3: Using XFire to create a web service
During the binding process, the annotated Java data type that you define in your source code is transformed into XML schema defined types; the annotated methods of your Java objects become web service methods.
In Figure 3 binding for POJO is performed using Aegis, the binding layer of XFire. Aegis also supports other bindings including Castor, XMLBeans, as well as JAXB 1.1 (JSR-33) and JAXB 2.0 (JSR–222). You may wish to use Castor binding if you want direct mapping to relational database capabilities. XMLBeans provides expressive coverage when you are working from XML schemas and you want to preserve the XML schema during binding; while JAXB (Java Architecture for XML Binding) bindings are applicable if you working with standards that are part of Java EE.
Based on your annotated POJOs, the XFire engine can: stream (marshal) an instance of a Java object as XML to be transmitted through SOAP reconstruct (unmarsal) an instance of the Java object from an incoming XML stream support incoming method invocations on Java objects by transforming incoming XML messages generate the WSDL for use by client
Code first versus Schema first
This is called “Code first” development – since you write the code and XFire generates and manages the XML schema. The other choice is “Schema first” development. In this case you start from XML schema, and XFire can generate the code for the Java objects. Schema first development is ideal for creating web service clients using XFire. Figure 4 shows both Code first and Schema first development.

Figure 4: Code and Schema first development
The Aegis POJO binding supports only Code first development at this time; but you can use the JAXB 2.0 binding if you need to do Schema first development. The example in this article will illustrate Code First development with Aegis; see XFire documentation for details on using Schema first development using the JAXB 2.0 binding.Support for Multiple Transport Technologies
While the majority of XFire users will be using HTTP as their primary transport to support web service operations, XFire has decoupled its transport layer and can support alternative transports – see Table 1 for a partial tabulation.
Table 1: Alternative transports
Transport Description
JMS (Java Message Service) Store and forward message queue based transport
In-JVM Bypasses the networking IO and can provide high performance communications
XMPP (Extensible Message and Persistence Protocol) Formerly Jabber protocol – seewww.xmpp.org
Any JBI (Java Business Integration) transports Channel based integration with Apache ServiceMix (see my VSJ December 2005 articlePlug into JBI with ServiceMix, also seeincubator.apache.org/servicemix)
As an example, if you repurpose your web service software components for local single process deployment you may embed XFire in your application and then use the In-JVM transport to communicate between client and service components.A core of open source technologies
Like most mainstream open source projects, XFire makes use of some of the latest open source technology to accomplish its work. A partial list of the open source technologies used by XFire includes:Maven 2 for building of web servicesWoodstox for fast Stax based XML processingApache Commons Attributes to support POJO annotative attributes for JDK 1.4 backwards compatibilityXmlSchema to support dynamic clients
Let us turn our attention now to the example web service, and get some hands-on working experience with XFire.Working with XFire
Before you start, you will need to download and install the following: latestJava SE 5 or Java SE 6 installed and workingMaven 2 installed and working (2.0.5 or later); this is used to build the XFire web serviceTomcat server installed and working (5.5.20 or later); Tomcat is used to host the web serviceXFire (1.2.4 or later)
You will also need a relatively high speed Internet connection. Maven 2 downloads dependencies, when building the XFire web service, from centralized repositories on the Internet.
You will find the code for the example under the code directory of the distribution. The first thing to notice is the typical Maven 2 directory layout for the project.

Figure 5: Maven 2 directory layout
Figure 5 shows the directory layout – the pom.xml is the project object model descriptor file required by Maven 2 (similar function to Ant’s build.xml if you’re more familiar with Ant). The target directory is created by Maven 2 when you compile and create the web service WAR file for deployment.
You will find the source tree rooted under the main directory. The central object class of this web service is the uk.co.vsj.xfire.shop.Order class.Coding the POJOs
The data that is passed around the web service, order information, is defined as a set of Plain Old Java Object classes.
An Order class contains an order number of String type, an array of LineItem type, and a reference to a customer (of Customer type). You will rely on XFire to generate the proper XML data binding and to marshal objects of this complex type across the web service.
The code for Order is shown here with its constructor and getter/setter accessor methods. You may want to reference Figure 2 to see the composition of this class. Note that there is no customer annotation in this source file:package uk.co.vsj.xfire.shop;public class Order {String orderNumber;LineItem [] lineItems;Customer customer;// constructorspublic Order(String orderNumber,LineItem[] lineItems,Customer customer) {this.orderNumber = orderNumber;this.lineItems = lineItems;this.customer = customer;}// getters and setterspublic Customer getCustomer() {return customer;}public void setCustomer(Customer customer) {this.customer = customer;}public LineItem[] getLineItems() {return lineItems;}public void setLineItems(LineItem[] lineItems) {this.lineItems = lineItems;}public String getOrderNumber() {return orderNumber;}public void setOrderNumber(String orderNumber) {this.orderNumber = orderNumber;}}The Customer class contains name and phone information, both of String type, for the customer.
The code for Customer is shown in the following listing; note again that there is no special annotation:package uk.co.vsj.xfire.shop;public class Customer {private String custName;private String phone;public Customer(String custName,String phone) {this.custName = custName;this.phone = phone;}public String getCustName() {return custName;}public void setCustName(String custName) {this.custName = custName;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}}The last component of the Order class is the LineItem class. Each LineItem contains an inventory identification SKU, a description, and a price. The following listing shows the LineItem class:package uk.co.vsj.xfire.shop;public class LineItem {private int quantity;private String sku;private String description;private double price;public LineItem(int quantity,String sku,String description,double price) {this.quantity = quantity;this.sku = sku;this.description = description;this.price = price;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public int getQuantity() {return quantity;}public void setQuantity(int quantity) {this.quantity = quantity;}public String getSku() {return sku;}public void setSku(String sku) {this.sku = sku;}}Up to this point in time, all the data objects are POJO classes and contain no special annotations or XFire-specific code additions. This enables them to be reused in any other application context. It also enables them to be unit tested easily.Coding the Web Service
The only source code file where you will need XFire annotation is the class that implements the actual web service. If you define an interface for the web service, you will also need to annotate that interface. In this case, the OrderQueryService class implements the web service.
The code for OrderQueryService is shown in the following listing, with annotations in red:package uk.co.vsj.xfire.shop;import javax.jws.WebMethod;import javax.jws.WebParam;import javax.jws.WebResult;import javax.jws.WebService;@WebService(name = "OrderQuery",targetNamespace ="http://www.vsj.co.uk/2007/webservice/OrderQuery")public class OrderQueryService {private static StringTEST_ORDER_NUMBER = "ON111";@WebMethod(operationName ="retrieveOrder",action = "urn:RetrieveOrder")@WebResult(name = "orderRetrieved")public Order getOrder(@WebParam(name ="orderNumberParam")String ordno) {return findOrder(ordno);}private Order findOrder(String orderNumber) {Order order = null;if (orderNumber.equals(TEST_ORDER_NUMBER)) {// create oneLineItem [] lineItems =new LineItem[2];lineItems[0]= new LineItem(1,"TV12", "LCD TV", 299.99f);lineItems[1]= new LineItem(2,"LANC32","LAN Cable 30 feet",12.94f);Customer cust = new Customer("Joe LaRose", "232-2222");order = new Order(TEST_ORDER_NUMBER,lineItems, cust);}return order;}}The OrderQueryService above provides one single operation on the web service called retrieveOrder. This operation is mapped to the public getOrder() method of the class. The getOrder() method in turn delegates to a private findOrder() method to do the work. In this code, if the incoming order number is “ON111”, an Order instance is created and returned – simulating the actual retrieval of an existing order.JSR 181 annotations and WSDL generation
The OrderQueryService class itself is annotated with:@WebService(name = "OrderQuery",targetNamespace ="http://www.vsj.co.uk/2007/webservice/OrderQuery")In the WSDL produced by XFire, this annotation generates the web service port type of QrderQuery, within the target namespace of http://www.vsj.co.uk/2007/webservice/OrderQuery.
This target namespace will typically be used by client code generation tools to create the name of the client code package.
The getOrder() method is annotated with:@WebMethod(operationName ="retrieveOrder",action = "urn:RetrieveOrder")@WebResult(name = "orderRetrieved")In the WSDL, this annotation generates the web service operation retrieveOrder, together with the retrieveOrderRequest and retrieveOrderResponse definitions. The return value is specified via generated orderRetrieved elements in the WSDL.
The argument of the getOrder() method is annotated with:@WebParam(name = "orderNumberParam")In the WSDL, this annotation will generate the appropriate input parameter for the web service operation.
The final WSDL generated by XFire is shown in the following edited listing (the full listing can be found at www.vsj.co.uk). You can see how the annotations cause the generation of the corresponding web service elements.... other elements removedOther data types message definitionsetc. removed for clarityThis WSDL provides sufficient description for web service code generation tools to create client side access code for the OrderQueryService.Configuring the XFire engine
The only XFire specific configuration file you need is the main/META-INF/xfire/service.xml file. It contains:uk.co.vsj.xfire.shop.OrderQueryServicejsr181It tells the XFire engine that the class to process for the web service is uk.co.vsj.xfire.shop.OrderQueryService, and the processor (serviceFactory) to use is the JSR-181 annotation processor. Consult the XFire documentation for other service factories available (e.g. for JDK 1.4 backward compatibility).
Since the web service is deployed as a WAR file to Tomcat, the servlet must also be configured properly to execute the XFire engine. This is done via the standard deployment descriptor web.xml. web.xml is located in the webapps/WEB-INF directory.
The XFire engine for HTTP transport is executed via XFireConfigurableServlet, configured to be triggered by incoming URL /services/* (and /servlet/XFireServlet/*) URL patterns.Building and Deploying the XFire Web Service
Make sure you’re connected to the Internet on a relatively high-speed connection before you start to build. Maven 2 will download its own dependency the first time you run it, which may take a little while.
From within the code directory, where Maven 2’s configuration file pom.xml is located, issue the following command:mvn packageThis will start Maven 2, compile your code, download the required dependencies, and create a deployable WAR file for your web service. The xfire-shop-1.0.war file is located in your target directory. It is a ready-to-deploy web service WAR.
Next, you need to deploy the xfire-shop-1.0.war file to Tomcat 5.5. The easiest way to deploy this application to Tomcat is: Make sure Tomcat is not running Copy the xfire-shop-1.0.war file to the webapps directory of the Tomcat distribution Start the Tomcat server
To verify that everything is working okay, try to access this URL:http://localhost:8080/xfire-shop-1.0/services/OrderQueryService?WSDLThis URL causes the XFire engine to generate the OrderQueryServer WSDL. The content of the WSDL should be similar to the WSDL shown earlier. Figure 6 shows the WSDL displayed via the above URL in a browser.

Figure 6: The generated WSDL file
This URL to the WSDL can be used by development tools and IDEs to generate the client code.Creating a Java Web service client on Eclipse 3.2
Using the WSDL URL, you can rapidly create a service client using Eclipse 3.2. Eclipse 3.2’s web client code generation facility is based on Apache Axis. Before you start, make sure that you have downloaded and installed Eclipse 3.2.1 or later, and that your Eclipse distribution includes the Web Tools Platform (WTP). The release of Eclipse 3.2, code-named Callisto, includes the simultaneous release of many related subcomponents that are optionally bundled with the IDE. You can find all the Eclipse downloads at: www.eclipse.org. There are several all-in-one distribution downloads that will include WTP.
To generate a client for accessing OrderQueryService with Eclipse 3.2, take the following steps: Create a new Java Eclipse project With the project name highlighted in the Navigator, right click and select New -> Other…; this will start the New Wizard. In the New Wizard, select Web Services -> Web Service Client. In the Web Service Client wizard, enter the URL of the WSDL into the Service Definition field as:
http://localhost:8080/xfire-shop-1.0/services/OrderQueryService?WSDL Click Finish, and Eclipse 3.2 will generate all the required web service client code into a code package based on the OrderQueryService’s target namespace in the WSDL. You can examine the code in the generated uk.co.vsj.www._2007.webservice.OrderQuery package. You can also find the Java data types generated in uk.co.vsj.xfire.shop package – including Order, Customer, and LineItem objects.
Testing the Generated Java Web Client Code
To make use of this client access code, add a class called uk.co.vsj.xfire.client.WSClient. Add the following code into this class:package uk.co.vsj.xfire.client;import uk.co.vsj.www._2007.webservice.OrderQuery.OrderQuery;import uk.co.vsj.www._2007.webservice.OrderQuery.OrderQueryServiceLocator;import uk.co.vsj.xfire.shop.LineItem;import uk.co.vsj.xfire.shop.Order;public class WSClient {private static StringTEST_ORDER_ID = "ON111";public static void main(String[]args) throws Exception {OrderQueryServiceLocator loc =new OrderQueryServiceLocator();OrderQuery port = (OrderQuery)loc.getPort(OrderQuery.class);Order order =port.retrieveOrder(TEST_ORDER_ID);if (order != null ){System.out.println("ORDER NUMBER "+ TEST_ORDER_ID);System.out.println("");System.out.println("Name: "+ order.getCustomer().getCustName());System.out.println("Phone: " +order.getCustomer().getPhone());System.out.println("");String header =String.format("%-10s%-10s%-30s%-10s","Quantity", "SKU","Description", "Price");System.out.println(header);for ( LineItem li:order.getLineItems()) {String fmt =String.format("%-10d%-10s%-30s$%,8.2f",li.getQuantity(), li.getSku(),li.getDescription(), li.getPrice());System.out.println(fmt);}} else {System.out.println("Order not found");}}}You can find the code for WSClient.java in the Eclipse32 directory of the source distribution. This code uses the generated OrderQueryServiceLocator to obtain the web service’s port. It then invokes the retrieveOrder() operation (mapped to the getOrder() method by XFire) using “ON111” as an input parameter. The query result is then formatted and printed to System.out.
If you compile and run the WSClient code, it will connect to the OrderQueryService running under Tomcat, make the order query, and display the results as shown in Figure 7.

Figure 7: The result of the order query
Note that the Apache Axis support runtime may complain about missing javax.activation.DataHandler and javax.mail.internet.MimeMultipart. You don’t need these for the example, but you may downloadJavaMail andJAF from Sun. These binaries cannot be freely included due to licensing restrictions. You will need to add mail.jar and activation.jar from the downloads to your Eclipse build classpath.Creating a .NET Web Service Client with Visual Studio 2005
To generate a C# .NET client for QrderQueryService using Visual Studio 2005, follow these steps: Create a new C# Windows application, using the Console Application template. Highlight the project name in the Solution Explorer, right click and select Add Web Reference….; this will start the Add Web Reference wizard. Enter the WSDL URL into the URL field of the wizard. The wizard will parse the WSDL and show you the mapped C# interface to the QrderQuery service. The Visual Studio wizard will detect the OrderQueryService and the retrieveOrder operation. It will also parse and create the Order data type in C# (together with Customer and LineItem). Click the Add Reference button to add this web service reference to the project. You can now write code to access the web service through the web reference.
Testing the C# .NET Client
Now, change the generated Program.cs to use the web service:using System;using System.Text;using ConsoleApplication.localhost;namespace ConsoleApplication{class Program{static void Main(string[] args){OrderQueryService service =new OrderQueryService();Order order =service.retrieveOrder("ON111");Console.WriteLine("Order number: "+ order.orderNumber);Console.WriteLine("");Console.WriteLine("Name: " +order.customer.custName);Console.WriteLine("Phone: "+ order.customer.phone);Console.WriteLine("Items -");foreach (LineItem li inorder.lineItems){Console.WriteLine(li.quantity + " X " +li.description);}while (!Console.KeyAvailable){}}}}You can find the modified Program.cs file in the vs2005 directory of the source distribution. This C# code queries the web service for order number “ON111” and then prints out the details of the order, similar to the Eclipse example earlier. It also waits for a key to be pressed on the console before terminating – this allows you to see the output.
Build and run the application, you should see the Order information obtained by the C# .NET client, through the Tomcat hosted, XFire engine managed, POJO web service. The console window should look similar to Figure 8.

Figure 8: Testing the C# clientCurrent State of Web Services
With the ability to create interoperable web services marshalling complex data types and objects, the Web Service design community is busy solving the higher-level problems. These include server-level security (instead of always relying on SSL at the transport), efficient use of bandwidth when transmitting binary contents, transactions, robust messaging, and so on.
Many of these features are going into .NET 3.0, which includes the Windows Communications Framework (formerly codenamed Indigo). The good news this time around is that the traditional arch-rivals – Sun and Microsoft – have joined forces on Web Services technology (indeed, non-interoperable web services is about as useful as a proprietary communication protocol). Sun has also released reference implementation of WSI for Java – designed to interoperate with the WCF in the same areas.Conclusions
With the tools and frameworks support available today, creating sophisticated interoperable web services quickly is no longer a pipe dream. The XFire web service framework allows you to quickly turn POJO code into web services with a minimum of configuration and custom coding. Web services created using XFire can be easily consumed by Java or .NET clients.