com.castlemock.web.mock.soap.model.project.service.CreateSoapPortsService.java Source code

Java tutorial

Introduction

Here is the source code for com.castlemock.web.mock.soap.model.project.service.CreateSoapPortsService.java

Source

/*
 * Copyright 2015 Karl Dahlgren
 *
 * 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.castlemock.web.mock.soap.model.project.service;

import com.castlemock.core.basis.model.Service;
import com.castlemock.core.basis.model.ServiceResult;
import com.castlemock.core.basis.model.ServiceTask;
import com.castlemock.core.basis.model.http.domain.HttpMethod;
import com.castlemock.core.mock.soap.model.project.domain.SoapMockResponseStatus;
import com.castlemock.core.mock.soap.model.project.domain.SoapOperationStatus;
import com.castlemock.core.mock.soap.model.project.domain.SoapResponseStrategy;
import com.castlemock.core.mock.soap.model.project.domain.SoapVersion;
import com.castlemock.core.mock.soap.model.project.dto.SoapMockResponseDto;
import com.castlemock.core.mock.soap.model.project.dto.SoapOperationDto;
import com.castlemock.core.mock.soap.model.project.dto.SoapPortDto;
import com.castlemock.core.mock.soap.model.project.service.message.input.CreateSoapPortsInput;
import com.castlemock.core.mock.soap.model.project.service.message.output.CreateSoapPortsOutput;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.wsdl.WSDLException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Karl Dahlgren
 * @since 1.0
 */
