Apache CXF Restful Web Service Example

24 December 2012 By Praveen Macherla 20,235 views 5 Comments
13 Flares Twitter 1 Facebook 6 Google+ 6 13 Flares ×

In my previous tutorial we saw how to create a simple RESTful web service using Jersey. In this RESTful web service example we will create the same webservice using Apache CXF as JAX-RS implementation.

CXF supports the Java API for RESTful Web Services: JAX-RS 2.0 (JSR-339) and JAX-RS 1.1 (JSR-311). We will be using JAX-RS 1.1.

The advantage of following JAX-RS specification is that there is minimal to no code change when there is a need to change the provider. So the RESTful webservice code which we used for Jersey in the previous tutorial can be used as it is. Only the service startup and client needs to be changed based on provider specific implementation.

Project Description

  • In this RESTful webservice example, we create a simple Java Calculator class with operations ‘add’ and ‘subtract’.
  • We expose these as restful web services which accepts HTTP GET Requests and sends the response as XML or Plain Text.
  • The restful web service is made available at http://localhost:9999/calcrest/calc/add/ and http://localhost:9999/calcrest/calc/sub/
  • We send the parameters in the URI itself. For example, to add 20 and 30, the URI is, http://localhost:9999/calcrest/calc/add/20/30
  • We then create a web service client which sends a HTTP GET Request to the mentioned URI and the response (result of the invoked web service) is displayed.

Environment Used

