Package ke.go.moh.oec.lib

Source Code of ke.go.moh.oec.lib.Mediator

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is OpenEMRConnect.
*
* The Initial Developer of the Original Code is International Training &
* Education Center for Health (I-TECH) <http://www.go2itech.org/>
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
package ke.go.moh.oec.lib;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import ke.go.moh.oec.IService;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Date;
import java.util.Properties;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.SimpleFormatter;
import ke.go.moh.oec.PersonRequest;
import ke.go.moh.oec.PersonResponse;

/**
* Mediator between OEC clients and services to forward requests
* and return responses. This may include storing and forwarding requests
* and/or responses when network connections cannot be made.
* <p>
* If the user of this class
* accepts unsolicited messages (as a server), then it must register
* a callback object that is compliant with the IService interface. If the
* user of this class is just making client requests, this is not necessary.
*
* @author JGitahi
* @author Jim Grace
*/
public class Mediator implements IService {

    /**
     * Maximum number of times a message may be transmitted through the
     * network on its way from source to destination.
     * <p>
     * For example, the first time a message is sent, the hop count is 1.
     * If the message is received by another program and forwarded on,
     * the hop count is 2, and so on.
     * <p>
     * In the present design, the hop count should never exceed 4. If it does,
     * it probably indicates a loop where one system is routing the message
     * to a second system, and the second system is routing it back to the
     * first system.
     * <p>
     * If the hop count exceeds the maximum, the message is discarded and
     * an error reported.
     */
    private static final int MAX_HOP_COUNT = 4;
    /**
     * The number of protocol message IDs generated since this library
     * was initialized. The sequence number forms part of the message ID.
     */
    private static int messageSequenceNumber = 0;
    /**
     * The queue of messages we have sent, for which we expect a response.
     */
    private static MessagePendingQueue pendingQueue = new MessagePendingQueue();
    /*
     * Allocate objects that we will use for this instance of Mediator.
     * These are instance variables, so they will be thread safe in the
     * event that different instances of Mediator are operating concurrently
     * on different threads.
     */
    /**
     * Instance to handle HTTP protocol.
     * Start the instance with a reference to us, so it knows where to
     * deliver any HTTP messages received.
     */
    private HttpService httpService;
    /*
     * Instance to manage the store-and-forward message queue.
     * Start the instance with a reference to the HTTP Handler, so it
     * knows how to send any messages on the network.
     */
    private QueueManager queueManager;
    /** Instance to pack and unpack XML */
    private XmlPacker xmlPacker;
    /**
     * Reference to our caller's callbackObject that implements
     * {@link IService#getData(int, java.lang.Object)}.
     */
    static private IService myCallbackObject = null;
    /**
     * A copy of the properties from standard file location
     */
    static Properties properties = null;
    /** Lock the properties file we are using, so multiple instances will use multiple properties files. */
    static FileLock pathLock = null;
    /** The logger level to use, configured from the properties file. */
    static Level loggerLevel = null;
    /** Should we use the logging service? */
    static boolean useLoggingService = true;
    /** Directory where we find our properties and QueueManager embedded database. */
    static String runtimeDirectory;

    /**
     * Initialize -- set up the runtime directory.
     */
    static {
        setRuntimeDirectory(); // Do this first!
    }

