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
Hello World Service WSDL (autogenerated)
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)




Nice work !
Is it possible to configure Spring security to work with both as well ?
Because from what I understand, the security is not the same in WS-SOAP and in REST…
Not sure about full blown spring security but basic http authentication is pretty easy.
There are some instructions on the code itself here http://chrisdail.com/2008/08/13/http-basic-authentication-with-apache-cxf-revisited/
Then in your context files you just place the interceptor configuration such as…
REST
<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:inInterceptors>
<ref bean="basicAuthInterceptor"/>
</jaxrs:inInterceptors>
</jaxrs:server>
SOAP
<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:inInterceptors>
<ref bean="basicAuthInterceptor"/>
</jaxws:inInterceptors>
</jaxws:endpoint>
Thanks for the link.
Another question about the tutorial : what is the adress of the rest service ?
http://localhost:8080/spring-ws-rest-example/api/helloWorldService?wsdl -> gives the WSDL
http://localhost:8080/spring-ws-rest-example/rest/helloWorldService/ -> HTTP ERROR: 404
My web.xml mapping for the cxf servlet is to /api/*
So I’m guessing the rest server is relative to that one.
Try http://localhost:8080/spring-ws-rest-example/api/rest/helloWorldService/
Ok now I manage to see rest and soap. Good.
I’ll try security later (and I will keep you updated if you like).
ATM, what bother me is the need for a XmlRootElement in the return type.
Is it needed to return an object with this type ?
As an example :
public String[] sayHello();
instead of HelloWorld sayHello() does not work in rest (it works under SOAP) :
.No message body writer found for response class : String[].
Thanks for any pointer that will help me because I am not able to go through all the returns type of my services to wrap them in an object with XmlRootElement
Another question : how does this work with parameters.
add @RequestParam(“name”) String name as a param for sayHello
the wsdl does not change AND when I call the soap service it always returns “hello null ! ”
when I call the rest service, I get a NullPointerException.
I am quite lost on this …
As for returning String[], I ran into the same problem and after reading about CXF discovered that this was actually related to a JAXB limitation. You might want to try changing in the config the implementation to point to something else instead of JAXB. For example CXF supports the following data binding impl AegisDatabinding, JAXBDataBinding, SourceDataBinding, StaxDataBinding. I have tried most of them and always ended up falling back to JAXB, because ALL of them have their own issues. You would think that you don’t have to worry about impl and they should all support the databindings in the same way but this is far from true.
AFAIK @PathParam should be ignored by the SOAP impl and just evaluated as a regular param since the soap url is always the same for all handlers of a service. One thing about CXF is that I found that all annotations should be placed in the Interface, If you place some annotations in the impl and some other in the interface you won’t get the expected results.
There is a few examples of working path params in REST here: http://raulraja.com/2009/08/27/spring-hibernate-jax-rs-putting-hibernate-to-rest/
hi,
I am getting No message body writer found for response class : User.
where user is my domain object that service api returns.
I am using maven and hence i think i need to provide dependency for the message body
provider.
I am lost , working since long on this ..Thanks in advance
Hi Ami,
Can you post your service config xml file, service impl and the class returned sources?
I’m pretty sure the “No message body…” is not related to missing maven dependencies.
Is your returned class annotated with @XmlRootElement or does it include any other xml related annotations?
I am not able to post here, this is a small test message
This is part of my beans.xml
-
-
-
-
-
I am not able to post my code soemhow, But i think i need to write messge body writer to
return JSON response.
and then configure it in the beans.xml, can you give me an example of how to do it? I do see
messagebody writers for returning strings, but I need one to return my USer class having, id,
firstName, lastName.
My return class is annotated with @XmlRootElement(name = “user”) and the service api is annotated with @Produces(“application/json”)
@GET
@Path(“/userbyname/{name}”)
this is my core service :
the JAX-RS wrapper service
And the JAX-RS service:
Looks like the code I paste here is either not visible or not posted.
hey,
I fiugured it out..
org.codehaus.jettison
jettison
1.1
and
in beans.xml
Thats all!! Thanks!
it again didnot post well. Anyways, I had to gibe maven dependency for jettison and configured JSONProvider in beans.xml. Tahts all!
Hi Ami, I’m glad you found the solution. Jettison is listed in the dependencies fro the parent pom for all the tutorials.http://raulrajatutorials.googlecode.com/svn/trunk/raulraja.com.tutorials/pom.xml
I did not know that you couldn’t add code to comments so I enabled a plugin for that by using the popular [ code ] [ / code ] syntax (no spaces)
public interface Interface { }I have a req that, the login method in my app should go over HTTPS. While all the other methos will us e . I HTTP. Any directions to go ahead. ???
FYI: Also, login will be a @POST
Hi Isha,
That actually depends more in the way your frontend is coded. There is nothing that will prevent you from querying some services in http vs https.
If you frontend for example is html, jsp or ajax based you could authenticate via post to a REST service or to a simple servlet. Any method in your service can intercept basic auth headers and create a session for your users if necessary.
There is some others way to authenticate too, like if you use web services security you could actually use cert based authentication to authenticate on every request for those cases where cookies or http sessions are not available. For example a desktop client that contacts your webservices and your users use a smartchip to authenticate.
hello,I try that is successful.but i have a question:
In IE client ,I access rest sever it return:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/octet-stream
Content-Length: 57
Date: Mon, 30 Nov 2009 08:26:37 GMT
{“helloWorld”:{“message”:”Hello world rest and soap!!!”}}
but in Firefox client ,it return:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/xml
Content-Length: 127
Date: Mon, 30 Nov 2009 08:26:26 GMT
Hello world rest and soap!!!
Why?
Hi Feidi,
Not sure why the behavior is different.
The extension mappings in the config define the Content-Type served…
.feed = application/atom+xml
.json = application/json
.xml = application/xml
.html =text/html
AFAIK if you don’t define one in the url the default is application/xml.
Have you tried http://yourserver/path.xml or http://yourserver/path.json ?
Via try again:
1.Soap
request url:http://127.0.0.1:8088/ws/api/helloWorldService/sayHello
response context:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=UTF-8
Content-Length: 274
Date: Fri, 04 Dec 2009 05:30:53 GMT
Hello world rest and soap!!!
2.rest with json:
request url : http://127.0.0.1:8088/ws/api/rest/helloWorldService.json
response context:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Content-Length: 57
Date: Fri, 04 Dec 2009 05:34:06 GMT
{“helloWorld”:{“message”:”Hello world rest and soap!!!”}}
3.rest with xml
request url : http://127.0.0.1:8088/ws/api/rest/helloWorldService.xml
response context:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/xml
Content-Length: 127
Date: Fri, 04 Dec 2009 05:35:24 GMT
Hello world rest and soap!!!
My xml element filte by your blogger in comment
Hi Feidi, so it looks like it works right? sorry about the markup, it looks like I have to change something in WP to make it work
HI ,
I downloaded your example but its not working properly i am new to spring ws and i dont maven.But in my application i need to develop single implementation for both soap and REST. Can any one tell me in detail how to do the above example. Very Urgent.. YOu can also mail me to sailajaprathi@gmail.com
Thanks in advance,
Sailaja
Hi Sailaja,
I’m not sure what issue you’re having with the code. If you give me more specifics like a stack-trace or description on how it’s not working for you maybe I can help.
If you don’t want to use maven for dependencies you can still take a look at the pom file at http://raulrajatutorials.googlecode.com/svn/trunk/raulraja.com.tutorials/pom.xml and see what the library dependencies for all the tutorials are.
In most cases is enough with running “mvn clean jetty:run” in the tutorial folder you want to run to get the tutorial running after having checked out with svn http://raulrajatutorials.googlecode.com/svn/trunk/raulraja.com.tutorials
good luck and let me know if I can help you with anything else
HI Raul,
I am not able to connect to the svn . When i am trying for this its giving the error like ..
Command: Checkout from http://raulrajatutorials.googlecode.com/svn/trunk/raulraja.com.tutorials, revision HEAD, Fully recursive, Externals included
Error: OPTIONS of
Error: ‘http://raulrajatutorials.googlecode.com/svn/trunk/raulraja.com.tutorials’:
Error: could not connect to server (http://raulrajatutorials.googlecode.com)
Finished!:
Please help me,
And I dont’t Have the dependicies jar file can you help me onthis. when am running the pom.xml its giving the error
”
Error stacktrace:
org.apache.maven.reactor.MavenExecutionException: Error scanning for extensions: Error building model lineage in order to pre-scan for extensions: Cannot find artifact for parent POM: com.raulraja:tutorials::0.1 for project com.raulraja:spring-ws-rest-example:jar:0.1 at F:\springs\SpringTraining\projects\spring-ws-rest-example\pom.xml
at org.apache.maven.DefaultMaven.getProjects(DefaultMaven.java:279)
at org.apache.maven.DefaultMaven.createReactorManager(DefaultMaven.java:103)
at org.apache.maven.DefaultMaven.execute_aroundBody0(DefaultMaven.java:160)
at org.apache.maven.DefaultMaven.execute_aroundBody1$advice(DefaultMaven.java:304)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:1)
at org.apache.maven.embedder.MavenEmbedder.execute_aroundBody2(MavenEmbedder.java:904)
at org.apache.maven.embedder.MavenEmbedder.execute_aroundBody3$advice(MavenEmbedder.java:304)
at org.apache.maven.embedder.MavenEmbedder.execute(MavenEmbedder.java:1)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:176)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:408)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:351)
at org.codehaus.classworlds.Launcher.main(Launcher.java:31)
Caused by: org.apache.maven.extension.ExtensionScanningException: Error building model lineage in order to pre-scan for extensions: Cannot find artifact for parent POM: com.raulraja:tutorials::0.1 for project com.raulraja:spring-ws-rest-example:jar:0.1 at F:\springs\SpringTraining\projects\spring-ws-rest-example\pom.xml
at org.apache.maven.extension.DefaultBuildExtensionScanner.buildModelLineage(DefaultBuildExtensionScanner.java:429)
at org.apache.maven.extension.DefaultBuildExtensionScanner.scanInternal(DefaultBuildExtensionScanner.java:137)
at org.apache.maven.extension.DefaultBuildExtensionScanner.scanForBuildExtensions(DefaultBuildExtensionScanner.java:107)
at org.apache.maven.DefaultMaven.getProjects(DefaultMaven.java:275)
… 18 more
Caused by: org.apache.maven.project.ProjectBuildingException: Cannot find artifact for parent POM: com.raulraja:tutorials::0.1 for project com.raulraja:spring-ws-rest-example:jar:0.1 at F:\springs\SpringTraining\projects\spring-ws-rest-example\pom.xml
at org.apache.maven.project.build.model.DefaultModelLineageBuilder.resolveParentFromRepositories(DefaultModelLineageBuilder.java:550)
at org.apache.maven.project.build.model.DefaultModelLineageBuilder.resolveParentPom(DefaultModelLineageBuilder.java:414)
at org.apache.maven.project.build.model.DefaultModelLineageBuilder.buildModelLineage(DefaultModelLineageBuilder.java:131)
at org.apache.maven.extension.DefaultBuildExtensionScanner.buildModelLineage(DefaultBuildExtensionScanner.java:425)
… 21 more
Caused by: org.apache.maven.artifact.resolver.ArtifactNotFoundException: Unable to download the artifact from any repository
com.raulraja:tutorials:pom:0.1
from the specified remote repositories:
central (http://repo1.maven.org/maven2)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:208)
. Can you mail me the stuff how to do please.
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:83)
at org.apache.maven.project.build.model.DefaultModelLineageBuilder.resolveParentFromRepositories(DefaultModelLineageBuilder.java:541)
… 24 more
Caused by: org.apache.maven.wagon.ResourceDoesNotExistException: Unable to download the artifact from any repository
at org.apache.maven.artifact.manager.DefaultWagonManager.getArtifact(DefaultWagonManager.java:357)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:193)
… 26 more
”
like this please help me. I was trying this from 3 days
Thanks and Regards,
Sailaja
Thanks .
HI Rahul ,
.
Could you please wxplain me the things how to do . I was tied searching for all these things
Thanks ,
Sailaja
Hi Sailaja,
To checkout the tutorials you should use…
svn checkout http://raulrajatutorials.googlecode.com/svn/trunk/ raulrajatutorials-read-only
Instructions are here:
http://code.google.com/p/raulrajatutorials/source/checkout
Then once checked out cd into the tutorial and run
mvn clean jetty:run
You can’t run the tutorial without the parent pom since it includes all dependencies.
If I understand your article correctly, you can receive JSON from the web service, but your call to the web service can’t be JSON, it must be simply an URL. Am I right or wrong?
Is there a way to pass JSON instead of the SOAP XML to a web service developed with cxf?
You can post JSON to the webservice. @Consumes({“application/json”, “application/xml”}) Indicates that your method accepts that content type. Even if CXF would not support json out of the box you can register services to those content types so that your app takes not just json but anything you can think of. For example your webservice could take an excel or a image or any kind of resource you can post through http and handle it accordingly. You can also have mixed content types for example upload a file, return json, or post json return a application/octect-stream with a file.
Web services are not limited by any content type and cxf is not limited either since it can be extended. If you can do it with http you can do it with a webservice.