CXF example : SOAP(JAX-WS), REST(JAX-RS), Spring


Goal of this example

Which one is better SOAP or REST? The answer is neither. REST is newer, fancier therefore many consider it better. Both solution has pros and cons therefore the nature of the project should drive the decision, not the trends. Luckily in Java it is fairly easy to implement both (even simultaneously) This example shows how to do it using Java Standards like JAX-WS (SOAP) and JAX-RS (REST) annotations and Apache CXF as the web service engine.

Technology Used

  • CXF 2.5.2 SOAP(JAX-WS), REST(JAX-RS) client and enpoint
  • Spring, Spring MVC 3.1.1
  • Spring Security 3.10
  • JSR 303 Bean validation

Structure of the example project

In this example the Spring MVC controller that handles the requests, has an instance of the SOAP and REST client. The example contains both the client and endpoint, in a real application these two would be deployed to a separate application server. Both the client and the endpoint are written in java and are regular POJO without any special markup. To keep them in sync, they implement the same interface. The interface contains the methods that will be published as a web service and the web service related annotations.


For the purpose of this example the same methods are published as REST and SOAP services. In a real example this is less likely to happen.

Implementing SOAP

All that is needed is to add the @WebService annotation to the Class/Interface definition and the @WebMethod annotation to the methods to be published.


package org.exampledriven.cxfexample;

@Path("/location/")
@WebService
public interface LocationService {

	@WebMethod
	@GET
	@Path("{location}")
	@Descriptions({
		@Description(value = "returns a location data ", target = DocTarget.METHOD),
		@Description(value = "the location data", target = DocTarget.RETURN)
	})
    public LocationData readLocation(@Description(value = "the string representation of the location") @PathParam("location") @NotNull @Size(max=10, min=5) String location) throws LocationNotFoundException;

	@WebMethod
	@GET
	@Path("*")
	@Descriptions({
		@Description(value = "returns all locations", target = DocTarget.METHOD),
		@Description(value = "the location data", target = DocTarget.RETURN)
	})
    public Collection readAllLocations();

	@WebMethod
	@POST
	@Descriptions({
		@Description(value = "stores a new location data", target = DocTarget.METHOD),
		@Description(value = "the newly created location data", target = DocTarget.RETURN)
	})
	public LocationData createLocation(@Valid LocationData locationData) throws DuplicateLocationException;

	@WebMethod
	@PUT
	@Descriptions({
		@Description(value = "updates or creates a new location data", target = DocTarget.METHOD),
		@Description(value = "the newly created location data", target = DocTarget.RETURN)
	})
	public LocationData updateorCreateLocation(@Valid LocationData locationData);

Additionally in the cxf.xml the SOAP service needs to be enabled

	<!-- SOAP SERVER -->
	<jaxws:endpoint id="location" implementor="#locationService" address="/soap" />

The Java client can be created like this


			JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
			factory.setServiceClass(LocationService.class);
			factory.setAddress(applicationURI + "/cxf/soap/");
			factory.setUsername("restuser");
			factory.setPassword("restuser");
			locationService = (LocationService) factory.create();

At this point we have everything set up : Endpoint, Client, WSDL, Exception handling, Complex type handling

Implementing REST

In REST the endpoint needs to have a Path, that is defined by the @Path(“/location/”) annotation. Additionally to that each method needs to define its URL pattern, and method (PUT, POST, GET, etc). There is no hard rule how to do it, but better to follow the convention. A great summary can be found here :

http://stackoverflow.com/a/630475/157591

In rest the URI contains information therefore some parts of the URI needs to be mapped to method parameters. An example can be seen at the readLocation() method. Below the REST (JAX-RS) annotations.

package org.exampledriven.cxfexample;
@Path("/location/")
@WebService
public interface LocationService {

 @WebMethod
 @GET
 @Path("{location}")
 @Descriptions({
 @Description(value = "returns a location data ", target = DocTarget.METHOD),
 @Description(value = "the location data", target = DocTarget.RETURN)
 })
 public LocationData readLocation(@Description(value = "the string representation of the location") @PathParam("location") @NotNull @Size(max=10, min=5) String location) throws LocationNotFoundException;

