diff --git a/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java b/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java
index af717f60..512c0fc0 100644
--- a/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java
+++ b/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -283,8 +283,8 @@ public class MessageDispatcher implements WebServiceMessageReceiver, BeanNameAwa
return endpointAdapter;
}
}
- throw new IllegalStateException("No adapter for endpoint [" + endpoint + "]: Does your endpoint implement a " +
- "supported interface like MessageHandler or PayloadEndpoint?");
+ throw new IllegalStateException("No adapter for endpoint [" + endpoint + "]: Is your endpoint annotated with " +
+ "@Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint?");
}
/**
diff --git a/samples/tutorial/src/main/java/com/mycompany/hr/service/StubHumanResourceService.java b/samples/tutorial/src/main/java/com/mycompany/hr/service/StubHumanResourceService.java
index 65fe8ab3..3da8b6d1 100644
--- a/samples/tutorial/src/main/java/com/mycompany/hr/service/StubHumanResourceService.java
+++ b/samples/tutorial/src/main/java/com/mycompany/hr/service/StubHumanResourceService.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2007 the original author or authors.
+ * Copyright 2005-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,7 +17,8 @@
package com.mycompany.hr.service;
import java.util.Date;
-import java.util.logging.Logger;
+
+import org.springframework.stereotype.Service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -27,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
*
* @author Arjen Poutsma
*/
+@Service
public class StubHumanResourceService implements HumanResourceService {
private static final Log logger = LogFactory.getLog(StubHumanResourceService.class);
diff --git a/samples/tutorial/src/main/java/com/mycompany/hr/ws/HolidayEndpoint.java b/samples/tutorial/src/main/java/com/mycompany/hr/ws/HolidayEndpoint.java
index 091659f8..5ec48bcd 100644
--- a/samples/tutorial/src/main/java/com/mycompany/hr/ws/HolidayEndpoint.java
+++ b/samples/tutorial/src/main/java/com/mycompany/hr/ws/HolidayEndpoint.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2007 the original author or authors.
+ * Copyright 2005-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -19,12 +19,16 @@ package com.mycompany.hr.ws;
import java.text.SimpleDateFormat;
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.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;
-import org.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint;
/**
* This endpoint handles holiday requests. It uses a combination of JDOM and XPath to extract interesting pieces of XML
@@ -32,7 +36,10 @@ import org.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint;
*
* @author Arjen Poutsma
*/
-public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
+@Endpoint
+public class HolidayEndpoint {
+
+ private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";
private XPath startDateExpression;
@@ -42,9 +49,10 @@ public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
private HumanResourceService humanResourceService;
+ @Autowired
public HolidayEndpoint(HumanResourceService humanResourceService) throws JDOMException {
this.humanResourceService = humanResourceService;
- Namespace namespace = Namespace.getNamespace("hr", "http://mycompany.com/hr/schemas");
+ Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);
startDateExpression = XPath.newInstance("//hr:StartDate");
startDateExpression.addNamespace(namespace);
endDateExpression = XPath.newInstance("//hr:EndDate");
@@ -53,14 +61,14 @@ public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
nameExpression.addNamespace(namespace);
}
- protected Element invokeInternal(Element holidayRequest) throws Exception {
+ @PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")
+ public void handleHolidayRequest(@RequestPayload Element holidayRequest) throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date startDate = dateFormat.parse(startDateExpression.valueOf(holidayRequest));
Date endDate = dateFormat.parse(endDateExpression.valueOf(holidayRequest));
String name = nameExpression.valueOf(holidayRequest);
humanResourceService.bookHoliday(startDate, endDate, name);
- return null;
}
}
diff --git a/samples/tutorial/src/main/webapp/WEB-INF/spring-ws-servlet.xml b/samples/tutorial/src/main/webapp/WEB-INF/spring-ws-servlet.xml
index f16e2228..452848b7 100644
--- a/samples/tutorial/src/main/webapp/WEB-INF/spring-ws-servlet.xml
+++ b/samples/tutorial/src/main/webapp/WEB-INF/spring-ws-servlet.xml
@@ -16,24 +16,11 @@
-->
+ 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">
-
-
-
-
-
-
-
-
-
- holidayEndpoint
-
-
-
-
-
-
+
diff --git a/src/docbkx/tutorial.xml b/src/docbkx/tutorial.xml
index 2b7031f9..c934ab3d 100644
--- a/src/docbkx/tutorial.xml
+++ b/src/docbkx/tutorial.xml
@@ -519,9 +519,10 @@
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'.
+ '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
@@ -532,94 +533,132 @@
Implementing the Endpoint
In Spring-WS, you will implement Endpoints to handle incoming XML messages.
- There are two flavors of endpoints: message endpoints and
- payload endpoints. Message endpoints
- give access to the entire XML message, including SOAP headers. Typically, the endpoint will only be
- interested in the payload of the message, that is the contents of the SOAP body.
- In that case, creating a payload endpoint makes more sense.
+ 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.
Handling the XML Message
In this sample application, we are going to use JDom 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. We extend our endpoint from AbstractJDomPayloadEndpoint,
- because that will give us a JDOM element to execute the XPath queries on.
+ 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;
+
+ Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);
+
+ startDateExpression = XPath.newInstance("//hr:StartDate");
+ startDateExpression.addNamespace(namespace);
+
+ endDateExpression = XPath.newInstance("//hr:EndDate");
+ endDateExpression.addNamespace(namespace);
+
+ nameExpression = XPath.newInstance("concat(//hr:FirstName,' ',//hr:LastName)");
+ nameExpression.addNamespace(namespace);
+ }
+
+ @PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")
+ public void handleHolidayRequest(@RequestPayload Element holidayRequest)
+ throws Exception {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ Date startDate = dateFormat.parse(startDateExpression.valueOf(holidayRequest));
+ Date endDate = dateFormat.parse(endDateExpression.valueOf(holidayRequest));
+ String name = nameExpression.valueOf(holidayRequest);
+
+ humanResourceService.bookHoliday(startDate, endDate, name);
+ }
+
+}]]>
+
+
+ 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. Next, we set up XPath expressions
- using the JDOM API. There are three expressions: //hr:StartDate for
+ inject the dependency via the constructor and annotate it with
+ @Autowired.
+ Next, we set up XPath expressions using the JDOM API.
+ There are three expressions: //hr:StartDate for
extracting the <StartDate> text value,
//hr:EndDate for
extracting the end date and concat(//hr:FirstName,' ',//hr:LastName)
for extracting and concatenating the names of the employee.
-
+
- The invokeInternal(..) method is a template method, which gets passed
- with the <HolidayRequest/> element from the incoming XML message. We
- use the XPath expressions to extract the string values from the XML messages,
+ 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.
+ We use the XPath expressions to extract the string values from the XML messages,
and convert these values to Date objects using a
- SimpleDateFormat. 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. Finally, we return
- null, which indicates to Spring-WS that we do not want to send a
- response message. If we wanted a response message, we could have returned a JDOM Element
+ SimpleDateFormat.
+ 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.
+ Finally, we define a void return type, which indicates to Spring-WS
+ that we do not want to send a response message.
+ If we wanted a response message, we could have returned a JDOM Element
that represents the payload of the response message.
@@ -628,7 +667,8 @@ public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
Using JDOM is just one of the options to handle the XML: other options include DOM, dom4j, XOM,
SAX, and StAX, but also marshalling techniques like JAXB, Castor, XMLBeans,
- JiBX, and XStream. We chose JDOM because it gives us access to the raw XML, and because it
+ JiBX, and XStream, as is explained in the next chapter.
+ We chose JDOM because it gives us access to the raw XML, and because it
is based on classes (not interfaces and factory methods as with W3C DOM and dom4j), which makes the
code less verbose. We use XPath because it is less fragile than marshalling technologies: we don't
care for strict schema conformance, as long as we can find the dates and the name.
@@ -641,7 +681,7 @@ public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
org.springframework.wsspring-ws-core
- 2.0.0-M1
+ 2.0.0jdom
@@ -653,61 +693,40 @@ public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
jaxen1.1
-
- javax.xml.soap
- saaj-api
- 1.3
- runtime
-
-
- com.sun.xml.messaging.saaj
- saaj-impl
- 1.3
- runtime
-
]]>
Here is how we would configure these classes in our spring-ws-servlet.xml
- Spring XML configuration file:
+ Spring XML configuration file, by using component scanning.
-
+
-
-
-
-
-
+ ]]>
Routing the Message to the Endpoint
- Now that we have written an endpoint that handles the message, we must define how incoming messages
- are routed to that endpoint. In Spring-WS, this is the responsibility of an
- EndpointMapping. In this tutorial, we will route messages based on
- their content, by using a PayloadRootQNameEndpointMapping. Here is how we
- configure a PayloadRootQNameEndpointMapping in spring-ws-servlet.xml:
-
-
-
-
- holidayEndpoint
-
-
-
-
-
-]]>
-
- This means that whenever an XML message is received with the namespace
+ As part of writing the endpoint, we also used the @PayloadRoot
+ annotation to indicate which sort of messages can be handled by the
+ handleHolidayRequest method.
+ In Spring-WS, this process is the responsibility of an
+ EndpointMapping.
+ In this tutorial, we route messages based on
+ their content, by using a PayloadRootAnnotationMethodEndpointMapping.
+ The annotation used above:
+@PayloadRoot(namespace = "http://mycompany.com/hr/schemas", localPart = "HolidayRequest")
+ basically means that whenever an XML message is received with the namespace
http://mycompany.com/hr/schemas and the
HolidayRequest local name, it will be routed to the
- holidayEndpoint.
- (It also adds a PayloadLoggingInterceptor,
- that dumps incoming and outgoing messages to the log.)
+ handleHolidayRequest method.
+
+
+ There are also other ways to map endpoints to XML messages, which will be described in the next
+ chapter.