/**
 * Copyright (c) 2008-2012 EBMWebsourcing, 2012-2025 Linagora
 * 
 * This program/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 program/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 program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package com.ebmwebsoucing.integration.client.rmi;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.Collections;
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.Fault;
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.jbi.servicedesc.ServiceEndpoint;
import javax.mail.util.ByteArrayDataSource;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.objectweb.petals.tools.rmi.client.ComponentContexFactoryLocator;
import org.objectweb.petals.tools.rmi.remote.RemoteComponentContextClient;
import org.ow2.easywsdl.wsdl.api.abstractItf.AbsItfOperation.MEPPatternConstants;
import org.ow2.petals.commons.log.FlowAttributes;
import org.ow2.petals.commons.log.FlowAttributesExchangeHelper;
import org.ow2.petals.commons.log.PetalsExecutionContext;
import org.ow2.petals.jbi.messaging.exchange.PetalsMessageExchange;

import com.ebmwebsourcing.easycommons.lang.ExceptionHelper;
import com.ebmwebsourcing.easycommons.lang.StringHelper;
import com.ebmwebsourcing.easycommons.uuid.SimpleUUIDGenerator;
import com.ebmwebsourcing.easycommons.xml.SourceHelper;
import com.ebmwebsourcing.easycommons.xml.Transformers;
import com.ebmwebsourcing.easycommons.xml.XMLComparator;
import com.ebmwebsourcing.easycommons.xml.XMLPrettyPrinter;
import com.ebmwebsourcing.integration.descriptor.IntegrationBuilder;
import com.ebmwebsourcing.integration.descriptor.IntegrationException;
import com.ebmwebsourcing.integration.descriptor.IntegrationRegexUtils;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Acknowledgment;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Actor;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Attachment;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Integration;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Mep;
import com.ebmwebsourcing.petals.integration.descriptor.generated.NormalizedMessageExchange;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Property;
import com.ebmwebsourcing.petals.integration.descriptor.generated.Test;

/**
 * RMI client sample
 * 
 * @author rnaudin
 * 
 */
public class RMIClient {

    private static final String UTF8 = "UTF-8";

    private static Integration integration;

    private static int rmiPort = 1099;

    private static String rmiContext = "RMIComponentContext";

    private static String rmiIP = "localhost";

    private static long defautAcceptTimeout = 60000;

    private static Map<String, String> returnedUuids = null;

    /**
     * @param args
     * @throws MessagingException
     */
    public static void main(final String[] args) throws Exception {

        InputStream propertyStream = null;
        InputStream integrationStream = null;

        returnedUuids = new HashMap<String, String>();

        if (args.length == 0) {
            usage();
        }

        Integer argRmiPort = null;
        String argRmiIP = null;
        String argRmiContext = null;
        Long argAcceptTimeout = null;
        Long argIterations = Long.valueOf(1);

        for (int i = 0; i < args.length; i++) {
            if ("-integration".equals(args[i])) {
                if (integrationStream != null) {
                    System.err.println("Only one Integration URL location is allowed.");
                    usage();
                }
                try {
                    integrationStream = new FileInputStream(args[i + 1]);
                } catch (final FileNotFoundException e) {
                    e.printStackTrace();
                    System.err.println("Integration URL location is invalid : " + args[i + 1]);
                    System.exit(-1);
                }
            } else if ("-properties".equals(args[i])) {
                if (propertyStream != null) {
                    System.err.println("Only one Properties URL location is allowed.");
                    usage();
                }
                try {
                    propertyStream = new FileInputStream(args[i + 1]);
                } catch (final FileNotFoundException e) {
                    e.printStackTrace();
                    System.err.println("Properties URL location is invalid : " + args[i + 1]);
                    System.exit(-1);
                }
            } else if ("-rmiPort".equals(args[i])) {
                try {
                    argRmiPort = Integer.parseInt(args[i + 1]);
                } catch (final NumberFormatException e) {
                    e.printStackTrace();
                    System.err.println("RMI port is invalid : " + args[i + 1]);
                    System.exit(-1);
                }
            } else if ("-rmiIP".equals(args[i])) {
                argRmiIP = args[i + 1];
            } else if ("-rmiContext".equals(args[i])) {
                argRmiContext = args[i + 1];
            } else if ("-acceptTimeout".equals(args[i])) {
                try {
                    argAcceptTimeout = Long.parseLong(args[i + 1]);
                } catch (final NumberFormatException e) {
                    e.printStackTrace();
                    System.err.println("Accept timeout is invalid : " + args[i + 1]);
                    System.exit(-1);
                }
            } else if ("-iterations".equals(args[i])) {
                final String iterationValue = args[i + 1];
                if (iterationValue.equalsIgnoreCase("infinite")) {
                    // Infinite loop
                    argIterations = Long.MAX_VALUE;
                } else {
                    try {
                        argIterations = Long.parseLong(args[i + 1]);
                    } catch (final NumberFormatException e) {
                        e.printStackTrace();
                        System.err.println("Iteration number is invalid : " + args[i + 1]);
                        System.exit(-1);
                    }
                }
            } else if ("-pid-file".equals(args[i])) {
                // this only works on linux... TODO support windows!
                final String pid = new File("/proc/self").getCanonicalFile().getName();
                Files.write(Paths.get(args[i + 1]), Collections.singletonList(pid), Charset.forName("UTF-8"));
                }
            }

        if (integrationStream == null) {
            usage();
        }

        if (propertyStream != null) {
            Properties rmiProperties = new Properties();
            try {
                rmiProperties.load(propertyStream);
                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 (final IOException e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }

        // let's use the command line arguments to override the property file
        if (argRmiPort != null) {
            rmiPort = argRmiPort;
        }
        if (argAcceptTimeout != null) {
            defautAcceptTimeout = argAcceptTimeout;
        }
        if (argRmiContext != null) {
            rmiContext = argRmiContext;
        }
        if (argRmiIP != null) {
            rmiIP = argRmiIP;
        }

        try {
            integration = IntegrationBuilder.buildJavaIntegration(integrationStream);


            for (long i = 1; i <= argIterations.longValue(); i++) {
                for (Actor provider : integration.getProvider()) {
                    runProviderIntegration(provider, i);
                }

                for (Actor consumer : integration.getConsumer()) {
                    runConsumerIntegration(consumer, i);
                }
            }

            System.out.println("Run integration tests successfully");
            System.exit(0);

        } catch (final Exception e) {
            e.printStackTrace();
            System.exit(-1);
        } finally {
            try {
                if (integrationStream != null) {
                    integrationStream.close();
                }
            } catch (IOException e) {
                // do nothing
            }
            try {
                if (propertyStream != null) {
                    propertyStream.close();
                }
            } catch (IOException e) {
                // do nothing
            }
        }
    }

    /**
     * TODO: Manage interfaces of the exposed services
     * 
     * @param provider
     * @throws JBIException
     * @throws TransformerException
     * @throws IntegrationException
     * @throws TransformerConfigurationException
     * @throws IOException
     * @throws NotBoundException
     */
    private static void runProviderIntegration(Actor provider, final long iteration) throws JBIException,
            TransformerConfigurationException, IntegrationException, TransformerException, IOException,
            NotBoundException {

        String endpoint = provider.getEndpoint();
        if (StringHelper.isNullOrEmpty(endpoint)) {
            final String id = new SimpleUUIDGenerator().getNewID();
            endpoint = new StringBuilder("edpt-").append(id).toString();
        }

        final ComponentContexFactoryLocator ccl = new ComponentContexFactoryLocator(rmiIP, rmiPort, rmiContext);
        final RemoteComponentContextClient componentClientContext = ccl.getComponentClientContextFactory()
                .createRemoteComponentContextClient();
        System.out.println("Client: component Name = " + componentClientContext.getComponentName());

        componentClientContext.activateEndpoint(provider.getInterface(), provider.getService(), endpoint);
        System.out.println(rmiContext + ", iteration #" + iteration + ", [provider]: " + provider.getDescription());

        for (Test test : provider.getTest()) {
            System.out.println(rmiContext + ", iteration #" + iteration + ": Running test '" + test.getDescription()
                    + "' on operation '" + test.getOperation() + "' with mep '" + test.getMep()
                    + "'");

            final MessageExchange exchange = accept(componentClientContext);

            if (exchange == null) {
                throw new IntegrationException(String.format(
                        "Test '%s', iteration '%d', failed. Unexpected timeout reached while accepting message.",
                        test.getDescription(), iteration));
            }

            checkProviderInMessage(exchange, test.getMessages().getIn(), test.getDescription(),
                    test.getOperation(), test.getMep());

            if (MEPPatternConstants.IN_ONLY.equals(exchange.getPattern())) {
                if (test.getMessages().getInack() != null) {
                    handleProviderAcknowledgment(exchange, test.getMessages().getInack(), componentClientContext);
                } else {
                    throw new IntegrationException(String.format(
                            "Test '%s', iteration '%d', must contain an InAck element for the patterm InOnly",
                            test.getDescription(), iteration));
                }
            } else if (MEPPatternConstants.ROBUST_IN_ONLY.equals(exchange.getPattern())) {
                if (test.getMessages().getInack() != null) {
                    handleProviderAcknowledgment(exchange, test.getMessages().getInack(), componentClientContext);
                } else if (test.getMessages().getFault() != null
                        && test.getMessages().getFaultack() != null) {
                    handleProviderFault(exchange, test.getMessages().getFault(), test.getMessages().getFaultack(),
                            test.getDescription(), iteration, componentClientContext);
                } else {
                    throw new IntegrationException(
                            String.format(
                                    "Test '%s', iteration '%d', contain an InAck element or Fault and FaultAck elements for the pattern RobustInOnly",
                                    test.getDescription(), iteration));
                }
            } else if (MEPPatternConstants.IN_OUT.equals(exchange.getPattern())) {
                if (test.getMessages().getInack() != null
                        && test.getMessages().getInack().getAck().equals(ExchangeStatus.ERROR.toString())) {
                    handleProviderAcknowledgment(exchange, test.getMessages().getInack(), componentClientContext);
                } else if (test.getMessages().getFault() != null) {
                    if (test.getMessages().getFaultack() != null) {
                        handleProviderFault(exchange, test.getMessages().getFault(), test.getMessages().getFaultack(),
                                test.getDescription(), iteration, componentClientContext);
                    } else {
                        throw new IntegrationException(
                                String.format(
                                        "Test '%s', iteration '%d', must contain an FaultAck element as it contains a Fault element for the pattern InOut",
                                        test.getDescription(), iteration));
                    }
                } else if (test.getMessages().getOut() != null) {
                    if (test.getMessages().getOutack() != null) {
                        handleProviderOutMessage(exchange, test.getMessages().getOut(), test.getMessages().getOutack(),
                                test.getDescription(), iteration, componentClientContext);
                    } else {
                        throw new IntegrationException(
                                String.format(
                                        "Test '%s', iteration '%d', must contain an OutAck element as it contains an Out element for the pattern InOut",
                                        test.getDescription(), iteration));
                    }
                } else {
                    throw new IntegrationException(
                            String.format(
                                    "Test '%s', iteration '%d', must contain an Out, a Fault or an InAckError element for the pattern InOut",
                                    test.getDescription(), iteration));

                }
            }

            // TODO where is InOptionalOut?

            System.out.println(rmiContext + ",iteration #" + iteration + ": Running test '" + test.getDescription()
                    + "' on operation '" + test.getOperation() + "' with mep '" + test.getMep()
                    + "'.....OK");
        }
    }

    private static void runConsumerIntegration(Actor consumer, final long iteration) throws MessagingException,
            IntegrationException, TransformerConfigurationException, TransformerException,
            InterruptedException, IOException, Exception {

        final ComponentContexFactoryLocator ccl = new ComponentContexFactoryLocator(rmiIP, rmiPort, rmiContext);
        final RemoteComponentContextClient componentClientContext = ccl.getComponentClientContextFactory()
                .createRemoteComponentContextClient();
        System.out.println("Client: component Name = " + componentClientContext.getComponentName());

        System.out.println(rmiContext + ", iteration #" + iteration + ", [consumer]: " + consumer.getDescription());

        for (final Test test : consumer.getTest()) {
            System.out.println(rmiContext + ", iteration #" + iteration + ": Running test '" + test.getDescription()
                    + "' on operation '" + test.getOperation() + "' with mep '" + test.getMep()
                    + "'");

            String exchangeId = null;
            MessageExchange exchange = null;
            try {
                final FlowAttributes flowAttributes = PetalsExecutionContext.initFlowAttributes();

                exchange = createMessageExchange(test, consumer.getInterface(), consumer.getService(),
                        consumer.getEndpoint(), componentClientContext, flowAttributes);

                exchangeId = exchange.getExchangeId();
                final URI exchangePattern = exchange.getPattern();

                componentClientContext.logConsumesExtFlowStepBegin(
                        flowAttributes.getFlowInstanceId(),
                        flowAttributes.getFlowStepId(), StringHelper.nonNullValue(exchange.getInterfaceName()),
                        StringHelper.nonNullValue(exchange.getService()),
                        exchange.getEndpoint() == null ? ""
                                : StringHelper.nonNullValue(exchange.getEndpoint().getEndpointName()),
                        StringHelper.nonNullValue(exchange.getOperation()));

                try {
                    if (test.getSynchronousSend() != null) {

                        exchange = componentClientContext.getDeliveryChannel().sendSync(exchange,
                                test.getSynchronousSend());

                        if (test.isTimeoutReached() != null && test.isTimeoutReached() && exchange != null) {
                            throw new IntegrationException(String.format(
                                            "Test '%s', iteration '%d', failed. Expected timeout not reached. ExchangeId: '%s'.",
                                            test.getDescription(), iteration, exchange.getExchangeId()));
                        }
                    } else {
                        componentClientContext.getDeliveryChannel().send(exchange);
                        exchange = accept(componentClientContext);
                    }
                } catch (final MessagingException | RemoteException e) {
                    // but not integration exception!
                    componentClientContext.logConsumesExtFlowStepFailure(flowAttributes.getFlowInstanceId(),
                            flowAttributes.getFlowStepId(), e.getMessage());
                    throw e;
                }

                if (exchange == null) {
                    final String msg = String.format(
                            "Test '%s', iteration '%d', failed. Unexpected timeout reached. ExchangeId: '%s'.",
                            test.getDescription(), iteration, exchangeId);
                    componentClientContext.logConsumesExtFlowStepFailure(flowAttributes.getFlowInstanceId(),
                            flowAttributes.getFlowStepId(), msg);
                    throw new IntegrationException(msg);
                } else {
                    componentClientContext.logConsumesExtFlowStepEnd(flowAttributes.getFlowInstanceId(),
                            flowAttributes.getFlowStepId());
                }

                if (exchangePattern == null || exchange.getPattern() == null
                        || !exchangePattern.toString().equalsIgnoreCase(exchange.getPattern().toString())) {
                    throw new Exception(
                            String.format(
                                    "Not same exchange (not same pattern):%n\t-> Identifier of the sent exchange: '%s'%n\t-> Identifier of the received exchange: '%s'%n\t-> Pattern of the sent exchange: '%s'%n\t-> Pattern of the received exchange: '%s'",
                                    exchangeId, exchange.getExchangeId(), exchangePattern, exchange.getPattern()));
                }

                if (!exchangeId.equals(exchange.getExchangeId())) {
                    throw new Exception(
                            String.format(
                                    "Not same exchange (not same pattern):%n\t-> Identifier of the sent exchange: '%s'%n\t-> Identifier of the received exchange: '%s'",
                                    exchangeId, exchange.getExchangeId()));
                }

                if (MEPPatternConstants.IN_ONLY.equals(exchange.getPattern())) {
                    if (test.getMessages().getInack() != null) {
                        checkAcknowledgment(exchange, test.getMessages().getInack(), test.getDescription());
                    } else {
                        throw new IntegrationException(String.format(
                                        "Test '%s', iteration '%d', must contain an InAck element for the patterm InOnly. ExchangeId: '%s'.",
                                        test.getDescription(), iteration, exchange.getExchangeId()));
                    }
                } else if (MEPPatternConstants.ROBUST_IN_ONLY.equals(exchange.getPattern())) {
                    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(), componentClientContext);
                        } else {
                            throw new IntegrationException(
                                    String.format(
                                            "Test '%s', iteration '%d', must contain a FaultAck element as it contains a Fault element for the pattern RobustInOnly. ExchangeId: '%s'.",
                                            test.getDescription(), iteration, exchange.getExchangeId()));
                        }
                    } else {
                        throw new IntegrationException(
                                String.format(
                                        "Test '%s', iteration '%d', must contain an InAck or a Fault element for the patterm RobustInOnly. ExchangeId: '%s'.",
                                        test.getDescription(), iteration, exchange.getExchangeId()));
                    }
                } else if (MEPPatternConstants.IN_OUT.equals(exchange.getPattern())) {
                    if (test.getMessages().getInack() != null
                            && test.getMessages().getInack().getAck().equals(ExchangeStatus.ERROR.toString())) {
                        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(), componentClientContext);
                        } else {
                            throw new IntegrationException(
                                    String.format(
                                            "Test '%s', iteration '%d', must contain an OutAck element as it contain Out element for the patterm InOut. ExchangeId: '%s'.",
                                            test.getDescription(), iteration, exchange.getExchangeId()));
                        }
                    } else if (test.getMessages().getFault() != null) {
                        if (test.getMessages().getFaultack() != null) {
                            checkConsumerFault(exchange, test.getMessages().getFault(), test.getMessages()
                                    .getFaultack(), test.getDescription(), componentClientContext);
                        } else {
                            throw new IntegrationException(
                                    String.format(
                                            "Test '%s', iteration '%d', must contain an OutAck element as it contain a Fault element  for the patterm InOut. ExchangeId: '%s'.",
                                            test.getDescription(), iteration, exchange.getExchangeId()));
                        }
                    } else {
                        throw new IntegrationException(
                                String.format(
                                        "Test '%s', iteration '%d', must contain an Out, a Fault or an InAckError element for the patterm InOut. ExchangeId: '%s'.",
                                        test.getDescription(), iteration, exchange.getExchangeId()));
                    }
                } 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(), componentClientContext);
                        } else if (test.getMessages().getFault() != null) {
                            if (test.getMessages().getFaultack() != null) {
                                checkConsumerOutMessageWithFault(exchange, test.getMessages().getOut(), test
                                        .getMessages().getFault(), test.getDescription(), componentClientContext);
                                exchange = accept(componentClientContext);
                                if (exchange == null) {
                                    throw new IntegrationException(String.format(
                                            "Test '%s', iteration '%d', failed. Unexpected timeout reached while accepting message.",
                                            test.getDescription(), iteration));
                                }
                                checkAcknowledgment(exchange, test.getMessages().getFaultack(), test.getDescription());
                            } else {
                                throw new IntegrationException(
                                        String.format(
                                                "Test '%s', iteration '%d', must contain an FaulAck element as it contain Out and Fault elements for the patterm InOptionalOut. ExchangeId: '%s'.",
                                                test.getDescription(), iteration, exchange.getExchangeId()));
                            }
                        } else {
                            throw new IntegrationException(
                                    String.format(
                                            "Test '%s', iteration '%d', must contain an OutAck element as it contain Out element for the patterm InOptionalOut. ExchangeId: '%s'.",
                                            test.getDescription(), iteration, exchange.getExchangeId()));
                        }
                    } else if (test.getMessages().getFault() != null) {
                        if (test.getMessages().getFaultack() != null) {
                            checkConsumerFault(exchange, test.getMessages().getFault(), test.getMessages()
                                    .getFaultack(), test.getDescription(), componentClientContext);
                        } else {
                            throw new IntegrationException(
                                    String.format(
                                            "Test '%s', iteration '%d', must contain an OutAck element as it contain a Fault element  for the patterm InOptionalOut. ExchangeId: '%s'.",
                                            test.getDescription(), iteration, exchange.getExchangeId()));
                        }
                    } else {
                        throw new IntegrationException(
                                String.format(
                                        "Test '%s', iteration '%d', must contain an Inack, Out or a Fault element for the patterm InOptionalOut",
                                        test.getDescription(), iteration));
                    }
                }
            } catch (final EndpointNotFoundException e) {
                // thrown by createMessageExchange
                if (test.isEndpointNotFound() == null || !test.isEndpointNotFound().booleanValue()) {
                    throw new IntegrationException(String.format(
                            "Test '%s', iteration '%d', has not found the endoint '%s' while it should",
                            test.getDescription(), iteration,
                            e.getEndpoint()));
                }
            } catch (final IntegrationException e) {
                if (exchange != null) {
                    throw new IntegrationException("Failed exchange: " + dumpToString(exchange), e);
                }
                throw e;
            }

            // Wait before to launch next service consumer
            if (test.getWaitAtEnd() != null) {
                Thread.sleep(test.getWaitAtEnd().longValue());
            }
        }

    }

    private static MessageExchange accept(final RemoteComponentContextClient componentClientContext)
            throws MessagingException, RemoteException {
        if (defautAcceptTimeout < 0) {
            return componentClientContext.getDeliveryChannel().accept();
        } else {
            return componentClientContext.getDeliveryChannel().accept(defautAcceptTimeout);
        }
    }

    private static String dumpToString(MessageExchange exchange) throws MessagingException, IOException {
        final StringBuilder b = new StringBuilder("Exchange: " + exchange.getExchangeId());
        b.append("\nStatus: " + exchange.getStatus());
        b.append("\nPattern: " + exchange.getPattern());
        b.append("\nRole: " + exchange.getRole());
        b.append("\nIn: " + dumpToString(exchange.getMessage("in")));
        b.append("\nOut: " + dumpToString(exchange.getMessage("out")));
        b.append("\nFault: " + dumpToString(exchange.getFault()));
        final Exception error = exchange.getError();
        b.append("\nError: " + (error == null ? "null" : ExceptionHelper.getStackTrace(error)));
        return b.toString();
    }

    private static String dumpToString(NormalizedMessage message) throws MessagingException, IOException {
        if (message != null) {
            // let's fork it if needed because we may need to access it again
            final Source content = forkAndReplace(message);
            if (content != null) {
                try {
                    return XMLPrettyPrinter.prettyPrint(content);
                } catch (final TransformerException e) {
                    return "transformer exception: " + ExceptionHelper.getStackTrace(e);
                }
            } else {
                return "null content";
            }
        } else {
            return "null message";
        }
    }

    private static void checkAcknowledgment(MessageExchange exchange, Acknowledgment expectedAck,
            String testDescription) throws IntegrationException {
        if (exchange.getStatus().toString().equals(expectedAck.getAck())) {
            if (expectedAck.getError() != null) {
                final String errorToCheck;
                if (expectedAck.getError().isStacktrace() != null && expectedAck.getError().isStacktrace()) {
                    errorToCheck = ExceptionHelper.getStackTrace(exchange.getError());
                } else {
                    errorToCheck = exchange.getError().getMessage();
                }
                if (errorToCheck == null) {
                    throw new IntegrationException("Test '" + testDescription
                            + "' failed. Error to check is NULL'. ExchangeId: " + exchange.getExchangeId());
                } else if (expectedAck.getError().isRegularExpression() != null
                        && expectedAck.getError().isRegularExpression()) {
                    Pattern pattern = Pattern.compile(expectedAck.getError().getValue(),
                            Pattern.DOTALL);
                    if (!pattern.matcher(errorToCheck).matches()) {
                        throw new IntegrationException("Test '" + testDescription
                                + "' failed. Expected exception pattern '"
                                + expectedAck.getError().getValue() + "' and got exception '"
                                + errorToCheck + "'. ExchangeId: " + exchange.getExchangeId());
                    }
                } else if (!errorToCheck.equals(expectedAck.getError().getValue())) {
                    throw new IntegrationException("Test '" + testDescription
                            + "' failed. Expected exception '" + expectedAck.getError().getValue()
                            + "' and get exception '" + errorToCheck + "'. ExchangeId: " + exchange.getExchangeId());
                }

            }
        } else {
            String errorMessage = "";
            if (exchange.getError() != null) {
                errorMessage = " with exception '" + exchange.getError().getMessage();
            }
            throw new IntegrationException("Test '" + testDescription
                    + "' failed. Expected Acknowledgment '" + expectedAck.getAck()
                    + "' and got Acknowledgment '" + exchange.getStatus().toString() + "'"
                    + errorMessage + ". ExchangeId: "+exchange.getExchangeId());
        }
    }

    private static void checkConsumerFault(MessageExchange exchange, NormalizedMessageExchange expectedFault,
            Acknowledgment faultAck, String testDescription, RemoteComponentContextClient componentClientContext)
            throws IntegrationException, MessagingException,
            TransformerConfigurationException, TransformerException, IOException {

        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()) + "'");
            }
        }

        final javax.jbi.messaging.Fault fault = exchange.getFault();
        if (fault == null)
            throw new IntegrationException("The exchange's fault is null.");

        // let's fork it if needed because we may need to access it again for prettyprinting
        final Source faultContent = forkAndReplace(fault);

        if (faultContent == null)
            throw new IntegrationException("The exchange's fault has no (XML) body.");
        
        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        final Transformer transformer = Transformers.takeTransformer();
        try {
            transformer.transform(faultContent, new StreamResult(buffer));
        } finally {
            Transformers.releaseTransformer(transformer);
        }
        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(fault, expectedFault, testDescription);
        exchange.setStatus(ExchangeStatus.valueOf(faultAck.getAck()));
        if (faultAck.getError() != null) {
            exchange.setError(new Exception(faultAck.getError().getValue()));
        }
        componentClientContext.getDeliveryChannel().send(exchange);
    }

    private static void checkConsumerOutMessage(MessageExchange exchange, NormalizedMessageExchange expectedOut,
            Acknowledgment outAck, String testDescription, RemoteComponentContextClient componentClientContext)
            throws IntegrationException, MessagingException,
            TransformerConfigurationException, TransformerException, IOException {

        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()) + "'");
            }
        }

        final NormalizedMessage outMessage = exchange.getMessage("out");
        if (outMessage == null)
            throw new IntegrationException("The exchange's OUT is null.");

        // let's fork it if needed because we may need to access it again for prettyprinting
        final Source outMessageContent = forkAndReplace(outMessage);
        if (outMessageContent == null)
            throw new IntegrationException("The exchange's OUT has no (XML) body.");

        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        final Transformer transformer = Transformers.takeTransformer();
        try {
            transformer.transform(outMessageContent, new StreamResult(buffer));
        } finally {
            Transformers.releaseTransformer(transformer);
        }
        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(outMessage, expectedOut, testDescription);

        exchange.setStatus(ExchangeStatus.valueOf(outAck.getAck()));
        if (outAck.getError() != null) {
            exchange.setError(new Exception(outAck.getError().getValue()));
        }
        componentClientContext.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,
            RemoteComponentContextClient componentClientContext)
            throws IntegrationException, MessagingException, TransformerConfigurationException, TransformerException,
            IOException {

        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()) + "'");
            }
        }

        final NormalizedMessage outMessage = exchange.getMessage("out");
        if (outMessage == null)
            throw new IntegrationException("The exchange's OUT is null.");

        // let's fork it if needed because we may need to access it again for prettyprinting
        final Source outMessageContent = forkAndReplace(outMessage);
        if (outMessageContent == null)
            throw new IntegrationException("The exchange's OUT has no (XML) body.");

        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        final Transformer transformer = Transformers.takeTransformer();
        try {
            transformer.transform(outMessageContent, new StreamResult(buffer));
        } finally {
            Transformers.releaseTransformer(transformer);
        }
        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(outMessage, expectedOut, testDescription);

        Fault fault = exchange.createFault();

        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);
        componentClientContext.getDeliveryChannel().send(exchange);
    }

    private static void checkProviderInMessage(MessageExchange exchange, NormalizedMessageExchange expectedIn,
            String testDescription, QName operation, Mep mep) throws IntegrationException, MessagingException,
            TransformerConfigurationException, TransformerException, IOException {

        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 ((MEPPatternConstants.IN_ONLY.equals(exchange.getPattern()) && Mep.IN_ONLY != mep)
                    || (MEPPatternConstants.ROBUST_IN_ONLY.equals(exchange.getPattern()) && Mep.ROBUST_IN_ONLY != mep)
                    || (MEPPatternConstants.IN_OUT.equals(exchange.getPattern()) && Mep.IN_OUT != mep)
                    || (MEPPatternConstants.IN_OPTIONAL_OUT.equals(exchange.getPattern()) && 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()) + "'");
                }
            }

        final NormalizedMessage inMessage = exchange.getMessage("in");
        if (inMessage == null)
            throw new MessagingException("The exchange's IN is null.");

        // let's fork it if needed because we may need to access it again for prettyprinting
        final Source inMessageContent = forkAndReplace(inMessage);
        if (inMessageContent == null)
            throw new MessagingException("The exchange's IN has no (XML) body.");

        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        final Transformer transformer = Transformers.takeTransformer();
        try {
            transformer.transform(inMessageContent, new StreamResult(buffer));
        } finally {
            Transformers.releaseTransformer(transformer);
        }
        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(inMessage, expectedIn, testDescription);
    }

    private static void handleProviderAcknowledgment(MessageExchange exchange, Acknowledgment ack,
            RemoteComponentContextClient componentClientContext)
            throws IntegrationException, MessagingException, RemoteException {
        exchange.setStatus(ExchangeStatus.valueOf(ack.getAck()));
        if (ack.getError() != null) {
            exchange.setError(new Exception(ack.getError().getValue()));
        }
        componentClientContext.getDeliveryChannel().send(exchange);
    }

    private static void handleProviderFault(MessageExchange exchange, NormalizedMessageExchange expectedFault,
            Acknowledgment faultAck, String testDescription, long iteration,
            RemoteComponentContextClient componentClientContext)
            throws IntegrationException, MessagingException, IOException {

        Fault fault = exchange.createFault();

        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);

        final MessageExchange answer;
        if (defautAcceptTimeout < 0) {
            answer = componentClientContext.getDeliveryChannel().sendSync(exchange);
        } else {
            answer = componentClientContext.getDeliveryChannel().sendSync(exchange, defautAcceptTimeout);
        }
        if (answer == null) {
            throw new IntegrationException(String.format(
                    "Test '%s', iteration '%d', failed. Unexpected timeout reached while sending back fault message. ExchangeId: '%s'.",
                    testDescription, iteration, exchange.getExchangeId()));
        }
        checkAcknowledgment(answer, faultAck, testDescription);
    }

    private static void handleProviderOutMessage(MessageExchange exchange, NormalizedMessageExchange expectedOutMessage,
 Acknowledgment outAck, String testDescription,
            long iteration, RemoteComponentContextClient componentClientContext)
                    throws IntegrationException, MessagingException, IOException {

        NormalizedMessage outMessage = exchange.createMessage();

        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, PetalsMessageExchange.OUT_MSG);

        final MessageExchange answer;
        if (defautAcceptTimeout < 0) {
            answer = componentClientContext.getDeliveryChannel().sendSync(exchange);
        } else {
            answer = componentClientContext.getDeliveryChannel().sendSync(exchange, defautAcceptTimeout);
        }
        if (answer == null) {
            throw new IntegrationException(String.format(
                    "Test '%s', iteration '%d', failed. Unexpected timeout reached while sending back out message. ExchangeId: '%s'.",
                    testDescription, iteration, exchange.getExchangeId()));
        }
        checkAcknowledgment(answer, outAck, testDescription);
    }

    private static MessageExchange createMessageExchange(final Test test, final QName interfaz, final QName service,
            final String endpoint, final RemoteComponentContextClient componentClientContext,
            final FlowAttributes flowAttributes)
            throws MessagingException, IOException, IntegrationException {

        final MessageExchange msg;
        final NormalizedMessage nm;
        switch (test.getMep()) {
        	case IN_ONLY: {
                msg = componentClientContext.getDeliveryChannel().createExchangeFactory()
                        .createInOnlyExchange();
                nm = msg.createMessage();
                ((InOnly) msg).setInMessage(nm);
                break;
        	}
        	case IN_OUT: {
                msg = componentClientContext.getDeliveryChannel().createExchangeFactory()
                        .createInOutExchange();
                nm = msg.createMessage();
                ((InOut) msg).setInMessage(nm);
                break;
        	}
        	case IN_OPTIONAL_OUT: {
                msg = componentClientContext.getDeliveryChannel().createExchangeFactory()
                        .createInOptionalOutExchange();
                nm = msg.createMessage();
                ((InOptionalOut) msg).setInMessage(nm);
                break;
        	}
        	case ROBUST_IN_ONLY: {
                msg = componentClientContext.getDeliveryChannel().createExchangeFactory()
                        .createRobustInOnlyExchange();
                nm = msg.createMessage();
                ((RobustInOnly) msg).setInMessage(nm);
                break;
        	}
            default: {
                throw new RuntimeException("Impossible case");
            }
        }

        if (endpoint != null) {
            final ServiceEndpoint sEndpoint = componentClientContext.getEndpoint(service, endpoint);
            if (sEndpoint == null) {
                throw new EndpointNotFoundException(service, endpoint);
            } else {
                if (test.isEndpointNotFound() != null && test.isEndpointNotFound().booleanValue()) {
                    // An endpoint has been found. It's an error
                    throw new IntegrationException(
                            String.format("Test '%s' has found the endoint '%s' while it should not",
                                    test.getDescription(), endpoint));
                } else {
                    msg.setEndpoint(sEndpoint);
                }
            }
        }

        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());
        }

        // Set flow attributes
        FlowAttributesExchangeHelper.setFlowAttributes(msg, flowAttributes);

        // setting IN properties
        for (Property property : test.getMessages().getIn().getProperty()) {
            nm.setProperty(property.getName(), property.getValue());
        }

        // setting content
        try {
            String 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;
    }

    private static Source forkAndReplace(final NormalizedMessage message) throws MessagingException, IOException {
        return (message == null || message.getContent() == null) ? null : SourceHelper.fork(message.getContent());
    }

    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.out.println("-rmiContext : the RMI context to use (overrides properties file)");
        System.out.println("-rmiPort : the RMI port to use (overrides properties file)");
        System.out.println("-rmiIP : the RMI ip to use (overrides properties file)");
        System.out.println("-acceptTimeout : the accept timeout to use (overrides properties file)");
        System.exit(-1);
    }

}