 @WebMethod
 @GET
 @Path("*")
 @Descriptions({
 @Description(value = "returns all locations", target = DocTarget.METHOD),
 @Description(value = "the location data", target = DocTarget.RETURN)
 })
 public Collection readAllLocations();

 @WebMethod
 @POST
 @Descriptions({
 @Description(value = "stores a new location data", target = DocTarget.METHOD),
 @Description(value = "the newly created location data", target = DocTarget.RETURN)
 })
 public LocationData createLocation(@Valid LocationData locationData) throws DuplicateLocationException;

 @WebMethod
 @PUT
 @Descriptions({
 @Description(value = "updates or creates a new location data", target = DocTarget.METHOD),
 @Description(value = "the newly created location data", target = DocTarget.RETURN)
 })
 public LocationData updateorCreateLocation(@Valid LocationData locationData);

Unlike Java in REST there is no concept of exception. REST is based on HTTP protocol therefore the most similar thing is HTTP error codes. The solution is that Java exceptions should be mapped to error codes on the server side, then error codes to be mapped again to Exception and Exceptions has to be thrown again on the client side.

This is done in two steps :

  1. All exceptions inherit from LocationBaseException that provides a constructor to define the HTTP error code
  2. The java client registers a provider called LocationResponseExceptionMapper that maps the error codes to exceptions

The rest client looks like this (the registering of the provider is highlighted)

			List&lt;Object&gt; providers = new LinkedList&lt;Object&gt;();
			providers.add(new LocationResponseExceptionMapper());
			locationService = JAXRSClientFactory.create(applicationURI + "/cxf/rest/", LocationService.class, providers, true);
			ClientConfiguration cfgProxy = WebClient.getConfig(locationService);
			cfgProxy.getHttpConduit().getAuthorization().setPassword("restuser");
			cfgProxy.getHttpConduit().getAuthorization().setUserName("restuser");

And the relevant XML looks like this :


	<!-- REST SERVER -->

    <jaxrs:server id="restContainer" address="/rest">
	<jaxrs:providers>
		<ref bean="jaxbProvider" />
		<ref bean="jsonProvider" />
		<ref bean="wadlProvider" />
	</jaxrs:providers>
	<jaxrs:serviceBeans>
		<ref bean="locationService"/>
	</jaxrs:serviceBeans>
	<jaxrs:extensionMappings>
		<entry key="xml" value="application/xml" />
		<entry key="json" value="application/json" />
	</jaxrs:extensionMappings>
	</jaxrs:server>

Summary

The above example shows that REST requires more annotation, configuration and manual handling of exceptions. If the client is Java, .Net or similar languages, the SOAP might be still a better option. If the client is JavaScript, or a Browser then REST is a better choice, but it is not even necessary to chose only one, both can be implemented easily.

12 comments

  1. I actually speculate exactly why you named this specific blog post, “CXF
    example : SOAP(JAX-WS), REST(JAX-RS), Spring | Example
    Driven (Self) Development”. Anyway I really appreciated the post!
    Thanks for the post-Agueda

  2. Pingback: Apache Thrift Java example | Example Driven (Self) Development

  3. ak

    Good article, But I am not able to run the downloaded application. I am using Tomcat 7.0. I have given url as: http://localhost:8080/cxfexample/. It gives 404.

  4. Peter you have not added Wiki or Issues on github so writing any issue and writing feedback for you is a little difficult.

    • Peter Szanto

      Hello, I double checked and both the issues and wiki is available. If you dont see could you please send me the URL you are referring?

  5. Hi All
    I think may of us not able to see the link to download the source code. It took some time to figure out the link
    https://github.com/exampledriven/cxf-example
    Thanks
    Srini

  6. chandan

    Please check the code highlighter it is printing &quote . Its becoming impossible to read.

  7. john

    Peter, can you please create a next topic for Client using below technology. Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: