如何将两个具有相同名称空间的单独Web服务和本地名称请求路由到不同的端点? [英] How can i have two separate web services with identical name space and local name requests be routed to different end points?

查看:152
本文介绍了如何将两个具有相同名称空间的单独Web服务和本地名称请求路由到不同的端点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在一个春季部署中创建两个单独的Web服务,两个都是从相同的xsd模式生成的wsdl,但是将它们路由到两个单独的端点,以便我可以在中处理不同的请求单独的上下文.

例如:

Web服务1:访问子集,较低的特权和安全性约束

Web服务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,因此无论我要访问哪个Web服务(/SpmlReadOnly或/SpmlCrud),请求的名称空间"和"localPart"都是完全相同的./p>

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

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

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

谢谢, 达米安

解决方案

您需要一些将URIPayloadRoot映射结合在一起的东西.不幸的是,Spring-Ws没有这样的东西.但是,因为它具有很好的可扩展性,所以很容易做到这一点.

TL; DR

有关工作示例,请参见此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提取的信息扩展消息的 keys (有效负载根元素的QNames).我为此使用了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上下文,并且将应用程序部署在该位置.

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 routed to two separate end points so i can handle the requests differently in the separate contexts.

Ex:

Webservice 1: subset of access, lower privileges and security constraints

Webservice 2: higher privileges

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

Now since both wsdls are based off of the same xsds, the 'namespace' and 'localPart" of the requests come across the wire identical, regardless of which web service i'm hitting (/SpmlReadOnly or /SpmlCrud).

Therefore, that's ruling out the deprecated PayloadRootQNameEndpointMapping since the localPart and namespace are still identical, etc,... and my current config simply routes the requests to the same endpoint method handler, and i have no way of distinguishing which web service was called:

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

Is what I'm able to do even possible? If the xsd's are shared and have identical namespaces at the root of the schema, and the same localPart method requests, will there ever be a way to distinguish between them and map to two different end points? Any information on this would be useful! I'm hoping i don't have to set up two separate .wars and deploy them separately with their own code bases on a server!

Thanks, Damian

解决方案

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.

这篇关于如何将两个具有相同名称空间的单独Web服务和本地名称请求路由到不同的端点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