Creating and Publishing Restful Web Service

  1. Create a Java Project ‘CalcJAXRSCXF’.
  2. In the Project Build Path add the required jars from the downloaded Apache CXF file. For the list of jar files to add, see the Project Structure (at the end of the post).
  3. Create a package ‘com.theopentutorials.jaxrs.calc’
  4. Create a Java class ‘CalcREST’ and type the following code.
    package com.theopentutorials.jaxrs.calc;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    @Path("/calc")
    public class CalcREST {
    
    	@GET
    	@Path("/add/{a}/{b}")
    	@Produces(MediaType.TEXT_PLAIN)
    	public String addPlainText(@PathParam("a") double a, @PathParam("b") double b) {
    		return (a + b) + "";
    	}
    	
    	@GET
    	@Path("/add/{a}/{b}")
    	@Produces(MediaType.TEXT_XML)
    	public String add(@PathParam("a") double a, @PathParam("b") double b) {
    		return "<?xml version=\"1.0\"?>" + "<result>" +  (a + b) + "</result>";
    	}
    	
    	@GET
    	@Path("/sub/{a}/{b}")
    	@Produces(MediaType.TEXT_PLAIN)
    	public String subPlainText(@PathParam("a") double a, @PathParam("b") double b) {
    		return (a - b) + "";
    	}
    	
    	@GET
    	@Path("/sub/{a}/{b}")
    	@Produces(MediaType.TEXT_XML)
    	public String sub(@PathParam("a") double a, @PathParam("b") double b) {
    		return "<?xml version=\"1.0\"?>" + "<result>" +  (a - b) + "</result>";
    	}
    }
    
  5. @Path may be used on classes and such classes are referred to as root resource classes. @PATH(“calc”) sets the path to the base URI + /calc. The base URI consists of the host, port and any context. We will set this base URI on creating the server.
  6. @Path may also be used on methods of root resource classes. This enables common functionality for a number of resources to be grouped together and potentially reused. So for example @PATH(“/add”) on the method signature indicates the URI path for this method to be invoked is base URI + /calc/add.
  7. To retrieve the parameters from the URI and get those values as variables, we use URI Path Templates which are variables enclosed within curly braces “{“ and “}” and are replaced at runtime to respond to a request based on the substituted URI. To obtain the value of the variable the @PathParam may be used on method parameter of a request method.
    • For example, for the relative URI /calc/add/20/30 with @Path(“/add/{a}/{b}”) and method signature String add(@PathParam(“a”) double a, @PathParam(“b”), the values 20 and 30 will be assigned to the variables a and b respectively.
  8. @GET annotation is used to indicate that the annotated method will be invoked in response to a HTTP GET request.
  9. @Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client. In our example, our web service will produce representations identified by the MIME media type “text/plain” and “text/xml” which are specified using the static fields TEXT_PLAIN and TEXT_XML of MediaType class.
  10. Now we need to start a HTTP server to publish our web service and accept requests.
  11. Create a class “CalcRESTStartUp” and type the following code.
    package com.theopentutorials.jaxrs.calc;
    
    import org.apache.cxf.endpoint.Server;
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
    
    public class CalcRESTStartUp {
    
    	public static void main(String[] args) {
    		 JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
    	        sf.setResourceClasses(CalcREST.class);
    	        sf.setResourceProvider(CalcREST.class, 
    	            new SingletonResourceProvider(new CalcREST()));
    	        sf.setAddress("http://localhost:9999/calcrest/");
    	        Server server = sf.create();
    	        
    	        // destroy the server
    	        // uncomment when you want to close/destroy it
    	        // server.destroy();
    	        
    	}
    }
    
  12. We use the CXF’s “JAXRSServerFactoryBean” bean to help easily create server endpoints for JAX-RS at the mentioned BASE URI. Make sure that port number 9999 is not used by any other application. If so, use any other unused port.
  13. Run this class “CalcRESTStartup” as Java Application.
  14. Our class “CalcREST” should be identified as root resource class and should be deployed.
  15. To test it, open a browser and type the following URL

    http://localhost:9999/calcrest/calc?_wadl

    A Web Application Description Language (WADL) should be displayed.

  16. To test the service we can type the following URLs in the browser
    http://localhost:9999/calcrest/calc/add/20/30/

    and

    http://localhost:9999/calcrest/calc/sub/20/30/

  17. The output on the browser will be in XML format even though we have our service produce plain text. This is because of the accept type HTTP header sent by the browser. By default the browser sends the text/html accept type and other xhtml and xml types. Since our service does not accept html and seeing that the browser accepts XML, it produces the output in XML.

Creating a Restful Web Service Client

Let us access this web service programmatically by creating a client.

  1. Create a new package “com.theopentutorials.jaxrs.calc.client”.
  2. Create a new class “CalcRESTClient” in that package and type the following code.
    package com.theopentutorials.jaxrs.calc.client;
    
    import org.apache.cxf.jaxrs.client.WebClient;
    
    public class CalcRESTClient {
    	static final String REST_URI = "http://localhost:9999/calcrest/";
    	static final String ADD_PATH = "calc/add";
    	static final String SUB_PATH = "calc/sub";
    	static final String MUL_PATH = "calc/mul";
    	static final String DIV_PATH = "calc/div";
    	
    	public static void main(String[] args) {
    		int a = 122;
    		int b = 34;
    		String s = "";
    		
    		WebClient plainAddClient = WebClient.create(REST_URI);
    		plainAddClient.path(ADD_PATH).path(a + "/" + b).accept("text/plain");
    		s = plainAddClient.get(String.class);
    		System.out.println(s);
    		
    		WebClient xmlAddClient = WebClient.create(REST_URI);
    		xmlAddClient.path(ADD_PATH).path(a + "/" + b).accept("text/xml");
    		s = xmlAddClient.get(String.class);
    		System.out.println(s);
    		
    		WebClient plainSubClient = WebClient.create(REST_URI);
    		plainSubClient.path(SUB_PATH).path(a + "/" + b).accept("text/plain");
    		s = plainSubClient.get(String.class);
    		System.out.println(s);
    		
    		WebClient xmlSubClient = WebClient.create(REST_URI);
    		xmlSubClient.path(SUB_PATH).path(a + "/" + b).accept("text/xml");
    		s = xmlSubClient.get(String.class);
    		System.out.println(s);
    	}
    }
    
  3. In the client, we create a WebClient (CXF WebClient API) object based on the provided URI.
  4. We specify the “add” and “sub” resource paths and include parameters “a” and “b” to it.
  5. We also need to specify the accept type of the client request and HTTP method as GET by using the methods “accept” and “get”. The “get” method parameter specifies the format (String type) to convert the output.
  6. Add required jars to the classpath and run this client application. (See the Project Structure Referenced Libraries for the list of required jars.)

Output

156.0
<?xml version=”1.0″?><result>156.0</result>
88.0
<?xml version=”1.0″?><result>88.0</result>

Project Structure

Tags: , , , , , , , , ,

  • Azahrudhin

    Article is pretty much simple , but by looking at the caption “Apache CXF Restful Web Service Example” it would illustrate how a simple web service can be deployed using spring , cxf. In below example how to form post response would be help ful, Thank you.

    //Service

    package example.soap.jaxws.soapfault;

    import javax.jws.WebMethod;
    import javax.jws.WebService;

    @WebService
    public interface SOAPFaultService {

    @WebMethod
    public String sayHello(String name) throws Exception;
    }

    //Service Implementation

    import javax.jws.WebService;
    import javax.naming.spi.ObjectFactory;

    @WebService(endpointInterface = “example.soap.jaxws.soapfault.SOAPFaultService”,
    serviceName = “SOAPFaultService”)
    public class SOAPFaultServiceImpl implements SOAPFaultService {

    public String sayHello(String name) throws Exception {
    return “Hello “+name;
    }
    }

    //Server
    package example.soap.jaxws.soapfault;

    import javax.xml.ws.Endpoint;

    public class Server {

    protected Server() throws Exception {
    // START SNIPPET: publish
    System.out.println(“Starting Server”);
    SOAPFaultService is = new SOAPFaultServiceImpl();
    String address = “http://localhost:9000/SOAPFaultService”;
    Endpoint.publish(address, is);
    // END SNIPPET: publish
    }

    public static void main(String args[]) throws Exception {
    new Server();
    System.out.println(“Server ready…”);

    Thread.sleep(5 * 60 * 1000);
    System.out.println(“Server exiting”);
    System.exit(0);
    }
    }

    //Client for Testing
    package example.soap.jaxws.soapfault;

    import java.net.URL;

    import javax.xml.namespace.QName;
    import javax.xml.ws.Service;

    public class ClientTest {
    private static final QName SERVICE_NAME = new QName(
    “http://soapfault.jaxws.soap.example/”, “SOAPFaultService”);
    private static final QName PORT_NAME = new QName(
    “http://soapfault.jaxws.soap.example/”, “SOAPFaultServicePort”);

    private static final String WSDLURL = “http://localhost:9000/SOAPFaultService?wsdl”;

    public static void main(String[] args) {
    testSayHello();
    }

    public static void testSayHello()
    {
    try
    {
    Service service;
    service = Service.create(new URL(WSDLURL), SERVICE_NAME);
    SOAPFaultService is = service.getPort(SOAPFaultService.class);

    String result = is.sayHello(“Peter”);
    System.out.println(“Response From WS : “+result);

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

    //cxf.xml

    //web.xml

    SOAPFaultCoderWS

    contextConfigLocation
    classpath:config/cxf.xml

    org.springframework.web.context.ContextLoaderListener

    IWSCXFServlet

    org.apache.cxf.transport.servlet.CXFServlet

    IWSCXFServlet
    /services/*

  • Azahrudhin
  • Anushree

    Hi, When typed http://localhost:9999/calcrest/calc?_wadl, I am getting “Unable to find page” error and while running the application, getting the error as:
    org.apache.cxf.interceptor.Fault: Could not send Message.
    at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:64)

    is there any other setup needed apart from what is mentioned here?

    TIA!

    • http://theopentutorials.com Praveen Macherla

      Check any errors are displayed while running the server part. Your server probably did not start due to some errors. Also check the url path is the same as given in code.

  • abi

    This is the one of the best tute . . . . thanks a ton!!