com.mirth.connect.plugins.dashboardstatus.DashboardConnectorStatusMonitor.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.plugins.dashboardstatus.DashboardConnectorStatusMonitor.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * http://www.mirthcorp.com
 *
 * The software in this package is published under the terms of the MPL
 * license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 */

package com.mirth.connect.plugins.dashboardstatus;

import java.net.Socket;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.SerializationException;
import org.apache.commons.lang.SerializationUtils;
import org.apache.log4j.Logger;

import com.mirth.connect.connectors.doc.DocumentWriterProperties;
import com.mirth.connect.connectors.file.FileWriterProperties;
import com.mirth.connect.connectors.http.HttpSenderProperties;
import com.mirth.connect.connectors.jdbc.DatabaseWriterProperties;
import com.mirth.connect.connectors.jms.JMSWriterProperties;
import com.mirth.connect.connectors.mllp.LLPSenderProperties;
import com.mirth.connect.connectors.smtp.SmtpSenderProperties;
import com.mirth.connect.connectors.tcp.TCPSenderProperties;
import com.mirth.connect.connectors.vm.ChannelWriterProperties;
import com.mirth.connect.connectors.ws.WebServiceSenderProperties;
import com.mirth.connect.model.Channel;
import com.mirth.connect.model.Connector;
import com.mirth.connect.model.ExtensionPermission;
import com.mirth.connect.plugins.ServicePlugin;
import com.mirth.connect.server.controllers.ControllerFactory;
import com.mirth.connect.server.controllers.MonitoringController.ConnectorType;
import com.mirth.connect.server.controllers.MonitoringController.Event;

public class DashboardConnectorStatusMonitor implements ServicePlugin {
    private Logger logger = Logger.getLogger(this.getClass());

    public static final String PLUGINPOINT = "Dashboard Connector Service";

    private static final String COLOR_BLACK = "black";
    private static final String COLOR_YELLOW = "yellow";
    private static final String COLOR_GREEN = "green";
    private static final String COLOR_RED = "red";

    private static final String STATE_UNKNOWN = "Unknown";
    private static final String STATE_IDLE = "Idle";
    private static final String STATE_RECEIVING = "Receiving";
    private static final String STATE_READING = "Reading";
    private static final String STATE_POLLING = "Polling";
    private static final String STATE_NOT_POLLING = "Not Polling";
    private static final String STATE_WRITING = "Writing";
    private static final String STATE_SENDING = "Sending";
    private static final String STATE_WAITING = "Waiting";
    private static final String STATE_CONNECTED = "Connected";
    private static final String STATE_DISCONNECTED = "Disconnected";
    private static final String STATE_ATTEMPTING = "Attempting to Connect";
    private static final String STATE_NO_SELECTION = "No Channel Selected";

    private static final String METHOD_GET_STATES = "getStates";
    private static final String METHOD_GET_CONNECTION_INFO_LOGS = "getConnectionInfoLogs";
    private static final String METHOD_REMOVE_SESSIONID = "removeSessionId";
    private static final String METHOD_CHANNELS_DEPLOYED = "channelsDeployed";

    private HashMap<String, String[]> connectorStateMap;
    private HashMap<String, Set<Socket>> socketSetMap;
    private ConcurrentHashMap<String, LinkedList<String[]>> connectorInfoLogs;
    private LinkedList<String[]> entireConnectorInfoLogs;
    private ConcurrentHashMap<String, HashMap<String, Long>> lastDisplayedLogIndexBySessionId = new ConcurrentHashMap<String, HashMap<String, Long>>();
    // maximum log size for each channel. and for entire logs.
    private static final int MAX_LOG_SIZE = 1000;
    private static long logId = 1;
    // stores channelsJustBeenDeployedFlag for each session (client). this flag
    // is used to signal clients to clear out all the Dashboard Monitoring Logs.
    private ConcurrentHashMap<String, Boolean> channelsDeployedFlagForEachClient = new ConcurrentHashMap<String, Boolean>();

    @Override
    public String getPluginPointName() {
        return PLUGINPOINT;
    }

