<commit_before>/*
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
 *
 * 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 org.switchyard.component.soap.composer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.wsdl.Operation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.Detail;
import javax.xml.soap.DetailEntry;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.dom.DOMSource;

import org.jboss.logging.Logger;
import org.switchyard.Exchange;
import org.switchyard.ExchangeState;
import org.switchyard.Message;
import org.switchyard.component.common.composer.BaseMessageComposer;
import org.switchyard.component.soap.SOAPMessages;
import org.switchyard.component.soap.util.SOAPUtil;
import org.switchyard.component.soap.util.WSDLUtil;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * The SOAP implementation of MessageComposer simply copies the SOAP body into
 * the Message and SOAP headers into the Message's context, and vice-versa.
 *
 * @author <NAME> <<EMAIL>> (C) 2011 Red Hat Inc.
 */
public class SOAPMessageComposer extends BaseMessageComposer<SOAPBindingData> {

    // Constant suffix used for the reply wrapper when the composer is configured to 
    // wrap response messages with operation name.
    private static final String DOC_LIT_WRAPPED_REPLY_SUFFIX = "Response";

    private static final String CONTENT_DISPOSITION = "Content-Disposition";
    private static final String CONTENT_ID = "Content-ID";
    private static final String CONTENT_ID_START = "<";
    private static final String CONTENT_DISPOSITION_NAME = "name=";
    private static final String CONTENT_DISPOSITION_QUOTE = "\"";
    private static final String TEMP_FILE_EXTENSION = ".tmp";

    private static Logger _log = Logger.getLogger(SOAPMessageComposer.class);
    private Port _wsdlPort;
    private Boolean _documentStyle = false;
    private Boolean _mtomEnabled = false;
    private Boolean _xopExpand = false;
    private Boolean _unwrapped = false;

