How to set wsdl inner schema to Jaxb2Marshaller to validate each post i wold make?

后端 未结 1 1058
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-28 14:38

I\'m consuming a SOAP webservice where I have to validate every xml post before call it.

So I\'m using:

  • The cxf-codegen-plugin to generate
相关标签:
1条回答
  • 2021-01-28 14:52

    finally I found an implementation that works for me.

    The idea is:

    • to extract the embedded schema's from the wsdl,
    • configure each schema namespace,
    • adding all posible namespaces non defined yet in it but that are defined on <wsdl:definition> tag,
    • configure each schema import and locations,
    • store each schema into an implementing class of spring's Resource interface,
    • store all of them into an Resource array
    • and pass this array to the Jaxb2Marshaller through setSchema[s], instead of setSchema (as I have been doing).

    Based on the question, I had to modify the XxxxMarshallerProperties class and the applicationContext-xxxx-base.xml files, only.

    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>
    

    Note that I'm using the Jaxb2Marshaller schemas, property (setSchemas), that allow me to set several schemas, instead of only one.

    0 讨论(0)
提交回复
热议问题