com.mirth.connect.connectors.tcp.TcpMessageReceiver.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.connectors.tcp.TcpMessageReceiver.java

Source

/*
 * Copyright (c) SymphonySoft Limited. All rights reserved.
 * http://www.symphonysoft.com
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE-MULE.txt file.
 */

package com.mirth.connect.connectors.tcp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Map;

import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkException;
import javax.resource.spi.work.WorkManager;

import org.apache.commons.codec.binary.Base64;
import org.mule.config.i18n.Message;
import org.mule.config.i18n.Messages;
import org.mule.impl.MuleMessage;
import org.mule.impl.ResponseOutputStream;
import org.mule.providers.AbstractMessageReceiver;
import org.mule.providers.ConnectException;
import org.mule.providers.TemplateValueReplacer;
import org.mule.umo.UMOComponent;
import org.mule.umo.UMOMessage;
import org.mule.umo.endpoint.UMOEndpoint;
import org.mule.umo.lifecycle.Disposable;
import org.mule.umo.lifecycle.DisposeException;
import org.mule.umo.lifecycle.InitialisationException;
import org.mule.umo.provider.UMOConnector;
import org.mule.umo.provider.UMOMessageAdapter;

import com.mirth.connect.model.MessageObject;
import com.mirth.connect.model.Response;
import com.mirth.connect.server.Constants;
import com.mirth.connect.server.controllers.AlertController;
import com.mirth.connect.server.controllers.ControllerFactory;
import com.mirth.connect.server.controllers.MonitoringController;
import com.mirth.connect.server.controllers.MonitoringController.ConnectorType;
import com.mirth.connect.server.controllers.MonitoringController.Event;
import com.mirth.connect.server.mule.transformers.JavaScriptPostprocessor;

import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;

/**
 * <code>TcpMessageReceiver</code> acts like a tcp server to receive socket
 * requests.
 * 
 * @author <a href="mailto:ross.mason@symphonysoft.com">Ross Mason</a>
 * @author <a href="mailto:tsuppari@yahoo.co.uk">P.Oikari</a>
 * 
 * @version $Revision: 1.23 $
 */
public class TcpMessageReceiver extends AbstractMessageReceiver implements Work {
    protected ServerSocket serverSocket = null;
    protected TcpConnector connector;
    private AlertController alertController = ControllerFactory.getFactory().createAlertController();
    private MonitoringController monitoringController = ControllerFactory.getFactory().createMonitoringController();
    private JavaScriptPostprocessor postProcessor = new JavaScriptPostprocessor();
    private TemplateValueReplacer replacer = new TemplateValueReplacer();
    private TcpWorker work;
    private ConnectorType connectorType = ConnectorType.LISTENER;

    public TcpMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
            throws InitialisationException {
        super(connector, component, endpoint);
        TcpConnector tcpConnector = (TcpConnector) connector;
        this.connector = tcpConnector;
    }

