/**
 * PETALS - PETALS Services Platform. Copyright (c) 2007 EBM Websourcing,
 * http://www.ebmwebsourcing.com/
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version. This library is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * 
 * -------------------------------------------------------------------------
 * $Id:$
 * -------------------------------------------------------------------------
 */

package com.ebmwebsoucing.integration.client.rmi;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.jbi.JBIException;
import javax.jbi.messaging.ExchangeStatus;
import javax.jbi.messaging.InOnly;
import javax.jbi.messaging.InOptionalOut;
import javax.jbi.messaging.InOut;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.NormalizedMessage;
import javax.jbi.messaging.RobustInOnly;
import javax.mail.util.ByteArrayDataSource;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.objectweb.petals.tools.rmi.client.ComponentContextLocator;
import org.objectweb.petals.tools.rmi.common.serializable.Fault;
import org.objectweb.petals.tools.rmi.server.remote.interfaces.RemoteComponentContext;
import org.objectweb.petals.tools.rmi.server.util.Convert;
import org.ow2.easywsdl.wsdl.api.abstractItf.AbsItfOperation.MEPPatternConstants;

import com.ebmwebsourcing.easycommons.xml.XMLComparator;
import com.ebmwebsourcing.integration.util.IntegrationBuilder;
import com.ebmwebsourcing.integration.util.IntegrationException;
import com.ebmwebsourcing.integration.util.IntegrationRegexUtils;
import com.ebmwebsourcing.petals.integration.util.generated.Acknowledgment;
import com.ebmwebsourcing.petals.integration.util.generated.Actor;
import com.ebmwebsourcing.petals.integration.util.generated.Attachment;
import com.ebmwebsourcing.petals.integration.util.generated.Integration;
import com.ebmwebsourcing.petals.integration.util.generated.Mep;
import com.ebmwebsourcing.petals.integration.util.generated.NormalizedMessageExchange;
import com.ebmwebsourcing.petals.integration.util.generated.Property;
import com.ebmwebsourcing.petals.integration.util.generated.Test;

/**
 * RMI client sample
 * 
 * @author rnaudin
 * 
 */
public class RMIClient {

    private static final String UTF8 = "UTF-8";

    private static Transformer transformer;

    private static RemoteComponentContext componentContext;

    private static Integration integration;

    private static int rmiPort = 1099;

    private static String rmiContext = "RMIComponentContext";

    private static String rmiIP = "localhost";

    private static int consumerDelay = 1000;

    private static long defautAcceptTimeout = 60000;

    private static Map<String, String> returnedUuids = null;

    /**
     * @param args
     * @throws MessagingException
     */
    public static void main(String[] args) {

        URI propertiesLocation = null;
        URI integrationLocation = null;

        returnedUuids = new HashMap<String, String>();

        if (args.length == 0) {
            usage();
        }

        for (int i = 0; i < args.length; i++) {
            if ("-integration".equals(args[i])) {
                try {
                    integrationLocation = URI.create(args[i + 1].replace(" ", "%20").replace("\\",
                            "/"));
                    integrationLocation.normalize();
                } catch (IllegalArgumentException e) {
                    System.out.println("Integration URL location is invalid : " + args[i + 1]);
                }
            } else if ("-properties".equals(args[i])) {
                try {
                    propertiesLocation = URI.create(args[i + 1].replace(" ", "%20").replace("\\",
                            "/"));
                    propertiesLocation.normalize();
                } catch (IllegalArgumentException e) {
                    System.out.println("Properties URL location is invalid : " + args[i + 1]);
                }
            }
        }

        if (integrationLocation == null) {
            usage();
        }

        if (propertiesLocation != null) {
            Properties rmiProperties = new Properties();
            try {
                rmiProperties.load(propertiesLocation.toURL().openStream());
                if (rmiProperties.get("rmiPort") != null) {
                    rmiPort = Integer.parseInt((String) rmiProperties.get("rmiPort"));
                }
                if (rmiProperties.get("rmiIP") != null) {
                    rmiIP = (String) rmiProperties.get("rmiIP");
                }
                if (rmiProperties.get("rmiContext") != null) {
                    rmiContext = (String) rmiProperties.get("rmiContext");
                }
                if (rmiProperties.get("acceptTimeout") != null) {
                    defautAcceptTimeout = Long.parseLong((String) rmiProperties
                            .get("acceptTimeout"));
                }
            } catch (IOException e1) {
                e1.printStackTrace();
                System.exit(-1);
            }
        }

        try {
            ComponentContextLocator ccl = new ComponentContextLocator(rmiIP, rmiPort, rmiContext);
            componentContext = ccl.getComponentContext();
            System.out.println("Client: component Name = " + componentContext.getComponentName());

            integration = IntegrationBuilder.buildJavaIntegration(integrationLocation.toURL()
                    .openStream());

            for (Actor provider : integration.getProvider()) {
                runProviderIntegration(provider);
            }

            for (Actor consumer : integration.getConsumer()) {
                runConsumerIntegration(consumer);
            }

        } catch (Exception exception) {
            exception.printStackTrace();
            System.exit(-1);
        }

        System.out.println("Run integration tests successfully");
    }

