如何将具有相同名称空间和本地名称请求的两个单独Web服务路由到不同的端点?

时间:2012-09-11 12:27:05

标签: web-services spring spring-ws spml

我正在尝试在一个弹簧部署中创建两个单独的Web服务,两者都使用相同的xsd架构生成wsdl,但是将它们路由到两个单独的端点,这样我就能以不同的方式处理请求单独的背景。

例如:

Webservice 1:访问权限,较低权限和安全性约束

Webservice 2:更高的权限

<sws:dynamic-wsdl id="spml-readonly" 
    portTypeName="SpmlReadOnlyService" 
    locationUri="SpmlReadOnly">
    <sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_core.xsd"/>
</sws:dynamic-wsdl>

<sws:dynamic-wsdl id="spml-crud" 
    portTypeName="SpmlCrudService" 
    locationUri="SpmlCrud">
    <sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_core.xsd"/>
    <sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_search.xsd"/>
    <sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_batch.xsd"/>
</sws:dynamic-wsdl>

现在因为两个wsdl都基于相同的xsds,所以请求的'namespace'和'localPart'都是相同的,无论我正在点击哪个Web服务(/ SpmlReadOnly或/ SpmlCrud)。 / p>

因此,排除了已弃用的PayloadRootQNameEndpointMapping,因为localPart和命名空间仍然相同,等等......我的当前配置只是将请求路由到同一个端点方法处理程序,我无法区分哪个Web服务被称为:

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "lookupRequest")
    @ResponsePayload
    public Source handleLookupRequest(SoapMessage message) throws Exception {
        ...
    }

我能做什么甚至可能?如果xsd是共享的并且在模式的根目录中具有相同的名称空间,并且相同的localPart方法请求,那么是否有办法区分它们并映射到两个不同的端点?有关这方面的任何信息都很有用!我希望我不必设置两个单独的.wars,并在服务器上使用自己的代码库单独部署它们!

谢谢, 达米安

1 个答案:

答案 0 :(得分:5)

您需要结合URIPayloadRoot映射的内容。不幸的是,Spring-Ws没有这样的东西。但是因为它非常易于实现,所以很容易实现这一点。

<强> TL; DR

请参阅This branch at GitHub了解工作示例

<强>详情

您需要创建组合URI + QName到org.springframework.ws.server.endpoint.MethodEndpoint实例的映射。您还应该最小化复制现有Spring-Ws函数的代码。

所以1)你需要在不使用<sws:annotation-driven />的情况下显式配置Spring-Ws注释:

这是你的要求(使用我的架构):

<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>

这就是您需要手动执行的操作,通常由<sws:annotation-driven />配置(一个适配器和一个JAXB编组器):

<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>

这是自定义映射:

<bean class="springws.PathAndPayloadRootAnnotationEndpointMapping" />

2)你应该创建自己的映射

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;
    }   
}

扩展了org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping。它所做的就是利用从端点URI提取的信息扩展消息的密钥(有效负载根元素的QNames)。我已经使用了Spring的@org.springframework.web.bind.annotation.RequestMapping注释,但有人认为这是一个黑客可能会创建他/她自己的注释。

对于像这样的端点:

@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");
    }
}

关键不是:

namespace = urn:test
localName = method1Request

但是这个:

namespace = urn:test
localName = /ws/SpmlReadOnly/method1Request

protected QName getLookupKeyForMessage(MessageContext messageContext)方法确保映射URI独立于WAR上下文,应用程序部署在。