Developing Spring Web Services with XML Marschalling – JAXB2 example

Following the previous post, an example of using JAXB2 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 dates);
}





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) {
            TemperatureInfo info = new TemperatureInfo();

            info.setCity(city);
            info.setAverage(123.0f);
            info.setMin(10.0f);
            info.setMax(200.2f);
            info.setDate(date);

            temperatures.add(info);
        }

        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 float min;
    private float max;
    private float average;

    public TemperatureInfo() {}

    public TemperatureInfo(String city, Date date, float min, float max, float average) {
        this.city = city;
        this.date = date;
        this.min = min;
        this.max = max;
        this.average = average;
    }

    public float getAverage() {
        return average;
    }

    public String getCity() {
        return city;
    }

    public Date getDate() {
        return date;
    }

    public float getMax() {
        return max;
    }

    public float getMin() {
        return min;
    }

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

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

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

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

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




II. Compile schema

Use xjc to generate Java types. For example, to generate Java classes from schema, use JAXB XJC Maven2 plugin


<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
        <configuration>
            <generateDirectory>src/main/generated-sources/xjc</generateDirectory>
            <generatePackage>com.apress.springrecipes.weather</generatePackage>
        </configuration>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>


note 1: The package and location of generated Java files can be specifically specified. note 2: Not all getter/setter methods are generated, sometimes you have to add them by yourself.



III. Marshalling Endpoint


package com.apress.springrecipes.weather;

import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

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

@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 GetTemperaturesResponse getTemperature(GetTemperaturesRequest request) {

        List xmlCalendarList = request.getDate();

        List dates = new LinkedList();

        for (XMLGregorianCalendar xmlCalendar : xmlCalendarList) {
            Date date = xmlCalendar.toGregorianCalendar().getTime();
            dates.add(date);
        }

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

        List temperatures =
            new LinkedList();

        for (TemperatureInfo info : infos) {
            GetTemperaturesResponse.TemperatureInfo temperature =
                new GetTemperaturesResponse.TemperatureInfo();

            temperature.setAverage(info.getAverage());
            temperature.setMin(info.getMin());
            temperature.setMax(info.getMax());
            temperature.setCity(info.getCity());

            try {
                GregorianCalendar calendar = new GregorianCalendar();
                calendar.setTime(info.getDate());

                XMLGregorianCalendar xmlCalendar =
                    DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);

                temperature.setDate(xmlCalendar);

            } catch (Exception e) {
                e.printStackTrace();
            }

            temperatures.add(temperature);
        }

        GetTemperaturesResponse response = new GetTemperaturesResponse();
        response.setTemperatureInfo(temperatures);

        return response;
    }
}





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.jaxb.Jaxb2Marshaller"> <property name="classesToBeBound"> <list> <value>com.apress.springrecipes.weather.GetTemperaturesRequest</value> <value>com.apress.springrecipes.weather.GetTemperaturesResponse</value> </list> </property> </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/srch16jaxb/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.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 temperatures = client.getTodayTemperature("Houston");

        for (TemperatureInfo temperature  : temperatures) {
            System.out.println("Date            : " + temperature.getDate().toString());
            System.out.println("Min temperature : " + temperature.getMin());
            System.out.println("Max temperature : " + temperature.getMax());
            System.out.println("Average temperature : " + temperature.getAverage());
        }
    }
}




WeatherServiceClient class


package com.apress.springrecipes.weather;

import java.util.Arrays;
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 = Arrays.asList(new Date[] { new Date() });

        List temperatures =
            weatherService.getTemperatures(city, dates);

        return temperatures;
    }
}





WeatherServiceProxy


package com.apress.springrecipes.weather;

import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedList;
import java.util.List;

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

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

public class WeatherServiceProxy extends WebServiceGatewaySupport
        implements WeatherService {

    public List getTemperatures(String city, List dates) {
        GetTemperaturesRequest request = new GetTemperaturesRequest();
        request.setCity(city);

        List xmlCalendarList = new LinkedList();

        for (Date date : dates) {
            try {
                GregorianCalendar calendar = new GregorianCalendar();

                calendar.setTime(date);

                XMLGregorianCalendar xmlCalendar =
                    DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);

                xmlCalendarList.add(xmlCalendar);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        request.setDate(xmlCalendarList);

        GetTemperaturesResponse response = (GetTemperaturesResponse)
            getWebServiceTemplate().marshalSendAndReceive(request);

        List remperatures = new LinkedList();

        for (GetTemperaturesResponse.TemperatureInfo info : response.getTemperatureInfo()) {
            TemperatureInfo temperature = new TemperatureInfo();
            temperature.setAverage(info.getAverage());
            temperature.setCity(info.getCity());
            temperature.setMax(info.getMax());
            temperature.setMin(info.getMin());
            temperature.setDate(info.getDate().toGregorianCalendar().getTime());

            remperatures.add(temperature);
        }        

        return remperatures;
    }
}




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/srch16jaxb/services" /> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> </bean> <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="classesToBeBound"> <list> <value>com.apress.springrecipes.weather.GetTemperaturesRequest</value> <value>com.apress.springrecipes.weather.GetTemperaturesResponse</value> </list> </property> </bean> </beans>



VII. WSDL and Maven2 POM files

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

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



    note: application context is srch16jaxb



<?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/srch16jaxb/services"/> </wsdl:port> </wsdl:service> </wsdl:definitions>



POM dependencies



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

	<pluginRepositories>
	    <pluginRepository>
	        <id>maven2-repository.dev.java.net</id>
	        <name>Java.net Maven 2 Repository</name>
	        <url>http://download.java.net/maven/2</url>
	    </pluginRepository>
	</pluginRepositories>	

	<dependencies>
	   <dependency>
	       <groupId>javax.xml.bind</groupId>
	       <artifactId>jaxb-api</artifactId>
	       <version>2.0</version>
	       <scope>compile</scope>
	   </dependency>
        <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>
	</dependencies>





Advertisements
Developing Spring Web Services with XML Marschalling – JAXB2 example

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

  1. alex says:

    can you elaborate on step II. Compile schema
    I added maven-jaxb2-plugin as you have it to my pom.xml
    running mvn install
    Please help I am getting an error:

    ———–
    [ERROR] BUILD ERROR
    [INFO] ————————————————————————
    [INFO] Error parsing the command line [[Ljava.lang.String;@1ba6076]

    Embedded error: grammar is not specified
    [INFO] ————————————————————————
    [INFO] Trace
    org.apache.maven.lifecycle.LifecycleExecutionException: Error parsing the comman
    d line [[Ljava.lang.String;@1ba6076]
    ————-

  2. alex says:

    i could not make it work
    can you, please, give more details for this example.
    It is missing steps.
    Do you also have a source code that you can share?

    thank you

  3. Stefan Rufer says:

    A minor detail but important to note: is an important config line in the weather-servlet.xml file. If omitted the endpoint can not be found by Spring and you get 404 back from your servlet engine and something like WARNING: No endpoint mapping found for [SaajSoapMessage {http://yourlocation/schemas}PingRequest]

    hth

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