    public void doConnect() throws ConnectException {
        disposing.set(false);
        URI uri = endpoint.getEndpointURI().getUri();
        try {
            serverSocket = createSocket(uri);
            monitoringController.updateStatus(connector, connectorType, Event.INITIALIZED);
        } catch (UnknownHostException e) {
            logger.error(e.getClass().getName() + ": " + e.getMessage());
            throw new org.mule.providers.ConnectException(new Message("tcp", 1, uri), e, this);
        } catch (Exception e) {
            throw new org.mule.providers.ConnectException(new Message("tcp", 1, uri), e, this);
        }

        try {
            getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, null);
        } catch (WorkException e) {
            throw new ConnectException(new Message(Messages.FAILED_TO_SCHEDULE_WORK), e, this);
        }
    }

    public void doDisconnect() throws ConnectException {
        // this will cause the server thread to quit
        disposing.set(true);
        try {
            if (serverSocket != null) {
                serverSocket.close();
            }
        } catch (IOException e) {
            logger.warn("Failed to close server socket: " + e.getMessage(), e);
        } finally {
            monitoringController.updateStatus(connector, connectorType, Event.DISCONNECTED);
        }
    }

    protected ServerSocket createSocket(URI uri) throws IOException {
        String host = uri.getHost();
        int backlog = connector.getBacklog();
        if (host == null || host.length() == 0) {
            host = "localhost";
        }
        InetAddress inetAddress = InetAddress.getByName(host);
        if (inetAddress.equals(InetAddress.getLocalHost()) || inetAddress.isLoopbackAddress()
                || host.trim().equals("localhost")) {
            return new ServerSocket(uri.getPort(), backlog);
        } else {
            return new ServerSocket(uri.getPort(), backlog, inetAddress);
        }
    }

    /**
     * Obtain the serverSocket
     */
    public ServerSocket getServerSocket() {
        return serverSocket;
    }

    public void run() {
        while (!disposing.get()) {
            if (connector.isStarted() && !disposing.get()) {
                Socket socket = null;
                try {
                    socket = serverSocket.accept();
                    logger.trace("Server socket Accepted on: " + serverSocket.getLocalPort());
                } catch (java.io.InterruptedIOException iie) {
                    logger.debug("Interupted IO doing serverSocket.accept: " + iie.getMessage());
                } catch (Exception e) {
                    if (!connector.isDisposed() && !disposing.get()) {
                        logger.warn("Accept failed on socket: " + e, e);
                        handleException(new ConnectException(e, this));
                    }
                }
                if (socket != null) {
                    try {
                        monitoringController.updateStatus(connector, connectorType, Event.CONNECTED, socket);
                        work = (TcpWorker) createWork(socket);
                        try {
                            getWorkManager().scheduleWork(work, WorkManager.IMMEDIATE, null, null);
                        } catch (WorkException e) {
                            logger.error("Tcp Server receiver Work was not processed: " + e.getMessage(), e);
                        }
                    } catch (SocketException e) {
                        alertController.sendAlerts(((TcpConnector) connector).getChannelId(), Constants.ERROR_411,
                                null, e);
                        monitoringController.updateStatus(connector, connectorType, Event.DISCONNECTED, socket);
                        handleException(e);
                    }

                }
            }
        }
    }

    public void release() {
    }

    public void doDispose() {
        try {
            monitoringController.updateStatus(connector, connectorType, Event.DISCONNECTED);
            if (serverSocket != null && !serverSocket.isClosed()) {
                serverSocket.close();
            }
            serverSocket = null;
            if (work != null) {
                work.dispose();
            }
        } catch (Exception e) {
            logger.error(new DisposeException(new Message("tcp", 2), e));
        }
        logger.info("Closed Tcp port");
    }

    protected Work createWork(Socket socket) throws SocketException {
        return new TcpWorker(socket);
    }

    protected class TcpWorker implements Work, Disposable {
        protected Socket socket = null;

        protected DataInputStream dataIn;

        protected DataOutputStream dataOut;

        protected AtomicBoolean closed = new AtomicBoolean(false);

        protected TcpProtocol protocol;

        public TcpWorker(Socket socket) {
            this.socket = socket;

            final TcpConnector tcpConnector = connector;
            this.protocol = tcpConnector.getTcpProtocol();
            tcpConnector.updateReceiveSocketsCount(true);
            try {
                socket.setReceiveBufferSize(tcpConnector.getBufferSize());
                socket.setSendBufferSize(tcpConnector.getBufferSize());
                socket.setSoTimeout(tcpConnector.getReceiveTimeout());
                socket.setTcpNoDelay(true);
                socket.setKeepAlive(tcpConnector.isKeepAlive());
            } catch (SocketException e) {
                logger.error("Failed to set Socket properties: " + e.getMessage(), e);
            }

            logger.info("TCP connection from " + socket.getRemoteSocketAddress().toString() + " on port "
                    + socket.getLocalPort());
        }

        public void release() {
            dispose();
        }

        public void dispose() {
            closed.set(true);
            try {
                if (socket != null && !socket.isClosed()) {
                    logger.debug("Closing listener: " + socket.getLocalSocketAddress().toString());
                    socket.shutdownInput();
                    socket.shutdownOutput();
                    socket.close();
                }
            } catch (IOException e) {
                logger.error("Socket close failed with: " + e);
            } finally {
                connector.updateReceiveSocketsCount(false);
            }
        }

        /**
         * Accept requests from a given TCP port
         */
        public void run() {
            try {
                dataIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
                dataOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));

                while (!socket.isClosed() && !disposing.get()) {
                    byte[] b;
                    try {
                        try {
                            b = protocol.read(dataIn);
                        } catch (SocketException e) {
                            /*
                             * If there was a socket exception, break and
                             * dispose the connection whether or not
                             * keepSendSocketOpen is true.
                             */
                            break;
                        } catch (SocketTimeoutException ste) {
                            /*
                             * No message was received because of a timeout. If
                             * keepSendSocketOpen is true then continue waiting,
                             * otherwise break.
                             */
                            if (connector.isKeepSendSocketOpen()) {
                                continue;
                            } else {
                                break;
                            }
                        }

                        if (b == null) {
                            /*
                             * The remote side of the connection was closed.
                             * Break and dispose of the connection whether or not
                             * keepSendSocketOpen is true.
                             */
                            break;
                        } else {
                            monitoringController.updateStatus(connector, connectorType, Event.BUSY, socket);
                            processData(b);
                            dataOut.flush();

                        }

                    } catch (SocketTimeoutException e) {
                        // getKeepAlive always returns true, since it is set
                        // manually.
                        if (!socket.getKeepAlive()) {
                            break;
                        }
                    } finally {
                        monitoringController.updateStatus(connector, connectorType, Event.DONE, socket);
                    }

                }
            } catch (Exception e) {
                handleException(e);
            } finally {
                monitoringController.updateStatus(connector, connectorType, Event.DISCONNECTED, socket);
                dispose();
            }
        }

        protected byte[] processData(byte[] data) throws Exception {
            if (data == null) {
                return null;
            }
            String str_data;
            // if we are receiving binary, base64 the bytes
            if (((TcpConnector) connector).isBinary()) {
                str_data = new String(Base64.encodeBase64Chunked(data));
            } else {
                String charset = connector.getCharsetEncoding();
                str_data = new String(data, charset);
            }
            UMOMessage returnMessage = null;
            OutputStream os;

            UMOMessageAdapter adapter = connector.getMessageAdapter(str_data);
            adapter.setProperty("receiverSocket", socket);
            os = new ResponseOutputStream(socket.getOutputStream(), socket);
            try {
                returnMessage = routeMessage(new MuleMessage(adapter), endpoint.isSynchronous(), os);
                // We need to check the message status
                if (returnMessage != null && returnMessage instanceof MuleMessage) {
                    Object payload = returnMessage.getPayload();
                    if (payload instanceof MessageObject) {
                        MessageObject messageObjectResponse = (MessageObject) payload;
                        postProcessor.doPostProcess(messageObjectResponse);
                        Map responseMap = messageObjectResponse.getResponseMap();
                        String errorString = "";

                        if (connector.isResponseFromTransformer()) {
                            if (connector.isAckOnNewConnection()) {
                                String endpointURI = connector.getAckIP() + ":" + connector.getAckPort();
                                endpointURI = replacer.replaceURLValues(endpointURI, messageObjectResponse);
                                Socket socket = initSocket("tcp://" + endpointURI);
                                BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
                                protocol.write(bos, ((Response) responseMap.get(connector.getResponseValue()))
                                        .getMessage().getBytes(connector.getCharsetEncoding()));
                                bos.flush();
                                bos.close();
                            } else {
                                protocol.write(os, ((Response) responseMap.get(connector.getResponseValue()))
                                        .getMessage().getBytes(connector.getCharsetEncoding()));
                                os.flush(); // need to flush the stream to
                                            // actually commit the bytes

                                // Only close the stream (and socket) if
                                // keepSendSocketOpen is false.
                                if (!connector.isKeepSendSocketOpen()) {
                                    os.close();
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                throw e;
            } finally {
                // Let the dispose take care of closing the socket
            }

            // The return message is always the last message routed if in a
            // batch
            // TODO: Check this for 1.2.1
            if (returnMessage != null) {
                return returnMessage.getPayloadAsBytes();
            } else {
                return null;
            }
        }

        protected Socket initSocket(String endpoint) throws IOException, URISyntaxException {
            URI uri = new URI(endpoint);
            int port = uri.getPort();
            InetAddress inetAddress = InetAddress.getByName(uri.getHost());
            Socket socket = createSocket(port, inetAddress);
            socket.setReuseAddress(true);
            socket.setReceiveBufferSize(connector.getBufferSize());
            socket.setSendBufferSize(connector.getBufferSize());
            socket.setSoTimeout(connector.getSendTimeout());
            return socket;
        }

        protected Socket createSocket(int port, InetAddress inetAddress) throws IOException {
            return new Socket(inetAddress, port);
        }
    }
}