All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.consol.citrus.generate.xml.WsdlXmlTestGenerator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.consol.citrus.generate.xml;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.consol.citrus.context.TestContext;
import com.consol.citrus.exceptions.CitrusRuntimeException;
import com.consol.citrus.generate.WsdlTestGenerator;
import com.consol.citrus.generate.dictionary.InboundXmlDataDictionary;
import com.consol.citrus.generate.dictionary.OutboundXmlDataDictionary;
import com.consol.citrus.message.Message;
import com.consol.citrus.model.testcase.ws.ObjectFactory;
import com.consol.citrus.util.XMLUtils;
import com.consol.citrus.ws.message.SoapMessage;
import com.consol.citrus.xml.XmlConfigurer;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeSystem;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.impl.xsd2inst.SampleXmlUtil;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;

/**
 * Test generator creates one to many test cases based on operations defined in a XML schema XSD.
 * @author Christoph Deppisch
 * @since 2.7.4
 */
public class WsdlXmlTestGenerator extends MessagingXmlTestGenerator implements WsdlTestGenerator {

    private String wsdl;

    private String operation;
    private String namePrefix;
    private String nameSuffix = "_IT";

    private InboundXmlDataDictionary inboundDataDictionary = new InboundXmlDataDictionary();
    private OutboundXmlDataDictionary outboundDataDictionary = new OutboundXmlDataDictionary();

    @Override
    public void create() {
        String wsdlNsDelaration = "declare namespace wsdl='http://schemas.xmlsoap.org/wsdl/' ";
        String soapNsDelaration = "declare namespace soap='http://schemas.xmlsoap.org/wsdl/soap/' ";

        // compile wsdl and xsds right now, otherwise later input is useless:
        XmlObject wsdlObject = compileWsdl(wsdl);
        SchemaTypeSystem schemaTypeSystem = compileXsd(wsdlObject);

        log.info("WSDL compilation successful");
        String serviceName = evaluateAsString(wsdlObject, wsdlNsDelaration + ".//wsdl:portType/@name");
        log.info("Found service: " + serviceName);

        if (!StringUtils.hasText(namePrefix)) {
            withNamePrefix(serviceName + "_");
        }

        log.info("Found service operations:");
        XmlObject[] messages = wsdlObject.selectPath(wsdlNsDelaration + ".//wsdl:message");
        XmlObject[] operations = wsdlObject.selectPath(wsdlNsDelaration + ".//wsdl:portType/wsdl:operation");
        for (XmlObject operation : operations) {
            log.info(evaluateAsString(operation, wsdlNsDelaration + "./@name"));
        }
        log.info("Generating test cases for service operations ...");

        for (XmlObject operation : operations) {
            SoapMessage request = new SoapMessage();
            SoapMessage response = new SoapMessage();

            String operationName = evaluateAsString(operation, wsdlNsDelaration + "./@name");
            if (StringUtils.hasText(this.operation) && !operationName.equals(this.operation)) {
                continue;
            }

            XmlObject[] bindingOperations = wsdlObject.selectPath(wsdlNsDelaration + ".//wsdl:binding/wsdl:operation");
            for (XmlObject bindingOperation : bindingOperations) {
                String bindingOperationName = evaluateAsString(bindingOperation, wsdlNsDelaration + "./@name");

                if (bindingOperationName.equals(operationName)) {
                    String soapAction = removeNsPrefix(evaluateAsString(bindingOperation, soapNsDelaration + "./soap:operation/@soapAction"));
                    request.soapAction(soapAction);
                    break;
                }
            }

            String inputMessage = removeNsPrefix(evaluateAsString(operation, wsdlNsDelaration + "./wsdl:input/@message"));
            String outputMessage = removeNsPrefix(evaluateAsString(operation, wsdlNsDelaration + "./wsdl:output/@message"));

            String inputElement = null;
            String outputElement = null;
            for (XmlObject message : messages) {
                String messageName = evaluateAsString(message, wsdlNsDelaration + "./@name");

                if (messageName.equals(inputMessage)) {
                    inputElement = removeNsPrefix(evaluateAsString(message, wsdlNsDelaration + "./wsdl:part/@element"));
                }

                if (messageName.equals(outputMessage)) {
                    outputElement = removeNsPrefix(evaluateAsString(message, wsdlNsDelaration + "./wsdl:part/@element"));
                }
            }

            // Now generate it
            withName(namePrefix + operationName + nameSuffix);

            SchemaType requestElem = getSchemaType(schemaTypeSystem, operationName, inputElement);
            request.setPayload(SampleXmlUtil.createSampleForType(requestElem));
            withRequest(request);

            SchemaType responseElem = getSchemaType(schemaTypeSystem, operationName, outputElement);
            response.setPayload(SampleXmlUtil.createSampleForType(responseElem));
            withResponse(response);

            XmlConfigurer configurer = new XmlConfigurer();
            configurer.initialize();
            configurer.setSerializeSettings(Collections.singletonMap(XmlConfigurer.XML_DECLARATION, false));
            XMLUtils.initialize(configurer);

            super.create();

            log.info("Successfully created new test case " + getTargetPackage() + "." + getName());
        }
    }

