Developing Spring Web Services with XML Marschalling – XMLBeans example

Spring-WS supports various XML marshalling APIs and an example of using Castor as the marshaller for the Weather Service application is given in chapter 16 of the book “Spring Recipes“.

Here, an example of using XMLBeans as the marshaller is provided.




I. Service interface and implementation

Weather Service interface


package com.apress.springrecipes.weather;

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

public interface WeatherService {
    public List getTemperatures(String city, List date);
}





Weather Service implementation


package com.apress.springrecipes.weather;

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

public class WeatherServiceImpl implements WeatherService {

    public List getTemperatures(String city, List dates) {

        List temperatures = new ArrayList();

        for (Date date : dates) {
            temperatures.add(new TemperatureInfo(city, date, 15.0, 110.0, 8.0));
        }                

        return temperatures;
    }
}




TemperatureInfo class

package com.apress.springrecipes.weather;

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

public class TemperatureInfo implements Serializable {

    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;
    }

    public double getAverage() {
        return average;
    }

    public String getCity() {
        return city;
    }

    public Date getDate() {
        return date;
    }

    public double getMax() {
        return max;
    }

    public double getMin() {
        return min;
    }

    public void setAverage(double average) {
        this.average = average;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setMax(double max) {
        this.max = max;
    }

    public void setMin(double min) {
        this.min = min;
    }
}




II. Compile schema

Use scomp to compile, generate and jar Java types. For example, to create the temperatures.jar file from the temperatures.xsd schema file:

    scomp -out temperatures.jar temperatures.xsd


note: The generated jar file has to be put into both server and client side classpath.



III. Marshalling Endpoint


package com.apress.springrecipes.weather;

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

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;

import com.apress.springrecipes.weather.schemas.GetTemperaturesRequestDocument;
import com.apress.springrecipes.weather.schemas.GetTemperaturesResponseDocument;
import com.apress.springrecipes.weather.schemas.GetTemperaturesRequestDocument.GetTemperaturesRequest;
import com.apress.springrecipes.weather.schemas.GetTemperaturesResponseDocument.GetTemperaturesResponse;

@Endpoint
public class TemperatureMarshallingEndpoint { 

    private static final String namespaceUri =
        "http://springrecipes.apress.com/weather/schemas";

    private WeatherService weatherService;

    public void setWeatherService(WeatherService weatherService) {
        this.weatherService = weatherService;
    }

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

        GetTemperaturesRequestDocument requestDoc = (GetTemperaturesRequestDocument)request;

        GetTemperaturesRequest in = requestDoc.getGetTemperaturesRequest();

        GetTemperaturesResponseDocument responseDoc = (GetTemperaturesResponseDocument)
            GetTemperaturesResponseDocument.Factory.newInstance();

        List dates = new ArrayList();

        for (Calendar calendar : in.getDateArray()) {
            dates.add(calendar.getTime());
        }

        List infos = this.weatherService.getTemperatures(
            in.getCity(), dates);

        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;
    }
}





IV. Server side context and web xml files

Context XML file ( weather-servlet.xml )

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        ">

    <bean id="weatherService"
        class="com.apress.springrecipes.weather.WeatherServiceImpl" />

    <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" />

    <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>

    <bean id="temperatureEndpoint"
        class="com.apress.springrecipes.weather.TemperatureMarshallingEndpoint">
        <property name="weatherService" ref="weatherService" />
    </bean>    

    <bean id="temperature"
        class="org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition">
        <property name="builder">
            <bean class="org.springframework.ws.wsdl.wsdl11.builder.XsdBasedSoap11Wsdl4jDefinitionBuilder">
                <property name="schema" value="/WEB-INF/classes/temperature.xsd" />
                <property name="portTypeName" value="Weather" />
                <property name="locationUri"
                    value="http://localhost:8080/srch16xmlbeans/services" />
                <property name="targetNamespace" value="http://springrecipes.apress.com/weather/schemas" />
            </bean>
        </property>
    </bean>
</beans>



Web XML file ( web.xml )

<web-app 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">

    <servlet>
        <servlet-name>weather</servlet-name>
        <servlet-class>
            org.springframework.ws.transport.http.MessageDispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

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



V. Client side implementation

Client class


package com.apress.springrecipes.weather;

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

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

    public static void main(String[] args) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext("client.xml");

        WeatherServiceClient client =
            (WeatherServiceClient) context.getBean("client");

        List dates = new ArrayList();
        dates.add(new Date());
        dates.add(new Date());

        List infos = client.getTodayTemperature("Houston", dates);

        for (TemperatureInfo info : infos) {

            System.out.println("Min temperature : " + info.getMin());
            System.out.println("Max temperature : " + info.getMax());
            System.out.println("Ave temperature : " + info.getAverage());
            System.out.println("City            : " + info.getCity());
            System.out.println("Date            : " + info.getDate());
        }
    }
}




WeatherServiceClient class


package com.apress.springrecipes.weather;

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

public class WeatherServiceClient {

    private WeatherService weatherService;

    public void setWeatherService(WeatherService weatherService) {
        this.weatherService = weatherService;
    }

    public List getTodayTemperature(String city, List dates) {

        return weatherService.getTemperatures(city, dates);
    }
}





WeatherServiceProxy


package com.apress.springrecipes.weather;

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

import org.springframework.ws.client.core.support.WebServiceGatewaySupport;

import com.apress.springrecipes.weather.schemas.GetTemperaturesRequestDocument;
import com.apress.springrecipes.weather.schemas.GetTemperaturesResponseDocument;
import com.apress.springrecipes.weather.schemas.GetTemperaturesRequestDocument.GetTemperaturesRequest;
import com.apress.springrecipes.weather.schemas.GetTemperaturesResponseDocument.GetTemperaturesResponse;

public class WeatherServiceProxy extends WebServiceGatewaySupport
    implements WeatherService {

    @Override
    public List getTemperatures(String city, List dates) {
        GetTemperaturesRequestDocument requestDoc =
            GetTemperaturesRequestDocument.Factory.newInstance();

        GetTemperaturesRequest request = requestDoc.addNewGetTemperaturesRequest();

        for (Date date : dates) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            request.addDate(calendar);
        }

        request.setCity(city);        

        GetTemperaturesResponseDocument responseDoc;

        responseDoc = (GetTemperaturesResponseDocument)
            this.getWebServiceTemplate().marshalSendAndReceive(requestDoc);

        List infos = new ArrayList();

        for (GetTemperaturesResponse.TemperatureInfo response :
            responseDoc.getGetTemperaturesResponse().getTemperatureInfoArray()) {

            TemperatureInfo info = new TemperatureInfo();                

            info.setAverage(response.getAverage());
            info.setCity(response.getCity());
            info.setMax(response.getMax());
            info.setMin(response.getMin());
            info.setDate(response.getDate().getTime());

            infos.add(info);
        }

        return infos;
    }

}




VI. Client side context xml file

(client.xml)

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="client" class="com.apress.springrecipes.weather.WeatherServiceClient">
		<property name="weatherService" ref="weatherServiceProxy" />
	</bean>

    <bean id="weatherServiceProxy"
		class="com.apress.springrecipes.weather.WeatherServiceProxy">
	    <property name="defaultUri" value="http://localhost:8080/srch16xmlbeans/services" />
	    <property name="marshaller" ref="marshaller" />
	    <property name="unmarshaller" ref="marshaller" />
	</bean>

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



VII. WSDL and Maven2 POM files

WSDL
Once service has been deployed, WSDL file can be obtained at:

http://localhost:8080/srch16xmlbeans/services/temperature.wsdl



    note: application context is srch16xmlbeans



<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:schema="http://springrecipes.apress.com/weather/schemas"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
targetNamespace="http://springrecipes.apress.com/weather/schemas"> <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified" elementFormDefault="qualified"
targetNamespace="http://springrecipes.apress.com/weather/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> </wsdl:types> <wsdl:message name="GetTemperaturesRequest"> <wsdl:part element="schema:GetTemperaturesRequest" name="GetTemperaturesRequest"> </wsdl:part> </wsdl:message> <wsdl:message name="GetTemperaturesResponse"> <wsdl:part element="schema:GetTemperaturesResponse" name="GetTemperaturesResponse"> </wsdl:part> </wsdl:message> <wsdl:portType name="Weather"> <wsdl:operation name="GetTemperatures"> <wsdl:input message="schema:GetTemperaturesRequest" name="GetTemperaturesRequest"> </wsdl:input> <wsdl:output message="schema:GetTemperaturesResponse" name="GetTemperaturesResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="WeatherBinding" type="schema:Weather"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="GetTemperatures"> <soap:operation soapAction=""/> <wsdl:input name="GetTemperaturesRequest"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="GetTemperaturesResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="WeatherService"> <wsdl:port binding="schema:WeatherBinding" name="WeatherPort"> <soap:address location="http://localhost:8080/srch16xmlbeans/services"/> </wsdl:port> </wsdl:service> </wsdl:definitions>