    /**
     * {@inheritDoc}
     */
    @Override
    public Message compose(SOAPBindingData source, Exchange exchange) throws Exception {
        final SOAPMessage soapMessage = source.getSOAPMessage();
        final Message message = exchange.createMessage();
        final Boolean input = exchange.getPhase() == null;
        getContextMapper().mapFrom(source, exchange.getContext(message));

        final SOAPEnvelope envelope = soapMessage.getSOAPPart().getEnvelope();
        if (envelope == null) {
            return message;
        }

        final SOAPBody soapBody = envelope.getBody();
        if (soapBody == null) {
            throw SOAPMessages.MESSAGES.missingSOAPBodyFromRequest();
        }

        try {
            if (soapBody.hasFault()) {
                // peel off the Fault element
                SOAPFault fault = soapBody.getFault();
                if (fault.hasDetail()) {
                    Detail detail = fault.getDetail();
                    // We only support one entry at this moment
                    DetailEntry entry = null;
                    Iterator<DetailEntry> entries = detail.getDetailEntries();
                    if (entries.hasNext()) {
                        entry = entries.next();
                    }
                    if (entry != null) {
                        Node detailNode = entry.getParentNode().removeChild(entry);
                        message.setContent(new DOMSource(detailNode));
                        return message;
                    }
                }
            }

            List<Element> bodyChildren = getChildElements(soapBody);
            if (bodyChildren.size() > 1) {
                throw SOAPMessages.MESSAGES.foundMultipleSOAPElementsInSOAPBody();
            } else if (bodyChildren.size() == 0 || bodyChildren.get(0) == null) {
                throw SOAPMessages.MESSAGES.couldNotFindSOAPElementInSOAPBody();
            }

            Node bodyNode = bodyChildren.get(0);
            if (_documentStyle) {
                if (_unwrapped) {
                    String opName = exchange.getContract().getConsumerOperation().getName();
                    // peel off the operation wrapper, if present
                    if (opName != null && opName.equals(bodyNode.getLocalName())) {
                        List<Element> subChildren = getChildElements(bodyNode);
                        if (subChildren.size() == 0 || subChildren.size() > 1) {
                            _log.debug("Unable to unwrap element: " + bodyNode.getLocalName()
                                   + ". A single child element is required.");
                        } else {
                            bodyNode = subChildren.get(0);
                        }
                    }
                }
            }
            bodyNode = bodyNode.getParentNode().removeChild(bodyNode);

            // SOAP Attachments
            Map<String, DataSource> attachments = new HashMap<String, DataSource>();
            Iterator<AttachmentPart> aparts = (Iterator<AttachmentPart>) soapMessage.getAttachments();
            while (aparts.hasNext()) {
                AttachmentPart apRequest = aparts.next();
                String[] contentId = apRequest.getMimeHeader(CONTENT_ID);
                String name = null;
                if (_mtomEnabled) {
                    if (contentId == null) {
                        throw SOAPMessages.MESSAGES.contentIDHeaderMissingForAttachmentPart();
                    }
                    name = contentId[0];
                } else {
                    name = apRequest.getDataHandler().getDataSource().getName();
                    if ((name == null) || (name.length() == 0)) {
                        String[] disposition = apRequest.getMimeHeader(CONTENT_DISPOSITION);
                        name = (contentId != null) ? contentId[0] : null;
                        if ((name == null) && (disposition != null)) {
                            int start = disposition[0].indexOf(CONTENT_DISPOSITION_NAME);
                            String namePart = disposition[0].substring(start + CONTENT_DISPOSITION_NAME.length() + 1);
                            int end = namePart.indexOf(CONTENT_DISPOSITION_QUOTE);
                            name = namePart.substring(0, end);
                        } else if (name == null) {
                            // TODO: Identify the extension using content-type
                            name = UUID.randomUUID() + TEMP_FILE_EXTENSION;
                        }
                    }
                }
                if (name.startsWith(CONTENT_ID_START)) {
                    name = name.substring(1, name.length() - 1);
                }
                if (_mtomEnabled && _xopExpand) {
                    // Using a different map because Camel throws java.lang.StackOverflowError
                    // when we do message.removeAttachment(cid);
                    attachments.put(name, apRequest.getDataHandler().getDataSource());
                } else {
                    message.addAttachment(name, apRequest.getDataHandler().getDataSource());
                }
            }
            if (_mtomEnabled && _xopExpand) {
                // Expand xop message by inlining Base64 content
                bodyNode = SOAPUtil.expandXop((Element)bodyNode, attachments);
            }
            message.setContent(new DOMSource(bodyNode));
        } catch (Exception ex) {
            if (ex instanceof SOAPException) {
                throw (SOAPException) ex;
            }
            throw new SOAPException(ex);
        }

        return message;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SOAPBindingData decompose(Exchange exchange, SOAPBindingData target) throws Exception {
        final SOAPMessage soapMessage = target.getSOAPMessage();
        final Message message = exchange.getMessage();<commit_msg>SWITCHYARD-1920 SOAP MessageComposer creates DOMSource without Document element<commit_after>/*
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
 *
 * 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 org.switchyard.component.soap.composer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.wsdl.Operation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.Detail;
import javax.xml.soap.DetailEntry;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;

import org.jboss.logging.Logger;
import org.switchyard.Exchange;
import org.switchyard.ExchangeState;
import org.switchyard.Message;
import org.switchyard.component.common.composer.BaseMessageComposer;
import org.switchyard.component.soap.SOAPMessages;
import org.switchyard.component.soap.util.SOAPUtil;
import org.switchyard.component.soap.util.WSDLUtil;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * The SOAP implementation of MessageComposer simply copies the SOAP body into
 * the Message and SOAP headers into the Message's context, and vice-versa.
 *
 * @author <NAME> <<EMAIL>> (C) 2011 Red Hat Inc.
 */
public class SOAPMessageComposer extends BaseMessageComposer<SOAPBindingData> {

    // Constant suffix used for the reply wrapper when the composer is configured to 
    // wrap response messages with operation name.
    private static final String DOC_LIT_WRAPPED_REPLY_SUFFIX = "Response";

    private static final String CONTENT_DISPOSITION = "Content-Disposition";
    private static final String CONTENT_ID = "Content-ID";
    private static final String CONTENT_ID_START = "<";
    private static final String CONTENT_DISPOSITION_NAME = "name=";
    private static final String CONTENT_DISPOSITION_QUOTE = "\"";
    private static final String TEMP_FILE_EXTENSION = ".tmp";

    private static Logger _log = Logger.getLogger(SOAPMessageComposer.class);
    private Port _wsdlPort;
    private Boolean _documentStyle = false;
    private Boolean _mtomEnabled = false;
    private Boolean _xopExpand = false;
    private Boolean _unwrapped = false;

    /**
     * {@inheritDoc}
     */
    @Override
    public Message compose(SOAPBindingData source, Exchange exchange) throws Exception {
        final SOAPMessage soapMessage = source.getSOAPMessage();
        final Message message = exchange.createMessage();
        final Boolean input = exchange.getPhase() == null;
        getContextMapper().mapFrom(source, exchange.getContext(message));

        final SOAPEnvelope envelope = soapMessage.getSOAPPart().getEnvelope();
        if (envelope == null) {
            return message;
        }

        final SOAPBody soapBody = envelope.getBody();
        if (soapBody == null) {
            throw SOAPMessages.MESSAGES.missingSOAPBodyFromRequest();
        }

        try {
            if (soapBody.hasFault()) {
                // peel off the Fault element
                SOAPFault fault = soapBody.getFault();
                if (fault.hasDetail()) {
                    Detail detail = fault.getDetail();
                    // We only support one entry at this moment
                    DetailEntry entry = null;
                    Iterator<DetailEntry> entries = detail.getDetailEntries();
                    if (entries.hasNext()) {
                        entry = entries.next();
                    }
                    if (entry != null) {
                        Node detailNode = entry.getParentNode().removeChild(entry);
                        message.setContent(detailNode);
                        return message;
                    }
                }
            }

            List<Element> bodyChildren = getChildElements(soapBody);
            if (bodyChildren.size() > 1) {
                throw SOAPMessages.MESSAGES.foundMultipleSOAPElementsInSOAPBody();
            } else if (bodyChildren.size() == 0 || bodyChildren.get(0) == null) {
                throw SOAPMessages.MESSAGES.couldNotFindSOAPElementInSOAPBody();
            }

            Node bodyNode = bodyChildren.get(0);
            if (_documentStyle) {
                if (_unwrapped) {
                    String opName = exchange.getContract().getConsumerOperation().getName();
                    // peel off the operation wrapper, if present
                    if (opName != null && opName.equals(bodyNode.getLocalName())) {
                        List<Element> subChildren = getChildElements(bodyNode);
                        if (subChildren.size() == 0 || subChildren.size() > 1) {
                            _log.debug("Unable to unwrap element: " + bodyNode.getLocalName()
                                   + ". A single child element is required.");
                        } else {
                            bodyNode = subChildren.get(0);
                        }
                    }
                }
            }
            bodyNode = bodyNode.getParentNode().removeChild(bodyNode);

            // SOAP Attachments
            Map<String, DataSource> attachments = new HashMap<String, DataSource>();
            Iterator<AttachmentPart> aparts = (Iterator<AttachmentPart>) soapMessage.getAttachments();
            while (aparts.hasNext()) {
                AttachmentPart apRequest = aparts.next();
                String[] contentId = apRequest.getMimeHeader(CONTENT_ID);
                String name = null;
                if (_mtomEnabled) {
                    if (contentId == null) {
                        throw SOAPMessages.MESSAGES.contentIDHeaderMissingForAttachmentPart();
                    }
                    name = contentId[0];
                } else {
                    name = apRequest.getDataHandler().getDataSource().getName();
                    if ((name == null) || (name.length() == 0)) {
                        String[] disposition = apRequest.getMimeHeader(CONTENT_DISPOSITION);
                        name = (contentId != null) ? contentId[0] : null;
                        if ((name == null) && (disposition != null)) {
                            int start = disposition[0].indexOf(CONTENT_DISPOSITION_NAME);
                            String namePart = disposition[0].substring(start + CONTENT_DISPOSITION_NAME.length() + 1);
                            int end = namePart.indexOf(CONTENT_DISPOSITION_QUOTE);
                            name = namePart.substring(0, end);
                        } else if (name == null) {
                            // TODO: Identify the extension using content-type
                            name = UUID.randomUUID() + TEMP_FILE_EXTENSION;
                        }
                    }
                }
                if (name.startsWith(CONTENT_ID_START)) {
                    name = name.substring(1, name.length() - 1);
                }
                if (_mtomEnabled && _xopExpand) {
                    // Using a different map because Camel throws java.lang.StackOverflowError
                    // when we do message.removeAttachment(cid);
                    attachments.put(name, apRequest.getDataHandler().getDataSource());
                } else {
                    message.addAttachment(name, apRequest.getDataHandler().getDataSource());
                }
            }
            if (_mtomEnabled && _xopExpand) {
                // Expand xop message by inlining Base64 content
                bodyNode = SOAPUtil.expandXop((Element)bodyNode, attachments);
            }
            message.setContent(bodyNode);
        } catch (Exception ex) {
            if (ex instanceof SOAPException) {
                throw (SOAPException) ex;
            }
            throw new SOAPException(ex);
        }

        return message;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SOAPBindingData decompose(Exchange exchange, SOAPBindingData target) throws Exception {
        final SOAPMessage soapMessage = target.getSOAPMessage();
        final Message message = exchange.getMessage();
{'fraction_non_alphanumeric': 0.09165913200723327, 'fraction_numerical': 0.003955696202531646, 'mean_word_length': 2.0246111775764826, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 17}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1119891', 'n_tokens_mistral': 4618, 'n_tokens_neox': 4369, 'n_words': 1150}