    @Override
    protected List getMarshallerContextPaths() {
        List contextPaths = super.getMarshallerContextPaths();
        contextPaths.add(ObjectFactory.class.getPackage().getName());
        return contextPaths;
    }

    @Override
    protected List getMarshallerSchemas() {
        List schemas = super.getMarshallerSchemas();
        schemas.add(new ClassPathResource("com/consol/citrus/schema/citrus-http-testcase.xsd"));
        schemas.add(new ClassPathResource("com/consol/citrus/schema/citrus-ws-testcase.xsd"));
        return schemas;
    }

    @Override
    protected Message generateInboundMessage(Message message) {
        inboundDataDictionary.process(message, new TestContext());
        return super.generateInboundMessage(message);
    }

    @Override
    protected Message generateOutboundMessage(Message message) {
        outboundDataDictionary.process(message, new TestContext());
        return super.generateOutboundMessage(message);
    }

    /**
     * Finds nested XML schema definition and compiles it to a schema type system instance
     * @param wsdl
     * @return
     */
    private XmlObject compileWsdl(String wsdl) {
        File wsdlFile;
        try {
            wsdlFile = new PathMatchingResourcePatternResolver().getResource(wsdl).getFile();
        } catch (IOException e) {
            wsdlFile = new File(wsdl);
        }

        if (!wsdlFile.exists()) {
            throw new CitrusRuntimeException("Unable to read WSDL - does not exist in " + wsdlFile.getAbsolutePath());
        }

        if (!wsdlFile.canRead()) {
            throw new CitrusRuntimeException("Unable to read WSDL - could not open in read mode");
        }

        try {
            return XmlObject.Factory.parse(wsdlFile, (new XmlOptions()).setLoadLineNumbers().setLoadMessageDigest().setCompileDownloadUrls());
        } catch (XmlException e) {
            for (Object error : e.getErrors()) {
                log.error(((XmlError)error).getLine() + "" + error.toString());
            }
            throw new CitrusRuntimeException("WSDL could not be parsed", e);
        } catch (Exception e) {
            throw new CitrusRuntimeException("WSDL could not be parsed", e);
        }
    }

    /**
     * Finds nested XML schema definition and compiles it to a schema type system instance.
     * @param wsdl
     * @return
     */
    private SchemaTypeSystem compileXsd(XmlObject wsdl) {
        // extract namespaces defined on wsdl-level:
        String[] namespacesWsdl = extractNamespacesOnWsdlLevel(wsdl);

        // calc the namespace-prefix of the schema-tag, default ""
        String schemaNsPrefix = extractSchemaNamespacePrefix(wsdl);

        // extract each schema-element and add missing namespaces defined on wsdl-level
        String[] schemas = getNestedSchemas(wsdl, namespacesWsdl, schemaNsPrefix);

        XmlObject[] xsd = new XmlObject[schemas.length];
        try {
            for (int i=0; i < schemas.length; i++) {
                xsd[i] = XmlObject.Factory.parse(schemas[i], (new XmlOptions()).setLoadLineNumbers().setLoadMessageDigest().setCompileDownloadUrls());
            }
        } catch (Exception e) {
            throw new CitrusRuntimeException("Failed to parse XSD schema", e);
        }

        SchemaTypeSystem schemaTypeSystem = null;
        try {
            schemaTypeSystem = XmlBeans.compileXsd(xsd, XmlBeans.getContextTypeLoader(), new XmlOptions());
        } catch (XmlException e) {
            for (Object error : e.getErrors()) {
                log.error("Line " + ((XmlError)error).getLine() + ": " + error.toString());
            }
            throw new CitrusRuntimeException("Failed to compile XSD schema", e);
        } catch (Exception e) {
            throw new CitrusRuntimeException("Failed to compile XSD schema", e);
        }
        return schemaTypeSystem;
    }

    /**
     * @param schemaTypeSystem
     * @param operation
     * @param elementName
     * @return
     */
    private SchemaType getSchemaType(SchemaTypeSystem schemaTypeSystem, String operation, String elementName) {

        for (SchemaType elem : schemaTypeSystem.documentTypes()) {
            if (elem.getContentModel().getName().getLocalPart().equals(elementName)) {
                return elem;
            }
        }

        throw new CitrusRuntimeException("Unable to find schema type declaration '" + elementName + "'" +
                " for WSDL operation '" + operation + "'");
    }

    /**
     * Removes namespace prefix if present.
     * @param elementName
     * @return
     */
    private String removeNsPrefix(String elementName) {
        return elementName.indexOf(':') != -1 ? elementName.substring(elementName.indexOf(':') + 1) : elementName;
    }

    /**
     * Finds nested schema definitions and puts globally WSDL defined namespaces to schema level.
     *
     * @param wsdl
     * @param namespacesWsdl
     * @param schemaNsPrefix
     */
    private String[] getNestedSchemas(XmlObject wsdl, String[] namespacesWsdl, String schemaNsPrefix) {
        List schemas = new ArrayList<>();
        String openedStartTag = "<" + schemaNsPrefix + "schema";
        String endTag = "";

        int cursor = 0;
        while (wsdl.xmlText().indexOf(openedStartTag, cursor) != -1) {
            int begin = wsdl.xmlText().indexOf(openedStartTag, cursor);
            int end = wsdl.xmlText().indexOf(endTag, begin) + endTag.length();
            int insertPointNamespacesWsdl = wsdl.xmlText().indexOf(" ", begin);

            StringBuilder builder = new StringBuilder();
            builder.append(wsdl.xmlText().substring(begin, insertPointNamespacesWsdl)).append(" ");

            for (String nsWsdl : namespacesWsdl) {
                String nsPrefix = nsWsdl.substring(0, nsWsdl.indexOf("="));
                if (!wsdl.xmlText().substring(begin, end).contains(nsPrefix)) {
                    builder.append(nsWsdl).append(" ");
                }
            }

            builder.append(wsdl.xmlText().substring(insertPointNamespacesWsdl, end));
            schemas.add(builder.toString());
            cursor = end;
        }

        return schemas.toArray(new String[] {});
    }

    /**
     * Finds schema tag and extracts the namespace prefix.
     * @param wsdl
     * @return
     */
    private String extractSchemaNamespacePrefix(XmlObject wsdl) {
        String schemaNsPrefix = "";
        if (wsdl.xmlText().contains(":schema")) {
            int cursor = wsdl.xmlText().indexOf(":schema");
            for (int i = cursor; i > cursor - 100; i--) {
                schemaNsPrefix = wsdl.xmlText().substring(i, cursor);
                if (schemaNsPrefix.startsWith("<")) {
                    return schemaNsPrefix.substring(1) + ":";
                }
            }
        }
        return schemaNsPrefix;
    }

    /**
     * Returns an array of all namespace declarations, found on wsdl-level.
     *
     * @param wsdl
     * @return
     */
    private String[] extractNamespacesOnWsdlLevel(XmlObject wsdl) {
        int cursor = wsdl.xmlText().indexOf(":") + ":definitions ".length();
        String nsWsdlOrig = wsdl.xmlText().substring(cursor, wsdl.xmlText().indexOf(">", cursor));
        int noNs = StringUtils.countOccurrencesOf(nsWsdlOrig, "xmlns:");
        String[] namespacesWsdl = new String[noNs];
        cursor = 0;
        for (int i=0; i") + 1;
        int end = xmlObject[0].xmlText().lastIndexOf(" mappings) {
        this.inboundDataDictionary.getMappings().putAll(mappings);
        return this;
    }

    /**
     * Add outbound XPath expression mappings to manipulate outbound message content.
     * @param mappings
     * @return
     */
    public WsdlXmlTestGenerator withOutboundMappings(Map mappings) {
        this.outboundDataDictionary.getMappings().putAll(mappings);
        return this;
    }

    /**
     * Add inbound XPath expression mappings file to manipulate inbound message content.
     * @param mappingFile
     * @return
     */
    public WsdlXmlTestGenerator withInboundMappingFile(String mappingFile) {
        this.inboundDataDictionary.setMappingFile(new PathMatchingResourcePatternResolver().getResource(mappingFile));
        this.inboundDataDictionary.initialize();
        return this;
    }

    /**
     * Add outbound XPath expression mappings file to manipulate outbound message content.
     * @param mappingFile
     * @return
     */
    public WsdlXmlTestGenerator withOutboundMappingFile(String mappingFile) {
        this.outboundDataDictionary.setMappingFile(new PathMatchingResourcePatternResolver().getResource(mappingFile));
        this.outboundDataDictionary.initialize();
        return this;
    }

    /**
     * Sets the wsdl.
     *
     * @param wsdl
     */
    public void setWsdl(String wsdl) {
        this.wsdl = wsdl;
    }

    /**
     * Gets the wsdl.
     *
     * @return
     */
    public String getWsdl() {
        return wsdl;
    }

    /**
     * Sets the nameSuffix.
     *
     * @param nameSuffix
     */
    public void setNameSuffix(String nameSuffix) {
        this.nameSuffix = nameSuffix;
    }

    /**
     * Gets the nameSuffix.
     *
     * @return
     */
    public String getNameSuffix() {
        return nameSuffix;
    }

    /**
     * Sets the namePrefix.
     *
     * @param namePrefix
     */
    public void setNamePrefix(String namePrefix) {
        this.namePrefix = namePrefix;
    }

    /**
     * Gets the namePrefix.
     *
     * @return
     */
    public String getNamePrefix() {
        return namePrefix;
    }

    /**
     * Sets the operation.
     *
     * @param operation
     */
    public void setOperation(String operation) {
        this.operation = operation;
    }

    /**
     * Gets the operation.
     *
     * @return
     */
    public String getOperation() {
        return operation;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy