9

我正在尝试在一个 spring 部署中创建 2 个单独的 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>

现在,由于两个 wsdls 都基于相同的 xsd,因此请求的“命名空间”和“localPart”通过相同的网络连接,无论我访问的是哪个 Web 服务(/SpmlReadOnly 或 /SpmlCrud)。

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

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

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

谢谢,达米安

4

1 回答 1

5

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

TL;博士

有关工作示例,请参阅GitHub 上的此分支

细节

您需要创建组合 URI+QName 到org.springframework.ws.server.endpoint.MethodEndpoint实例的映射。此外,您应该最小化重复现有 Spring-Ws 功能的代码。

所以 1)您需要显式配置 Spring-Ws 注释而不使用<sws:annotation-driven />

这是您的要求(使用我的模式):

<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 中提取的信息来扩展消息的(有效负载根元素的 QName)。我为此使用了 Spring 的@org.springframework.web.bind.annotation.RequestMapping注释,但认为这是一个 hack 的人可能会创建他/她自己的注释。

所以对于这样的端点:

@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 上下文。

于 2013-04-03T07:25:11.353 回答