    /**
     * TODO: Manage interfaces of the exposed services
     * 
     * @param provider
     * @throws JBIException
     * @throws TransformerException
     * @throws IntegrationException
     * @throws TransformerConfigurationException
     * @throws IOException
     */
    private static void runProviderIntegration(Actor provider) throws JBIException,
            TransformerConfigurationException, IntegrationException, TransformerException,
            IOException {

        componentContext.activateEndpoint(provider.getInterface(), provider.getService(),
                provider.getEndpoint());

        System.out.println(rmiContext + "[provider]: " + provider.getDescription());

        for (Test test : provider.getTest()) {
            System.out.println(rmiContext + ": Running test '" + test.getDescription()
                    + "' on operation '" + test.getOperation() + "' with mep '" + test.getMep()
                    + "'");

            MessageExchange exchange = componentContext.getDeliveryChannel().accept(
                    defautAcceptTimeout);

            checkProviderInMessage(exchange, test.getMessages().getIn(), test.getDescription(),
                    test.getOperation(), test.getMep());

            if (exchange
                    .getPattern()
                    .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_ONLY_PATTERN)) {
                if (test.getMessages().getInack() != null) {
                    handleProviderAcknowledgment(exchange, test.getMessages().getInack());
                } else {
                    throw new IntegrationException("Test '" + test.getDescription()
                            + "' must contain an InAck element for the patterm InOnly");
                }
            } else if (exchange
                    .getPattern()
                    .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.ROBUST_IN_ONLY_PATTERN)) {
                if (test.getMessages().getInack() != null) {
                    handleProviderAcknowledgment(exchange, test.getMessages().getInack());
                } else if (test.getMessages().getFault() != null
                        && test.getMessages().getFaultack() != null) {
                    handleProviderFault(exchange, test.getMessages().getFault(), test.getMessages()
                            .getFaultack(), test.getDescription());
                } else {
                    throw new IntegrationException(
                            "Test '"
                                    + test.getDescription()
                                    + "' must contain an InAck element or Fault and FaultAck elements for the pattern RobustInOnly");
                }
            } else if (exchange
                    .getPattern()
                    .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_OUT_PATTERN)
                    || exchange.getPattern().equals(MEPPatternConstants.IN_OUT)) {
                if (test.getMessages().getFault() != null) {
                    if (test.getMessages().getFaultack() != null) {
                        handleProviderFault(exchange, test.getMessages().getFault(), test
                                .getMessages().getFaultack(), test.getDescription());
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain an FaultAck element as it contains a Fault element for the pattern InOut");
                    }
                } else if (test.getMessages().getOut() != null) {
                    if (test.getMessages().getOutack() != null) {
                        handleProviderOutMessage(exchange, test.getMessages().getOut(), test
                                .getMessages().getOutack(), test.getDescription());
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain an OutAck element as it contains an Out element for the pattern InOut");
                    }
                } else {
                    throw new IntegrationException("Test '" + test.getDescription()
                            + "' must contain an Out or a Fault element for the pattern InOut");

                }
            }
            System.out.println("Exchange with exchangeID: "+exchange.getExchangeId()+" processed for Test: "+test.getDescription()+" RMIContext: "+rmiContext);
            System.out.println(rmiContext + ": Running test '" + test.getDescription()
                    + "' on operation '" + test.getOperation() + "' with mep '" + test.getMep()
                    + "'.....OK");
        }
    }

    private static void runConsumerIntegration(Actor consumer) throws MessagingException,
            IntegrationException, TransformerConfigurationException, TransformerException,
            InterruptedException, IOException, Exception {

        System.out.println(rmiContext + "[consumer]: " + consumer.getDescription());

        for (Test test : consumer.getTest()) {
            // Sleep a while to let the providers activate their
            // endpoints
            Thread.sleep(consumerDelay);
            System.out.println(rmiContext + ": Running test '" + test.getDescription()
                    + "' on operation '" + test.getOperation() + "' with mep '" + test.getMep()
                    + "'");

            MessageExchange exchange = createMessageExchange(test, consumer.getInterface(),
                    consumer.getService(), consumer.getEndpoint(), componentContext);

            MessageExchange ref1 = exchange;

            // System.out.println("exchange 1: "+
            // exchange.getPattern().toString() + " - "
            // + "ExchangeId :"+exchange.getExchangeId()+" - "
            // + "Ref : "+exchange);

            if (test.getSynchronousSend() != null) {

                exchange = componentContext.getDeliveryChannel().sendSync(exchange,
                        test.getSynchronousSend());
                if (test.isTimeoutReached() != null && test.isTimeoutReached() && exchange != null) {
                    throw new IntegrationException("Test '" + test.getDescription()
                            + "' failed. Expected timeout not reached");
                } else if (exchange == null) {
                    throw new IntegrationException("Test '" + test.getDescription()
                            + "' failed. Unexpected timeout reached");
                }
            } else {

                componentContext.getDeliveryChannel().send(exchange);

                exchange = componentContext.getDeliveryChannel().accept(defautAcceptTimeout);
            }

            MessageExchange ref2 = exchange;

            // TODO Check exchange mismatch ...
            if (!ref1.getPattern().toString().equalsIgnoreCase(ref2.getPattern().toString())) {
                throw new Exception("Not same exchange (not same pattern) -> Ref1 ExchangeId "
                        + ref1.getExchangeId() + " Ref2 ExchangeId " + ref2.getExchangeId()
                        + "\nRef1 Pattern : " + ref1.getPattern() + " Ref2 pattern : "
                        + ref2.getPattern());
            }

            if (!ref1.getExchangeId().equalsIgnoreCase(ref2.getExchangeId())) {
                throw new Exception("Not same exchange (not same exchangeId)");
            }
            // END TODO

            if (MEPPatternConstants.IN_ONLY.equals(exchange.getPattern())
                    || exchange
                            .getPattern()
                            .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_ONLY_PATTERN)) { // Mep.IN_ONLY.equals(test.getMep()))
                                                                                                                           // {
                if (test.getMessages().getInack() != null) {
                    checkAcknowledgment(exchange, test.getMessages().getInack(),
                            test.getDescription());
                } else {
                    throw new IntegrationException("Test '" + test.getDescription()
                            + "' must contain an InAck element for the patterm InOnly");
                }
            } else if (Mep.ROBUST_IN_ONLY.equals(test.getMep())
                    || exchange
                            .getPattern()
                            .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.ROBUST_IN_ONLY_PATTERN)) {
                if (test.getMessages().getInack() != null) {
                    checkAcknowledgment(exchange, test.getMessages().getInack(),
                            test.getDescription());
                } else if (test.getMessages().getFault() != null) {
                    if (test.getMessages().getFaultack() != null) {
                        checkConsumerFault(exchange, test.getMessages().getFault(), test
                                .getMessages().getFaultack(), test.getDescription());
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain a FaultAck element as it contains a Fault element for the pattern RobustInOnly");
                    }
                } else {
                    throw new IntegrationException(
                            "Test '"
                                    + test.getDescription()
                                    + "' must contain an InAck or a Fault element for the patterm RobustInOnly");
                }
            } else if (MEPPatternConstants.IN_OUT.equals(exchange.getPattern())
                    || exchange
                            .getPattern()
                            .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_OUT_PATTERN)) { // Mep.IN_OUT.equals(test.getMep()))
                                                                                                                          // {

                if (test.getMessages().getOut() != null) {
                    if (test.getMessages().getOutack() != null) {
                        checkConsumerOutMessage(exchange, test.getMessages().getOut(), test
                                .getMessages().getOutack(), test.getDescription());
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain an OutAck element as it contain Out element for the patterm InOut");
                    }
                } else if (test.getMessages().getFault() != null) {
                    if (test.getMessages().getFaultack() != null) {
                        checkConsumerFault(exchange, test.getMessages().getFault(), test
                                .getMessages().getFaultack(), test.getDescription());
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain an OutAck element as it contain a Fault element  for the patterm InOut");
                    }
                } else {
                    throw new IntegrationException("Test '" + test.getDescription()
                            + "' must contain an Out or a Fault element for the patterm InOut");
                }
            } else {
                if (test.getMessages().getInack() != null) {
                    checkAcknowledgment(exchange, test.getMessages().getInack(),
                            test.getDescription());
                } else if (test.getMessages().getOut() != null) {
                    if (test.getMessages().getOutack() != null) {
                        checkConsumerOutMessage(exchange, test.getMessages().getOut(), test
                                .getMessages().getOutack(), test.getDescription());
                    } else if (test.getMessages().getFault() != null) {
                        if (test.getMessages().getFaultack() != null) {
                            checkConsumerOutMessageWithFault(exchange, test.getMessages().getOut(),
                                    test.getMessages().getFault(), test.getDescription());
                            exchange = componentContext.getDeliveryChannel().accept(
                                    defautAcceptTimeout);
                            checkAcknowledgment(exchange, test.getMessages().getFaultack(),
                                    test.getDescription());
                        } else {
                            throw new IntegrationException(
                                    "Test '"
                                            + test.getDescription()
                                            + "' must contain an FaulAck element as it contain Out and Fault elements for the patterm InOptionalOut");
                        }
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain an OutAck element as it contain Out element for the patterm InOptionalOut");
                    }
                } else if (test.getMessages().getFault() != null) {
                    if (test.getMessages().getFaultack() != null) {
                        checkConsumerFault(exchange, test.getMessages().getFault(), test
                                .getMessages().getFaultack(), test.getDescription());
                    } else {
                        throw new IntegrationException(
                                "Test '"
                                        + test.getDescription()
                                        + "' must contain an OutAck element as it contain a Fault element  for the patterm InOptionalOut");
                    }
                } else {
                    throw new IntegrationException(
                            "Test '"
                                    + test.getDescription()
                                    + "' must contain an Inack, Out or a Fault element for the patterm InOptionalOut");
                }
            }
        }

    }

    private static void checkAcknowledgment(MessageExchange exchange, Acknowledgment expectedAck,
            String testDescription) throws IntegrationException {
        if (exchange.getStatus().toString().equals(expectedAck.getAck())) {
            if (expectedAck.getError() != null) {
                if (expectedAck.getError().isRegularExpression() != null
                        && expectedAck.getError().isRegularExpression()) {
                    Pattern pattern = Pattern.compile(expectedAck.getError().getValue(),
                            Pattern.DOTALL);
                    if (!pattern.matcher(exchange.getError().getMessage()).matches()) {
                        throw new IntegrationException("Test '" + testDescription
                                + "' failed. Expected exception pattern '"
                                + expectedAck.getError().getValue() + "' and get exception '"
                                + exchange.getError().getMessage() + "'");
                    }
                } else if (!exchange.getError().getMessage()
                        .equals(expectedAck.getError().getValue())) {
                    throw new IntegrationException("Test '" + testDescription
                            + "' failed. Expected exception '" + expectedAck.getError().getValue()
                            + "' and get exception '" + exchange.getError().getMessage() + "'");
                }

            }
        } else {
            String errorMessage = "";
            if (exchange.getError() != null) {
                errorMessage = " with exception '" + exchange.getError().getMessage();
            }
            throw new IntegrationException("Test '" + testDescription
                    + "' failed. Expected Acknowledgment '" + expectedAck.getAck()
                    + "' and get Acknowledgment '" + exchange.getStatus().toString() + "'"
                    + errorMessage);
        }
    }

    private static void checkConsumerFault(MessageExchange exchange,
            NormalizedMessageExchange expectedFault, Acknowledgment faultAck, String testDescription)
            throws IntegrationException, MessagingException, RemoteException,
            TransformerConfigurationException, TransformerException {

        for (Property property : expectedFault.getProperty()) {
            if (exchange.getFault().getProperty(property.getName()) == null
                    || !exchange.getFault().getProperty(property.getName())
                            .equals(property.getValue())) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected Fault property named '" + property.getName()
                        + "' with value '" + property.getValue() + "' and get value '"
                        + exchange.getFault().getProperty(property.getName()) + "'");
            }
        }

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        Result result = new StreamResult(buffer);
        getTransformer().transform(exchange.getFault().getContent(), result);
        if (expectedFault.getContent().isRegularExpression() != null
                && expectedFault.getContent().isRegularExpression()) {
            Pattern pattern = Pattern
                    .compile(expectedFault.getContent().getValue(), Pattern.DOTALL);
            if (!pattern.matcher(buffer.toString()).matches()) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected Fault content pattern '"
                        + expectedFault.getContent().getValue() + "' and get '" + buffer.toString()
                        + "'");
            }
        } else if (!XMLComparator.isEquivalent(expectedFault.getContent().getValue(),
                buffer.toString())) {
            throw new IntegrationException("Test '" + testDescription
                    + "' failed. Expected Fault content '" + expectedFault.getContent().getValue()
                    + "' and get '" + buffer.toString() + "'");
        }
        checkAttachment(exchange.getFault(), expectedFault, testDescription);
        exchange.setStatus(ExchangeStatus.valueOf(faultAck.getAck()));
        if (faultAck.getError() != null) {
            exchange.setError(new Exception(faultAck.getError().getValue()));
        }
        componentContext.getDeliveryChannel().send(exchange);
    }

    private static void checkConsumerOutMessage(MessageExchange exchange,
            NormalizedMessageExchange expectedOut, Acknowledgment outAck, String testDescription)
            throws IntegrationException, MessagingException, RemoteException,
            TransformerConfigurationException, TransformerException {

        for (Property property : expectedOut.getProperty()) {
            if (exchange.getMessage("out").getProperty(property.getName()) == null
                    || !exchange.getMessage("out").getProperty(property.getName())
                            .equals(property.getValue())) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected Out property named '" + property.getName()
                        + "' with value '" + property.getValue() + "' and get value '"
                        + exchange.getMessage("out").getProperty(property.getName()) + "'");
            }
        }

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        Result result = new StreamResult(buffer);
        getTransformer().transform(exchange.getMessage("out").getContent(), result);
        if (expectedOut.getContent().isRegularExpression() != null
                && expectedOut.getContent().isRegularExpression()) {

            // -----------------------------------------------------------------
            // ----- Added by tdejean (store returned uuids values using regex)
            // : ----
            String messageContentValue = expectedOut.getContent().getValue();
            IntegrationRegexUtils.storeReturnedValue(messageContentValue, "ebm:Uuid",
                    IntegrationRegexUtils.UUID_REGEX, returnedUuids);
            Pattern pattern = Pattern.compile(messageContentValue, Pattern.DOTALL);
            // -----------------------------------------------------------------
            // Pattern pattern =
            // Pattern.compile(expectedOut.getContent().getValue(),
            // Pattern.DOTALL);

            if (!pattern.matcher(buffer.toString()).matches()) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected Out content pattern '"
                        + expectedOut.getContent().getValue() + "' and get '" + buffer.toString()
                        + "'");
            }
        } else if (!XMLComparator.isEquivalent(expectedOut.getContent().getValue(),
                buffer.toString())) {
            throw new IntegrationException("Test '" + testDescription
                    + "' failed. Expected Out content '" + expectedOut.getContent().getValue()
                    + "' and get '" + buffer.toString() + "'");
        }

        checkAttachment(exchange.getMessage("out"), expectedOut, testDescription);

        exchange.setStatus(ExchangeStatus.valueOf(outAck.getAck()));
        if (outAck.getError() != null) {
            exchange.setError(new Exception(outAck.getError().getValue()));
        }
        componentContext.getDeliveryChannel().send(exchange);
    }

    private static void checkAttachment(NormalizedMessage currentNM,
            NormalizedMessageExchange expectedNM, String testDescription)
            throws IntegrationException {
        for (Attachment attachment : expectedNM.getAttachment()) {
            if (currentNM.getAttachment(attachment.getName()) != null) {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                try {
                    currentNM.getAttachment(attachment.getName()).writeTo(byteArrayOutputStream);
                } catch (IOException e) {
                    throw new IntegrationException("Test '" + testDescription
                            + "' failed. Expected attachment named '" + attachment.getName()
                            + "' with value '" + attachment.getValue() + "' and get IOException : "
                            + e.getMessage());
                }
                String contentType = attachment.getContentType();
                if (contentType != null
                        && (contentType.equalsIgnoreCase("text/xml") || contentType
                                .equalsIgnoreCase("application/xml"))) {
                    if (!XMLComparator.isEquivalent(new ByteArrayInputStream(attachment.getValue()
                            .getBytes()),
                            new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))) {
                        throw new IntegrationException("Test '" + testDescription
                                + "' failed. Expected attachment named '" + attachment.getName()
                                + "' with value '" + attachment.getValue() + "' and get value '"
                                + byteArrayOutputStream.toString() + "'");
                    }
                } else {
                    if (!byteArrayOutputStream.toString().equals(attachment.getValue())) {
                        throw new IntegrationException("Test '" + testDescription
                                + "' failed. Expected attachment named '" + attachment.getName()
                                + "' with value '" + attachment.getValue() + "' and get value '"
                                + byteArrayOutputStream.toString() + "'");
                    }
                }
            } else {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected attachment named '" + attachment.getName()
                        + "' and get no attachment");
            }
        }
    }

    private static void checkConsumerOutMessageWithFault(MessageExchange exchange,
            NormalizedMessageExchange expectedOut, NormalizedMessageExchange outFault,
            String testDescription) throws IntegrationException, MessagingException,
            RemoteException, TransformerConfigurationException, TransformerException {

        for (Property property : expectedOut.getProperty()) {
            if (exchange.getMessage("out").getProperty(property.getName()) == null
                    || !exchange.getMessage("out").getProperty(property.getName())
                            .equals(property.getValue())) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected Out property named '" + property.getName()
                        + "' with value '" + property.getValue() + "' and get value '"
                        + exchange.getMessage("out").getProperty(property.getName()) + "'");
            }
        }

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        Result result = new StreamResult(buffer);
        getTransformer().transform(exchange.getMessage("out").getContent(), result);
        if (expectedOut.getContent().isRegularExpression() != null
                && expectedOut.getContent().isRegularExpression()) {
            Pattern pattern = Pattern.compile(expectedOut.getContent().getValue(), Pattern.DOTALL);
            if (!pattern.matcher(buffer.toString()).matches()) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected Out content pattern '"
                        + expectedOut.getContent().getValue() + "' and get '" + buffer.toString()
                        + "'");
            }
        } else if (!XMLComparator.isEquivalent(expectedOut.getContent().getValue(),
                buffer.toString())) {
            throw new IntegrationException("Test '" + testDescription
                    + "' failed. Expected Out content '" + expectedOut.getContent().getValue()
                    + "' and get '" + buffer.toString() + "'");
        }

        checkAttachment(exchange.getMessage("out"), expectedOut, testDescription);

        Fault fault = new Fault();

        for (Property property : outFault.getProperty()) {
            fault.setProperty(property.getName(), property.getValue());
        }

        fault.setContent(new StreamSource(new ByteArrayInputStream(outFault.getContent().getValue()
                .getBytes())));

        // TODO handle attachments

        exchange.setFault(fault);
        componentContext.getDeliveryChannel().send(exchange);
    }

    private static void checkProviderInMessage(MessageExchange exchange,
            NormalizedMessageExchange expectedIn, String testDescription, QName operation, Mep mep)
            throws IntegrationException, MessagingException, RemoteException,
            TransformerConfigurationException, TransformerException {

        if (operation != null) {
            if (exchange.getOperation() == null || !exchange.getOperation().equals(operation)) {

                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected operation named '" + operation + "' and get '"
                        + exchange.getOperation() + "'");
            }
        }

        if ((exchange.getPattern().equals(
                org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_ONLY_PATTERN) && Mep.IN_ONLY != mep)
                || (exchange
                        .getPattern()
                        .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.ROBUST_IN_ONLY_PATTERN) && Mep.ROBUST_IN_ONLY != mep)
                || (exchange
                        .getPattern()
                        .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_OUT_PATTERN) && Mep.IN_OUT != mep)
                || (exchange
                        .getPattern()
                        .equals(org.objectweb.petals.tools.rmi.common.serializable.MessageExchange.IN_OPTIONAL_OUT_PATTERN) && Mep.IN_OPTIONAL_OUT != mep)) {
            throw new IntegrationException("Test '" + testDescription + "' failed. Expected MEP '"
                    + mep + "' and get pattern '" + exchange.getPattern() + "'");
        }

        for (Property property : expectedIn.getProperty()) {
            if (exchange.getMessage("in").getProperty(property.getName()) == null) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected In property named '" + property.getName()
                        + "' and get no property of this name");
            } else if (!exchange.getMessage("in").getProperty(property.getName())
                    .equals(property.getValue())) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected In property named '" + property.getName()
                        + "' with value '" + property.getValue() + "' and get value '"
                        + exchange.getMessage("in").getProperty(property.getName()) + "'");
            }
        }

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        Result result = new StreamResult(buffer);
        getTransformer().transform(exchange.getMessage("in").getContent(), result);
        if (expectedIn.getContent().isRegularExpression() != null
                && expectedIn.getContent().isRegularExpression()) {
            Pattern pattern = Pattern.compile(expectedIn.getContent().getValue(), Pattern.DOTALL);
            if (!pattern.matcher(buffer.toString()).matches()) {
                throw new IntegrationException("Test '" + testDescription
                        + "' failed. Expected In content '" + expectedIn.getContent().getValue()
                        + "' and get '" + buffer.toString() + "'");
            }
        } else if (!XMLComparator.isEquivalent(expectedIn.getContent().getValue(),
                buffer.toString())) {
            throw new IntegrationException("Test '" + testDescription
                    + "' failed. Expected In content '" + expectedIn.getContent().getValue()
                    + "' and get '" + buffer.toString() + "'");
        }

        checkAttachment(exchange.getMessage("in"), expectedIn, testDescription);
    }

    private static void handleProviderAcknowledgment(MessageExchange exchange, Acknowledgment ack)
            throws IntegrationException, MessagingException, RemoteException {
        exchange.setStatus(ExchangeStatus.valueOf(ack.getAck()));
        if (ack.getError() != null) {
            exchange.setError(new Exception(ack.getError().getValue()));
        }
        componentContext.getDeliveryChannel().send(exchange);
    }

    private static void handleProviderFault(MessageExchange exchange,
            NormalizedMessageExchange expectedFault, Acknowledgment faultAck, String testDescription)
            throws IntegrationException, MessagingException, IOException {

        Fault fault = new Fault();

        for (Property property : expectedFault.getProperty()) {
            fault.setProperty(property.getName(), property.getValue());
        }

        fault.setContent(new StreamSource(new ByteArrayInputStream(expectedFault.getContent()
                .getValue().getBytes())));

        for (Attachment attachment : expectedFault.getAttachment()) {
            DataSource dataSource = new ByteArrayDataSource(new ByteArrayInputStream(attachment
                    .getValue().getBytes()), attachment.getContentType());
            fault.addAttachment(attachment.getName(), new DataHandler(dataSource));
        }

        exchange.setFault(fault);

        exchange = componentContext.getDeliveryChannel().sendSync(exchange);
        checkAcknowledgment(exchange, faultAck, testDescription);
    }

    private static void handleProviderOutMessage(MessageExchange exchange,
            NormalizedMessageExchange expectedOutMessage, Acknowledgment outAck,
            String testDescription) throws IntegrationException, MessagingException, IOException {

        NormalizedMessage outMessage = new org.objectweb.petals.tools.rmi.common.serializable.NormalizedMessage();

        for (Property property : expectedOutMessage.getProperty()) {
            outMessage.setProperty(property.getName(), property.getValue());
        }

        outMessage.setContent(new StreamSource(new ByteArrayInputStream(expectedOutMessage
                .getContent().getValue().getBytes())));

        for (Attachment attachment : expectedOutMessage.getAttachment()) {
            DataSource dataSource = new ByteArrayDataSource(new ByteArrayInputStream(attachment
                    .getValue().getBytes()), attachment.getContentType());
            outMessage.addAttachment(attachment.getName(), new DataHandler(dataSource));
        }

        exchange.setMessage(outMessage, Convert.OUT_MSG);

        exchange = componentContext.getDeliveryChannel().sendSync(exchange);
        checkAcknowledgment(exchange, outAck, testDescription);
    }

    private static MessageExchange createMessageExchange(Test test, QName interfaz, QName service,
            String endpoint, RemoteComponentContext componentContext) throws MessagingException,
            IOException {
        MessageExchange msg = null;
        NormalizedMessage nm = null;
        String inContent = null;

        if (Mep.IN_ONLY.equals(test.getMep())) {
            msg = componentContext.getDeliveryChannel().createExchangeFactory()
                    .createInOnlyExchange();
            nm = msg.createMessage();
            ((InOnly) msg).setInMessage(nm);
        } else if (Mep.IN_OUT.equals(test.getMep())) {

            msg = componentContext.getDeliveryChannel().createExchangeFactory()
                    .createInOutExchange();

            nm = msg.createMessage();

            ((InOut) msg).setInMessage(nm);
        } else if (Mep.IN_OPTIONAL_OUT.equals(test.getMep())) {
            msg = componentContext.getDeliveryChannel().createExchangeFactory()
                    .createInOptionalOutExchange();
            nm = msg.createMessage();
            ((InOptionalOut) msg).setInMessage(nm);
        } else if (Mep.ROBUST_IN_ONLY.equals(test.getMep())) {
            msg = componentContext.getDeliveryChannel().createExchangeFactory()
                    .createRobustInOnlyExchange();
            nm = msg.createMessage();
            ((RobustInOnly) msg).setInMessage(nm);
        }

        if (endpoint != null) {
            if (componentContext.getEndpoint(service, endpoint) == null) {
                throw new MessagingException("Unable to find the endpoint '" + endpoint
                        + "' with its associated service " + service + "'");
            }
            msg.setEndpoint(componentContext.getEndpoint(service, endpoint));
        }

        msg.setService(service);
        msg.setInterfaceName(interfaz);

        if (test.getOperation() != null) {
            msg.setOperation(test.getOperation());
        }

        // setting global properties
        for (Property property : test.getMessages().getProperty()) {
            msg.setProperty(property.getName(), property.getValue());
        }

        // setting IN properties
        for (Property property : test.getMessages().getIn().getProperty()) {
            nm.setProperty(property.getName(), property.getValue());
        }

        // setting content
        try {
            inContent = test.getMessages().getIn().getContent().getValue();
            if (!returnedUuids.isEmpty()) {
                inContent = IntegrationRegexUtils.substituteKey(inContent, returnedUuids);
            }
            nm.setContent(new StreamSource(new ByteArrayInputStream(inContent.getBytes(UTF8))));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        // setting attachments
        for (Attachment attachment : test.getMessages().getIn().getAttachment()) {
            String contentType = "application/octet-stream";
            if (attachment.getContentType() != null) {
                contentType = attachment.getContentType();
            }

            DataSource dataSource;
            if (attachment.getFile() == null) {
                dataSource = new ByteArrayDataSource(attachment.getValue().getBytes(), contentType);
            } else {
                File attachmentFile = new File(attachment.getFile());
                if (!attachmentFile.exists()) {
                    throw new IOException("File '" + attachmentFile.getCanonicalPath()
                            + "' do not exist");
                }
                dataSource = new FileDataSource(attachmentFile);
            }
            nm.addAttachment(attachment.getName(), new DataHandler(dataSource));
        }

        return msg;
    }

    /**
     * Get the transformer instance
     * 
     * @return the transformer instance
     * @throws TransformerConfigurationException
     */
    private synchronized static Transformer getTransformer()
            throws TransformerConfigurationException {
        if (transformer == null) {
            transformer = TransformerFactory.newInstance().newTransformer();
        }
        return transformer;
    }

    private static void usage() {
        System.out
                .println("PEtALS integration RMI client usage: -integration integrationFilePath [-properties rmiPropertiesFilePath] ");
        System.out.println("-integration : the absolute location of the integration file");
        System.out.println("-properties : the absolute location of the RMI properties file");
        System.exit(-1);
    }

}
