0% found this document useful (0 votes)
45 views

Spring Web Services 2

The document describes creating a data contract and service contract for a web service that handles holiday requests. It discusses defining an XML schema (XSD) from sample data to formalize the data format. The schema is refined to add data types and ensure order is not significant. A WSDL is then created to define the service interface by importing the XSD and adding messages and operations.

Uploaded by

Neemias Júnior
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
45 views

Spring Web Services 2

The document describes creating a data contract and service contract for a web service that handles holiday requests. It discusses defining an XML schema (XSD) from sample data to formalize the data format. The schema is refined to add data types and ensure order is not significant. A WSDL is then created to define the service interface by importing the XSD and adding messages and operations.

Uploaded by

Neemias Júnior
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

09/03/2022 08:44 Spring Web Services Reference Documentation

3.3 Data Contract
Now that we have seen some examples of the XML data that we will use,
it makes sense to formalize this into a schema. This
data contract
defines the message format we accept. There are four different ways
of defining such a contract for XML:

DTDs
XML Schema (XSD)
RELAX NG
Schematron

DTDs have limited namespace support, so they are not suitable for Web
services. Relax NG and Schematron certainly are easier
than XML Schema.
Unfortunately, they are not so widely supported across platforms. We
will use XML Schema.

By far the easiest way to create an XSD is to infer it from sample


documents. Any good XML editor or Java IDE offers this
functionality.
Basically, these tools use some sample XML documents, and generate a
schema from it that validates them all. The
end result certainly needs
to be polished up, but it's a great starting point.

Using the sample described above, we end up with the following


generated schema:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

elementFormDefault="qualified"

targetNamespace="http://mycompany.com/hr/schemas"

xmlns:hr="http://mycompany.com/hr/schemas">

<xs:element name="HolidayRequest">

<xs:complexType>

<xs:sequence>

<xs:element ref="hr:Holiday"/>

<xs:element ref="hr:Employee"/>

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="Holiday">

<xs:complexType>

<xs:sequence>

<xs:element ref="hr:StartDate"/>

<xs:element ref="hr:EndDate"/>

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="StartDate" type="xs:NMTOKEN"/>

<xs:element name="EndDate" type="xs:NMTOKEN"/>

<xs:element name="Employee">

<xs:complexType>

<xs:sequence>

<xs:element ref="hr:Number"/>

<xs:element ref="hr:FirstName"/>

<xs:element ref="hr:LastName"/>

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="Number" type="xs:integer"/>

<xs:element name="FirstName" type="xs:NCName"/>

<xs:element name="LastName" type="xs:NCName"/>

</xs:schema>

This generated schema obviously can be improved. The first thing


to notice is that every type has a root-level element
declaration.
This means that the Web service should be able to accept all of
these elements as data. This is not desirable: we
only want to
accept a <HolidayRequest/> . By removing
the wrapping element tags (thus keeping the types), and inlining
the
results, we can accomplish this.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

xmlns:hr="http://mycompany.com/hr/schemas"

elementFormDefault="qualified"

targetNamespace="http://mycompany.com/hr/schemas">

https://docs.spring.io/spring-ws/docs/2.4.0.RELEASE/reference/htmlsingle/ 10/71
09/03/2022 08:44 Spring Web Services Reference Documentation
<xs:element name="HolidayRequest">

<xs:complexType>

<xs:sequence>

<xs:element name="Holiday" type="hr:HolidayType"/>

<xs:element name="Employee" type="hr:EmployeeType"/>

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:complexType name="HolidayType">

<xs:sequence>

<xs:element name="StartDate" type="xs:NMTOKEN"/>

<xs:element name="EndDate" type="xs:NMTOKEN"/>

</xs:sequence>

</xs:complexType>

<xs:complexType name="EmployeeType">

<xs:sequence>

<xs:element name="Number" type="xs:integer"/>

<xs:element name="FirstName" type="xs:NCName"/>

<xs:element name="LastName" type="xs:NCName"/>

</xs:sequence>

</xs:complexType>

</xs:schema>

The schema still has one problem: with a schema like this, you
can expect the following messages to validate:

<HolidayRequest xmlns="http://mycompany.com/hr/schemas">

