如何将wsdl内部架构设置为Jaxb2Marshaller来验证我要发布的每个帖子?

时间:2017-03-22 13:00:58

标签: java spring web-services validation wsdl

我正在使用SOAP Web服务,我必须在调用它之前验证每个xml帖子。

所以我正在使用:

  • cxf-codegen-plugin 以生成POJO树结构。
  • 第三部分wsdl(xxxx-soap-service.wsdl)
  • 实施 WebServiceGatewaySupport 弹簧界面以进行调用的类。
  • Jaxb2Marshaller spring marshaller / unmarshaller转换pojo<···· XML

嗯,我现在有了代码片段:

  • 的applicationContext-XXXX-base.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
                            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
                            http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
    
        <mybatis:scan base-package="com.company.integration.xxxx.domain.mybatis.mapper" />
    
        <context:component-scan base-package="com.company.integration.remo" />
    
        <bean id="xxxxMarshallerProperties" class="com.company.integration.xxxx.domain.properties.XxxxMarshallerProperties"/>
    
        <bean id="xxxxMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" depends-on="xxxxMarshallerProperties">
            <property name="contextPath" value="#{xxxxMarshallerProperties.contextPath}"/>
            <property name="validationEventHandler" value="#{xxxxMarshallerProperties.validationEventHandler}"/>
            <property name="schema" value="#{xxxxMarshallerProperties.schema}"/>
        </bean>
    
        <bean id="xxxxService" class="com.company.integration.xxxx.domain.client.XxxxServiceImpl">
            <property name="defaultUri" value="#{'${xxxx.baseUrl}' + '${xxxx.url.sufix}'}" />
            <property name="marshaller" ref="xxxxMarshaller" />
            <property name="unmarshaller" ref="xxxxMarshaller" />
        </bean>
    
        <bean id="xxxxObjectFactory" class="com.company.integration.xxxx.domain.model.ObjectFactory" />
    
    
    </beans>
    
    • XxxxMarshallerProperties ,用于配置marshaller / unmarshaller bean:

      // @零件 public class XxxxMarshallerProperties {     @Autowired     private ResourceLoader resourceLoader;

      public Resource getSchema() throws IOException, SAXException {
          WebServiceClient wscAnnotation = Service.class.getAnnotation(WebServiceClient.class);
          String wsdlLocationPath = wscAnnotation.wsdlLocation();
          Resource wsdlResource = resourceLoader.getResource(wsdlLocationPath);
          final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
          URI wsdlUri = wsdlResource.getURI();
          Source[] streamSources = new Source[] { new StreamSource(wsdlUri.toString()) };
          final Schema wsdlSchema = schemaFactory.newSchema(streamSources);
          return wsdlSchema;
      }
      
      public DefaultValidationEventHandler getValidationEventHandler() {
          return new DefaultValidationEventHandler();
      }
      
      public String getContextPath() {
          return XXXXX.class.getPackage().getName();
      }
      

      }

    • 用于调用webservice的 xxxxService bean类:

      公共类XxxxServiceImpl扩展WebServiceGatewaySupport实现ServiceSoap {

      @Autowired
      XxxxProperties xxxxProperties;
      
      @Autowired
      private ObjectFactory xxxxObjectFactory;
      
      @Autowired
      private ResourceLoader resourceLoader;
      
      private String getActionUrl(String actionName) {
          return xxxxProperties.getActionNamespace() + actionName;
      }
      
      public CXXXX callXXXX(CallXXXX request) {
          Jaxb2Marshaller marshaller = (Jaxb2Marshaller) getMarshaller();
          Result result = new StringResult();
      
          CallXXXXResponse response = (CallXXXXResponse) getWebServiceTemplate().marshalSendAndReceive(request,
              new WebServiceMessageCallback() {
      
                  public void doWithMessage(WebServiceMessage message) {
                      ((SoapMessage) message).setSoapAction(getActionUrl(xxxxProperties.getSoapActionCallXXXX()));
                  }
              });
          return response.getCallXXXXResult();
      }
      

      }

    • xxxxObjectFactory 是来自自动生成的cxf类的bean,用于创建存储请求数据值的pojos。

    我的目的是将wsdl( xxxx-soap-service.wsdl )附加到编组器( xxxxMarshaller )以检查由其生成的xml,在调用make之前,wsdl中包含的模式。

    我遇到的问题是,如果我只是附加wsdl(在bean定义中通过schema属性),Jaxb2Marshaller似乎认为它是一个xsd并给我一个 org.xml.sax.SAXParseException:s4s- elt-schema-ns:'definitions'命名空间必须是......

    我现在正在尝试从wsdl中提取内部xsd并将其附加到marshaller,但xsd必须是 org.springframework.core.io.Resource 来执行此操作。那是因为我的代码尚未编译。

    我已经看到 JaxbMarshaller (不是Jaxb 2 Marshaller),这是可能的。

你能以这种方式帮助我吗?

我愿意探索其他方法或更好的解决方案。

1 个答案:

答案 0 :(得分:0)

最后我发现了一个适合我的实现。

这个想法是:

  • 从wsdl中提取嵌入式模式,
  • 配置每个架构名称空间,
  • 添加尚未定义的所有可用名称空间,但在&lt; wsdl:definition&gt;上定义标签
  • 配置每个架构导入和位置,
  • 将每个架构存储到spring的Resource接口的实现类中,
  • 将所有这些内容存储到资源数组
  • 并通过 setSchema [s] 将此数组传递给Jaxb2Marshaller,而不是 setSchema (正如我一直在做的那样)。

根据这个问题,我不得不修改 XxxxMarshallerProperties 类和 applicationContext-xxxx-base.xml 文件。

<强> XxxxMarshallerProperties

@Component
public class XxxxMarshallerProperties {
    @Autowired
    private ResourceLoader resourceLoader;

    /**
     * @return A new transformer.
     * @throws TransformerFactoryConfigurationError
     * @throws TransformerConfigurationException
     */
    private Transformer newTransformer()
        throws TransformerFactoryConfigurationError, TransformerConfigurationException {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        return transformer;
    }

    /**
     * Load the wsdl into a dom Document. Used at:<br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<b>getSchemas</b>
     *
     * @param wsdlUrl
     *            url where the WSDL is located at
     * @param transformer
     *            used to load the document
     * @return The wsdl dom Document
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws TransformerException
     */
    private Document loadWsdlDoc(URL wsdlUrl, Transformer transformer)
        throws ParserConfigurationException, IOException, TransformerException {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder schemaBuilder = docFactory.newDocumentBuilder();
        Document wsdlDoc = schemaBuilder.newDocument();
        BufferedReader wsdlReader = new BufferedReader(new InputStreamReader(wsdlUrl.openStream()));
        Source source = new StreamSource(wsdlReader);
        transformer.transform(source, new DOMResult(wsdlDoc));
        return wsdlDoc;
    }

    /**
     * Store into a map all namespaces defined on the wsdl. Used at:<br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<b>getSchemas</b>
     *
     * @param defNode
     *            &lt;<b><i>wsdl:definitions</i></b>&gt; dom node where to look
     *            up.
     * @return A map of namespace definition attributes. Format: [(nodeName,
     *         node)....]
     */
    private Map<String, Node> getWsdlDefinedNamespaces(Element defNode) {
        Map<String, Node> namespaces = new TreeMap<>();
        NamedNodeMap defNodeAtt = defNode.getAttributes();
        int defNodeAttSz = defNodeAtt.getLength();
        for (int attIndex = 0; attIndex < defNodeAttSz; attIndex++) {
            String ns = defNodeAtt.item(attIndex).getPrefix();
            if ("xmlns".equals(ns)) {
                namespaces.put(//
                    defNodeAtt.item(attIndex).getNodeName(), //
                    defNodeAtt.item(attIndex));
            }
        }

        return namespaces;
    }

    /**
     * Store into a map all the atributes present in a xsd schema node. Used at:
     * <br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<b>addDefinitionNamespaces</b>
     *
     * @param wsdlSchemaNode
     *            &lt;<b><i>s:schema</i></b>&gt; dom node where to look up.
     * @return A map of attributes. Format: [(nodeName, node)....]
     */
    private Map<String, Node> getCurrentSchemaAtt(Node wsdlSchemaNode) {
        Map<String, Node> schemaXmlnss = new HashMap<>();

        NamedNodeMap schemaNodeAtt = wsdlSchemaNode.getAttributes();
        int schemaNodeAttSz = schemaNodeAtt.getLength();
        for (int attIndex = 0; attIndex < schemaNodeAttSz; attIndex++) {
            String nodeAttName = schemaNodeAtt.item(attIndex).getNodeName();
            Node nodeAtt = ((NamedNodeMap) schemaNodeAtt).item(attIndex);
            schemaXmlnss.put(nodeAttName, nodeAtt);
        }
        return schemaXmlnss;
    }

    /**
     * Adds all non existing namespace definition attributes to a schema node in
     * that schema node. If a namespace definition attribute name is found into
     * schema node, it's not added to the current schema node attributes. Used
     * at: <br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<b>getSchemas</b>
     *
     * @param schemaNode
     *            &lt;<b><i>s:schema</i></b>&gt; dom node where to add the
     *            namespace definition attributes.
     * @param namespaces
     *            map storing all namespace definition attributes.
     */
    private void addDefinitionNamespaces(Node schemaNode, Map<String, Node> namespaces) {
        Map<String, Node> currSchemaAttMap = getCurrentSchemaAtt(schemaNode);
        for (Node xmlns : namespaces.values()) {
            String nodeName = xmlns.getNodeName();
            if (!currSchemaAttMap.containsKey(nodeName)) {
                String namespaceURI = xmlns.getNamespaceURI();
                String nodeValue = xmlns.getNodeValue();
                ((Element) schemaNode).setAttributeNS(namespaceURI, nodeName, nodeValue);
            }
        }
    }

