// Path: rsimulator-core/src/main/java/com/github/bjuvensjo/rsimulator/core/Simulator.java
// @ImplementedBy(SimulatorImpl.class)
// public interface Simulator {
// 
//     /**
//      * Returns a SimulatorResponse that matches the specified request.
//      *
//      * @param rootPath         the root path on which to search recursively for matches to the specified request
//      * @param rootRelativePath the path on which to search recursively for matches to the specified request
//      * @param request          the request
//      * @param contentType      the content type of the request, e.g. txt or xml
//      * @param vars             vars that can be used in for example scripts
//      * @return a SimulatorResponse that matches the specified request
//      */
//     Optional<SimulatorResponse> service(String rootPath, String rootRelativePath, String request, String contentType, Map<String, Object>... vars);
// }
// 
// Path: rsimulator-core/src/main/java/com/github/bjuvensjo/rsimulator/core/SimulatorResponse.java
// public interface SimulatorResponse {
// 
//     /**
//      * Returns the properties of a specific test data request and response pair. If the name of the test data request
//      * file for instance is Test1Request.xml, the properties file must be named Test1.properties. For supported
//      * properties, see *PropertiesInterceptor classes.
//      *
//      * @return the properties of a specific test data request and response pair
//      */
//     Optional<Properties> getProperties();
// 
//     /**
//      * Sets the specified properties.
//      *
//      * @param properties the properties to set
//      */
//     void setProperties(Optional<Properties> properties);
// 
//     /**
//      * Returns the test data response.
//      *
//      * @return the test data response
//      */
//     String getResponse();
// 
//     /**
//      * Sets the specified response.
//      *
//      * @param response the response to set
//      */
//     void setResponse(String response);
// 
//     /**
//      * Returns the matching request path.
//      *
//      * @return the matching request path
//      */
//     Path getMatchingRequest();
// 
//     /**
//      * Sets the specified path.
//      *
//      * @param path the path to set
//      */
//     void setMatchingRequest(Path path);
// }


import com.github.bjuvensjo.rsimulator.core.Simulator;
import com.github.bjuvensjo.rsimulator.core.SimulatorResponse;
import org.apache.cxf.Bus;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.transport.AbstractConduit;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.Properties;
import java.util.logging.Logger;
    RSimulatorConduit(EndpointInfo endpointInfo, Simulator simulator, Bus bus, String rootPath) {
        super(endpointInfo.getTarget());
        this.simulator = simulator;
        this.bus = bus;
        this.rootPath = rootPath;
    }

    @Override
    public void prepare(Message message) {
        message.setContent(OutputStream.class, new CachedOutputStream());
    }

    @Override
    public void close(Message message) throws IOException {
        super.close(message);
        if (!isInbound(message)) {
            // notify caller that a response is received
            this.getMessageObserver().onMessage(createResponseMessage(message));
        }
    }

    private boolean isInbound(Message message) {
        return Boolean.TRUE.equals(message.get("org.apache.cxf.message.inbound"));
    }

    private Message createResponseMessage(Message requestMessage) throws IOException {
        Message responseMessage = new MessageImpl();
        OutputStream os = requestMessage.getContent(OutputStream.class);
        String encoding = getEncoding(requestMessage);
        String request = toString((CachedOutputStream) os, encoding);
// Path: rsimulator-core/src/main/java/com/github/bjuvensjo/rsimulator/core/Simulator.java
// @ImplementedBy(SimulatorImpl.class)
// public interface Simulator {
// 
//     /**
//      * Returns a SimulatorResponse that matches the specified request.
//      *
//      * @param rootPath         the root path on which to search recursively for matches to the specified request
//      * @param rootRelativePath the path on which to search recursively for matches to the specified request
//      * @param request          the request
//      * @param contentType      the content type of the request, e.g. txt or xml
//      * @param vars             vars that can be used in for example scripts
//      * @return a SimulatorResponse that matches the specified request
//      */
//     Optional<SimulatorResponse> service(String rootPath, String rootRelativePath, String request, String contentType, Map<String, Object>... vars);
// }
// 
// Path: rsimulator-core/src/main/java/com/github/bjuvensjo/rsimulator/core/SimulatorResponse.java
// public interface SimulatorResponse {
// 
//     /**
//      * Returns the properties of a specific test data request and response pair. If the name of the test data request
//      * file for instance is Test1Request.xml, the properties file must be named Test1.properties. For supported
//      * properties, see *PropertiesInterceptor classes.
//      *
//      * @return the properties of a specific test data request and response pair
//      */
//     Optional<Properties> getProperties();
// 
//     /**
//      * Sets the specified properties.
//      *
//      * @param properties the properties to set
//      */
//     void setProperties(Optional<Properties> properties);
// 
//     /**
//      * Returns the test data response.
//      *
//      * @return the test data response
//      */
//     String getResponse();
// 
//     /**
//      * Sets the specified response.
//      *
//      * @param response the response to set
//      */
//     void setResponse(String response);
// 
//     /**
//      * Returns the matching request path.
//      *
//      * @return the matching request path
//      */
//     Path getMatchingRequest();
// 
//     /**
//      * Sets the specified path.
//      *
//      * @param path the path to set
//      */
//     void setMatchingRequest(Path path);
// }


// Path: rsimulator-cxf-rt-transport/src/main/java/com/github/bjuvensjo/rsimulator/cxf/transport/RSimulatorConduit.java
import com.github.bjuvensjo.rsimulator.core.Simulator;
import com.github.bjuvensjo.rsimulator.core.SimulatorResponse;
import org.apache.cxf.Bus;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.transport.AbstractConduit;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.Properties;
import java.util.logging.Logger;

    RSimulatorConduit(EndpointInfo endpointInfo, Simulator simulator, Bus bus, String rootPath) {
        super(endpointInfo.getTarget());
        this.simulator = simulator;
        this.bus = bus;
        this.rootPath = rootPath;
    }

    @Override
    public void prepare(Message message) {
        message.setContent(OutputStream.class, new CachedOutputStream());
    }

    @Override
    public void close(Message message) throws IOException {
        super.close(message);
        if (!isInbound(message)) {
            // notify caller that a response is received
            this.getMessageObserver().onMessage(createResponseMessage(message));
        }
    }

    private boolean isInbound(Message message) {
        return Boolean.TRUE.equals(message.get("org.apache.cxf.message.inbound"));
    }

    private Message createResponseMessage(Message requestMessage) throws IOException {
        Message responseMessage = new MessageImpl();
        OutputStream os = requestMessage.getContent(OutputStream.class);
        String encoding = getEncoding(requestMessage);
        String request = toString((CachedOutputStream) os, encoding);
        Optional<SimulatorResponse> response = findResponse(getPath(requestMessage), request);