<Holiday>

<StartDate>this is not a date</StartDate>

<EndDate>neither is this</EndDate>

</Holiday>

<!-- ... -->

</HolidayRequest>

Clearly, we must make sure that the start and end date are really dates.
XML Schema has an excellent built-in date type which
we can use. We also change the NCName s to
string s. Finally, we change the sequence in
<HolidayRequest/> to all .
This tells the XML parser that the order of
<Holiday/> and
<Employee/> is not significant. Our final
XSD now looks like this:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

xmlns:hr="http://mycompany.com/hr/schemas"

elementFormDefault="qualified"

targetNamespace="http://mycompany.com/hr/schemas">

<xs:element name="HolidayRequest">

<xs:complexType>

<xs:all>

<xs:element name="Holiday" type="hr:HolidayType"/>


<xs:element name="Employee" type="hr:EmployeeType"/>

</xs:all>

</xs:complexType>

</xs:element>

<xs:complexType name="HolidayType">

<xs:sequence>

<xs:element name="StartDate" type="xs:date"/>

<xs:element name="EndDate" type="xs:date"/>


</xs:sequence>
</xs:complexType>

<xs:complexType name="EmployeeType">

<xs:sequence>

<xs:element name="Number" type="xs:integer"/>

<xs:element name="FirstName" type="xs:string"/>

<xs:element name="LastName" type="xs:string"/>


</xs:sequence>
</xs:complexType>

</xs:schema>

all tells the XML parser that the order of


<Holiday/> and
<Employee/> is not significant.

https://docs.spring.io/spring-ws/docs/2.4.0.RELEASE/reference/htmlsingle/ 11/71
09/03/2022 08:44 Spring Web Services Reference Documentation

We use the xsd:date data type, which consist of a year, month, and day, for
<StartDate/> and <EndDate/> .
xsd:string is used for the first and last name.

We store this file as hr.xsd .

3.4 Service contract
A service contract is generally expressed as a WSDL file.
Note that in Spring-WS, writing the WSDL by hand is not required.
Based on the XSD and
some conventions, Spring-WS can create the WSDL for you, as explained in the section entitled
Section 3.6, “Implementing the Endpoint”.
You can skip to the next section if you want to; the
remainder of this section will show
you how to write your own WSDL by hand.

We start our WSDL with the standard preamble, and by importing our existing XSD. To
separate the schema from the definition,
we will use a separate namespace for the WSDL definitions:
http://mycompany.com/hr/definitions .

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

xmlns:schema="http://mycompany.com/hr/schemas"

xmlns:tns="http://mycompany.com/hr/definitions"

targetNamespace="http://mycompany.com/hr/definitions">

<wsdl:types>

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:import namespace="http://mycompany.com/hr/schemas" schemaLocation="hr.xsd"/>

</xsd:schema>

</wsdl:types>

Next, we add our messages based on the written schema types. We only have one message: one with the
<HolidayRequest/> we put in the schema:

<wsdl:message name="HolidayRequest">

<wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/>

</wsdl:message>

We add the message to a port type as an operation:

<wsdl:portType name="HumanResource">

<wsdl:operation name="Holiday">

<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/>

</wsdl:operation>

</wsdl:portType>

That finished the abstract part of the WSDL (the interface, as it were), and leaves the concrete part.
The concrete part consists of
a binding , which tells the client how
to invoke the operations you've just defined; and a service , which tells it
where to
invoke it.