    /**
     * Constructs an instance of the Mediator.
     * (Note: there should be only one instance of the mediator. At some
     * point in the future, this class, and all who call it, should
     * properly be refactored to follow the Java singleton pattern.)
     * <p>
     * If we are to use our own distributed logging service, set up the
     * LoggingServiceHandler to handle all calls to the standard logger.
     * <p>
     * Allocate other library class objects as needed, and start them
     * as needed. In particular, the HttpManager and QueueManager need
     * to be started.
     */
    public Mediator() {
        setLoggerLevel();
        LogManager man = LogManager.getLogManager();
        Logger rootLogger = man.getLogger("");
        if (useLoggingService) {
            Handler loggingServiceHandler = new LoggingServiceHandler(this);
            Formatter formatter = new SimpleFormatter();
            loggingServiceHandler.setFormatter(formatter);
            rootLogger.addHandler(loggingServiceHandler);
        }
        String loggerFile = getProperty("Logger.File");
        if (loggerFile != null && Boolean.parseBoolean(loggerFile)) {
            try {
                String logFileName = runtimeDirectory + "openemrconnect%g.log";
                Handler loggingFileHandler = new FileHandler(logFileName, 100000, 100);
                Formatter formatter = new SimpleFormatter();
                loggingFileHandler.setFormatter(formatter);
                loggingFileHandler.setLevel(loggerLevel);
                rootLogger.addHandler(loggingFileHandler);
            } catch (IOException ex) {
                Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, "Can''t start file logger.", ex);
            } catch (SecurityException ex) {
                Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, "Can''t start file logger.", ex);
            }
        }
        httpService = new HttpService(this);
        queueManager = new QueueManager(httpService);
        xmlPacker = new XmlPacker();
        try {
            httpService.start();
        } catch (IOException ex) {
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, null, ex);
        }
        queueManager.start();
        Logger.getLogger(Mediator.class.getName()).log(Level.FINE,
                "{0} started.", getProperty("Instance.Name"));
    }

    /**
     * Sets up the runtimeDirectory string to select a working directory relative
     * to the default application directory. The purpose is to allow multiple
     * instances of the code to run from the same directory. This can be
     * especially useful in debugging and testing environments. The default
     * directory for each instance is used to contain the openemrconnect.properties
     * file used by that instance. It may contain other directories or files as
     * well, such as the embedded JavaDB database used for the QueueManager.
     * <p>
     * The runtimeDirectory is determined as follows: The first time an application
     * is run, it places a lock on a dummy file in the default application directory.
     * When this lock is successfully in place, it then leaves the runtimeDirectory
     * set to an empty string -- meaning that the runtime directory is the same
     * as the default application directory.
     * <p>
     * The second time an application is run concurrently from the same
     * application directory, we will find that the dummy file is already
     * locked by the first instance of the application. In that event, we
     * will try a subdirectory of "runtime2/", relative to the default
     * application directory.
     */
    static void setRuntimeDirectory() {
        try {
            runtimeDirectory = System.getProperty("runtimeDirectory");
            if (runtimeDirectory == null) {
                runtimeDirectory = "";
            }
            for (int i = 2;; i++) { // Try current directory, then "runtime2/", "runtime3/", etc.
                RandomAccessFile raf = new RandomAccessFile(runtimeDirectory + "lockfile.lck", "rw");
                FileChannel fc = raf.getChannel();
                pathLock = fc.tryLock();
                if (pathLock != null) {
                    break;
                }
                runtimeDirectory = "runtime" + i + "/"; // Construct a subdirectory name "runtime2/", "runtime3/", etc.
            }
        } catch (Exception ex) {
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                    "Can''t lock directory {0}. please either create the directory or run the app fewer times.",
                    runtimeDirectory);
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage());
            System.exit(1);
        }
        Logger.getLogger(Mediator.class.getName()).log(Level.INFO,
            "Using runtimeDirectory {0}", runtimeDirectory);
    }

    static public String getRuntimeDirectory() {
        return runtimeDirectory;
    }

    /**
     * Stops the OpenEMRConnect library services.
     *
     * This routine should be called for an orderly shut-down
     * of the Mediator library.
     * <p>
     * Call this method last, after you are through using the services.
     */
    public void stop() {
        Logger.getLogger(Mediator.class.getName()).log(Level.INFO, "OpenEMRConnect library services stopped.");
        queueManager.stop();
        httpService.stop();
    }

    /**
     * Supresses the use of the logging service to send messages to the logging server.
     * This method is intended for use by the logging service itself,
     * so it won't try to send all log entries to itself.
     */
    public static void suppressLoggingService() {
        useLoggingService = false;
    }

    /**
     * Sets the logging level according to the properties file.
     * The logging level is also set in the root handler if it is not the default.
     * Otherwise, new loggers will not be able to log anything at a
     * lower level than the default level.
     */
    private static void setLoggerLevel() {
        if (loggerLevel == null) {
            loggerLevel = Level.INFO; // Default unless changed below.
            String loggerLevelName = getProperty("Logger.Level");
            if (loggerLevelName != null) {
                try {
                    loggerLevel = Level.parse(loggerLevelName);
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger(Mediator.class.getName()).log(Level.WARNING,
                            "Logger.Level property ''{0}'' not a valid logger level.", loggerLevelName);
                }
                if (loggerLevel != Level.INFO) { // Need to change default handler level?
                    LogManager m = LogManager.getLogManager();
                    Logger rootLogger = m.getLogger("");
                    Handler rootHandler = rootLogger.getHandlers()[0];
                    rootHandler.setLevel(loggerLevel);
                }
            }
        }
    }

    /**
     * Tests to see if we should log something at a given level.
     * <p>
     * This method can be used to save the CPU time of a call to a logger.
     * It can be useful if the call to the logger itself may use a non-trivial amount of CPU.
     * For example, a logger call may invoke other methods to get some of the
     * arguments needed for the call. These tests need only be done if the
     * logger level is set low enough that the logger call will actually do something.
     *
     * @param testLevel Level to check if it would be logged.
     * @return true if this Level would be logged, otherwise false.
     */
    public static boolean testLoggerLevel(Level testLevel) {
        boolean returnValue;
        if (loggerLevel == null) {
            returnValue = Level.INFO.intValue() <= testLevel.intValue();
        } else {
            returnValue = loggerLevel.intValue() <= testLevel.intValue();
        }
        return returnValue;
    }

    /**
     * Gets a standard java.util.logging.Logger, set to the logging level property, if any.
     * If the logging level property is not the default Level.INFO, the logging level
     * is also changed in the root logging handler currently defined.
     *
     * @param loggerName Name of the logger to create.
     * @return The Logger requested.
     */
    public static Logger getLogger(String loggerName) {
        setLoggerLevel();
        Logger logger = Logger.getLogger(loggerName);
        logger.setLevel(loggerLevel);
        return logger;
    }

    /**
     * Gets the standard properties class.
     * @return properties.
     * <p>
     * The default property file is named openemrconnect.properties.
     */
    public static Properties getProperties() {
        if (properties == null) {
            properties = new Properties();
           
            // If a system property defining the configuration directory is
            // available, use it.
            String configDirectory = System.getProperty("configDirectory");
            if (configDirectory == null) {
                configDirectory = runtimeDirectory;
            }
           
            // First attempt to load from the filesystem, which will only work
            // in a dev environment (i.e. from within the IDE), and for many,
            // only for runtime2 or better.

            // On deployments, the properties file should live in the jar, and
            // thus require loading as a resource (unless the system property
            // for "configDirectory" is supplied).

            String propFileName = "openemrconnect.properties";
            try {
                String propPathName = configDirectory + propFileName;
                Logger.getLogger(Mediator.class.getName()).log(Level.INFO,
                        "Attempt property load from file ''{0}''",
                        propPathName);
                try {
                    FileInputStream fis = new FileInputStream(propPathName);
                    properties.load(fis);
                    fis.close();
                } catch (IOException ex) {
                    Logger.getLogger(Mediator.class.getName()).log(Level.INFO,
                            "Attempt property load as resource ''{0}''",
                            propFileName);
                    InputStream in = (Mediator.class.getResourceAsStream(propFileName));
                    properties.load(in);
                    in.close();
                }
            } catch (Exception ex) {
                Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                        "getProperty() Can''t open ''{0}'' -- Please create the properties file if it doesn''t exist and then restart the app",
                        propFileName);
                System.exit(1);
            }
        }
        return properties;
    }

    /**
     * Gets the value of a named property from the standard properties list.
     *
     * @param propertyName name of the property whose value we want
     * @return the value of the requested property,
     * or null if the property is not found.
     */
    public static String getProperty(String propertyName) {
        if (properties == null) {
            getProperties();
        }
        return properties.getProperty(propertyName);
    }

    /**
     * Registers a listener to receive data requests
     *
     * This method is used by server software to register an object supporting
     * the IService interface getData() method. When a message for this server
     * is received (and is not a response to an outstanding request), it will
     * be given to the getData() method of this object.
     *
     * @param callbackObject object implementing IService interface
     */
    public static void registerCallback(IService callbackObject) {
        myCallbackObject = callbackObject;
    }

    /**
     * Makes a remote data request. This version is called by our user
     * to send a new data request to a server.
     *
     * @param requestTypeId type of request (see RequestType.java)
     * @param requestData object containing data for the request
     * @return object containing response data resulting from the request, or null if none
     */
    public Object getData(int requestTypeId, Object requestData) {
        Message m = new Message();
        m.setMessageData(requestData);
        m.setSourceAddress(getProperty("Instance.Address"));
        m.setSourceName(getProperty("Instance.Name"));

        /*
         * Determine the Type of message we are to send.
         */
        MessageType messageType = MessageTypeRegistry.find(requestTypeId);
        m.setMessageType(messageType);
        if (messageType == null) {
            /*
             * This is most likely an error on the part of our caller. We were
             * called with a request type ID that is not found as a request
             * in our MessageType list.
             */
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                    "getData() - Message type not found for Request type ID ''{0}''", requestTypeId);
            return null;
        }
        /*
         * If there is a response message type, then set responseExpected to true.
         * Note that if the message type is createPerson or modifyPerson, this
         * may be overridden by the caller's desire, below.
         */
        if (messageType.getResponseMessageType() != null) {
            m.setResponseExpected(true);
        } else {
            m.setResponseExpected(false);
        }
        /*
         * Find the destination address and name. This is usually the default
         * destination for the message type. However if our caller is passing
         * us <code>PersonRequest</code> data, they may choose to explicitly
         * specify the destination rather than leaving it to the default.
         * Also, if we have a <code>PersonRequest</code>, then the caller
         * has the option of specifying an XML string to be used
         * instead of the standard template for the message.
         */
        String defaultDestinationAddress = getProperty(messageType.getDefaultDestinationAddressProperty());
        m.setDestinationAddress(defaultDestinationAddress);
        m.setDestinationName(messageType.getDefaultDestinationName());
        String messageId = generateMessageId();
        m.setMessageId(messageId);
        m.setToBeQueued(messageType.isToBeQueued());
        if (requestData instanceof PersonRequest) {
            PersonRequest pr = (PersonRequest) requestData;
            if (pr.getDestinationAddress() != null) {
                m.setDestinationAddress(pr.getDestinationAddress());
            }
            if (pr.getDestinationName() != null) {
                m.setDestinationName(pr.getDestinationName());
            }
            m.setXml(pr.getXml());
            if (pr.getRequestReference() != null) {
                m.setMessageId(pr.getRequestReference()); // Overwrite the auto-generated message ID.
            }
            if (!pr.isResponseRequested()) {
                MessageType.TemplateType templateType = messageType.getTemplateType();
                if (templateType == MessageType.TemplateType.createPerson
                        || templateType == MessageType.TemplateType.modifyPerson) {
                    m.setResponseExpected(false);
                    m.setToBeQueued(true);
                }
            }
        }
        Object returnData = null;
        if (m.getDestinationAddress() == null) {
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                    "getData() - Can''t find {0} in properties file.",
                    messageType.getDefaultDestinationAddressProperty());
        } else {
            /*
             * Send the request to the server.
             */
            returnData = sendData(m);
        }
        return returnData;
    }

    /**
     * Constructs a new message ID to use for a message. The ID must be
     * unique for requests coming from this instance address (this
     * client or service running on this machine.)
     * <p>
     * We construct the message ID by putting two components together. The first
     * is the current time in milliseconds (since January 1, 1970.) The
     * second is a sequence number that starts at 0 when this process
     * started running. So even if we generate two message IDs without the
     * system time changing, they will be unique.
     * <p>
     * Note that if a message is a response to another message, it typically
     * uses the request message ID as its own, so the request and response
     * can be correlated. In this case, a new ID is not generated for the
     * response message.
     *
     * @return the new request ID.
     */
    public static synchronized String generateMessageId() {
        long milliseconds = new Date().getTime();
        return Long.toString(milliseconds) + Long.toString(messageSequenceNumber++);
    }

    /**
     * Sends data to a remote destination. This version is called internally, in one of two ways:
     * <p>
     * 1. From the public <code>getData</code> request, to continue processing a user
     * client request that needs to be sent to a server.
     * <p>
     * 2. From our received message handling. This is when an unsolicited request has
     * come here for a server that is bound to us. We have delivered the request to
     * the server. The server has given us back a response. And now we need to pack
     * up the response and return it to the client.
     *
     * @param m message to be sent
     * @return object containing response data from the request
     */
    Object sendData(Message m) {
        Object returnData = null;
        MessageType messageType = m.getMessageType(); // For handy reference.
        NextHop nextHop = NextHop.getNextHopByAddress(m.getDestinationAddress());
        m.setNextHop(nextHop);
        if (nextHop == null) {
            /*
             * This is an error in our routing mechanism. We have a desination
             * address, but we were unable to translate it into next hop information.
             */
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                    "getData() - Next hop information not found for ''{0}'': {1}",
                    new Object[]{m.getDestinationAddress(), m.summarize()});
            return null;
        }
        /*
         * Pack the data into the XML message.
         */
        String xml = xmlPacker.pack(m);
        m.setXml(xml);
        /*
         * If we may get a response to this message, add it to the list of responses we are expecting.
         */
        MessagePendingQueue.Entry queueEntry = null;
        if (m.isResponseExpected()) {
            queueEntry = pendingQueue.enqueue(m);
        }
        /*
         * Send the message.
         */
        m.setHopCount(1); // This will be the first hop.
        if (Mediator.testLoggerLevel(Level.FINE)) {
            Mediator.getLogger(Mediator.class.getName()).log(Level.FINE, "Sending message {0}", m.summarize());
        }
        boolean messageSent = sendMessage(m);
        /*
         * If we expect a response to this message, wait for the response.
         * When we get the response, return it to our caller. If there is no
         * response message after a defined timeout period, return failure.
         */
        if (m.isResponseExpected()) {
            Message responseMessage = null;
            if (messageSent) {
                responseMessage = pendingQueue.waitForResponse(queueEntry);
            } else {
                pendingQueue.dequeue(queueEntry);
            }
            MessageType.TemplateType templateType = m.getMessageType().getTemplateType();
            switch (templateType) {
                case findPerson:
                case createPerson:
                case modifyPerson:
                    //added above two lines in place of the two below  to specify the message
                    //templates that expect a response
//                case createPersonAccepted:
//                case modifyPersonAccepted:
                    PersonResponse personResponse;
                    if (responseMessage != null) {
                        returnData = responseMessage.getMessageData();
                        personResponse = (PersonResponse) returnData;
                        personResponse.setSuccessful(true);
                    } else {
                        personResponse = new PersonResponse();
                        personResponse.setSuccessful(false);
                    }
                    break;

                default:
                    Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                            "sendData() Message with requestId {0} and templateType {1} is expecting a response but not handled.",
                            new Object[]{m.getMessageType().getRequestTypeId(), templateType.name()});
                    break;
            }
        }
        /*
         * Return.
         */
        return returnData;
    }

    /**
     * Process a received HTTP message. Either this is a message that is
     * destined for us, or we are an intermediate node that should forward the
     * message on its way to the next node. If we find that we have an
     * IP address to which we should forward this message, than forward it
     * on its way. Otherwise we will unpack and process it locally.
     *
     * @param m Message received
     */
    void processReceivedMessage(Message m) {
        String destinationAddress = m.getDestinationAddress();
        if (destinationAddress == null) {
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                    "Message has no destination address.");
        } else {
            String ourInstanceAddress = getProperty("Instance.Address");
            if (destinationAddress.equalsIgnoreCase(ourInstanceAddress)) { // If the message is addressed to us:
                xmlPacker.unpack(m);
                if (m.getMessageData() == null) {
                    Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                            "Received message did not unpack into messageData: {0}", m.summarize());
                } else {
                    if (m.getMessageData().getClass() == PersonRequest.class) {
                        PersonRequest req = (PersonRequest) m.getMessageData();
                        req.setSourceAddress(m.getSourceAddress());
                        req.setSourceName(m.getSourceName());
                        req.setRequestReference(m.getMessageId());
                        req.setXml(m.getXml()); // Return raw XML through the API in case it is wanted.
                    } else if (m.getMessageData().getClass() == PersonResponse.class) {
                        PersonResponse rsp = (PersonResponse) m.getMessageData();
                        rsp.setSuccessful(true);
                        rsp.setRequestReference(m.getMessageId());
                    }
                    boolean responseDelivered = pendingQueue.findRequest(m);
                    if (responseDelivered) { // Was the message a response to a request that we just delivered?
                        if (Mediator.testLoggerLevel(Level.FINE)) {
                            Mediator.getLogger(Mediator.class.getName()).log(Level.FINE,
                                    "Received message delivered as response to API: {0}", m.summarize());
                        }
                    } else {
                        if (Mediator.testLoggerLevel(Level.FINE)) {
                            Mediator.getLogger(Mediator.class.getName()).log(Level.FINE,
                                    "Received message delivered unsolicited to API: {0}", m.summarize());
                        }
                        processUnsolicitedMessage(m);
                    }
                }
            } else {    // If the message is not addressed to us:
                NextHop nextHop = NextHop.getNextHopByAddress(destinationAddress);
                if (nextHop == null) {
                    /*
                     * The message destination does not match our own,
                     * and the router is not giving us next hop information.
                     * This is a configuration error.
                     */
                    Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                            "Next hop not found for received message {0}", m.summarize());
                } else {
                    /*
                     * The message destination does not match our own,
                     * and we have found an external IP address/port for it.
                     * It is not destined for us, so we will pass it though
                     * to its destination.
                     */
                    m.setNextHop(nextHop);
                    int hopCount = m.getHopCount();
                    hopCount++;
                    m.setHopCount(hopCount);
                    if (Mediator.testLoggerLevel(Level.FINE)) {
                        Mediator.getLogger(Mediator.class.getName()).log(Level.FINE,
                                "Relaying message {0}", m.summarize());
                    }
                    sendMessage(m);
                }
            }
        }
    }

    /**
     * Process an unsolicited message. We have already determined that this
     * was not the response to a request that we were waiting for.
     *
     * @param m the unpacked message information.
     */
    private void processUnsolicitedMessage(Message m) {
        MessageType messageType = m.getMessageType(); // For convenience below (code readability).
        if (messageType.getRequestTypeId() != 0) { // Does this message have a request ID?
            if (myCallbackObject != null) { // Yes. Did the user register a callback routine?
                CallbackThread c = new CallbackThread(this, myCallbackObject, m);
                Thread t = new Thread(c);
                t.start();
            } else {
                /*
                 * The user has not defined a callback routine. Meanwhile, someone sent us
                 * an unsolicited message -- at least a message that was not a reply we were
                 * waiting for.
                 */
                Logger.getLogger(Mediator.class.getName()).log(Level.WARNING,
                        "Unsolicited message with request type {0} received. No user callback is registered: {1}",
                        new Object[]{messageType.getRequestTypeId(), m.summarize()});
            }
        } else {
            /*
             * We received a message that wasn't a response we were waiting for. Also, it
             * didn't have a request message type.
             *
             * This could happen if we sent a message for which we were expecting a response,
             * and meanwhile our program restarted, loosing the memory of which responses
             * we were expecting. Then the response finally came but we weren't expecting it.
             *
             * Or this could be an error of some sort.
             */
            Logger.getLogger(Mediator.class.getName()).log(Level.WARNING,
                    "Unsolicited message with XML root ''{0}'' received, but it isn''t registered as a request: {1}",
                    new Object[]{messageType.getRootXmlTag(), m.summarize()});
        }
    }

    /**
     * Sends a packed XML message. This is common code that is called
     * for any of the following reasons:
     * <p>
     * 1. Forward a received message that is not ultimately destined for us.
     * <p>
     * 2. Send a new request to a server.
     * <p>
     * 3. Send a response back from a server.
     * <p>
     * We check the hop count to make sure the message is not caught in a
     * routing loop. Then we see whether the message should be sent
     * with our without the queuing mechanism for storing and forwarding.
     * Then we send it.
     *
     * @param m Message to send
     * @return true if the message was queued or sent successfully (to the next hop), otherwise false
     */
    private boolean sendMessage(Message m) {
        boolean messageSent = false;
        if (m.getHopCount() > MAX_HOP_COUNT) {
            /*
             * A message has been forwarded too many times, exceeding the maximum hop count.
             * This may indicate a routing loop between two or more systems.
             */
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE,
                    "sendMessage() - Hop count {0} exceeds maximum hop count {1} for destination ''{2}'', routed to ''{3}'': {4}",
                    new Object[]{m.getHopCount(), MAX_HOP_COUNT, m.getDestinationAddress(), m.getNextHop().getIpAddressPort(), m.summarize()});
        } else if (m.isToBeQueued()) {
            messageSent = queueManager.enqueue(m);
        } else {
            try {
                messageSent = httpService.send(m); // (toBeQueued = false)
            } catch (MalformedURLException ex) {
                Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, "Error sending: " + m.summarize(), ex);
            } catch (IOException ex) {
                Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, "Error sending: " + m.summarize(), ex);
            }
        }
        return messageSent;
    }
}
TOP

Related Classes of ke.go.moh.oec.lib.Mediator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.