POM dependencies


    <properties>
        <spring.version>2.5.6</spring.version>
    </properties>   

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws</artifactId>
            <version>1.5.6</version>
            <scope>compile</scope>
            <classifier>all</classifier>
        </dependency>
        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core-tiger</artifactId>
            <version>1.5.6</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.13</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>2.4.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.ws.commons.axiom</groupId>
            <artifactId>axiom-api</artifactId>
            <version>1.2.8</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.ws.commons.axiom</groupId>
            <artifactId>axiom-impl</artifactId>
            <version>1.2.8</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.srch16</groupId>
            <artifactId>temperature</artifactId>
            <version>1.0</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>


    note: temperatures.jar file is also installed at the local repository, as
          indicated at the last dependency.
Advertisements
Developing Spring Web Services with XML Marschalling – XMLBeans example

6 thoughts on “Developing Spring Web Services with XML Marschalling – XMLBeans example

  1. Pranay says:

    Thanks for such a good tutorial , I was wondering if I could have got the source code of the code with proper project any ways I will copy paste the code and test it .
    Some things that I miss in this tutorial are
    1] I did not find temperature.xsd file any where .
    2] anlso you have mention that “note: The generated jar file has to be put into both server and client side classpath.

    I was wondering if the client web service was other then Java how can it work ?
    Clients proxy should be able to generate its own stub as required .
    I thought server side interface , implentor and tempInfo should have been generated by using XML bean
    Regards,
    Pranay

  2. cchweblog says:

    1. temperature.xsd can be found either from the WSDL file in this post (it is embedded inside the WSDL file) or can be obtained from
    the book web site (http://www.apress.com/book/downloadfile/4004).

    2. the reason why jar file is needed at the client side is only because the client side implementation also uses xml marshaller and
    the classes generated from compiling schema file. You can certainly choose any other way to create the client side implementation.

    3. what is not mentioned in the post is that scomp generated a bunch of classes such as GetTemperaturesRequestDocument, GetTemperaturesResponseDocument,
    GetTemperaturesResponse.TemperatureInfo, etc. Since these classes are XMLBeans related, I chose to use top level TemperatureInfo class in the WeatherServiceImpl class and the conversion between GetTemperaturesResponse.TemperatureInfo and TemperatureInfo classes is done in the TemperatureMarshallingEndpoint class, which is also XMLBeans related. Therefore, if next time we choose a different xml marshaller, we can still keep using the top level TemperatureInfo class instead of GetTemperaturesResponse.TemperatureInfo class.

  3. Ashaar Riaz says:

    I have successfully implemented and deployed the web service as mention. But when I am running the service, I am getting the following error message.

    No adapter for endpoint [public org.example.temperatures.TemperatureResponseDocument com.apress.springrecipes.weather.TemperatureMarshallingEndpoint.getTemperature(org.example.temperatures.TemperatureRequestDocument)]: Does your endpoint implement a supported interface like MessageHandler or PayloadEndpoint?

    Any one got the idea how to fix it?

  4. Tex Martin says:

    Hi there cch 🙂
    I had the castor example from “Spring Recipes” running under maven, and was experimenting with making it work using xmlBeans. Your MarshallingEndpoint implemented with xmlBeans was a great help. I’m really happy that you made this available. Thanks a lot.
    Regards Tex Martin

  5. Israel Elias says:

    Yes, you need to add annontations, for your response @ResponsePayload and for your request @RequestPayload. also you need to import the classes from spring
    import org.springframework.ws.server.endpoint.annotation.RequestPayload;
    import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

    Final Endpoint Working
    package com.apress.springrecipes.weather;

    import java.util.List;

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

    @Endpoint
    public class TemperatureMarshallingEndpoint {

    private static final String namespaceUri = “http://springrecipes.apress.com/weather/schemas”;

    @Autowired
    private WeatherService weatherService;

    public void setWeatherService(WeatherService weatherService) {
    this.weatherService = weatherService;
    }

    @PayloadRoot(localPart=”GetTemperaturesRequest”,namespace=namespaceUri)
    @ResponsePayload
    public GetTemperaturesResponse getTemperature(@RequestPayload GetTemperaturesRequest request) {
    List temperatures = weatherService.getTemperatures(request.getCity(), request.getDates());
    return new GetTemperaturesResponse(temperatures);
    }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s