@org.springframework.stereotype.Service
public class CreateSoapPortsService extends AbstractSoapProjectService
        implements Service<CreateSoapPortsInput, CreateSoapPortsOutput> {

    private static final Logger LOGGER = Logger.getLogger(CreateSoapPortsService.class);
    private static final String AUTO_GENERATED_MOCK_RESPONSE_DEFAULT_NAME = "Auto-generated mocked response";
    private static final Integer DEFAULT_RESPONSE_SEQUENCE_INDEX = 0;
    private static final Integer DEFAULT_HTTP_STATUS_CODE = 200;
    private static final String WSDL_NAMESPACE = "http://schemas.xmlsoap.org/wsdl/";
    private static final String SOAP_11_NAMESPACE = "http://schemas.xmlsoap.org/wsdl/soap/";
    private static final String SOAP_12_NAMESPACE = "http://schemas.xmlsoap.org/wsdl/soap12/";

    /**
     *
     * The process message is responsible for processing an incoming serviceTask and generate
     * a response based on the incoming serviceTask input
     * @param serviceTask The serviceTask that will be processed by the service
     * @return A result based on the processed incoming serviceTask
     * @see ServiceTask
     * @see ServiceResult
     */
    @Override
    public ServiceResult<CreateSoapPortsOutput> process(final ServiceTask<CreateSoapPortsInput> serviceTask) {
        final CreateSoapPortsInput input = serviceTask.getInput();
        final String soapProjectId = input.getSoapProjectId();
        List<SoapPortDto> soapPorts = null;
        try {
            soapPorts = createPorts(input.getFiles(), input.isGenerateResponse());
        } catch (WSDLException e) {
            throw new IllegalStateException("Unable to parse the WSDL file");
        }

        for (SoapPortDto newSoapPort : soapPorts) {
            SoapPortDto existingSoapPort = repository.findSoapPortWithName(soapProjectId, newSoapPort.getName());

            if (existingSoapPort == null) {
                repository.saveSoapPort(soapProjectId, newSoapPort);
                continue;
            }

            for (SoapOperationDto newSoapOperation : newSoapPort.getOperations()) {
                SoapOperationDto existingSoapOperation = repository.findSoapOperationWithName(soapProjectId,
                        existingSoapPort.getId(), newSoapOperation.getName());

                if (existingSoapOperation != null) {
                    existingSoapOperation.setOriginalEndpoint(newSoapOperation.getOriginalEndpoint());
                    existingSoapOperation.setSoapVersion(newSoapOperation.getSoapVersion());
                    repository.updateSoapOperation(soapProjectId, existingSoapPort.getId(),
                            existingSoapOperation.getId(), existingSoapOperation);
                } else {
                    repository.saveSoapOperation(soapProjectId, existingSoapPort.getId(), newSoapOperation);
                }
            }
        }
        return createServiceResult(new CreateSoapPortsOutput());
    }

    /**
     * The method creates ports based on provided definitions.
     * @param files Definitions used to create new ports.
     * @param generateResponse Boolean value for if a default response should be generated for each new operation
     * @return List of ports generated from the provided definitions.
     * @throws WSDLException Throws an WSDLException if WSDL parsing failed.
     */
    private List<SoapPortDto> createPorts(final List<File> files, final boolean generateResponse)
            throws WSDLException {
        try {
            final List<SoapPortDto> soapPorts = new ArrayList<>();
            final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            documentBuilderFactory.setValidating(false);
            documentBuilderFactory.setNamespaceAware(true);

            for (File file : files) {
                final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                final Document document = documentBuilder.parse(file);
                document.getDocumentElement().normalize();

                final List<SoapPortDto> fileSoapPorts = getSoapPorts(document, generateResponse);
                soapPorts.addAll(fileSoapPorts);
            }
            return soapPorts;
        } catch (Exception e) {
            throw new IllegalArgumentException("Unable parse WSDL file", e);
        }
    }

    /**
     * THe method provides the functionality to parse a document and extract all the SOAP ports from the SOAP ports.
     * The method will also generate mocked responses if the {@code generateResponse}
     * @param document The document which will be parsed
     * @param generateResponse Boolean value determining if a response should be generated for each extracted
     *                         operation.
     * @return A list of SOAP ports
     */
    private List<SoapPortDto> getSoapPorts(final Document document, final boolean generateResponse) {
        final List<SoapPortDto> soapPorts = new ArrayList<SoapPortDto>();
        final NodeList serviceNodeList = document.getDocumentElement().getElementsByTagNameNS(WSDL_NAMESPACE,
                "service");
        for (int serviceIndex = 0; serviceIndex < serviceNodeList.getLength(); serviceIndex++) {
            final Node serviceNode = serviceNodeList.item(serviceIndex);
            if (serviceNode.getNodeType() == Node.ELEMENT_NODE) {
                final Element serviceElement = (Element) serviceNode;
                final NodeList portNodeList = serviceElement.getElementsByTagNameNS(WSDL_NAMESPACE, "port");
                for (int portIndex = 0; portIndex < portNodeList.getLength(); portIndex++) {
                    Node portNode = portNodeList.item(portIndex);
                    if (portNode.getNodeType() == Node.ELEMENT_NODE) {
                        final Element portElement = (Element) portNode;

                        String soapOperationAddress = extractSoapAddress(portElement, SOAP_11_NAMESPACE);
                        SoapVersion soapOperationVersion = SoapVersion.SOAP11;

                        if (soapOperationAddress == null) {
                            soapOperationAddress = extractSoapAddress(portElement, SOAP_12_NAMESPACE);
                            soapOperationVersion = SoapVersion.SOAP12;
                        }
                        if (soapOperationAddress == null) {
                            // The port element is not a SOAP port
                            continue;
                        }

                        final String portName = getAttribute(portElement, "name");
                        final String portBinding = getAttribute(portElement, "binding");
                        final List<SoapOperationDto> soapOperations = getSoapOperations(document, portBinding,
                                soapOperationAddress, soapOperationVersion, generateResponse);
                        final SoapPortDto soapPort = new SoapPortDto();
                        soapPort.setName(portName);
                        soapPort.setOperations(soapOperations);
                        soapPort.setUri(portName);
                        soapPorts.add(soapPort);

                    }
                }
            }
        }
        return soapPorts;
    }

    /**
     * The method provides the functionality to extract SOAP operation from a document
     * @param document The document which will be parsed
     * @param soapPortBinding The SOAP port binding which the operations belongs to
     * @param soapOperationAddress The address that will be assigned as the default address to the operations
     * @param soapVersion The SOAP operation version (SOAP 11 or SOAP 12)
     * @param generateResponse Boolean value determining if a response should be generated for each extracted
     *                         operation.
     * @return A list of extracted SOAP operations
     */
    private List<SoapOperationDto> getSoapOperations(final Document document, final String soapPortBinding,
            final String soapOperationAddress, final SoapVersion soapVersion, final boolean generateResponse) {
        final List<SoapOperationDto> soapOperations = new LinkedList<SoapOperationDto>();
        final Element bindingElement = findElement(document, WSDL_NAMESPACE, "binding", soapPortBinding);
        if (bindingElement == null) {
            return soapOperations;
        }
        final String bindingType = getAttribute(bindingElement, "type");

        if (bindingType == null) {
            return soapOperations;
        }

        final Element portTypeElement = findElement(document, WSDL_NAMESPACE, "portType", bindingType);

        if (portTypeElement == null) {
            return soapOperations;
        }

        final NodeList operationNodeList = portTypeElement.getElementsByTagNameNS(WSDL_NAMESPACE, "operation");
        for (int operationIndex = 0; operationIndex < operationNodeList.getLength(); operationIndex++) {
            final Node operationNode = operationNodeList.item(operationIndex);
            if (operationNode.getNodeType() == Node.ELEMENT_NODE) {
                final Element operationElement = (Element) operationNode;
                final String operationName = getAttribute(operationElement, "name");
                final SoapOperationDto soapOperation = new SoapOperationDto();
                final String defaultBody = generateDefaultBody(operationName, operationElement.getNamespaceURI());
                final String inputMessageName = getInputMessageName(operationElement);
                final String identifier = inputMessageName != null && !inputMessageName.isEmpty() ? inputMessageName
                        : operationName;

                soapOperation.setName(operationName);
                soapOperation.setIdentifier(identifier);
                soapOperation.setHttpMethod(HttpMethod.POST);
                soapOperation.setStatus(SoapOperationStatus.MOCKED);
                soapOperation.setResponseStrategy(SoapResponseStrategy.RANDOM);
                soapOperation.setForwardedEndpoint(soapOperationAddress);
                soapOperation.setOriginalEndpoint(soapOperationAddress);
                soapOperation.setSoapVersion(soapVersion);
                soapOperation.setMockResponses(new ArrayList<SoapMockResponseDto>());
                soapOperation.setDefaultBody(defaultBody);
                soapOperation.setCurrentResponseSequenceIndex(DEFAULT_RESPONSE_SEQUENCE_INDEX);
                if (generateResponse) {
                    final SoapMockResponseDto mockResponse = new SoapMockResponseDto();
                    mockResponse.setBody(soapOperation.getDefaultBody());
                    mockResponse.setStatus(SoapMockResponseStatus.ENABLED);
                    mockResponse.setName(AUTO_GENERATED_MOCK_RESPONSE_DEFAULT_NAME);
                    mockResponse.setHttpStatusCode(DEFAULT_HTTP_STATUS_CODE);
                    soapOperation.getMockResponses().add(mockResponse);
                }

                soapOperations.add(soapOperation);
            }
        }
        return soapOperations;
    }

    /**
     * Search and find for a specific element
     * @param document The document that will be parsed
     * @param namespace The element namespace
     * @param type The type or name of the element
     * @param identifier The identifier is used to determine if the element is the correct element being searched for.
     * @return An element that matches the provided search criteria
     */
    private Element findElement(final Document document, final String namespace, final String type,
            final String identifier) {
        final NodeList nodeList = document.getElementsByTagNameNS(namespace, type);
        for (int index = 0; index < nodeList.getLength(); index++) {
            Node node = nodeList.item(index);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element element = (Element) node;
                String name = element.getAttribute("name");
                if (name.equals(identifier)) {
                    return element;
                }
            }
        }
        return null;
    }

    /**
     * Extracts an attribute from an element. The method will also remove namespace prefix
     * if it is present.
     * @param element The element which the attribute will be extracted from
     * @param name The name of the attribute that will be extracted
     * @return The attribute value
     */
    private String getAttribute(Element element, String name) {
        final String value = element.getAttribute(name);
        if (value == null) {
            return null;
        }
        String[] splitValues = value.split(":");
        if (splitValues.length == 1) {
            return splitValues[0];
        }
        return splitValues[1];
    }

    /**
     * Extract the SOAP address from a port element
     * @param portElement The port element that contains the address
     * @param namespace The namespace of the address
     * @return The SOAP port address
     */
    private String extractSoapAddress(Element portElement, String namespace) {
        NodeList addressElements = portElement.getElementsByTagNameNS(namespace, "address");

        for (int addressIndex = 0; addressIndex < addressElements.getLength(); addressIndex++) {
            Node soapAddressNode = addressElements.item(addressIndex);
            if (soapAddressNode.getNodeType() == Node.ELEMENT_NODE) {
                Element soapAddressElement = (Element) soapAddressNode;
                String soapAddress = soapAddressElement.getAttribute("location");
                if (soapAddress != null) {
                    return soapAddress;
                }
            }
        }
        return null;
    }

    private String getInputMessageName(Element operationElement) {
        String inputMessageName = null;
        final NodeList inputNodeList = operationElement.getElementsByTagNameNS(WSDL_NAMESPACE, "input");
        for (int inputIndex = 0; inputIndex < inputNodeList.getLength(); inputIndex++) {
            final Node inputNode = inputNodeList.item(inputIndex);
            if (inputNode.getNodeType() == Node.ELEMENT_NODE) {
                final Element inputElement = (Element) inputNode;
                inputMessageName = getAttribute(inputElement, "name");
            }
        }
        return inputMessageName;
    }

    /**
     * The method provides the functionality to generate a new mocked response
     * @param name The operation contains all the information that is needed to compose a operation default body
     * @param targetNamespace The target namespace that the response will use
     * @return A string value of the response
     */
    public static String generateDefaultBody(final String name, final String targetNamespace) {
        final String prefix = "web";
        final String resultElement = name + "Result";
        return "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:" + prefix
                + "=\"" + targetNamespace + "\">\n" + "   <soapenv:Header/>\n" + "   <soapenv:Body>\n" + "      <"
                + prefix + ":" + resultElement + ">?</" + prefix + ":" + resultElement + ">\n"
                + "   </soapenv:Body>\n" + "</soapenv:Envelope>";
    }
}