    /**
     * Update schema location by adding path. Used at: <br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<b>getSchemas</b>
     *
     * @param schemaNode
     *            &lt;<b><i>s:schema</i></b>&gt; dom node to update its
     *            location.
     * @return The updated schema.
     */
    private DOMSource updateSchemaLocationByAddingPath(Node schemaNode) {
        DOMSource schemaDomSource = new DOMSource(schemaNode);
        NodeList noteList = schemaDomSource.getNode().getChildNodes();
        for (int j = 0; j < noteList.getLength(); j++) {
            if ("xsd:import".equals(noteList.item(j).getNodeName())) {
                NamedNodeMap nodeMap = noteList.item(j).getAttributes();
                for (int attIndex = 0; attIndex < nodeMap.getLength(); attIndex++) {
                    if ("schemaLocation".equals(nodeMap.item(attIndex).getNodeName())) {
                        nodeMap.item(attIndex).setNodeValue(nodeMap.item(attIndex).getNodeValue());
                    }
                }
            }
        }
        return schemaDomSource;
    }

    /**
     * Transform a DOMSource schema into a spring's {@link Resource} to be
     * attached to a {@link Jaxb2Marshaller}.
     *
     * @param schemaDomSource
     *            The schema to be transformed.
     * @param transformer
     *            The transformer used.
     * @return A spring's {@link Resource} interface.
     * @throws TransformerException
     */
    private Resource transformToResource(DOMSource schemaDomSource, Transformer transformer)
        throws TransformerException {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        Result result = new StreamResult(outStream);
        transformer.transform(schemaDomSource, result);
        ByteArrayResource schemaResource = new ByteArrayResource(outStream.toByteArray());
        return schemaResource;
    }

    /**
     * Generate and retrieves all schemas contained into a wsdl file stored in
     * the classpath, in an {@link Resource} array, to be attached to a
     * {@link Jaxb2Marshaller} .
     *
     * @return An {@link Resource} array.
     * @throws IOException
     * @throws SAXException
     * @throws ParserConfigurationException
     * @throws TransformerException
     */
    public Resource[] getSchemas()
        throws IOException, SAXException, ParserConfigurationException, TransformerException {
        Resource[] schemaResources = null;

        WebServiceClient wscAnnotation = Service.class.getAnnotation(WebServiceClient.class);
        String wsdlLocationPath = wscAnnotation.wsdlLocation();
        Resource wsdlResource = resourceLoader.getResource(wsdlLocationPath);
        URL wsdlUri = wsdlResource.getURL();

        Transformer transformer = newTransformer();
        Document wsdlDoc = loadWsdlDoc(wsdlUri, transformer);
        NodeList schemaNodes = wsdlDoc.getElementsByTagNameNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "schema");
        int nrSchemas = schemaNodes.getLength();
        if (nrSchemas > 0) {
            Element defNode = wsdlDoc.getDocumentElement();
            Map<String, Node> namespaces = getWsdlDefinedNamespaces(defNode);

            schemaResources = new Resource[nrSchemas];

            for (int i = 0; i < nrSchemas; i++) {
                Node schemaNode = schemaNodes.item(i);
                addDefinitionNamespaces(schemaNode, namespaces);
                DOMSource schemaDomSource = updateSchemaLocationByAddingPath(schemaNode);
                schemaResources[i] = transformToResource(schemaDomSource, transformer);
            }
        }

        return schemaResources;
    }
}

<强>的applicationContext-XXXX-base.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
                        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
                        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

    <mybatis:scan base-package="com.company.integration.xxxx.domain.mybatis.mapper" />

    <context:component-scan base-package="com.company.integration.remo" />

    <bean id="xxxxMarshallerProperties" class="com.company.integration.xxxx.domain.properties.XxxxMarshallerProperties"/>

    <bean id="xxxxMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" depends-on="xxxxMarshallerProperties">
        <property name="contextPath" value="#{xxxxMarshallerProperties.contextPath}"/>
        <property name="validationEventHandler" value="#{xxxxMarshallerProperties.validationEventHandler}"/>
        <property name="schemas" value="#{xxxxMarshallerProperties.schemas}"/>
    </bean>

    <bean id="xxxxService" class="com.company.integration.xxxx.domain.client.XxxxServiceImpl">
        <property name="defaultUri" value="#{'${xxxx.baseUrl}' + '${xxxx.url.sufix}'}" />
        <property name="marshaller" ref="xxxxMarshaller" />
        <property name="unmarshaller" ref="xxxxMarshaller" />
    </bean>

    <bean id="xxxxObjectFactory" class="com.company.integration.xxxx.domain.model.ObjectFactory" />


</beans>

请注意,我使用的是Jaxb2Marshaller 架构,属性(setSchemas),它允许我设置多个架构,而不是只有一个架构。