SpringBoot为Soap请求生成端点映射

时间:2019-09-20 16:13:41

标签: java spring-boot soap spring-ws dispatch

我正在使用springboot创建易于配置的网络服务模拟程序,并将“ spring-boot-starter-web-services”工件嵌入了我的项目中。 我遵循了来自spring-io的指南,了解如何配置端点。但是我想实例化一个bean,该bean将处理使用定义的名称空间传入的每个soap请求并处理响应的创建。 我已经搜索了很多有关spring-webservice的参考,尝试了拦截器和侦听器,但是没有成功,我总是得到404'找不到用于[SaajSoapMessage {myNamespace} MyRequest]的终结点映射”。

我还使用javassist生成了具有所有操作映射的Endpoint注释类,但是调度程序不会加载此Endpoint。

感谢您的帮助/建议,

编辑:

根据建议,我添加了Poc存储库以显示wip:https://github.com/Servhome/sb2-ws-sample

这是我运行该应用程序时获得的起始日志:

2019-09-23 11:16:06.504 DEBUG 6184 --- [  restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Creating port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [  restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchByName] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [  restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchById] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.513 DEBUG 6184 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Creating binding [{http://sample.com/int/Sample/v1}SamplePortTypeSoap11]
2019-09-23 11:16:06.519 DEBUG 6184 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Creating service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.520 DEBUG 6184 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Adding port [SamplePortTypeSoap11] to service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.529 DEBUG 6184 --- [  restartedMain] yloadRootAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.562 DEBUG 6184 --- [  restartedMain] oapActionAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.593 DEBUG 6184 --- [  restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodArgumentResolvers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@7c332390, org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@4c642359, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@31c15cce, org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@3c00b80a, org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@594a23b9, org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@3459ad22, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@f45b04b, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@363c8941, org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@3ee333d4]
2019-09-23 11:16:06.599 DEBUG 6184 --- [  restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodReturnValueHandlers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@47aaa997, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@65cf9ec1, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@376b65b8, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@10b19d12]

这表示WSDL定义可以正常工作,但是没有端点可以绑定到这些操作定义。 我希望能够通过单个Bean'SampleEndpoint'处理来自名称空间http://sample.com/int/Sample/v1的每个请求。而且,能够像通过CustomWsInitializer类使WSDL定义可配置那样,使其可配置。

1 个答案:

答案 0 :(得分:0)

我设法找到了一种解决方案,该方案满足了我对全局wap端点的需求,以便对给定wsdl进行可配置的列表操作。

如果您对这种解决方案感兴趣,请参见https://github.com/Servhome/sb2-ws-sample。我将分几步说明该解决方案。

目标1: 创建wsdl定义并通过配置自动发布这些端点:

application-local.properties github link

soap.endpoints.path=/services

soap.endpoints=Sample
soap.endpoints.Sample.wsdl.location=classpath:/xsd/Sample.xsd
soap.endpoints.Sample.portType.name=SamplePortType
soap.endpoints.Sample.target.namespace=http://sample.com/int/Sample/v1

这里是wsdl定义配置:wsdl位置,端口类型名称和名称空间(请参阅xsd的targetnamespace)。

我在应用程序主CustomWsInitializer(github link)中注册了一个上下文初始化器:

private void registerEndpointService(GenericApplicationContext genericApplicationContext, Environment env, String endpointName, String locationUri) {
  Resource resource = genericApplicationContext.getResource(env.getProperty(SOAP_ENDPOINTS + endpointName + ".wsdl.location"));
  SimpleXsdSchema schema = new SimpleXsdSchema(resource);
  genericApplicationContext.registerBean(endpointName + "Schema", SimpleXsdSchema.class, () -> schema);

  String portTypeName = env.getProperty(SOAP_ENDPOINTS + endpointName + ".portType.name");
  String targetNamespace = env.getProperty(SOAP_ENDPOINTS + endpointName + ".target.namespace");

  genericApplicationContext.registerBean(endpointName + "Service",
    DefaultWsdl11Definition.class,
    () -> {
      DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
      wsdl11Definition.setPortTypeName(portTypeName);
      wsdl11Definition.setLocationUri(locationUri);
      wsdl11Definition.setTargetNamespace(targetNamespace);
      wsdl11Definition.setSchema(schema);

      return wsdl11Definition;
    });
}

此实用程序方法首先注册一个xsd模式实例,然后根据先前的属性初始化已配置的DefaultWsdl11Definition。

如果此时运行该应用程序,您将看到确认日志,说明此WsdlDefinition已正确加载:

2019-09-23 11:16:06.504 DEBUG 6184 --- [  restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Creating port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [  restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchByName] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.511 DEBUG 6184 --- [  restartedMain] o.s.w.w.w.p.SuffixBasedPortTypesProvider : Adding operation [searchById] to port type [{http://sample.com/int/Sample/v1}SamplePortType]
2019-09-23 11:16:06.513 DEBUG 6184 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Creating binding [{http://sample.com/int/Sample/v1}SamplePortTypeSoap11]
2019-09-23 11:16:06.519 DEBUG 6184 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Creating service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.520 DEBUG 6184 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Adding port [SamplePortTypeSoap11] to service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-23 11:16:06.529 DEBUG 6184 --- [  restartedMain] yloadRootAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.562 DEBUG 6184 --- [  restartedMain] oapActionAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6dea6e9c, started on Mon Sep 23 11:16:02 CEST 2019
2019-09-23 11:16:06.593 DEBUG 6184 --- [  restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodArgumentResolvers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@7c332390, org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@4c642359, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@31c15cce, org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@3c00b80a, org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@594a23b9, org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@3459ad22, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@f45b04b, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@363c8941, org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@3ee333d4]
2019-09-23 11:16:06.599 DEBUG 6184 --- [  restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodReturnValueHandlers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@47aaa997, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@65cf9ec1, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@376b65b8, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@10b19d12]

最后的日志行表明api正在搜索端点,这是我的第二个目标(我坚持了一段时间的原始目标)。

目标2: :创建动态单端点以配置Web服务的操作映射:

application-local.properties github link

soap.endpoints.Sample.operations.size=1
soap.endpoints.Sample.operations.0.localPart=searchByNameRequest
soap.endpoints.Sample.operations.0.requestType=com.sample._int.sample.v1.SearchByNameRequestType
soap.endpoints.Sample.operations.0.responseType=com.sample._int.sample.v1.GeneralResponseType

这些配置行指示必须将哪些操作映射到我希望分派器将请求转发到的全局端点。

这是棘手的部分,我在项目中添加了Javassist和Velocity工件。出于两个原因:能够使用映射自动生成带注释的类。为此,我创建了一个要加载的方法模板(github link):

public javax.xml.transform.dom.DOMSource $localPart(javax.xml.transform.dom.DOMSource request) throws Exception {
    org.slf4j.LoggerFactory.getLogger("custom.EndpointMapping").debug("Entered endpoint $localPart : " + request.toString());
    return com.sample.controller.GlobalSoapEndpoint.handle(request, "$namespaceUri", "$localPart", "$requestType", "$responseType");
}

MockedEndpointGenerator类加载了哪个,该实用工具类生成了一个带注释的编译类:

MockedEndpointGenerator.java(github link):

public static Class<?> generateMockEndpoint(MockEndpointDefinition def) {

    ClassPool pool = ClassPool.getDefault();

    CtClass cc = pool.makeClass(def.getServiceName() + "Endpoint");
    ClassFile classFile = cc.getClassFile();
    ConstPool constpool = classFile.getConstPool();

    classFile.addAttribute(addSingleAnnotation(constpool, Endpoint.class.getName()));

    for (MockEndpointDefinition.MockOperation operation : def.getOperations()) {
        try {
            CtMethod mthd = CtNewMethod.make(templateMethod(operation, def.getNamespace()), cc);
            ConstPool mthdConstPool = mthd.getMethodInfo().getConstPool();

            // add method annotations
            AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
            Annotation[] annotations = new Annotation[]{
                    addAnnotation(mthdConstPool, ResponsePayload.class.getName()),
                    addAnnotation(mthdConstPool, PayloadRoot.class.getName(),
                            new String[][]{
                                    new String[]{"namespace", def.getNamespace()},
                                    new String[]{"localPart", operation.getLocalPart()}
                            }
                    )
            };
            annotationsAttribute.setAnnotations(annotations);
            mthd.getMethodInfo().addAttribute(annotationsAttribute);

            // add method's parameter annotation
            ParameterAnnotationsAttribute parameterAttributeInfo = new ParameterAnnotationsAttribute(mthdConstPool, ParameterAnnotationsAttribute.visibleTag);
            ConstPool parameterConstPool = parameterAttributeInfo.getConstPool();

            Annotation annotation = addAnnotation(parameterConstPool, RequestPayload.class.getName());
            Annotation[][] annotations2 = new Annotation[][]{
                new Annotation[] {annotation}
            };
            parameterAttributeInfo.setAnnotations(annotations2);
            mthd.getMethodInfo().addAttribute(parameterAttributeInfo);

            cc.addMethod(mthd);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }
    try {
        return cc.toClass();
    } catch (Exception e) {
        throw new IllegalStateException("Custom Endpoint class creation failed", e);
    }
}

然后实例化该类并将其注册到Spring的上下文中。 然后,您会在启动日志中看到对api的确认:

2019-09-24 11:42:34.614 DEBUG 16220 --- [  restartedMain] o.s.w.w.wsdl11.provider.Soap11Provider   : Adding port [SamplePortTypeSoap11] to service [{http://sample.com/int/Sample/v1}SamplePortTypeService]
2019-09-24 11:42:34.621 DEBUG 16220 --- [  restartedMain] yloadRootAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5289cd7, started on Tue Sep 24 11:42:28 CEST 2019
2019-09-24 11:42:34.624 DEBUG 16220 --- [  restartedMain] yloadRootAnnotationMethodEndpointMapping : Mapped [{http://sample.com/int/Sample/v1}searchByNameRequest] onto endpoint [public javax.xml.transform.dom.DOMSource SampleEndpoint.searchByNameRequest(javax.xml.transform.dom.DOMSource) throws java.lang.Exception]
2019-09-24 11:42:34.649 DEBUG 16220 --- [  restartedMain] oapActionAnnotationMethodEndpointMapping : Looking for endpoints in application context: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5289cd7, started on Tue Sep 24 11:42:28 CEST 2019
2019-09-24 11:42:34.690 DEBUG 16220 --- [  restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodArgumentResolvers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@1d88a335, org.springframework.ws.server.endpoint.adapter.method.MessageContextMethodArgumentResolver@5c309f36, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@349ea8a8, org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver@72d801b0, org.springframework.ws.soap.server.endpoint.adapter.method.SoapMethodArgumentResolver@2a1185d5, org.springframework.ws.soap.server.endpoint.adapter.method.SoapHeaderElementMethodArgumentResolver@7200768e, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@331a4b8e, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@1fd09dc, org.springframework.ws.server.endpoint.adapter.method.StaxPayloadMethodArgumentResolver@27e40b1b]
2019-09-24 11:42:34.693 DEBUG 16220 --- [  restartedMain] o.s.w.s.e.a.DefaultMethodEndpointAdapter : No MethodReturnValueHandlers set, using defaults: [org.springframework.ws.server.endpoint.adapter.method.dom.DomPayloadMethodProcessor@5621ad6f, org.springframework.ws.server.endpoint.adapter.method.SourcePayloadMethodProcessor@3706ca1e, org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor@5edc86cb, org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor@59e7f2d8]

下一步:现在,使用单个控制器,我可以查询一个缓存管理器,该缓存管理器按操作/场景保存模拟的响应。

改进:使可配置的类可以通过操作调用(但是通过使用@Endpoint创建带注释的类已经达到了此目的)。

相关问题