JAX–WS with Spring and Maven Tutorial
Spring framework provides remoting support for web services via JAX–WS, in fact, as stated in Spring reference documentation, there are three ways of exposing Spring POJO services as a JAX–WS web services :
- Exposing Servlet – based web services (suitable for Java EE 5 environments)
- Exporting standalone web services (suitable when utilizing Sun’s JDK 1.6 build–in JAX–WS provider)
- Exporting web services using the JAX–WS RI’s Spring support (similar to the standalone approach, but this time in a Servlet environment. Suitable for non – Java EE environments such as Tomcat, embedding the JAX–WS RI as part of the web application)
Selecting one of the three approaches mentioned above depends mainly on your execution environment. Having said that, we will show you how to setup and expose a simple Spring service using all of the above approaches.
Our preferred development environment is Eclipse, so as a prerequisite you must have Eclipse with Maven support installed. The installation of Maven plugin for Eclipse is out of the scope of this tutorial and will not be discussed. Nevertheless you will need the following components :
We will be using Eclipse Galileo, „m2eclipse“ Maven Integration for Eclipse Plugin version 0.10.0 and Spring version 3.0.1 for this tutorial.
Lets begin,
- Create a new Maven project, go to File ? Project ? Maven ? Maven Project
- In the „Select project name and location“ page of the wizard, make sure that „Create a simple project (skip archetype selection)“ option is unchecked, hit „Next“ to continue with default values
- In the „Select an Archetype“ page of the wizard, select „Nexus Indexer“ at the „Catalog“ drop down list and after the archetypes selection area is refreshed, select the „webapp-javaee6“ archetype from „org.codehaus.mojo.archetypes“ to use. You can use the „filter“ text box to narrow search results. Hit „Next“ to continue
- In the „Enter an artifact id“ page of the wizard, you can define the name and main package of your project. We will set the „Group Id“ variable to „com.javacodegeeks“ and the „Artifact Id“ variable to „jaxwsspring“. The aforementioned selections compose the main project package as „com.javacodegeeks.jaxwsspring “ and the project name as „jaxwsspring“. Hit „Finish“ to exit the wizard and to create your project
Let’s recap a few things about the Maven Web project structure
- /src/main/java folder contains source files for the dynamic content of the application
- /src/test/java folder contains all source files for unit tests
- /src/main/webapp folder contains essential files for creating a valid web application, e.g. „web.xml“
- /target folder contains the compiled and packaged deliverables
- The „pom.xml“ is the project object model (POM) file. The single file that contains all project related configuration.
One thing to notice here is that we have chosen to utilize the Java EE6 compatible Maven project archetype. Our selection is made on purpose so as to be easy to demonstrate all available approaches mentioned above. Nevertheless you can follow this tutorial and expose your Spring services as JAX–WS services even if you utilize a different environment (such as a standalone Servlet environment e.g. Apache Tomcat or a Java EE 5 compatible application server e.g. JBoss Application Server).
In order to properly use Spring at runtime, we must provide all necessary libraries to the web application. Open the graphical editor of your „pom.xml“ and perform the following changes :
- Locate the „Properties“ section at the „Overview“ page of the POM editor and perform the following changes :
- Create a new property with name org.springframework.version and value 3.0.1.RELEASE
- Navigate to the „Dependencies“ page of the POM editor and create the following dependencies (you should fill the „GroupId“, „Artifact Id“ and „Version“ fields of the „Dependency Details“ section at that page) :
- Group Id : org.springframework Artifact Id : spring-web Version : ${org.springframework.version}
As you can see Maven manages library dependencies declaratively. A local repository is created (by default under {user_home}/.m2 folder) and all required libraries are downloaded and placed there from public repositories. Furthermore intra – library dependencies are automatically resolved and manipulated.
The next step is to provide hooks for the web application so as to load the Spring context upon startup.
Create a „WEB-INF /web.xml“ file under /src/main/webapp folder and add the following :
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>jaxwsspring</display-name> <session-config> <session-timeout>30</session-timeout> </session-config> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
Now lets create the applicationContext.xml file that will drive Spring container. Create the file under /src/main/webapp/WEB-INF directory. An example „applicationContext.xml“ is presented below :
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <context:component-scan base-package="com.javacodegeeks.jaxwsspring" /> </beans>
Things to notice here :
- Change the base-package attribute of the context:component-scan element to whatever is the base package of your project so as to be scanned for Spring components
Lets create now a “greeting” Spring service. Create a sub – package named “services” under your main package and place the “GreetingService” class there. An example “greeting” service is shown below :
package com.javacodegeeks.jaxwsspring.services; import org.springframework.stereotype.Service; @Service("greetingService") public class GreetingService { public String sayHello() { return "Hello from Greeting Service"; } }
JAX–WS requires working with dedicated endpoint classes. If an existing service needs to be exported, a wrapper class is the simplest JAX–WS compliant way. Lets create now a “greeting” endpoint class. Create a sub – package named “endpoints” under your main package and place the “GreetingServiceEndpoint” class there. An example “greeting” endpoint class is shown below :
package com.javacodegeeks.jaxwsspring.endpoints; import javax.jws.WebMethod; import javax.jws.WebService; import org.springframework.beans.factory.annotation.Autowired; import com.javacodegeeks.jaxwsspring.services.GreetingService; @WebService(serviceName="GreetingService") public class GreetingServiceEndpoint { @Autowired private GreetingService greetingService; @WebMethod public String sayHello() { return greetingService.sayHello(); } }
As you can see this is a JAX–WS compliant “greeting” service implementation that simply delegates to the “GreetingService” implementation. This is the class that will be registered with the JAX–WS engine. In the case of a Java EE 5 server (the Servlet – based web services approach), the web service engine manages the lifecycle of instances of the endpoint class. In the other two cases, the endpoint instances are defined and managed as Spring beans themselves so their lifecycle will be up to the Spring application context.
Spring provides a convenient base class for JAX–WS Servlet endpoint implementations – “SpringBeanAutowiringSupport”. To expose our “GreetingService” as a Servlet based web service we must proceed as follows :
- Modify our “GreetingServiceEndpoint” class to extend Spring’s “SpringBeanAutowiringSupport” class. That way the @Autowired annotation will be honored and Spring’s IoC container will be able to inject the actual “greeting” service instance to our endpoint class for delegating invocations properly.
- Our “GreetingServiceEndpoint” needs to run in the same web application as the Spring context to allow for access to Spring’s facilities. This is the case by default in Java EE 5 environments, using the standard contract for JAX–WS Servlet endpoint deployment.
- The endpoint is the class registered with the server–side JAX–WS implementation. In the case of a Java EE 5 server, this would simply be defined as a Servlet in “web.xml”, with the server detecting that this is a JAX–WS endpoint and reacting accordingly. The Servlet name usually needs to match the specified web service service name.
Having the above in mind alter your endpoint class as shown below :
package com.javacodegeeks.jaxwsspring.endpoints; import javax.jws.WebMethod; import javax.jws.WebService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.support.SpringBeanAutowiringSupport; import com.javacodegeeks.jaxwsspring.services.GreetingService; @WebService(serviceName="GreetingService") public class GreetingServiceEndpoint extends SpringBeanAutowiringSupport { @Autowired private GreetingService greetingService; @WebMethod public String sayHello() { return greetingService.sayHello(); } }
Finally add to your “web.xml” the following Servlet declaration :
<servlet> <servlet-name>GreetingService</servlet-name> <servlet-class> com.javacodegeeks.jaxwsspring.endpoints.GreetingServiceEndpoint </servlet-class> </servlet> <servlet-mapping> <servlet-name>GreetingService</servlet-name> <url-pattern>/GreetingService</url-pattern> </servlet-mapping>
To build the application right click on your project ? Run As ? Maven package
To deploy the web application just copy the „.war“ file from the „target“ directory to your Java EE 5 server, web application deploy folder. For the purpose of this tutorial we are going to deploy our web application to a JBoss 5.1.0 Application Server.
To launch the application point your browser to the following address
http://localhost:8080/jaxwsspring/
If all went well you should see your main web page displaying the following :
“HelloWorld!”
Our web service address location should be at :
http://localhost:8080/jaxwsspring/GreetingService
We can view our web service WSDL if we point our browser to :
http://localhost:8080/jaxwsspring/GreetingService?wsdl
The result should be like the following :
<definitions name="GreetingService" targetNamespace="http://endpoints.jaxwsspring.javacodegeeks.com/"> <types> <xs:schema targetNamespace="http://endpoints.jaxwsspring.javacodegeeks.com/" version="1.0"> <xs:element name="sayHello" type="tns:sayHello"/> <xs:element name="sayHelloResponse" type="tns:sayHelloResponse"/> <xs:complexType name="sayHello"> <xs:sequence/> </xs:complexType> <xs:complexType name="sayHelloResponse"> <xs:sequence> <xs:element minOccurs="0" name="return" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema> </types> <message name="GreetingServiceEndpoint_sayHello"> <part element="tns:sayHello" name="sayHello"/> </message> <message name="GreetingServiceEndpoint_sayHelloResponse"> <part element="tns:sayHelloResponse" name="sayHelloResponse"/> </message> <portType name="GreetingServiceEndpoint"> <operation name="sayHello" parameterOrder="sayHello"> <input message="tns:GreetingServiceEndpoint_sayHello"/> <output message="tns:GreetingServiceEndpoint_sayHelloResponse"/> </operation> </portType> <binding name="GreetingServiceEndpointBinding" type="tns:GreetingServiceEndpoint"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="GreetingService"> <port binding="tns:GreetingServiceEndpointBinding" name="GreetingServiceEndpointPort"> <soap:address location="http://127.0.0.1:8080/jaxwsspring/GreetingService"/> </port> </service> </definitions>
Now that we have our web service definition we can use our favorite SOAP client – generate or implement one – to invoke operations of the “greeting” web service. Accessing the web service is out of the scope of this tutorial and thus we will not discuss it further. Nevertheless we are going to provide an example request – response dialog when invoking the “sayHello” operation of the “GreetingService” using our preferred tool for web service testing, soapUI
The SOAP request as posted by the soapUI tool :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:end="http://endpoints.jaxwsspring.javacodegeeks.com/"> <soapenv:Header/> <soapenv:Body> <end:sayHello/> </soapenv:Body> </soapenv:Envelope>
The relevant SOAP response as receieved from the “greeting” web service :
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header/> <env:Body> <ns2:sayHelloResponse xmlns:ns2="http://endpoints.jaxwsspring.javacodegeeks.com/"> <return>Hello from Greeting Service</return> </ns2:sayHelloResponse> </env:Body> </env:Envelope>
As stated above, the built–in JAX–WS provider that comes with Sun’s JDK 1.6 supports exposure of web services using the built–in HTTP server that’s included in JDK 1.6 as well. Spring’s “SimpleJaxWsServiceExporter” detects all @WebService annotated beans in the Spring application context, exporting them through the default JAX–WS server (the JDK 1.6 HTTP server).
In this scenario, the endpoint instances are defined and managed as Spring beans themselves; they will be registered with the JAX–WS engine but their lifecycle will be up to the Spring application context. This means that Spring functionality like explicit dependency injection may be applied to the endpoint instances. Of course, annotation-driven injection through @Autowired will work as well.
To utilize this approach all we have to do is to declare our endpoint class as a Spring bean by annotating it with the @Service annotation. Additionally we must add the “SimpleJaxWsServiceExporter” bean declaration in our application context file as shown below :
package com.javacodegeeks.jaxwsspring.endpoints; import javax.jws.WebMethod; import javax.jws.WebService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.javacodegeeks.jaxwsspring.services.GreetingService; @Service("greetingServiceEndpoint") @WebService(serviceName="GreetingService") public class GreetingServiceEndpoint { @Autowired private GreetingService greetingService; @WebMethod public String sayHello() { return greetingService.sayHello(); } }
Things to notice here :
- The GreetingServiceEndpoint may derive from Spring’s “SpringBeanAutowiringSupport” but doesn’t have to since the endpoint is a fully Spring–managed bean here
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"> <context:component-scan base-package="com.javacodegeeks.jaxwsspring" /> <bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"> <property name="baseAddress" value="http://localhost:9999/" /> </bean> </beans>
Things to notice here :
- Change the base-package attribute of the context:component-scan element to whatever is the base package of your project so as to be scanned for Spring components
- You can change the “baseAddress” property of “ SimpleJaxWsServiceExporter” to fit your needs. The “baseAddress” is the address under which all detected web services will be exported. The default “baseAddress” is “http://localhost:8080/”
To build the application right click on your project ? Run As ? Maven package
To test the specific approach we are going to deploy our web application to an Apache Tomcat 6 server. Do not forget that we will be utilizing Sun’s JDK 1.6 build–in JAX–WS provider; even though we are operating in a Servlet container. In this specific scenario we are using the Servlet container just for launching the Spring application. Alternatively you could implement a Main class and launch the Spring application programmatically.
To launch the application point your browser to the following address
http://localhost:8080/jaxwsspring/
If all went well you should see your main web page displaying the following :
“HelloWorld!”
Our web service address location should be at :
http://localhost:9999/GreetingService
We can view our web service WSDL if we point our browser to :
http://localhost:9999/GreetingService?wsdl
Once again we can use the web service definition to generate and/or implement a SOAP client so as to invoke operations of the “greeting” web service.
Sun’s JAX–WS RI, developed as part of the GlassFish project, ships Spring support as part of its JAX–WS Commons project. This allows for defining JAX–WS endpoints as Spring–managed beans, similar to the standalone mode discussed in the previous section – but this time in a Servlet environment. Note that this is not portable in a Java EE 5 environment; it is mainly intended for non-EE environments such as Tomcat, embedding the JAX–WS RI as part of the web application.
The difference to the standard style of exporting Servlet–based endpoints is that the lifecycle of the endpoint instances themselves will be managed by Spring here, and that there will be only one JAX–WS Servlet defined in “web.xml”. With the standard Java EE 5 style (as illustrated above), you’ll have one Servlet definition per service endpoint, with each endpoint typically delegating to Spring beans.
This last approach is Spring 2.0 specific, so prior altering any of our code we should change our project configuration a little. In particular we must remove the dependency for the Spring 3 libraries and add a new one to the jaxws-spring library. Open the graphical editor of your „pom.xml“ and perform the following changes :
- Locate the „Properties“ section at the „Overview“ page of the POM editor and remove the property with name org.springframework.version if exists – in case you followed this tutorial from the start
- Navigate to the „Dependencies“ page of the POM editor and create the following dependencies (you should fill the „GroupId“, „Artifact Id“ and „Version“ fields of the „Dependency Details“ section at that page) :
- Group Id : org.jvnet.jax-ws-commons.spring Artifact Id : jaxws-spring Version : 1.8
- Since the time of this writing there is a dependency declaration flaw in the “pom” file of the latest version (1.8) of jaxws-spring library. In particular an unnecessary dependency declaration for the 2.1.2 version of the jaxb-impl causes the code to break. To overcome this issue you must configure an exclusion for the specific version of the jaxb-impl library. To do so, select the newly created dependency, locate the “Exclusions” section of the editor’s page and create the exclusion as shown below :
- Group Id : com.sun.xml.bind Artifact Id : jaxb-impl
- Additionally create another explicit dependency for the 2.1.6 version of the jaxb-impl library as shown below :
- Group Id : com.sun.xml.bind Artifact Id : jaxb-impl Version : 2.1.6
- Last but not least we have to configure two new repositories in order for maven to be able to download the project dependences. Navigate to the „Repositories“ page of the POM editor and create the following repositories :
- Id : download.java.net repo 1 URL : http://download.java.net/maven/1/ Layout : legacy
- Id : download.java.net repo 2 URL : http://download.java.net/maven/2/
To utilize this approach we have to follow the steps described below :
Locate the “web.xml” file and alter it by defining the main JAX–WS Servlet along with as many Servlet mappings as necessary for the services we want to expose as web services; one Servlet mapping per service. Thus for our specific test case we should add the following declarations:
<servlet> <servlet-name>jaxws-servlet</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>jaxws-servlet</servlet-name> <url-pattern>/GreetingService</url-pattern> </servlet-mapping>
Alter “applicationContext.xml” file as shown below :
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ws="http://jax-ws.dev.java.net/spring/core" xmlns:wss="http://jax-ws.dev.java.net/spring/servlet" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet.xsd"> <wss:binding url="/GreetingService"> <wss:service> <ws:service bean="#greetingService" /> </wss:service> </wss:binding> <bean id="greetingService" class="com.javacodegeeks.jaxwsspring.services.GreetingService" /> </beans>
Things to notice here :
- This approach is Spring 2.0 specific, so a Spring 2.0 compatible “applicationContext.xml” file has been created. Notice that we have declared the “greeting” Spring service bean directly in the “applicationContext.xml” file since the @Service annotation is not available in Spring 2.0
- The wss:binding definition define what service is exposed to which part of the URL space. In the above code, it deploys the “GreetingService” on the /GreetingService URL
- The ws:service definition realizes a web service instance. To its simplest form the bean attribute should be configured in order to delegate all invocations to the designated Spring bean. As you can see with this approach we can omit the creation of an “endpoint” class to proxy our Spring services.
Finally we implement our “GreetingService” as shown below :
package com.javacodegeeks.jaxwsspring.services; import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; import javax.jws.soap.SOAPBinding.Use; @WebService(serviceName="GreetingService") @SOAPBinding(style = Style.RPC, use = Use.LITERAL) public class GreetingService { @WebMethod public String sayHello() { return "Hello from Greeting Service"; } }
Things to notice here :
- You should run the “wsgen” tool if you are using “Style.DOCUMENT”. “Style.RPC” is used only for demonstration purposes.
- The @Service annotation is not available in Spring 2.0 so we declare it directly in the “applicationContext.xml” file
To build the application right click on your project ? Run As ? Maven package
To test the specific approach we are going to deploy our web application to an Apache Tomcat 6 server.
To launch the application point your browser to the following address
http://localhost:8080/jaxwsspring/
If all went well you should see your main web page displaying the following :
“HelloWorld!”
Our web service address location should be at :
http://localhost:8080/jaxwsspring/GreetingService
We can view our web service WSDL if we point our browser to :
http://localhost:8080/jaxwsspring/GreetingService?wsdl
Once again we can use the web service definition to generate and/or implement a SOAP client so as to invoke operations of the “greeting” web service.
Happy coding
Justin
- GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial
- GWT Spring and Hibernate enter the world of Data Grids
- Spring 3 HornetQ 2.1 Integration Tutorial
- Spring 3 RESTful Web Services
- GWT 2 Spring 3 JPA 2 Hibernate 3.5 Tutorial – Eclipse and Maven 2 showcase
Kray I am also having the same problem
Varun
Hi thank you, very helpful
I am also having the same error originally reported by Kray below (Class com.javacodegeeks.jaxwsspring.endpoints.GreetingServiceEndpoint is not a Servlet). I have deployed it in both Tomcat 7 and Glassfish 3.1.1. Both times I am getting this error. I’m not if anyone has posted a solution yet? I’m only seeing 5 of 12 comments below. Has anyone found the solution? I would be very interested. Thanks.
thanks ,BTY how to configuration jax-ws handler in spring applicationContext.xml file ?
Really helpful guide, helped me a lot to finish my JAXWS + Spring WS
You need to set up using the spring service (as given in the middle) to use the WS. Strangely, the first part was giving me the same servlet not found error.
Get the same error when deploy to tomcat 7
javax.servlet.ServletException: Class **** is not a Servlet
Do anyone know why?
In my case I solved the “Class **** is not a Servlet” error with:
– declaring the servlet in the web.xml
– declaring the SimpleJaxWsServiceExporter bean in the applicationContext.xml
– adding my Services and Endpoints in the applicationContext.xml
– having both @Service and @WebService annotation on my Endpoints without having to extend the SpringBeanAutowiringSupport class
Hope it helps
Hi,
Could you publish your solution or a link to it? I have declared servlet in a web.xml, but the problem is that declaring simultanously the servlet url in web.xml and baseAddress of SimpleJaxWsServiceExporter brings a conflict -> ‘address already in use’. What dependencies do you have in your pom.xml or have you copied some extra libs to tomcat libs folder?
Best regards,
Paulo
Hi, I am unable to log soap request/response. I used SOAPHandler earlier, but it is not working with this implementation. Do you have any idea?
Hi,
Have you try this:
http://www.coderanch.com/t/547986/Web-Services/java/Spring-Web-Service-PayloadLoggingInterceptor-log