Kurs:Neueste Internet- und WWW-Technologien/ApacheCXF

Aus Wikiversity

Webservices with ApacheCXF[Bearbeiten]

ApacheCXF[1] is an open-source Java framework for the development of Web services, published under the Apache License 2.0. The name is an abbreviation for CeltiXFire, a combination of the names of the frameworks CXF combines[2], IONA Celtix[3] and Codehaus XFire[4]. Its goal is to combine different programming models and standards and to enable easy integration into the usual Java development process. ApacheCXF runs with JDK 1.5 and 1.6, its compliance with 1.7 has to be determined yet.

Supported Technology[Bearbeiten]

CXF supports a variety of standards like

  • SOAP
  • JSON
  • CORBA
  • WSDL

and programming models like:

  • JAX-WS
  • JAX-RS
  • code-first
  • contract-first

To easily fit into the development of Java web applications CXF provides support for Spring XML configuration, and Maven plugins for build process management.

Code First Approach[Bearbeiten]

To create a code-first web service you need to provide an annotated java interface (the Service Endpoint Interface, SEI) and its implementation, wich contains the service code. Upon publishing, CXF uses your annotations to create a WSDL-file, which is a common human and machine readable descriptor of your web service.

To annotate your service you can use JAX-WS annotations from the javax.jws.* package. You can annotate methods, parameters and return-values as well as the interface itself. With more provided annotations the generated WSDL gets preciser and thus clients for your service can be implemented easier. The annotations you need at least are @WebService and @WebMethod.

You can find the annotated interface of an example web service below. @WebService denotes your SEI, @WebMethod can be used to annotate the publicly available methods of your service and with the @WebParam annotation you can define meaningful names for your parameters since CXF ist not able to extract the parameter names from the method declaration.

package de.hpi.cxftut.service;

import javax.jws.*;		//contains the Annotations

@WebService
public interface AdderService {

    @WebMethod
    public abstract int add(@WebParam(name="a")int a, @WebParam(name="b")int b);
}

After you have defined the SEI you need to build a java class implementing this interface and providing the functionality of your service. You can find the so called service bean of our example service below. Please note that it needs to provide an @WebService annotation as well, but none of the other annotations.

package de.hpi.cxftut.service;

import javax.jws.WebService;


@WebService		//this annotation is need here as well
public class AdderServiceImpl implements AdderService {

	@Override
	public int add(int a, int b) {
		System.out.printf("Oh my, I have to add %d and %d! \n", a, b);
		return a+b;
	}
}

At this point you have completed the code first implementation of a CXF Web Service. To see how to test this implementation read section testing and to find out how to publish this service read section deployment.

Development and Testing[Bearbeiten]

To aid the development of services CXF provides factories for the creation of service endpoints and client proxies. To test whether your implementation works you need to implement a server and a client class using this factories.
A test server has to define three things: the SEI, the service bean and the address the service should be published at. The example server implementation can be found below. The CXF class JaxWsServerFactoryBean is responsible for creating an endpoint and publishing it. By executing the main method of AdderStandaloneServer your service starts to listen for method invocations. You can check this by calling http://localhost:8080/adder?wsdl in your browser which should provide the complete generated WSDL-File for your service.

package de.hpi.cxftut.service;

import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

public class AdderStandaloneServer {

	public AdderStandaloneServer(String serviceAddress) {
		JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
		
		//register and publish WebService interface
		factory.setServiceClass(AdderService.class);
		factory.setAddress(serviceAddress);
		factory.setServiceBean(new AdderServiceImpl());
		
		//create WebService instance
		factory.create();	
	}
	
	public static void main(String[] args) {
		new AdderStandaloneServer("http://localhost:8080/adder");
		System.out.println("Server ready.");       //for logging purpose
	}

}

The example of a test client which can connect to this server can be found below. As you can see the CXF class JaxWsProxyFactoryBean has to be configured with the SEI of the service you want to use as well as the address it is published at. It can be used to generate a proxy. This proxy supports the interface of the service you want to call and forwards method calls to the service bean managed by the server, which has to be up an running.

package de.hpi.cxftut.client;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import de.hpi.cxftut.service.AdderService;

public class TestClient {

	private AdderService adder;
	
	public TestClient(String serviceAddress) {
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setServiceClass(AdderService.class);
	        factory.setAddress(serviceAddress);
		this.adder = (AdderService) factory.create();	
	}

	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		TestClient client = new TestClient("http://localhost:8080/adder");
		int a = 15;
		int b = 17;
		