    @Override
    public void init(Properties properties) {
        socketSetMap = new HashMap<String, Set<Socket>>();
        connectorStateMap = new HashMap<String, String[]>();
        connectorInfoLogs = new ConcurrentHashMap<String, LinkedList<String[]>>();
        entireConnectorInfoLogs = new LinkedList<String[]>();

        channelsDeployedFlagForEachClient.clear();
    }

    @Override
    public void start() {

    }

    @Override
    public void stop() {

    }

    @Override
    public void update(Properties properties) {

    }

    public void updateStatus(String connectorId, ConnectorType type, Event event, Socket socket) {
        String stateImage = COLOR_BLACK;
        String stateText = STATE_UNKNOWN;
        boolean updateStatus = true;

        switch (event) {
        case INITIALIZED:
            switch (type) {
            case LISTENER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_WAITING;
                break;
            case READER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_IDLE;
                break;
            }
            break;
        case CONNECTED:
            switch (type) {
            case LISTENER:
                if (socket != null) {
                    addConnectionToSocketSet(socket, connectorId);
                    stateImage = COLOR_GREEN;
                    stateText = STATE_CONNECTED + " (" + getSocketSetCount(connectorId) + ")";
                } else {
                    stateImage = COLOR_GREEN;
                    stateText = STATE_CONNECTED;
                }
                break;
            case READER:
                stateImage = COLOR_GREEN;
                stateText = STATE_POLLING;
                break;
            }
            break;
        case DISCONNECTED:
            switch (type) {
            case LISTENER:
                if (socket != null) {
                    removeConnectionInSocketSet(socket, connectorId);
                    int connectedSockets = getSocketSetCount(connectorId);
                    if (connectedSockets == 0) {
                        stateImage = COLOR_YELLOW;
                        stateText = STATE_WAITING;
                    } else {
                        stateImage = COLOR_GREEN;
                        stateText = STATE_CONNECTED + " (" + connectedSockets + ")";
                    }
                } else {
                    clearSocketSet(connectorId);
                    stateImage = COLOR_RED;
                    stateText = STATE_DISCONNECTED;
                }
                break;
            case READER:
                stateImage = COLOR_RED;
                stateText = STATE_NOT_POLLING;
                break;
            case WRITER:
                stateImage = COLOR_RED;
                stateText = STATE_DISCONNECTED;
                break;
            case SENDER:
                stateImage = COLOR_RED;
                stateText = STATE_DISCONNECTED;
                break;
            }
            break;
        case BUSY:
            switch (type) {
            case READER:
                stateImage = COLOR_GREEN;
                stateText = STATE_READING;
                break;
            case LISTENER:
                stateImage = COLOR_GREEN;
                stateText = STATE_RECEIVING;
                break;
            case WRITER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_WRITING;
                break;
            case SENDER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_SENDING;
                break;
            }
            break;
        case DONE:
            switch (type) {
            case READER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_IDLE;
                break;
            case LISTENER:
                if (socket != null) {
                    stateImage = COLOR_GREEN;
                    stateText = STATE_CONNECTED + " (" + getSocketSetCount(connectorId) + ")";
                } else {
                    stateImage = COLOR_YELLOW;
                    stateText = STATE_WAITING;
                }
                break;
            }
            break;
        case ATTEMPTING:
            switch (type) {
            case WRITER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_ATTEMPTING;
                break;
            case SENDER:
                stateImage = COLOR_YELLOW;
                stateText = STATE_ATTEMPTING;
                break;
            }
            break;
        default:
            updateStatus = false;
            break;
        }

        if (updateStatus) {
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");

            String channelName = "";
            // this will be overwritten down below. If not, something's wrong.
            String connectorType = type.toString();
            String information = "";

            /*
             * check 'connectorId' - contains destination_1_connector, etc.
             * connectorId consists of id_source_connector for sources, and
             * id_destination_x_connector for destinations. i.e. tokenCount will
             * be 3 for sources and 4 for destinations. Note that READER and
             * LISTENER are sources, and WRITER and SENDER are destinations.
             */
            StringTokenizer tokenizer = new StringTokenizer(connectorId, "_");
            String channelId = tokenizer.nextToken();
            int destinationIndex;
            LinkedList<String[]> channelLog = null;

            Channel channel = ControllerFactory.getFactory().createChannelController()
                    .getDeployedChannelById(channelId);

            if (channel != null) {
                channelName = channel.getName();
                // grab the channel's log from the HashMap, if not exist, create
                // one.
                if (connectorInfoLogs.containsKey(channelName)) {
                    channelLog = connectorInfoLogs.get(channelName);
                } else {
                    channelLog = new LinkedList<String[]>();
                }

                Connector connector = null;

                switch (type) {
                case READER:
                    connectorType = "Source: " + channel.getSourceConnector().getTransportName() + "  ("
                            + channel.getSourceConnector().getTransformer().getInboundProtocol().toString() + " -> "
                            + channel.getSourceConnector().getTransformer().getOutboundProtocol().toString() + ")";
                    break;
                case LISTENER:
                    connectorType = "Source: " + channel.getSourceConnector().getTransportName() + "  ("
                            + channel.getSourceConnector().getTransformer().getInboundProtocol().toString() + " -> "
                            + channel.getSourceConnector().getTransformer().getOutboundProtocol().toString() + ")";
                    break;
                case WRITER:
                    tokenizer.nextToken();
                    // destinationId begins from 1, so subtract by 1 for the
                    // arrayIndex.
                    destinationIndex = Integer.valueOf(tokenizer.nextToken()) - 1;
                    connector = channel.getDestinationConnectors().get(destinationIndex);
                    connectorType = "Destination: " + connector.getTransportName() + " - " + connector.getName();

                    if (connector.getTransportName().equals(FileWriterProperties.name)) {
                        // Destination - File Writer.
                        switch (event) {
                        case BUSY:
                            information = FileWriterProperties.getInformation(connector.getProperties());
                            break;
                        }
                    } else if (connector.getTransportName().equals(DatabaseWriterProperties.name)) {
                        // Destination - Database Writer.
                        information = DatabaseWriterProperties.getInformation(connector.getProperties());
                    } else if (connector.getTransportName().equals(JMSWriterProperties.name)) {
                        // Destination - JMS Writer.
                        information = JMSWriterProperties.getInformation(connector.getProperties());
                    } else if (connector.getTransportName().equals(DocumentWriterProperties.name)) {
                        // Destination - Document Writer.
                        information = DocumentWriterProperties.getInformation(connector.getProperties());
                    }
                    break;
                case SENDER:
                    tokenizer.nextToken();
                    // destinationId begins from 1, so subtract by 1 for the
                    // arrayIndex.
                    destinationIndex = Integer.valueOf(tokenizer.nextToken()) - 1;
                    connector = channel.getDestinationConnectors().get(destinationIndex);
                    connectorType = "Destination: " + connector.getTransportName() + " - " + connector.getName();

                    if (connector.getTransportName().equals(HttpSenderProperties.name)) {
                        // Destination - HTTP Sender.
                        information = HttpSenderProperties.getInformation(connector.getProperties());
                    } else if (connector.getTransportName().equals(ChannelWriterProperties.name)) {
                        // Destination - Channel Writer.
                        Channel targetChannel = ControllerFactory.getFactory().createChannelController()
                                .getDeployedChannelById(
                                        ChannelWriterProperties.getInformation(connector.getProperties()));

                        if (targetChannel == null) {
                            information = "Target Channel: None";
                        } else {
                            information = "Target Channel: " + targetChannel.getName();
                        }
                    } else if (connector.getTransportName().equals(SmtpSenderProperties.name)) {
                        // Destination - SMTP Sender.
                        information = SmtpSenderProperties.getInformation(connector.getProperties());
                    } else if (connector.getTransportName().equals(TCPSenderProperties.name)) {
                        // Destination - TCP Sender.
                        // The useful info for TCP Sender - host:port will
                        // be taken care of by the socket below.
                    } else if (connector.getTransportName().equals(LLPSenderProperties.name)) {
                        // Destination - LLP Sender.
                        // The useful info for LLP Sender - host:port will
                        // be taken care of by the socket below.
                    } else if (connector.getTransportName().equals(WebServiceSenderProperties.name)) {
                        // Destination - Web Service Sender.
                        // information = "";
                    }
                    break;
                }
            }

            if (socket != null) {
                String sendingAddress = socket.getLocalAddress().toString() + ":" + socket.getLocalPort();
                String receivingAddress = socket.getInetAddress().toString() + ":" + socket.getPort();

                // If addresses begin with a slash "/", remove it.
                if (sendingAddress.startsWith("/")) {
                    sendingAddress = sendingAddress.substring(1);
                }

                if (receivingAddress.startsWith("/")) {
                    receivingAddress = receivingAddress.substring(1);
                }

                information += "Sender: " + sendingAddress + "  Receiver: " + receivingAddress;
            }

            if (channelLog != null) {
                synchronized (this) {
                    if (channelLog.size() == MAX_LOG_SIZE) {
                        channelLog.removeLast();
                    }
                    channelLog.addFirst(new String[] { String.valueOf(logId), channelName,
                            dateFormat.format(timestamp), connectorType, event.toString(), information });

                    if (entireConnectorInfoLogs.size() == MAX_LOG_SIZE) {
                        entireConnectorInfoLogs.removeLast();
                    }
                    entireConnectorInfoLogs.addFirst(new String[] { String.valueOf(logId), channelName,
                            dateFormat.format(timestamp), connectorType, event.toString(), information });

                    logId++;

                    // put the channel log into the HashMap.
                    connectorInfoLogs.put(channelName, channelLog);
                }
            }

            connectorStateMap.put(connectorId, new String[] { stateImage, stateText });
        }
    }

