Spring – Exposing a bean as both REST (xml, json, …) and SOAP WebServices

Often times you have Spring services that you want to expose as web-services. In this small tutorial I’ll just show a few configuration files and code that demonstrate how a service and its implementation can accommodate both REST and SOAP access to the same backbone.

Thanks Justin for the REST part.

This tutorial is available for download with svn:
svn checkout http://raulrajatutorials.googlecode.com/svn/trunk/ raulrajatutorials-read-only
Comments in the code itself should be self-explanatory if you already have some java + spring experience.
I have not included the dependencies on CXF for simplicity but you can take a look at them and some for the other tutorials here.

And now to the point.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cxf="http://cxf.apache.org/core"
       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-2.0.xsd
       http://cxf.apache.org/core
       http://cxf.apache.org/schemas/core.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"
       default-autowire="byName">
 
    <!-- Load CXF modules from cxf.jar -->
    <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"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml"/>
 
    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus>
 
    <!-- The hello world service -->
    <bean id="helloWorldService" class="com.raulraja.ws.impl.HelloWorldServiceImpl" autowire="autodetect"/>
 
    <!-- Exposing the Helloworld service as a rest service -->
    <jaxrs:server id="restServer" address="/rest/">
        <jaxrs:serviceBeans>
            <ref bean="helloWorldService"/>
        </jaxrs:serviceBeans>
        <jaxrs:extensionMappings>
            <entry key="feed" value="application/atom+xml"/>
            <entry key="json" value="application/json"/>
            <entry key="xml" value="application/xml"/>
            <entry key="html" value="text/html"/>
        </jaxrs:extensionMappings>
    </jaxrs:server>
 
 
    <!-- Exposing the HelloWorld service as a SOAP service -->
    <bean id="jaxbBean"
          class="org.apache.cxf.jaxb.JAXBDataBinding"
          scope="prototype"/>
 
    <bean id="jaxws-and-aegis-service-factory"
          class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"
          scope="prototype">
        <property name="dataBinding" ref="jaxbBean"/>
        <property name="serviceConfigurations">
            <list>
                <bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>
                <bean class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration"/>
                <bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/>
            </list>
        </property>
    </bean>
 
 
    <jaxws:endpoint id="helloWorldServiceEndpoint"
                    serviceName="HelloWorld"
                    implementorClass="com.raulraja.ws.HelloWorldService"
                    implementor="#helloWorldService"
                    address="/helloWorldService">
        <jaxws:serviceFactory>
            <ref bean="jaxws-and-aegis-service-factory"/>
        </jaxws:serviceFactory>
    </jaxws:endpoint>
 
</beans>

HelloWorldService.java

package com.raulraja.ws;
 
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.jws.WebService;
import javax.jws.WebMethod;
 
/**
 * The hello world service interface exposed as soap and rest
 *
 * @ WebService is for soap
 * @ Path is for the rest top path
 */
@Path("/helloWorldService/")
@WebService(serviceName = "HelloWorld", name = "HelloWorldService", targetNamespace = "http://ws.raulraja.com")
public interface HelloWorldService {
 
	/**
	 * Simple methods that says hello
	 *
	 * @return hello world rest and soap!!!
	 * @ WebMethod is for soap
	 * @ GET is for REST
	 * @ Path is for the REST service path
	 */
	@WebMethod
	@GET
	@Path("/")
	HelloWorld sayHello();
 
}

HelloWorldServiceImpl.java

package com.raulraja.ws.impl;
 
import com.raulraja.ws.HelloWorld;
import com.raulraja.ws.HelloWorldService;
 
/**
 * Implementation of the hello world web service.
 */
public class HelloWorldServiceImpl implements HelloWorldService {
 
	/**
	 * Simple methods that says hello
	 *
	 * @return hello world rest and soap!!!
	 */
	public HelloWorld sayHello() {
		HelloWorld helloWorld = new HelloWorld();
		helloWorld.setMessage("Hello world rest and soap!!!");
		return helloWorld;
	}
}

HelloWorld.java

package com.raulraja.ws;
 
import javax.xml.bind.annotation.XmlRootElement;
 
/**
 * Hello world object that demonstrates the service
 */
@XmlRootElement
public class HelloWorld {
 
	/**
	 * the message
	 */
	private String message;
 
	/**
	 * @return the message
	 */
	public String getMessage() {
		return message;
	}
 
	/**
	 * @param message sets the message
	 */
	public void setMessage(String message) {
		this.message = message;
	}
}

And here is some screen captures for the results…

Soap Services list

CXF exposed soap services

CXF exposed soap services

Hello World Service WSDL (autogenerated)

Hello World Service WSDL

Hello World Service WSDL

Hello World Service REST XML Result (add .json or .xml, … at the end of the service path to get results in the different format exposed)

Hello World REST xml result

Hello World REST xml result