Pages

Wednesday, July 6, 2011

Spring Web Service (2.0) using xmlbeans as XML marshaling

When I googled “spring webservice xmlbean”, I found this link that provides an example to use xmlbeans as xml marshalling when implementing a spring web service.


The example is based on Spring web service version older than 2 (spring ws 1.5.6 with spring 2.5.6), while now (at the time of writing this post) Spring is now on version 3 and Spring-WS on version 2. When I tried the example, I can not make it work straight away on the new Spring/Spring-WS version due to some changed java classes and/or configurations.

The latest Spring-WS Tutorial does provide an example on how to develop web services using the latest spring-ws version (version 2). However, the simple example does not cover XML marshaling (e.g. xmlbeans).



So the purpose of this post is to provide an example that use spring-ws version 2 to implement web service with xmlbeans as XML marshaling. This example is based on Developing Spring Web Services with XML Marschalling – XMLBeans example

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  attributeFormDefault="unqualified" elementFormDefault="qualified"
  targetNamespace="http://robin.mytechtip.com/springws2example/temperature/schemas">

  <xs:element name="GetTemperaturesRequest">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="city" type="xs:string" />
        <xs:element maxOccurs="5" minOccurs="1" name="date" type="xs:date" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:element name="GetTemperaturesResponse">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="5" minOccurs="1" name="TemperatureInfo">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="min" type="xs:float" />
              <xs:element name="max" type="xs:float" />
              <xs:element name="average" type="xs:float" />
            </xs:sequence>
            <xs:attribute name="city" type="xs:string" use="optional" />
            <xs:attribute name="date" type="xs:date" use="optional" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>


Using xmlbeans, we can create the java classes for this xml schema. The following command generates the jar file  temperature.jar that we need when we create the service end point.

scomp -out temperature.jar temperature.xsd



Then we create the plain web service interface

package com.mytechtip.robin.springws2example;

import java.util.Date;
import java.util.List;

public interface TemperatureService {
  public List<TemperatureInfo> getTemperatures(String city, List<Date> date);
}


and a sample implemetation.

package com.mytechtip.robin.springws2example;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

public class TemperatureServiceImpl implements TemperatureService {

  private Random rand = new Random(7);

  @Override
  public List<TemperatureInfo> getTemperatures(String city, List<Date> dates) {
    List<TemperatureInfo> temperatures = new ArrayList<TemperatureInfo>();
    // Just return some random data
    for (Date date : dates) {
      temperatures.add(new TemperatureInfo(city, date, rand.nextInt(15),
          rand.nextInt(15) + 15, (rand.nextInt(30) + 15) / 2.0));
    }

    return temperatures;
  }
}


This service requires a  data model “TemperatureInfo”, which is a plain java object:

package com.mytechtip.robin.springws2example;

import java.io.Serializable;
import java.util.Date;

public class TemperatureInfo implements Serializable {
    private static final long serialVersionUID = 1L;
  
    private String city;
    private Date date;
    private double min;
    private double max;
    private double average;

    public TemperatureInfo() {}

    public TemperatureInfo(String city, Date date, double min, double max, double average) {
        this.city = city;
        this.date = date;
        this.min = min;
        this.max = max;
        this.average = average;
    }
    
    // Some getter and setter methods
    // ...
    
}



Based on the above, we create the end point - “TemperatureMarshallingEndpoint”

package com.mytechtip.robin.springws2example;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;

import com.mytechtip.robin.springws2Example.temperature.schemas.GetTemperaturesRequestDocument;
import com.mytechtip.robin.springws2Example.temperature.schemas.GetTemperaturesRequestDocument.GetTemperaturesRequest;
import com.mytechtip.robin.springws2Example.temperature.schemas.GetTemperaturesResponseDocument;
import com.mytechtip.robin.springws2Example.temperature.schemas.GetTemperaturesResponseDocument.GetTemperaturesResponse;