		System.out.printf("Service said: %d + %d = %d \n", a,b, client.adder.add(a, b));
	}
}

With this setup you can see whether the implementation of your service works and is sufficiently annotated.

Deployment[Bearbeiten]

To publish your service it should be deployed in a web container. To do so your project should be a web project, which means that it has to obey a specific directory structure:

.
├── src
├── WebContent

├── META-INF
├── WEB-INF
├── classes
├── lib


Class-files should be deployed to WebConten/WEB-INF/classes and dependencies should be placed in WebConten/WEB-INF/lib. Further more a web.xml file has to be located in WebConten/WEB-INF/. It is responsible for defining the behavior of the servlet containing your service. More precisely the servlet and the listener classes as well as the URL-patterns mapping to your service. An example web.xml file can be found below. It links to a services.xml which uses spring-xml to define the service endpoint configuration and adequately uses a spring listener class.

web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
  <display-name> Adder </display-name>
  <context-param> 
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:de/hpi/cxftut/service/services.xml
	</param-value>
 </context-param>

  <listener>
     <listener-class>
        org.springframework.web.context.ContextLoaderListener
     </listener-class>
  </listener>


  <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <display-name>CXF Servlet</display-name>
    <servlet-class>
       org.apache.cxf.transport.servlet.CXFServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup> 
  </servlet>

  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
</web-app>

services.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xmlns:jaxrs="http://cxf.apache.org/jaxrs"
      xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://cxf.apache.org/jaxws
         http://cxf.apache.org/schemas/jaxws.xsd
         http://cxf.apache.org/jaxrs
         http://cxf.apache.org/schemas/jaxrs.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml"/>
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>

  <jaxws:endpoint id="adderService"
      implementor="de.hpi.cxftut.service.AdderServiceImpl"
      address="/adderService"/>
</beans>

To be able to use the Spring XML configuration you have to define dependencies to all spring libraries (Spring-*.jar) which get shipped with CXF. Now you can create a web archive for example using maven or eclipse and deploy it to your favorite web container. The example project has been tested only with Tomcat 7.

Client Generation[Bearbeiten]

The test client we have created so far can be used to develop the service. However it has to import code from the service and thus can not be used by a random user because he would first need to obtain our code. To address this matter the wsimport tool can be used to create completely autonomous clients from the WSDL file of your published service. Wsimport gets shipped with the JDK and should be called with with the wsdl-url. It supports the following flags:

  • -keep generates the source files
  • -d directory name for binary files
  • -s directory name for source files
  • -p package name for generated files
  • -b provides optional xml file with more configurations

An example usage would be:

wsimport -keep -d bin -s src -p de.hpi.cxftutclient http://localhost:8080/adder?wsdl


The generated files contain no client, which has to be implemented by hand. It has to create a port to the service and can then use it to invoke the service. An example client can be found below.

package de.hpi.cxftutclient;

import java.rmi.RemoteException;
import java.util.concurrent.ExecutionException;

public class AdderClient {
	
	public static void main(String[] args) throws RemoteException, InterruptedException, ExecutionException{
	    // get connection to service, both classes were generated by wsimport
	    AdderServiceService service = new AdderServiceService();
	    AdderService adder = service.getAdderServicePort();
	    
		int a = 15;
		int b = 17;
		System.out.printf("%d + %d = %d", a,b, adder.add(a, b));    
	}
}

Full Example[Bearbeiten]

The complete source code for the example web service of this tutorial can be found on github.

Conclusion[Bearbeiten]

My personal experience with CXF leads to the following conclusion: The framework provides a clear API and good abstractions. No knowledge of the underlying protocols or technologies is needed to implement web services. However the official documentation is quite messy and sometimes provides incomplete or unexplained code examples. Due to the big amount of supported technologies and models there are many different ways to configure the service. Errors tend to get introduced during this configuration and are usually hard to locate.
As to the differences to other frameworks the developers of CXF them self note that they most SOAP frameworks vary mostly in attitude and community and not in functionality [5].

References[Bearbeiten]

  1. ApacheCXF, Homepage
  2. Announcement of Codehaus XFire on the merge of Celtix and XFire
  3. IONA Celtix, Homepage
  4. CodehausXfire, Homepage
  5. Interview with Dan Diephouse and Paul Brown