    private synchronized void addConnectionToSocketSet(Socket socket, String connectorId) {
        if (socket != null) {
            Set<Socket> socketSet = socketSetMap.get(connectorId);

            if (socketSet == null) {
                socketSet = new HashSet<Socket>();
                socketSetMap.put(connectorId, socketSet);
            }

            socketSet.add(socket);
        }
    }

    private synchronized void removeConnectionInSocketSet(Socket socket, String connectorId) {
        if (socket != null) {
            Set<Socket> socketSet = socketSetMap.get(connectorId);

            if (socketSet != null) {
                socketSet.remove(socket);
            }
        }
    }

    private synchronized void clearSocketSet(String connectorId) {
        socketSetMap.remove(connectorId);
    }

    private int getSocketSetCount(String connectorId) {
        Set<Socket> socketSet = socketSetMap.get(connectorId);

        if (socketSet == null) {
            return 0;
        } else {
            return socketSet.size();
        }
    }

    public Properties getDefaultProperties() {
        return new Properties();
    }

    public synchronized Object invoke(String method, Object object, String sessionId) {
        if (method.equals(METHOD_GET_STATES)) {
            return connectorStateMap;
        } else if (method.equals(METHOD_GET_CONNECTION_INFO_LOGS)) {
            String channelName;
            LinkedList<String[]> channelLog;

            if (object == null) {
                /*
                 * object is null - no channel is selected. return the latest
                 * entire log entries of all channels combined. ONLY new
                 * entries.
                 */
                channelName = STATE_NO_SELECTION;
                channelLog = entireConnectorInfoLogs;
            } else {
                // object is not null - a channel is selected. return the latest
                // (LOG_SIZE) of that particular channel.
                channelName = object.toString();
                // return only the newly added log entries for the client with
                // matching sessionId.
                channelLog = connectorInfoLogs.get(channelName);

                if (channelLog == null) {
                    channelLog = new LinkedList<String[]>();
                    connectorInfoLogs.put(channelName, channelLog);
                }
            }

            HashMap<String, Long> lastDisplayedLogIdByChannel;

            if (lastDisplayedLogIndexBySessionId.containsKey(sessionId)) {
                // client exist with the sessionId.
                lastDisplayedLogIdByChannel = lastDisplayedLogIndexBySessionId.get(sessionId);

                if (lastDisplayedLogIdByChannel.containsKey(channelName)) {
                    // existing channel on an already open client.
                    // -> only display new log entries.
                    long lastDisplayedLogId = lastDisplayedLogIdByChannel.get(channelName);
                    LinkedList<String[]> newChannelLogEntries = new LinkedList<String[]>();

                    // FYI, channelLog.size() will never be larger than LOG_SIZE
                    // = 1000.
                    for (String[] aChannelLog : channelLog) {
                        if (lastDisplayedLogId < Long.parseLong(aChannelLog[0])) {
                            newChannelLogEntries.addLast(aChannelLog);
                        }
                    }

                    if (newChannelLogEntries.size() > 0) {
                        /*
                         * put the lastDisplayedLogId into the HashMap. index 0
                         * is the most recent entry, and index0 of that entry
                         * contains the logId.
                         */
                        lastDisplayedLogIdByChannel.put(channelName,
                                Long.parseLong(newChannelLogEntries.get(0)[0]));
                        lastDisplayedLogIndexBySessionId.put(sessionId, lastDisplayedLogIdByChannel);
                    }

                    try {
                        return SerializationUtils.clone(newChannelLogEntries);
                    } catch (SerializationException e) {
                        logger.error(e);
                    }
                } else {
                    /*
                     * new channel viewing on an already open client. -> all log
                     * entries are new. display them all. put the
                     * lastDisplayedLogId into the HashMap. index0 is the most
                     * recent entry, and index0 of that entry object contains
                     * the logId.
                     */
                    if (channelLog.size() > 0) {
                        lastDisplayedLogIdByChannel.put(channelName, Long.parseLong(channelLog.get(0)[0]));
                        lastDisplayedLogIndexBySessionId.put(sessionId, lastDisplayedLogIdByChannel);
                    }

                    try {
                        return SerializationUtils.clone(channelLog);
                    } catch (SerializationException e) {
                        logger.error(e);
                    }
                }

            } else {
                // brand new client.
                // thus also new channel viewing.
                // -> all log entries are new. display them all.
                lastDisplayedLogIdByChannel = new HashMap<String, Long>();

                if (channelLog.size() > 0) {
                    lastDisplayedLogIdByChannel.put(channelName, Long.parseLong(channelLog.get(0)[0]));
                } else {
                    // no log exist at all. put the currentLogId-1, which is the
                    // very latest logId.
                    lastDisplayedLogIdByChannel.put(channelName, logId - 1);
                }

                lastDisplayedLogIndexBySessionId.put(sessionId, lastDisplayedLogIdByChannel);

                try {
                    return SerializationUtils.clone(channelLog);
                } catch (SerializationException e) {
                    logger.error(e);
                }
            }

        } else if (method.equals(METHOD_CHANNELS_DEPLOYED)) {
            if (channelsDeployedFlagForEachClient.containsKey(sessionId)) {
                // sessionId found. no (re)deploy occurred.
                return false;
            } else {
                // no sessionId found, which means channels have just been
                // (re)deployed - clear out all clients' Dashboard Connector
                // Logs.
                channelsDeployedFlagForEachClient.put(sessionId, true);
                return true;
            }
        } else if (method.equals(METHOD_REMOVE_SESSIONID)) {
            // client shut down, or user logged out -> remove everything
            // involving this sessionId.
            if (lastDisplayedLogIndexBySessionId.containsKey(sessionId)) {
                lastDisplayedLogIndexBySessionId.remove(sessionId);
            }

            if (channelsDeployedFlagForEachClient.containsKey(sessionId)) {
                channelsDeployedFlagForEachClient.remove(sessionId);
            }

            return null;
        }

        return null;
    }

    @Override
    public ExtensionPermission[] getExtensionPermissions() {
        ExtensionPermission viewPermission = new ExtensionPermission(PLUGINPOINT, "View Connection Status",
                "Displays the connection status and history of the selected channel on the Dashboard.",
                new String[] { METHOD_GET_STATES, METHOD_GET_CONNECTION_INFO_LOGS }, new String[] {});

        return new ExtensionPermission[] { viewPermission };
    }
}