Adding a concrete part is pretty standard: just refer to the abstract part you defined previously, make sure
you use
document/literal for the soap:binding elements
( rpc/encoded is deprecated), pick a soapAction for the operation
(in this
case http://mycompany.com/RequestHoliday , but any URI will do), and determine the
location URL where you want
request to come in (in this case
http://mycompany.com/humanresources ):

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

xmlns:schema="http://mycompany.com/hr/schemas"

xmlns:tns="http://mycompany.com/hr/definitions"

targetNamespace="http://mycompany.com/hr/definitions">

<wsdl:types>

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:import namespace="http://mycompany.com/hr/schemas"
schemaLocation="hr.xsd"/>

</xsd:schema>

</wsdl:types>

<wsdl:message name="HolidayRequest">

https://docs.spring.io/spring-ws/docs/2.4.0.RELEASE/reference/htmlsingle/ 12/71
09/03/2022 08:44 Spring Web Services Reference Documentation
<wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/>
</wsdl:message>

<wsdl:portType name="HumanResource">
<wsdl:operation name="Holiday">

<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/>


</wsdl:operation>

</wsdl:portType>

<wsdl:binding name="HumanResourceBinding" type="tns:HumanResource">


<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="Holiday">

<soap:operation soapAction="http://mycompany.com/RequestHoliday"/>
<wsdl:input name="HolidayRequest">

<soap:body use="literal"/>
</wsdl:input>

</wsdl:operation>

</wsdl:binding>

<wsdl:service name="HumanResourceService">

<wsdl:port binding="tns:HumanResourceBinding" name="HumanResourcePort">


<soap:address location="http://localhost:8080/holidayService/"/>
</wsdl:port>

</wsdl:service>

</wsdl:definitions>

We import the schema defined in Section 3.3, “Data Contract”.


We define the HolidayRequest message, which gets used in the
portType .
The HolidayRequest type is defined in the schema.
We define the HumanResource port type, which gets used in the
binding .
We define the HumanResourceBinding binding, which gets used in the
port .
We use a document/literal style.
The literal http://schemas.xmlsoap.org/soap/http signifies a
HTTP transport.
The soapAction attribute signifies the SOAPAction HTTP
header that will be sent with every request.
The http://localhost:8080/holidayService/ address is the URL where the Web
service can be invoked.

This is the final WSDL. We will describe how to implement the resulting schema and WSDL in the next section.

3.5 Creating the project


In this section, we will be using Maven3 to create the
initial project structure for us. Doing so is not required, but greatly reduces
the amount of code we
have to write to setup our HolidayService.

The following command creates a Maven3 web application project for us, using the Spring-WS archetype
(that is, project
template)

mvn archetype:create -DarchetypeGroupId=org.springframework.ws \

-DarchetypeArtifactId=spring-ws-archetype \

-DarchetypeVersion= \

-DgroupId=com.mycompany.hr \

-DartifactId=holidayService

This command will create a new directory called holidayService . In this directory,
there is a 'src/main/webapp' directory,
which will contain the root of the WAR file.
You will find the standard web application deployment descriptor
'WEB-INF/web.xml'
here, which defines a Spring-WS MessageDispatcherServlet and maps all incoming
requests to this
servlet.

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

version="2.4">

<display-name>MyCompany HR Holiday Service</display-name>


https://docs.spring.io/spring-ws/docs/2.4.0.RELEASE/reference/htmlsingle/ 13/71
09/03/2022 08:44 Spring Web Services Reference Documentation
<display name>MyCompany HR Holiday Service</display name>

<!-- take especial notice of the name of this servlet -->

<servlet>

<servlet-name>spring-ws</servlet-name>

<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>spring-ws</servlet-name>

<url-pattern>/*</url-pattern>

</servlet-mapping>

</web-app>

In addition to the above 'WEB-INF/web.xml' file, you will also need another,
Spring-WS-specific configuration file, named
'WEB-INF/spring-ws-servlet.xml' .
This file contains all of the Spring-WS-specific beans such as EndPoints ,
WebServiceMessageReceivers , and suchlike, and is used to create a new Spring container.
The name of this file is derived
from the name of the attendant servlet (in this case
'spring-ws' ) with '-servlet.xml' appended to it.
So if you defined a
MessageDispatcherServlet with the name
'dynamite' , the name of the Spring-WS-specific configuration file would be
'WEB-INF/dynamite-servlet.xml' .

(You can see the contents of the 'WEB-INF/spring-ws-servlet.xml' file for this
example in ???.)

Once you had the project structure created, you can put the schema and wsdl from previous section into
'WEB-INF/' folder.

3.6 Implementing the Endpoint


In Spring-WS, you will implement Endpoints to handle incoming XML messages.
An endpoint is typically created by annotating a
class with the @Endpoint
annotation.
In this endpoint class, you will create one or more methods that handle incoming request.
The method signatures can be quite flexible: you can include just about any sort of parameter type related
to the incoming XML
message, as will be explained later.

3.6.1 Handling the XML Message


In this sample application, we are going to use JDom 2 to handle
the XML message.
We are also using XPath, because it allows
us to
select particular parts of the XML JDOM tree, without requiring strict schema conformance.

package com.mycompany.hr.ws;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Arrays;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;

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

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

import org.springframework.ws.server.endpoint.annotation.RequestPayload;

import com.mycompany.hr.service.HumanResourceService;

import org.jdom2.Element;

import org.jdom2.JDOMException;

import org.jdom2.Namespace;

import org.jdom2.filter.Filters;

import org.jdom2.xpath.XPathExpression;

import org.jdom2.xpath.XPathFactory;

@Endpoint
public class HolidayEndpoint {

private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";

https://docs.spring.io/spring-ws/docs/2.4.0.RELEASE/reference/htmlsingle/ 14/71
09/03/2022 08:44 Spring Web Services Reference Documentation
private XPathExpression<Element> startDateExpression;

private XPathExpression<Element> endDateExpression;

private XPathExpression<Element> firstNameExpression;

private XPathExpression<Element> lastNameExpression;

private HumanResourceService humanResourceService;

@Autowired

public HolidayEndpoint(HumanResourceService humanResourceService) throws JDOMException {

this.humanResourceService = humanResourceService;

Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);

XPathFactory xPathFactory = XPathFactory.instance();

startDateExpression = xPathFactory.compile("//hr:StartDate", Filters.element(), null, namespace);

endDateExpression = xPathFactory.compile("//hr:EndDate", Filters.element(), null, namespace);

firstNameExpression = xPathFactory.compile("//hr:FirstName", Filters.element(), null, namespace);

lastNameExpression = xPathFactory.compile("//hr:LastName", Filters.element(), null, namespace);

@PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")


public void handleHolidayRequest(@RequestPayload Element holidayRequest) throws Exception {
Date startDate = parseDate(startDateExpression, holidayRequest);

Date endDate = parseDate(endDateExpression, holidayRequest);

String name = firstNameExpression.evaluateFirst(holidayRequest).getText() + " " + lastNameExpression.e

humanResourceService.bookHoliday(startDate, endDate, name);

private Date parseDate(XPathExpression<Element> expression, Element element) throws ParseException {

Element result = expression.evaluateFirst(element);

if (result != null) {

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

return dateFormat.parse(result.getText());

} else {

throw new IllegalArgumentException("Could not evaluate [" + expression + "] on [" + element + "]")
}

The HolidayEndpoint is annotated with


@Endpoint .
This marks the class as a special sort of @Component ,
suitable for
handling XML messages in Spring-WS, and also making it eligible for suitable
for component scanning.
The HolidayEndpoint requires the
HumanResourceService business service to operate, so we
inject the dependency
via the constructor and annotate it with
@Autowired .
Next, we set up XPath expressions using the JDOM2 API.
There are four expressions: //hr:StartDate for
extracting the
<StartDate> text value,
//hr:EndDate for
extracting the end date and two for extracting the names of
the employee.
The @PayloadRoot annotation tells Spring-WS that the
handleHolidayRequest method is suitable for handling XML
messages.
The sort of message that this method can handle is indicated by the annotation values,
in this case, it can
handle XML elements that have the HolidayRequest
local part and the http://mycompany.com/hr/schemas
namespace.
More information about mapping messages to endpoints is provided in the next section.
The handleHolidayRequest(..) method is the main handling method
method, which gets passed with the
<HolidayRequest/> element from
the incoming XML message.
The @RequestPayload annotation indicates that the
holidayRequest parameter should be mapped to the payload of the
request message.
We use the XPath expressions to extract the string values from the XML messages,
and convert these values to Date
objects using a
SimpleDateFormat (the parseData method).
With these values, we invoke a method on the business service.
Typically, this will result in a database transaction being
started, and some records being
altered in the database.

https://docs.spring.io/spring-ws/docs/2.4.0.RELEASE/reference/htmlsingle/ 15/71

You might also like