@Endpoint
public class TemperatureMarshallingEndpoint {

  private static final String namespaceUri = "http://robin.mytechtip.com/springws2example/temperature/schemas";

  private TemperatureService temperatureService;

  @Autowired
  public void setTemperatureService(TemperatureService tempService) {
    this.temperatureService = tempService;
  }

  @PayloadRoot(localPart = "GetTemperaturesRequest", namespace = namespaceUri)
  public GetTemperaturesResponseDocument getTemperatures(
      GetTemperaturesRequestDocument request) {

    GetTemperaturesRequestDocument requestDoc = request;
    GetTemperaturesRequest in = requestDoc.getGetTemperaturesRequest();

    List<Date> dates = new ArrayList<Date>();
    for (Calendar calendar : in.getDateArray()) {
      dates.add(calendar.getTime());
    }

    List<TemperatureInfo> infos = temperatureService.getTemperatures(
        in.getCity(), dates);

    GetTemperaturesResponseDocument responseDoc = GetTemperaturesResponseDocument.Factory
        .newInstance();
    GetTemperaturesResponse response = responseDoc
        .addNewGetTemperaturesResponse();

    for (TemperatureInfo info : infos) {
      GetTemperaturesResponse.TemperatureInfo out = response
          .addNewTemperatureInfo();

      out.setCity(info.getCity());
      out.setAverage((float) info.getAverage());

      Calendar calendar = Calendar.getInstance();
      calendar.setTime(info.getDate());
      out.setDate(calendar);
      out.setMax((float) info.getMax());
      out.setMin((float) info.getMin());
    }

    return responseDoc;
  }
}



We need to configure the web application and the spring framework to make the web service work.  So the web.xml looks like this.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <servlet>
    <servlet-name>springws2example</servlet-name>
    <servlet-class>
      org.springframework.ws.transport.http.MessageDispatcherServlet
        </servlet-class>
    <init-param>
      <param-name>transformWsdlLocations</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>springws2example</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
</web-app>


and we have the corresponding spring context configuration (“springws2example-servlet.xml” under WEB-INFO) for the servlet “springws2example”  defined in “web.xml”. NOTE: we need to make sure the schema “temperature.xsd” is under  folder “WEB-INF” so it can automatically generate the “WSDL” file, which can be accessed from “http://<host>:<port>/<context-root>/services/temperature.wsdl”.

<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"
  xmlns:sws="http://www.springframework.org/schema/web-services"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <context:component-scan base-package="com.mytechtip.robin.springws2example" />

  <sws:annotation-driven />
  
  <bean class="com.mytechtip.robin.springws2example.TemperatureServiceImpl"></bean>

  <bean
    class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
    <property name="marshaller" ref="marshaller" />
    <property name="unmarshaller" ref="marshaller" />
  </bean>

  <bean id="marshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller">
  </bean>

  <sws:dynamic-wsdl id="temperature" portTypeName="TempeatureService"
    locationUri="/services/temperature" >
    <sws:xsd location="/WEB-INF/temperature.xsd" />
  </sws:dynamic-wsdl>

</beans>



Since I don’t use maven, I need to manually put the following list of required jars into WEB-INFO/lib



org.springframework.aop-3.0.5.RELEASE.jar
org.springframework.asm-3.0.5.RELEASE.jar
org.springframework.beans-3.0.5.RELEASE.jar
org.springframework.context.support-3.0.5.RELEASE.jar
org.springframework.context-3.0.5.RELEASE.jar
org.springframework.core-3.0.5.RELEASE.jar
org.springframework.expression-3.0.5.RELEASE.jar
org.springframework.oxm-3.0.5.RELEASE.jar
org.springframework.web.servlet-3.0.5.RELEASE.jar
org.springframework.web-3.0.5.RELEASE.jar
spring-ws-2.0.2.RELEASE-all.jar
temperature.jar
wsdl4j-1.6.1.jar
xbean.jar

No comments:

Post a Comment