XFire: The easy and simple way to develop Web services

Expose your POJO methods as Web services

Web services enable us to build distributed systems where application components on a network can be accessed in a platform-neutral, language-agnostic, and implementation-independent way. It doesn't matter how an application is developed, what language it uses, or what OS platform it runs on. If it is available as a Web service and designed addressing interoperability issues, your application, developed in any language or platform, will be able to utilize its services. That's the main concept of Web services.

To achieve the platform-neutral and implementation-independent accessibility of Web services, the software industry has agreed on a few technologies as standards. Some of them are:

  • XML: The default data format used across all layers of Web services environment.
  • SOAP: The default protocol for packaging and exchanging messages. When first introduced, it was an acronym for Simple Object Access Protocol. But now SOAP is considered a proper noun, a name in itself, as most people now realize it is a misnomer: SOAP is not really for accessing objects. Plus, it is not simple any more.
  • WSDL (Web Services Description Language): The language that describes Web services. Although based on XML and understandable by humans, WSDL is mainly for machine consumption, to be read and understood by client programs.

The following high-level diagram, based on the document "Web Services Architecture" published by the World Wide Web Consortium, shows how all these technologies are engaged in a working environment:

The process showing how core technologies engage in Web services. Click on thumbnail to view full-size image.

Here, Provider is the application component that would provide the service, and Requester is the client program that would consume it. Many other technologies may participate in the interactions, but this figure shows the core components that must be in a Web services environment.

XFire is a free and open source SOAP framework that not only enables you to implement such an environment with great ease and simplicity, but also provides you many advanced features identified in Web services specifications, but not yet available in most commercial or open source tools. You read the words correctly: great ease and simplicity. In this article, you'll see how simple it is to build a Web service with XFire.

If your Web application has a Java class and you want its methods to be exposed as Web services, you may not have to write a single line of additional Java code when you use XFire. Just work on the deployment descriptors and you'll get a Web service. Yes, it's that easy. Let's look at an example.

A simple Java class

Our example is a banking application hosted in Apache Tomcat 5.5.7 and running under J2SE 1.4.2_07. I assume you already know how to write Web applications in Java and deploy onto Apache Tomcat servers. Our Web application is simple; it does just one thing—transfer funds from one account to another. A plain-old Java class BankingService containing a method named transferFunds() does the job for us. It needs four input parameters:

  1. String fromAccount
  2. String toAccount
  3. double amount
  4. String currency

Here is the code:

 

package com.mybank.xfire.example;

import java.text.NumberFormat; import java.text.DecimalFormat;

