Documentation
This commit is contained in:
@@ -586,13 +586,13 @@
|
|||||||
Consider the following sample endpoint:
|
Consider the following sample endpoint:
|
||||||
<programlistingco>
|
<programlistingco>
|
||||||
<areaspec>
|
<areaspec>
|
||||||
<area coords="9" id="server-endpoint-atEndpoint"/>
|
<area coords="10" id="server-endpoint-atEndpoint"/>
|
||||||
<area coords="14" id="server-endpoint-constructor"/>
|
<area coords="15" id="server-endpoint-constructor"/>
|
||||||
<area coords="20" id="server-endpoint-order"/>
|
<area coords="21" id="server-endpoint-order"/>
|
||||||
<area coords="27" id="server-endpoint-getOrder"/>
|
<area coords="28" id="server-endpoint-getOrder"/>
|
||||||
<areaset coords="" id="server-endpoint-payloadRoot">
|
<areaset coords="" id="server-endpoint-payloadRoot">
|
||||||
<area coords="19" id="server-endpoint-payloadRoot-1"/>
|
<area coords="20" id="server-endpoint-payloadRoot-1"/>
|
||||||
<area coords="25" id="server-endpoint-payloadRoot-2"/>
|
<area coords="26" id="server-endpoint-payloadRoot-2"/>
|
||||||
</areaset>
|
</areaset>
|
||||||
</areaspec>
|
</areaspec>
|
||||||
<programlisting><![CDATA[package samples;
|
<programlisting><![CDATA[package samples;
|
||||||
@@ -602,6 +602,7 @@ import org.w3c.dom.Element;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.ws.server.endpoint.annotation.Endpoint;
|
import org.springframework.ws.server.endpoint.annotation.Endpoint;
|
||||||
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
|
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
|
||||||
|
import org.springframework.ws.soap.SoapHeader;
|
||||||
|
|
||||||
@Endpoint
|
@Endpoint
|
||||||
public class AnnotationOrderEndpoint {
|
public class AnnotationOrderEndpoint {
|
||||||
@@ -621,7 +622,8 @@ public class AnnotationOrderEndpoint {
|
|||||||
|
|
||||||
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
|
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
|
||||||
@ResponsePayload
|
@ResponsePayload
|
||||||
public Order getOrder(@RequestPayload OrderRequest orderRequest) {
|
public Order getOrder(@RequestPayload OrderRequest orderRequest, SoapHeader header) {
|
||||||
|
checkSoapHeaderForSomething(header);
|
||||||
return orderService.getOrder(orderRequest.getId());
|
return orderService.getOrder(orderRequest.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,7 +664,9 @@ public class AnnotationOrderEndpoint {
|
|||||||
<interfacename>@XmlRootElement</interfacename>).
|
<interfacename>@XmlRootElement</interfacename>).
|
||||||
This means that the payload of the message is passed on to this method as a unmarshalled
|
This means that the payload of the message is passed on to this method as a unmarshalled
|
||||||
object.
|
object.
|
||||||
The method is also annotated with <interfacename>@ResponseBody</interfacename>,
|
The <interfacename>SoapHeader</interfacename> type is also given as a parameter.
|
||||||
|
On invocation, this parameter will contain the SOAP header of the request message.
|
||||||
|
The method is also annotated with <interfacename>@ResponsePayload</interfacename>,
|
||||||
indicating that the return value (the <classname>Order</classname>) is used as the payload
|
indicating that the return value (the <classname>Order</classname>) is used as the payload
|
||||||
of the response message.
|
of the response message.
|
||||||
</para>
|
</para>
|
||||||
@@ -833,7 +837,7 @@ public void order(@RequestPayload Element orderElement) {
|
|||||||
and that is annotated with <interfacename>@XPathParam</interfacename>.
|
and that is annotated with <interfacename>@XPathParam</interfacename>.
|
||||||
</entry>
|
</entry>
|
||||||
<entry align="center">✗</entry>
|
<entry align="center">✗</entry>
|
||||||
<entry>Enabled by default.</entry>
|
<entry>Enabled by default, see <xref linkend="server-xpath-param" />.</entry>
|
||||||
</row>
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry align="center">Message context</entry>
|
<entry align="center">Message context</entry>
|
||||||
@@ -909,451 +913,170 @@ public void order(@RequestPayload Element orderElement) {
|
|||||||
Refer to the class-level Javadoc of <classname>DefaultMethodEndpointAdapter</classname> and
|
Refer to the class-level Javadoc of <classname>DefaultMethodEndpointAdapter</classname> and
|
||||||
<interfacename>MethodArgumentResolver</interfacename> to see how.
|
<interfacename>MethodArgumentResolver</interfacename> to see how.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
<section id="server-xpath-param">
|
||||||
<section>
|
<title><interfacename>@XPathParam</interfacename></title>
|
||||||
<title>Handling method return types</title>
|
<para>
|
||||||
<para>
|
One parameter type needs some extra explanation: <interfacename>@XPathParam</interfacename>.
|
||||||
To map the return value to the payload of the response message, you will need to annotate the
|
The idea here is that you simply annotate one or more method
|
||||||
method with the <interfacename>@ResponsePayload</interfacename> annotation.
|
parameter with an XPath expression, and that each such annotated parameter will be bound to the
|
||||||
This annotation tells Spring-WS that the return value needs to be bound to the response payload.
|
evaluation of the expression.
|
||||||
</para>
|
Here is an example:
|
||||||
</section>
|
</para>
|
||||||
</section>
|
<programlisting id="server-payload-root-annotation">package samples;
|
||||||
<section>
|
|
||||||
<title><classname>AbstractDomPayloadEndpoint</classname> and other DOM endpoints</title>
|
|
||||||
<para>
|
|
||||||
One of the most basic ways to handle the incoming XML payload is by using a DOM (Document Object Model)
|
|
||||||
API. By extending from <classname>AbstractDomPayloadEndpoint</classname>, you can use the
|
|
||||||
<package>org.w3c.dom.Element</package> and related classes to handle the request and create the
|
|
||||||
response. When using the <classname>AbstractDomPayloadEndpoint</classname> as the baseclass for your
|
|
||||||
endpoints you only have to override the <methodname>invokeInternal(Element, Document)</methodname>
|
|
||||||
method, implement your logic, and return an <interfacename>Element</interfacename> if a response is
|
|
||||||
necessary. Here is a short example consisting of a class and a declaration in the application context.
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[package samples;
|
|
||||||
|
|
||||||
public class SampleEndpoint extends AbstractDomPayloadEndpoint {
|
|
||||||
|
|
||||||
private String responseText;
|
|
||||||
|
|
||||||
public SampleEndpoint(String responseText) {
|
|
||||||
this.responseText = responseText;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Element invokeInternal(
|
|
||||||
Element requestElement,
|
|
||||||
Document document) throws Exception {
|
|
||||||
String requestText = requestElement.getTextContent();
|
|
||||||
System.out.println("Request text: " + requestText);
|
|
||||||
|
|
||||||
Element responseElement = document.createElementNS("http://samples", "response");
|
|
||||||
responseElement.setTextContent(responseText);
|
|
||||||
return responseElement;
|
|
||||||
}
|
|
||||||
}]]></programlisting>
|
|
||||||
<programlisting><![CDATA[<bean id="sampleEndpoint" class="samples.SampleEndpoint">
|
|
||||||
<constructor-arg value="Hello World!"/>
|
|
||||||
</bean>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
The above class and the declaration in the application context are all you need besides setting up an
|
|
||||||
endpoint mapping (see the section entitled <xref linkend="server-endpoint-mapping"/>) to get this very
|
|
||||||
simple endpoint working. The SOAP message handled by this endpoint will look something like:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<SOAP-ENV:Body>]]><emphasis role="bold"><![CDATA[
|
|
||||||
<request xmlns="http://samples">
|
|
||||||
Hello
|
|
||||||
</request>]]></emphasis><![CDATA[
|
|
||||||
</SOAP-ENV:Body>
|
|
||||||
</SOAP-ENV:Envelope>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
Though it could also handle the following Plain Old XML (POX) message, since we are only working on
|
|
||||||
the <emphasis>payload</emphasis> of the message, and do not care whether it is SOAP or POX.
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<request xmlns="http://samples">
|
|
||||||
Hello
|
|
||||||
</request>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
The SOAP response looks like:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<SOAP-ENV:Body>]]><emphasis role="bold"><![CDATA[
|
|
||||||
<response xmlns="http://samples">
|
|
||||||
Hello World!
|
|
||||||
</response>]]></emphasis><![CDATA[
|
|
||||||
</SOAP-ENV:Body>
|
|
||||||
</SOAP-ENV:Envelope>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
Besides the <classname>AbstractDomPayloadEndpoint</classname>, which uses W3C DOM, there are other
|
|
||||||
base classes which use alternative DOM APIs. Spring Web Services supports most DOM APIs, so that you
|
|
||||||
can use the one you are familiar with. For instance, the
|
|
||||||
<classname>AbstractJDomPayloadEndpoint</classname> allows you to use JDOM, and the
|
|
||||||
<classname>AbstractXomPayloadEndpoint</classname> uses XOM to handle the XML. All of these endpoints
|
|
||||||
have an <methodname>invokeInternal</methodname> method similar to above.
|
|
||||||
Also, consider using Spring-WS's XPath support to extract the information you need out of the payload.
|
|
||||||
(See the section entitled <xref linkend="xpath"/> for details.)
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<title><classname>AbstractMarshallingPayloadEndpoint</classname></title>
|
|
||||||
<para>
|
|
||||||
Rather than handling XML directly using DOM, you can use marshalling to convert the payload of the XML
|
|
||||||
message into a Java Object. Spring Web Services offers the
|
|
||||||
<classname>AbstractMarshallingPayloadEndpoint</classname> for this purpose, which is built on the
|
|
||||||
Spring's OXM package. The
|
|
||||||
<classname>AbstractMarshallingPayloadEndpoint</classname> has two properties:
|
|
||||||
<property>marshaller</property> and <property>unmarshaller</property>, in which you can inject in the
|
|
||||||
constructor or by setters.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
When extending from <classname>AbstractMarshallingPayloadEndpoint</classname>, you have to override
|
|
||||||
the <methodname>invokeInternal(Object)</methodname> method, where the passed
|
|
||||||
<classname>Object</classname> represents the unmarshalled request payload, and return an
|
|
||||||
<classname>Object</classname> that will be marshalled into the response payload. Here is an
|
|
||||||
example:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[package samples;
|
|
||||||
|
|
||||||
import org.springframework.oxm.Marshaller;
|
|
||||||
import org.springframework.oxm.Unmarshaller;
|
|
||||||
|
|
||||||
public class MarshallingOrderEndpoint extends AbstractMarshallingPayloadEndpoint{
|
|
||||||
|
|
||||||
private final OrderService orderService;
|
|
||||||
|
|
||||||
public MarshallingOrderEndpoint(OrderService orderService, Marshaller marshaller) {
|
|
||||||
super(marshaller);
|
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object invokeInternal(Object request) throws Exception {
|
|
||||||
OrderRequest orderRequest = (OrderRequest) request;
|
|
||||||
Order order = orderService.getOrder(orderRequest.getId());
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
}]]></programlisting>
|
|
||||||
<programlisting><![CDATA[<beans>
|
|
||||||
<bean id="orderEndpoint" class="samples.MarshallingOrderEndpoint">
|
|
||||||
<constructor-arg ref="orderService"/>
|
|
||||||
<constructor-arg ref="marshaller"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
|
|
||||||
<property name="classesToBeBound">
|
|
||||||
<list>
|
|
||||||
<value>samples.OrderRequest</value>
|
|
||||||
<value>samples.Order</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="orderService" class="samples.DefaultOrderService"/>
|
|
||||||
|
|
||||||
]]><lineannotation><!-- Other beans, such as the endpoint mapping --></lineannotation><![CDATA[
|
|
||||||
</beans>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
In this sample, we configure a
|
|
||||||
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/oxm.html#oxm-jaxb2">Jaxb2Marshaller</ulink>
|
|
||||||
for the
|
|
||||||
<classname>OrderRequest</classname> and <classname>Order</classname> classes, and inject that
|
|
||||||
marshaller together with the
|
|
||||||
<classname>DefaultOrderService</classname> into our endpoint. This business service is not shown, but
|
|
||||||
it is a normal transactional service, probably using DAOs to obtain data from a database.
|
|
||||||
In the <methodname>invokeInternal</methodname> method, we cast the request object to an
|
|
||||||
<classname>OrderRequest</classname> object, which is the JAXB object representing the payload of the
|
|
||||||
request. Using the identifier of that request, we obtain an order from our business service and return
|
|
||||||
it. The returned object is marshalled into XML, and used as the payload of the response message.
|
|
||||||
The SOAP request handled by this endpoint will look like:
|
|
||||||
</para>
|
|
||||||
<programlisting id="server-order-request"><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<SOAP-ENV:Body>
|
|
||||||
<orderRequest xmlns="http://samples" id="42"/>
|
|
||||||
</SOAP-ENV:Body>
|
|
||||||
</SOAP-ENV:Envelope>
|
|
||||||
]]></programlisting>
|
|
||||||
<para>
|
|
||||||
The resulting response will be something like:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<SOAP-ENV:Body>
|
|
||||||
<order xmlns="http://samples" id="42">
|
|
||||||
<item id="100">
|
|
||||||
<quantity>1</quantity>
|
|
||||||
<price>20.0</price>
|
|
||||||
</item>
|
|
||||||
<item id="101">
|
|
||||||
<quantity>1</quantity>
|
|
||||||
<price>10.0</price>
|
|
||||||
</item>
|
|
||||||
</order>
|
|
||||||
</SOAP-ENV:Body>
|
|
||||||
</SOAP-ENV:Envelope>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
Instead of JAXB 2, we could have used <ulink
|
|
||||||
url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/oxm.html">any Spring OXM marshallers</ulink>.
|
|
||||||
The only thing that would change in the above example is the configuration of the
|
|
||||||
<literal>marshaller</literal> bean.
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<title>Using Spring <interfacename>Validator</interfacename> with Marshalling Endpoints</title>
|
|
||||||
<para>
|
|
||||||
It is possible to use <ulink url="http://static.springframework.org/spring/docs/2.5.x/reference/validation.html#validator"><interfacename>Validator</interfacename></ulink>
|
|
||||||
objects in conjunction with marshalling endpoints in order to validate the unmarshalled payloads.
|
|
||||||
Spring-WS provides 2 extensions of <classname>AbstractMarshallingPayloadEndpoint</classname> for that purpose:
|
|
||||||
<classname>AbstractValidatingMarshallingPayloadEndpoint</classname> and
|
|
||||||
<classname>AbstractFaultCreatingValidatingMarshallingPayloadEndpoint</classname>. The former is the most
|
|
||||||
general whereas the latter specializes in creating SOAP faults in response to validation errors.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
Both classes support setting one or more <interfacename>Validator</interfacename> objects via the
|
|
||||||
<property>validator</property> and <property>validators</property> properties respectively.
|
|
||||||
Note that all of the injected validators
|
|
||||||
<emphasis role="bold">must</emphasis> support the request object (through the <methodname>supports</methodname> method)
|
|
||||||
or else an <exceptionname>IllegalArgumentException</exceptionname> will be thrown.
|
|
||||||
</para>
|
|
||||||
<note>
|
|
||||||
<para>
|
|
||||||
The default request object name used in the validator is <literal>request</literal>.
|
|
||||||
The error codes are generated in consequence. For instance, assuming a POJO with a
|
|
||||||
"<property>name</property>" property of type <literal>java.lang.String</literal>,
|
|
||||||
calling <methodname>errors.rejectValue("name","invalidValue")</methodname> in the
|
|
||||||
<methodname>validate</methodname> method of a <interfacename>Validator</interfacename>
|
|
||||||
generates the following error codes:
|
|
||||||
<literal>invalidValue.request.name</literal>, <literal>invalidValue.name</literal>,
|
|
||||||
<literal>invalidValue.java.lang.String</literal> and <literal>invalidValue</literal>.
|
|
||||||
Similarly, calling <methodname>errors.reject("invalidValue")</methodname>
|
|
||||||
generates <literal>invalidValue.request</literal> and <literal>invalidValue</literal> as error codes.
|
|
||||||
</para>
|
|
||||||
</note>
|
|
||||||
<section>
|
|
||||||
<title><classname>AbstractValidatingMarshallingPayloadEndpoint</classname></title>
|
|
||||||
<para>
|
|
||||||
Subclasses of <classname>AbstractValidatingMarshallingPayloadEndpoint</classname>
|
|
||||||
implement the validation error handling logic by overriding the <methodname>onValidationErrors</methodname>
|
|
||||||
method. This method is called when a validation error occurs and its return value
|
|
||||||
indicates whether the endpoint should continue processing the request or not.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
In the following example, a custom error POJO is marshalled and sent as a response:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[public class MyMarshallingEndpoint extends AbstractValidatingMarshallingPayloadEndpoint{
|
|
||||||
|
|
||||||
private MessageSource messageSource;
|
|
||||||
|
|
||||||
protected Object invokeInternal(Object requestObject) throws Exception {
|
|
||||||
// process the payload
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean onValidationErrors(MessageContext messageContext, Object requestObject, Errors errors) {
|
|
||||||
FieldError error = errors.getFieldError("name");
|
|
||||||
CustomError customError = new CustomError();
|
|
||||||
String message = messageSource.getMessage(error, Locale.ENGLISH);
|
|
||||||
customError.setMessage(message);
|
|
||||||
try {
|
|
||||||
getMarshaller().marshal(customError, messageContext.getResponse().getPayloadResult());
|
|
||||||
} catch (XmlMappingException ex) {
|
|
||||||
// handle the exception
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// handle the exception
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]]></programlisting>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<title><classname>AbstractFaultCreatingValidatingMarshallingPayloadEndpoint</classname></title>
|
|
||||||
<para>
|
|
||||||
Endpoints of this type generate a SOAP fault whenever a validation error occurs.
|
|
||||||
By default, a fault detail element is generated for each validation error.
|
|
||||||
The error codes are resolved using the application context message source.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
The properties of <classname>AbstractFaultCreatingValidatingMarshallingPayloadEndpoint</classname>
|
|
||||||
have sensible defaults, which makes its subclasses quite simple to configure as in the following example:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
|
|
||||||
<property name="basename" value="message"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="orderValidator" class="samples.OrderValidator"/>
|
|
||||||
|
|
||||||
<bean id="marshallingOrderEndpoint" class="samples.MarshallingOrderEndpoint">
|
|
||||||
<property name="marshaller" ref="marshaller"/>
|
|
||||||
<property name="unmarshaller" ref="marshaller"/>
|
|
||||||
<property name="validator" ref="orderValidator"/>
|
|
||||||
</bean>
|
|
||||||
]]></programlisting>
|
|
||||||
<para>
|
|
||||||
In case of validation error, here is how the response might look like:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
|
||||||
<SOAP-ENV:Header/>
|
|
||||||
<SOAP-ENV:Body>
|
|
||||||
<SOAP-ENV:Fault>
|
|
||||||
<faultcode>SOAP-ENV:Client</faultcode>
|
|
||||||
<faultstring xml:lang="en">Validation error</faultstring>
|
|
||||||
<detail>
|
|
||||||
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">
|
|
||||||
invalid user id: Ernie
|
|
||||||
</spring-ws:ValidationError>
|
|
||||||
<spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">
|
|
||||||
invalid order
|
|
||||||
</spring-ws:ValidationError>
|
|
||||||
</detail>
|
|
||||||
</SOAP-ENV:Fault>
|
|
||||||
</SOAP-ENV:Body>
|
|
||||||
</SOAP-ENV:Envelope>
|
|
||||||
]]></programlisting>
|
|
||||||
<para>
|
|
||||||
It is possible though to customize various aspects of the generated SOAP faults, such as the fault string
|
|
||||||
and the soap detail.
|
|
||||||
Please refer to the <ulink url="http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/soap/server/endpoint/AbstractFaultCreatingValidatingMarshallingPayloadEndpoint.html">Javadoc</ulink>
|
|
||||||
for the full list of available options.
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
<section id="server-at-endpoint">
|
|
||||||
<title><interfacename>@Endpoint</interfacename></title>
|
|
||||||
<para>
|
|
||||||
The previous two programming models were based on inheritance, and handled individual XML messages.
|
|
||||||
Spring Web Services offer another endpoint with which you can aggregate multiple handling into one
|
|
||||||
controller, thus grouping functionality together. This model is based on annotations, so you can use
|
|
||||||
it only with Java 5 and higher. Here is an example that uses the same marshalled objects as above:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[package samples;
|
|
||||||
|
|
||||||
import org.springframework.ws.server.endpoint.annotation.Endpoint;
|
|
||||||
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
|
|
||||||
|
|
||||||
@Endpoint
|
|
||||||
public class AnnotationOrderEndpoint {
|
|
||||||
private final OrderService orderService;
|
|
||||||
|
|
||||||
public AnnotationOrderEndpoint(OrderService orderService) {
|
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
|
|
||||||
public Order getOrder(OrderRequest orderRequest) {
|
|
||||||
return orderService.getOrder(orderRequest.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@PayloadRoot(localPart = "order", namespace = "http://samples")
|
|
||||||
public void order(Order order) {
|
|
||||||
orderService.createOrder(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
}]]></programlisting>
|
|
||||||
<para>
|
|
||||||
By annotating the class with <interfacename>@Endpoint</interfacename>, you mark it as a Spring-WS
|
|
||||||
endpoint. Because the endpoint class can have multiple request handling methods, we need to instruct
|
|
||||||
Spring-WS which method to invoke for which request. This is done using the
|
|
||||||
<interfacename>@PayloadRoot</interfacename> annotation: the <methodname>getOrder</methodname> method
|
|
||||||
will be invoked for requests with a <literal>orderRequest</literal> local name and a
|
|
||||||
<uri>http://samples</uri> namespace URI; the <methodname>order</methodname> method for requests with
|
|
||||||
a <literal>order</literal> local name. For more information about these annotations, refer to
|
|
||||||
<xref linkend="server-method-endpoint-mapping"/>.
|
|
||||||
We also need to configure Spring-WS to support the JAXB objects <classname>OrderRequest</classname> and
|
|
||||||
<classname>Order</classname> by defining a <classname>Jaxb2Marshaller</classname>:
|
|
||||||
</para>
|
|
||||||
<programlisting><![CDATA[<beans>
|
|
||||||
|
|
||||||
<bean id="orderEndpoint" class="samples.AnnotationOrderEndpoint">
|
|
||||||
<constructor-arg ref="orderService"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="orderService" class="samples.DefaultOrderService"/>
|
|
||||||
|
|
||||||
<bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
|
|
||||||
<constructor-arg ref="marshaller"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
|
|
||||||
<property name="classesToBeBound">
|
|
||||||
<list>
|
|
||||||
<value>samples.OrderRequest</value>
|
|
||||||
<value>samples.Order</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
|
|
||||||
|
|
||||||
</beans>]]></programlisting>
|
|
||||||
<para>
|
|
||||||
The <classname>GenericMarshallingMethodEndpointAdapter</classname> converts the incoming
|
|
||||||
XML messages to marshalled objects used as parameters and return value; the
|
|
||||||
<classname>PayloadRootAnnotationMethodEndpointMapping</classname> is the mapping that detects and
|
|
||||||
handles the <interfacename>@PayloadRoot</interfacename> annotations.
|
|
||||||
</para>
|
|
||||||
<section>
|
|
||||||
<title><interfacename>@XPathParam</interfacename></title>
|
|
||||||
<para>
|
|
||||||
As an alternative to using marshalling, we could have used <link linkend="xpath">XPath</link> to
|
|
||||||
extract the information out of the incoming XML request. Spring-WS offers another annotation for
|
|
||||||
this purpose: <interfacename>@XPathParam</interfacename>. You simply annotate one or more method
|
|
||||||
parameter with this annotation (each), and each such annotated parameter will be bound to the
|
|
||||||
evaluation of that annotation. Here is an example:
|
|
||||||
</para>
|
|
||||||
<programlisting id="server-payload-root-annotation"><![CDATA[package samples;
|
|
||||||
|
|
||||||
import javax.xml.transform.Source;
|
import javax.xml.transform.Source;
|
||||||
|
|
||||||
import org.springframework.ws.server.endpoint.annotation.Endpoint;
|
import org.springframework.ws.server.endpoint.annotation.Endpoint;
|
||||||
|
import org.springframework.ws.server.endpoint.annotation.Namespace;
|
||||||
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
|
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
|
||||||
import org.springframework.ws.server.endpoint.annotation.XPathParam;
|
import org.springframework.ws.server.endpoint.annotation.XPathParam;
|
||||||
|
|
||||||
@Endpoint
|
@Endpoint
|
||||||
public class AnnotationOrderEndpoint {
|
public class AnnotationOrderEndpoint {
|
||||||
|
|
||||||
private final OrderService orderService;
|
private final OrderService orderService;
|
||||||
|
|
||||||
public AnnotationOrderEndpoint(OrderService orderService) {
|
public AnnotationOrderEndpoint(OrderService orderService) {
|
||||||
this.orderService = orderService;
|
this.orderService = orderService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
|
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
|
||||||
public Source getOrder(]]><emphasis role="bold"><![CDATA[@XPathParam("/s:orderRequest/@id") double orderId]]></emphasis><![CDATA[) {
|
<emphasis role="bold">@Namespace(prefix = "s", uri="http://samples")</emphasis>
|
||||||
Order order = orderService.getOrder((int) orderId);
|
public Order getOrder(<emphasis role="bold">@XPathParam("/s:orderRequest/@id") int orderId</emphasis>) {
|
||||||
]]><lineannotation>// create <interfacename>Source</interfacename> from order and return it</lineannotation><![CDATA[
|
Order order = orderService.getOrder(orderId);
|
||||||
}
|
<lineannotation>// create <interfacename>Source</interfacename> from order and return it</lineannotation>
|
||||||
|
}
|
||||||
|
|
||||||
}]]></programlisting>
|
}</programlisting>
|
||||||
|
<para>
|
||||||
|
Since we use the prefix '<literal>s</literal>' in our XPath expression, we must bind it to the
|
||||||
|
<uri>http://samples</uri> namespace.
|
||||||
|
This is accomplished with the <interfacename>@Namespace</interfacename> annotation.
|
||||||
|
Alternatively, we could have placed this annotation on the type-level to use the same namespace
|
||||||
|
mapping for all handler methods, or even the package-level
|
||||||
|
(in <filename>package-info.java</filename>) to use it for multiple endpoints.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Using the <interfacename>@XPathParam</interfacename>, you can bind to all the data types
|
||||||
|
supported by XPath:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><para><type>boolean</type> or <classname>Boolean</classname></para></listitem>
|
||||||
|
<listitem><para><type>double</type> or <classname>Double</classname></para></listitem>
|
||||||
|
<listitem><para><classname>String</classname></para></listitem>
|
||||||
|
<listitem><para><interfacename>Node</interfacename></para></listitem>
|
||||||
|
<listitem><para><interfacename>NodeList</interfacename></para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
In addition to this list, you can use any type that can be converted from a
|
||||||
|
<classname>String</classname> by a Spring 3
|
||||||
|
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert">conversion service</ulink>.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<title>Handling method return types</title>
|
||||||
<para>
|
<para>
|
||||||
Since we use the prefix '<literal>s</literal>' in our XPath expression, we must bind it to the
|
To send a response message, the handling needs to specify a return type.
|
||||||
<uri>http://samples</uri> namespace:
|
If no response message is required, the method can simply declare a <literal>void</literal> return
|
||||||
|
type.
|
||||||
|
Most commonly, the return type is used to create the payload of the response message, but it is
|
||||||
|
also possible to map to other parts of the response message.
|
||||||
|
This section will describe the return types you can use in your handling method signatures.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<programlisting><![CDATA[<beans>
|
|
||||||
<bean id="orderEndpoint" class="samples.AnnotationOrderEndpoint">
|
|
||||||
<constructor-arg ref="orderService"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="orderService" class="samples.DefaultOrderService"/>
|
|
||||||
|
|
||||||
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
|
|
||||||
|
|
||||||
]]><emphasis role="bold"><![CDATA[<bean class="org.springframework.ws.server.endpoint.adapter.XPathParamAnnotationMethodEndpointAdapter">
|
|
||||||
<property name="namespaces">
|
|
||||||
<props>
|
|
||||||
<prop key="s">http://samples</prop>
|
|
||||||
</props>
|
|
||||||
</property>
|
|
||||||
</bean>]]></emphasis><![CDATA[
|
|
||||||
|
|
||||||
</beans>]]></programlisting>
|
|
||||||
<para>
|
<para>
|
||||||
Using the <interfacename>@XPathParam</interfacename>, you can bind to all the data types supported by
|
To map the return value to the payload of the response message, you will need to annotate the
|
||||||
XPath:
|
method with the <interfacename>@ResponsePayload</interfacename> annotation.
|
||||||
<itemizedlist>
|
This annotation tells Spring-WS that the return value needs to be bound to the response payload.
|
||||||
<listitem><para><type>boolean</type> or <classname>Boolean</classname></para></listitem>
|
</para>
|
||||||
<listitem><para><type>double</type> or <classname>Double</classname></para></listitem>
|
<para>
|
||||||
<listitem><para><classname>String</classname></para></listitem>
|
The following table describes the supported return types.
|
||||||
<listitem><para><interfacename>Node</interfacename></para></listitem>
|
It shows the supported types, whether the parameter should be annotated with
|
||||||
<listitem><para><interfacename>NodeList</interfacename></para></listitem>
|
<interfacename>@ResponsePayload</interfacename>, and any additional notes.
|
||||||
</itemizedlist>
|
<informaltable>
|
||||||
|
<tgroup cols="4">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Name</entry>
|
||||||
|
<entry>Supported return types</entry>
|
||||||
|
<entry><interfacename>@ResponsePayload</interfacename> required?</entry>
|
||||||
|
<entry>Additional notes</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry align="center">No response</entry>
|
||||||
|
<entry>
|
||||||
|
<literal>void</literal>
|
||||||
|
</entry>
|
||||||
|
<entry align="center">✗</entry>
|
||||||
|
<entry>Enabled by default.</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">TrAX</entry>
|
||||||
|
<entry>
|
||||||
|
<interfacename>javax.xml.transform.Source</interfacename> and sub-interfaces
|
||||||
|
(<interfacename>DOMSource</interfacename>, <interfacename>SAXSource</interfacename>,
|
||||||
|
<interfacename>StreamSource</interfacename>, and <interfacename>StAXSource</interfacename>)
|
||||||
|
</entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>Enabled by default.</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">W3C DOM</entry>
|
||||||
|
<entry><interfacename>org.w3c.dom.Element</interfacename></entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>Enabled by default</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">dom4j</entry>
|
||||||
|
<entry><interfacename>org.dom4j.Element</interfacename></entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>Enabled when dom4j is on the classpath.</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">JDOM</entry>
|
||||||
|
<entry><classname>org.jdom.Element</classname></entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>Enabled when JDOM is on the classpath.</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">XOM</entry>
|
||||||
|
<entry><classname>nu.xom.Element</classname></entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>Enabled when XOM is on the classpath.</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">JAXB2</entry>
|
||||||
|
<entry>
|
||||||
|
Any type that is annotated with
|
||||||
|
<interfacename>javax.xml.bind.annotation.XmlRootElement</interfacename>,
|
||||||
|
and <classname>javax.xml.bind.JAXBElement</classname>.
|
||||||
|
</entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>Enabled when JAXB2 is on the classpath.</entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry align="center">OXM</entry>
|
||||||
|
<entry>
|
||||||
|
Any type supported by a Spring OXM
|
||||||
|
<ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/oxm.html#d0e26096"><interfacename>Marshaller</interfacename></ulink>.
|
||||||
|
</entry>
|
||||||
|
<entry align="center">✓</entry>
|
||||||
|
<entry>
|
||||||
|
Enabled when the <literal>marshaller</literal> attribute of
|
||||||
|
<literal><sws:annotation-driven/></literal> is specified.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</informaltable>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
As you can see, there are a lot of possibilities when it comes to defining handling method
|
||||||
|
signatures.
|
||||||
|
It is even possible to extend this mechanism, and to support your own parameter types.
|
||||||
|
Refer to the class-level Javadoc of <classname>DefaultMethodEndpointAdapter</classname> and
|
||||||
|
<interfacename>MethodReturnValueHandler</interfacename> to see how.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
@@ -1361,8 +1084,8 @@ public class AnnotationOrderEndpoint {
|
|||||||
<section id="server-endpoint-mapping">
|
<section id="server-endpoint-mapping">
|
||||||
<title>Endpoint mappings</title>
|
<title>Endpoint mappings</title>
|
||||||
<para>
|
<para>
|
||||||
The endpoint mapping is responsible for mapping incoming messages to appropriate endpoints. There are some
|
The endpoint mapping is responsible for mapping incoming messages to appropriate endpoints.
|
||||||
endpoint mappings you can use out of the box, for example, the
|
There are some endpoint mappings you can use out of the box, for example, the
|
||||||
<classname>PayloadRootQNameEndpointMapping</classname> or the
|
<classname>PayloadRootQNameEndpointMapping</classname> or the
|
||||||
<classname>SoapActionEndpointMapping</classname>, but let's first examine the general concept of an
|
<classname>SoapActionEndpointMapping</classname>, but let's first examine the general concept of an
|
||||||
<interfacename>EndpointMapping</interfacename>.
|
<interfacename>EndpointMapping</interfacename>.
|
||||||
|
|||||||
Reference in New Issue
Block a user