How can i have two separate web services with identical name space and local name requests be routed to different end points?

后端 未结 1 359
一生所求
一生所求 2020-12-16 22:26

I\'m attempting to create 2 separate web services, both within one spring deployment, both with the wsdl\'s being generated from the same xsd schemas, yet have them be route

相关标签:
1条回答
  • 2020-12-16 22:58

    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.

    0 讨论(0)
提交回复
热议问题