/** XFire WebServices sample implementation class. */ public class BankingService implements IBankingService { //Default constructor. public BankingService(){ } /** Transfers fund from one account to another. */ public String transferFunds( String fromAccount, String toAccount, double amount, String currency){ String statusMessage = ""; //Call business objects and other components to get the job done. //Then create a status message and return. try { NumberFormat formatter = new DecimalFormat("###,###,###,###.00"); statusMessage = "COMPLETED: " + currency + " " + formatter.format(amount)+ " was successfully transferred from A/C# " + fromAccount + " to A/C# " + toAccount; } catch (Exception e){ statusMessage = "BankingService.transferFunds(): EXCEPTION: " + e.toString(); } return statusMessage; } }

Do you see anything exceptional here? Probably not, except the default constructor, which is public. It is required. Otherwise, XFire would not be able to instantiate the class.

Since designing with interfaces is good practice, our Java class also implements an interface named IBankingService. The code is simple:

 

package com.mybank.xfire.example;

public interface IBankingService {

public String transferFunds( String fromAccount, String toAccount, double amount, String currency); }

In actual implementation, such a method may include all kinds of complex calls, queries, and processing operations. But our example code is bare minimum so that we can focus on our main objective: exposing the method as a Web service.

You can see that BankingService is a plain Java class, with no code whatsoever to tell whether it should be used in Web services. And that's fine. We don't need to add anything here. All our work will be done in the deployment descriptors.

Deployment descriptors of the Web application

In Java, Web applications are usually configured using at least one deployment descriptor, named web.xml. XFire itself is a servlet-based application. Hence, we need to add necessary references to this file. Then we have to configure the Web service we are creating. We will use a new file named services.xml to do that.

web.xml

First, let's work on web.xml. We need to add the following XFire servlet-related entries:

 

<servlet> <servlet-name>XFireServlet</servlet-name> <display-name>XFire Servlet</display-name> <servlet-class>org.codehaus.xfire.transport.http.XfireConfigurableServlet </servlet-class> </servlet>

<servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/servlet/XFireServlet/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>

services.xml

Now we have to say what our Web services consist of. This is done in a file named services.xml, which is placed under the META-INF/xfire directory. This whole directory is placed under the WEB-INF/classes folder, which is in the standard classpath of Web applications. Here are the basic configuration entries in services.xml:

 <beans xmlns="http://xfire.codehaus.org/config/1.0">
  
  <service>
    <name>Banking</name>
    <namespace>mybank</namespace>
    <serviceClass>com.mybank.xfire.example.IBankingService</serviceClass>
    <implementationClass>com.mybank.xfire.example.BankingService</implementationClass>
  </service>  
  
</beans>

Let's see what we have here. The definition of our Web service is contained inside a <service> element, which contains a few child elements. The first child element is <name>, which can be any valid XML name you provide. This will be used by client programs and other components to locate your service. For example, after the service is up, you'll use this name on a browser to see the WSDL.

The next child element is <namespace>. Any valid XML name is fine. <namespace> will be used to uniquely identify various parameters of your service.

The <serviceClass> element contains the Java class name that specifies the method signature. In our example, it is the interface IBankingService. If your Java class does not implement any interface, you'll put that class name here. You may have several methods in your Java class or interface. Just one entry is needed to expose them all as Web services.

The <implementationClass> holds the Java class name that has the method implementations. This is an optional element. If the previous element <serviceClass> contained an interface, the corresponding implementation class must be named here.

That's it. Configuration of our Web service is complete.

XFire and other libraries

Now we come to the last step, which is to get all necessary library files. How do we get them? Go to the XFire Website, download xfire-distribution-1.0.zip, and unzip it in a local folder. Copy the following jar files from the distribution and its lib directory into the WEB-INF\lib:

  • activation-1.0.2.jar
  • commons-codec-1.3.jar
  • commons-httpclient-3.0.jar
  • commons-logging-1.0.4.jar
  • jaxen-1.1-beta-8.jar
  • jdom-1.0.jar
  • log4j-1.2.x.jar
  • mail-1.3.3_01.jar
  • spring-1.2.x.jar
  • stax-api-1.0.jar
  • wsdl4j-1.5.2.jar
  • wstx-asl-2.9.jar
  • xbean-2.1.0.jar
  • xbean-spring-2.2.jar
  • xfire-all-1.0.jar
  • XmlSchema-1.0.jar

We are done. Let's deploy and start the application. To deploy the example application, just copy websvc.war into the webapps directory in your Apache Tomcat environment and wait a few seconds. It should start automatically. The application's complete source code is also contained in this war file. Our program is now ready as a Web service.

How do we know the Web service is working?

To see if the Web service is working, we'll have to test. First, we test to see if the WSDL is available. Let's type the URL on a browser. Which URL? Since our application's war file is websvc.war and the service name given in services.xml is Banking, the WSDL URL would be: http://localhost:8080/websvc/services/Banking?wsdl.

Please note: The first part of your URL, i.e., http://localhost:8080, may differ depending on your application server's setup. Regardless, as you type the URL, you'll see an XML document whose root element is <wsdl:definitions>. This document is called the service's WSDL. If you see it, this is the first verification that your application is available as a Web service.

But this test is not enough. Situations may arise where you could see the WSDL, yet the service may not be accessible from clients. So to verify whether the service is up, we must go for a real test using a client program that makes an actual call to the service.

Developing a client

You can create clients with any SOAP tool, such as .Net or Apache Axis, in a variety of ways: using stubs generated from WSDL, using dynamic proxy, etc. In our example, we use a dynamic proxy in a simple servlet named WsClient.java. To keep coding efforts minimum, all screen building elements are put inside the doGet() method. The actual call to the Web service is made in the callWebService() method, which is pretty simple. It looks like this:

 

/* Call the Web service * */ public String callWebService( String fromAccount, String toAccount, double amount, String currency) throws MalformedURLException, Exception { //Create a metadata of the service Service serviceModel = new ObjectServiceFactory().create(IBankingService.class); log.debug("callSoapServiceLocal(): got service model." ); //Create a proxy for the deployed service XFire xfire = XFireFactory.newInstance().getXFire(); XFireProxyFactory factory = new XFireProxyFactory(xfire); String serviceUrl = "http://localhost:8080/websvc/services/Banking"; IBankingService client = null; try { client = (IBankingService) factory.create(serviceModel, serviceUrl); } catch (MalformedURLException e) { log.error("WsClient.callWebService(): EXCEPTION: " + e.toString()); } //Invoke the service String serviceResponse = ""; try { serviceResponse = client.transferFunds(fromAccount, toAccount, amount, currency); } catch (Exception e){ log.error("WsClient.callWebService(): EXCEPTION: " + e.toString()); serviceResponse = e.toString(); } log.debug("WsClient.callWebService(): status=" + serviceResponse);

//Return the response return serviceResponse; }

What's going on in this code? Let me explain: First, we create a service model, which contains the service specifications—in other words, the service's metadata. We create this model from the interface IBankingService.class using XFire's ObjectServiceFactory.

The next step is to get a proxy factory object for XFire, which involves routine code, and is pretty simple and straightforward. There is nothing application-specific in this step. From this proxyFactory, using the service model and the service endpoint URL (used to get the WSDL), we get a local proxy for the service.

That's it. This proxy is the actual client. We can now invoke its transferFunds() method to get the Web service we want.

Once the example application is deployed and started, try the servlet URL: http://localhost:8080/websvc/ws.

The servlet uses default parameters to call the Web service and displays the response received. The last two lines of the page should read:

 Response Received
COMPLETED: CDN$ 500.00 was successfully transferred from A/C# 11111-01234 to A/C# 99999-05678

Now you can be sure that the Web service is up and running.

1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more