You need something that combines URI
and PayloadRoot
mapping. Unfortunately Spring-Ws doesn't have something like this. But because it's very extensible it's really easy to achieve this.
TL;DR
See This branch at GitHub for working example
Details
You need to create mapping of combined URI+QName to org.springframework.ws.server.endpoint.MethodEndpoint
instances. Also you should minimize the code which would duplicate existing Spring-Ws functions.
So 1) You need to explicitly configure Spring-Ws annotations without using <sws:annotation-driven />
:
This is your requirement (with my schemas):
<ws:dynamic-wsdl id="spml-readonly" portTypeName="SpmlReadOnlyService" locationUri="SpmlReadOnly">
<ws:xsd location="classpath:springws/model/schema.xsd" />
</ws:dynamic-wsdl>
<ws:dynamic-wsdl id="spml-crud" portTypeName="SpmlCrudService" locationUri="SpmlCrud">
<ws:xsd location="classpath:springws/model/schema.xsd" />
<ws:xsd location="classpath:springws/model/schema2.xsd" />
</ws:dynamic-wsdl>
This is all you need to do by hand which normally is configured by <sws:annotation-driven />
(one adapter with one JAXB marshaller):
<bean class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
<property name="methodArgumentResolvers">
<list>
<ref local="marshallingPayloadMethodProcessor"/>
</list>
</property>
<property name="methodReturnValueHandlers">
<list>
<ref local="marshallingPayloadMethodProcessor"/>
</list>
</property>
</bean>
<bean id="marshallingPayloadMethodProcessor" class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>springws.model</value>
</list>
</property>
</bean>
This is custom mapping:
<bean class="springws.PathAndPayloadRootAnnotationEndpointMapping" />
And 2) You should create your own mapping
public class PathAndPayloadRootAnnotationEndpointMapping extends PayloadRootAnnotationMethodEndpointMapping
{
@Override
protected QName getLookupKeyForMessage(MessageContext messageContext) throws Exception
{
String urlPart = "";
QName payloadRootPart = super.getLookupKeyForMessage(messageContext);
TransportContext transportContext = TransportContextHolder.getTransportContext();
if (transportContext != null) {
WebServiceConnection connection = transportContext.getConnection();
if (connection != null && connection instanceof HttpServletConnection) {
String requestURI = ((HttpServletConnection)connection).getHttpServletRequest().getRequestURI();
String contextPath = ((HttpServletConnection)connection).getHttpServletRequest().getContextPath();
urlPart = requestURI.substring(contextPath.length());
}
}
return new QName(payloadRootPart.getNamespaceURI(), urlPart + "/" + payloadRootPart.getLocalPart());
}
@Override
protected List<QName> getLookupKeysForMethod(Method method)
{
List<QName> result = new ArrayList<QName>();
RequestMapping rm = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequestMapping.class);
String urlPart = rm == null || rm.value().length != 1 ? "" : rm.value()[0];
List<QName> methodPart = super.getLookupKeysForMethod(method);
for (QName qName : methodPart) {
result.add(new QName(qName.getNamespaceURI(), urlPart + "/" + qName.getLocalPart()));
}
return result;
}
}
which extends org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping
. And all it does is extending the keys (QNames of payload root elements) of messages with the information extracted from the endpoint URI. I've used Spring's @org.springframework.web.bind.annotation.RequestMapping
annotation for that, but someone thinking it's a hack may create his/her own annotation.
So for endpoint like this:
@org.springframework.ws.server.endpoint.annotation.Endpoint
@RequestMapping("/ws/SpmlReadOnly")
public class Endpoint1
{
@ResponsePayload
@PayloadRoot(namespace = "urn:test", localPart = "method1Request")
public Response2 method(@RequestPayload Request1 request) throws Exception
{
return new Response2("e1 m1");
}
}
the key is not:
namespace = urn:test
localName = method1Request
but this:
namespace = urn:test
localName = /ws/SpmlReadOnly/method1Request
The protected QName getLookupKeyForMessage(MessageContext messageContext)
method ensures that the mapping URI is independent of the WAR context, the application is deployed at.