Spring Jax-Ws. Build and consume Web Services – part 1
Posted on July 12, 2012 Marco
Following the official guide, at the chapter 19 we’ll find the Spring support about web service (honestly, could it miss?).
In this article I’ll describe the use of it with Jax-WS standard (introduced in Java EE 5 and Java 6) and we’ll see how build and consume the web service.
First of all, you have to notice that there are a huge variety of methods about build and consume web service. I think I’ve never used two times the same method for building web services. I don’t know why, probably that is the evidence about the large number of technologies for building web services.
My personal opinion is to use technologies which implements java standard, like Jax-WS.
In this first part we’ll take a look about build web service. I’m an old style developer and I’m not able to start from the WSDL definition. So, my approach is to define the interface with methods exposed rather than define the WSDL definition.
package it.springjaxws;
public interface OrderService {
public void Check(Order order) throws Exception;
public Order Process(Order order);
public Order Shipping(Order order);
}
And its implementation:
package it.springjaxws;
import java.util.Date;
public class OrderServiceImpl implements OrderService{
@Override
public void Check(Order order) throws Exception{
if (order.getItemNumber()== 0 )
{
throw new Exception( "Quantity cannot be 0!!" );
}
}
@Override
public Order Process(Order order) {
order.setInvoice(order.getItemNumber() * 1.3 );
return order;
}
@Override
public Order Shipping(Order order) {
order.setShippingDate( new Date());
return order;
}
}
The Order bean:
package it.springjaxws;
import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement (name= "Order" )
public class Order implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String orderId;
private double invoice;
private int itemNumber;
private Date shippingDate;
public Order()
{}
public Order(String orderId, double invoice, int itemNumber)
{
this .orderId = orderId;
this .invoice = invoice;
this .itemNumber = itemNumber;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this .orderId = orderId;
}
public double getInvoice() {
return invoice;
}
public void setInvoice( double invoice) {
this .invoice = invoice;
}
public int getItemNumber() {
return itemNumber;
}
public void setItemNumber( int itemNumber) {
this .itemNumber = itemNumber;
}
public Date getShippingDate() {
return shippingDate;
}
public void setShippingDate(Date shippingDate) {
this .shippingDate = shippingDate;
}
@Override
public String toString()
{
return " orderId:" + orderId +
" itemNumber:" + itemNumber +
" invoice:" + invoice +
" shippingDate:" + shippingDate;
}
}
Now we have to expose the class “OrderService” by an endpoint. For this achieve we need to use a class which extends SpringBeanAutowiringSupport spring’s class. Take a look at the code.
package it.springjaxws;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
@WebService (serviceName= "OrderService" )
public class OrderServiceEndpoint extends SpringBeanAutowiringSupport{
@Autowired
private OrderService orderService;
@WebMethod
public void Check( @WebParam (name = "order" ) Order order) throws Exception
{
orderService.Check(order);
}
@WebMethod
public Order Process( @WebParam (name = "order" ) Order order)
{
return orderService.Process(order);
}
@WebMethod
public Order Shipping( @WebParam (name = "order" ) Order order)
{
return orderService.Shipping(order);
}
}
Briefly, we’ve mapped the orderService methods with the WebMethod.
The spring configuration file
<? 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:context = "http://www.springframework.org/schema/context"
xsi:schemaLocation="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">
<!-- Use @Component annotations for bean definitions -->
< context:component-scan base-package = "it.springjaxws" />
< bean id = "orderService" class = "it.springjaxws.OrderServiceImpl" />
< bean class = "org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter" >
< property name = "baseAddress" value = "http://localhost:8081/" />
</ bean >
< bean id = "orderServiceEndpoint" class = "it.springjaxws.OrderServiceEndpoint" />
</ beans >
We expose the web service at port 8081 of localhost.
Deploy it to your tomcat and run. You’ll see something like above code when you’ll browse /OrderServiceEndpoint?WSDL
<? xml version = "1.0" encoding = "UTF-8" ?> <!-- Published by JAX-WS RI at http://jax-ws.dev.java.net.
RI's version is JAX-WS RI 2.1.6 in JDK 6. --> <!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net.
RI's version is JAX-WS RI 2.1.6 in JDK 6. -->
< definitions xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns = "http://springjaxws.it/" xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
xmlns = "http://schemas.xmlsoap.org/wsdl/" targetNamespace = "http://springjaxws.it/"
name = "OrderService" >
< types >
< xsd:schema >
< xsd:import namespace = "http://springjaxws.it/"
schemaLocation = "http://localhost:8081/OrderServiceEndpoint?xsd=1" ></ xsd:import >
</ xsd:schema >
</ types >
< message name = "Check" >
< part name = "parameters" element = "tns:Check" ></ part >
</ message >
< message name = "CheckResponse" >
< part name = "parameters" element = "tns:CheckResponse" ></ part >
</ message >
< message name = "Exception" >
< part name = "fault" element = "tns:Exception" ></ part >
</ message >
< message name = "Process" >
< part name = "parameters" element = "tns:Process" ></ part >
</ message >
< message name = "ProcessResponse" >
< part name = "parameters" element = "tns:ProcessResponse" ></ part >
</ message >
< message name = "Shipping" >
< part name = "parameters" element = "tns:Shipping" ></ part >
</ message >
< message name = "ShippingResponse" >
< part name = "parameters" element = "tns:ShippingResponse" ></ part >
</ message >
< portType name = "OrderServiceEndpoint" >
< operation name = "Check" >
< input message = "tns:Check" ></ input >
< output message = "tns:CheckResponse" ></ output >
< fault message = "tns:Exception" name = "Exception" ></ fault >
</ operation >
< operation name = "Process" >
< input message = "tns:Process" ></ input >
< output message = "tns:ProcessResponse" ></ output >
</ operation >
< operation name = "Shipping" >
< input message = "tns:Shipping" ></ input >
< output message = "tns:ShippingResponse" ></ output >
</ operation >
</ portType >
< binding name = "OrderServiceEndpointPortBinding" type = "tns:OrderServiceEndpoint" >
< soap:binding transport = "http://schemas.xmlsoap.org/soap/http"
style = "document" ></ soap:binding >
< operation name = "Check" >
< soap:operation soapAction = "" ></ soap:operation >
< input >
< soap:body use = "literal" ></ soap:body >
</ input >
< output >
< soap:body use = "literal" ></ soap:body >
</ output >
< fault name = "Exception" >
< soap:fault name = "Exception" use = "literal" ></ soap:fault >
</ fault >
</ operation >
< operation name = "Process" >
< soap:operation soapAction = "" ></ soap:operation >
< input >
< soap:body use = "literal" ></ soap:body >
</ input >
< output >
< soap:body use = "literal" ></ soap:body >
</ output >
</ operation >
< operation name = "Shipping" >
< soap:operation soapAction = "" ></ soap:operation >
< input >
< soap:body use = "literal" ></ soap:body >
</ input >
< output >
< soap:body use = "literal" ></ soap:body >
</ output >
</ operation >
</ binding >
< service name = "OrderService" >
< port name = "OrderServiceEndpointPort" binding = "tns:OrderServiceEndpointPortBinding" >
< soap:address location = "http://localhost:8081/OrderServiceEndpoint" ></ soap:address >
</ port >
</ service >
</ definitions >
In the next part I describe how consume it.
UPDATE
You can find very useful to host the web services in the same web server port (the 8080 for tomcat). For this purpose you have to use JAX-WS commons from Metro project of glassfish. More info are available at this url http://jax-ws-commons.java.net/spring/
To apply this technology at the previous example, we need to change and update some files.
First, in web.xml, we put the servlet definition
< web-app id = "WebApp_ID" version = "2.4"
xmlns = "http://java.sun.com/xml/ns/j2ee"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
< display-name >SpringJaxWsEmbedded</ display-name >
< servlet >
< servlet-name >jaxwsembedded</ servlet-name >
< servlet-class >
com.sun.xml.ws.transport.http.servlet.WSSpringServlet
</ servlet-class >
</ servlet >
< servlet-mapping >
< servlet-name >jaxwsembedded</ servlet-name >
< url-pattern >/order</ url-pattern >
</ servlet-mapping >
<!-- Register Spring Listener -->
< listener >
< listener-class >
org.springframework.web.context.ContextLoaderListener
</ listener-class >
</ listener >
</ web-app >
After this, we edit the applicationContext.xml file
<? 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:ws = "http://jax-ws.dev.java.net/spring/core"
xmlns:wss = "http://jax-ws.dev.java.net/spring/servlet"
xmlns:context = "http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://jax-ws.dev.java.net/spring/core http://jax-ws.java.net/spring/core.xsd
http://jax-ws.dev.java.net/spring/servlet http://jax-ws.java.net/spring/servlet.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Use @Component annotations for bean definitions -->
< context:component-scan base-package = "it.springjaxwsembedded" />
< bean id = "orderService" class = "it.springjaxws.OrderServiceImpl" />
< wss:binding url = "/order" >
< wss:service >
< ws:service bean = "#orderWs" />
</ wss:service >
</ wss:binding >
<!-- Web service methods -->
< bean id = "orderWs" class = "it.springjaxws.OrderServiceEndpoint" />
</ beans >
Last point is OrderServiceEndpoint definition
public class OrderServiceEndpoint {
...
}
We’ve removed the SpringBeanAutowiringSupport extension.