node.Mailbox.java Source code

Java tutorial

Introduction

Here is the source code for node.Mailbox.java

Source

/**
 * Copyright (c) 2012 Evolutionary Design and Optimization Group
 * 
 * Licensed under the MIT License.
 * 
 * See the "LICENSE" file for a copy of the license.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.  
 *
 */
package node;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.BlockingQueue;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.minidev.json.JSONObject;
import network.AbstractMessage;
import network.ControlMessage;
import network.EvolveMessage;
import network.MessageType;
import network.MessageWrapper;

import org.apache.commons.lang3.exception.ExceptionUtils;

import utility.TextFormatter;

/**
 * The Mailbox thread which accepts and stores incoming messages
 * 
 * @author Dylan Sherry
 */
public class Mailbox implements Runnable, Publisher {
    // the queue for received Control messages
    private final BlockingQueue<ControlMessage> ctrlMsgsRecv;
    // the received Evolve message queue
    private final BlockingQueue<EvolveMessage> evolveMsgsRecv;
    // the queue for messages to send;
    private final BlockingQueue<MessageWrapper<AbstractMessage>> msgsSend;
    // should we report things via the reporter?
    private final Boolean REPORT;
    // the queue to send out reports on
    private final BlockingQueue<JSONObject> reportSend;

    // the mailbox socket for incoming messages
    ServerSocket serverSocket;

    // a signaling variable shared with Control for shutdown
    boolean shutdown;

    // the port number and default serverSocket backlog size
    final int port;
    final NodeList nodeList;
    int backlog;

    private static Logger log = Logger.getLogger("node.Mailbox");

    /**
     * set up the mailbox
     * 
     * @param _msgsSend
     * @param _evolveMsgsRecv
     * @param _ctrlMsgsRecv
     * @param reportSend2
     */
    public Mailbox(int _port, NodeList _nodeList, BlockingQueue<ControlMessage> _ctrlMsgsRecv,
            BlockingQueue<EvolveMessage> _evolveMsgsRecv, BlockingQueue<MessageWrapper<AbstractMessage>> _msgsSend,
            BlockingQueue<JSONObject> _reportSend) {
        // for now, hard-code REPORT to false
        REPORT = false;
        ctrlMsgsRecv = _ctrlMsgsRecv;
        evolveMsgsRecv = _evolveMsgsRecv;
        msgsSend = _msgsSend;
        reportSend = _reportSend;
        port = _port;
        nodeList = _nodeList;
        shutdown = false;
        try {
            log.setUseParentHandlers(false);
            Handler fh = new FileHandler("mailbox.log");
            fh.setFormatter(new TextFormatter());
            log.addHandler(fh);
            log.setLevel(Level.ALL);
        } catch (SecurityException e) {
            e.printStackTrace();
            log.severe(ExceptionUtils.getStackTrace(e));
        } catch (IOException e) {
            e.printStackTrace();
            log.severe(ExceptionUtils.getStackTrace(e));
        }
        Control.logOneTime("Mailbox started up");
    }

    public Mailbox(int _port, int _backlog, NodeList _nodeList, BlockingQueue<ControlMessage> _ctrlMsgsRecv,
            BlockingQueue<EvolveMessage> _evolveMsgsRecv, BlockingQueue<MessageWrapper<AbstractMessage>> _msgsSend,
            BlockingQueue<JSONObject> _reportSend) {
        this(_port, _nodeList, _ctrlMsgsRecv, _evolveMsgsRecv, _msgsSend, _reportSend);
        backlog = _backlog;
    }

    public static void println(String arg) {
        println(arg, false);
    }

    // TODO make these methods part of AbstractThread, and make
    // Mailbox extend AbstractThread
    public static void println(String arg, boolean error) {
        ((error) ? System.err : System.out).println("Mailbox: " + arg);
        if (error) {
            log.warning(arg);
        } else {
            log.info(arg);
        }
    }

    /**
     * main loop
     */
    @Override
    public void run() {
        Socket socket = null;
        // set up server socket
        try {
            if (backlog == 0) {
                serverSocket = new ServerSocket(port);
            } else {
                serverSocket = new ServerSocket(port, backlog);
            }
            serverSocket.setSoTimeout(100);
            println("Opened up server socket on port " + port);
        } catch (SocketException e) {
            Control.logOneTime(e.getLocalizedMessage(), Level.SEVERE);
            println(e.getLocalizedMessage());
            System.exit(-1);
        } catch (IOException e) {
            Control.logOneTime(
                    "Mailbox error: could not listen on port " + port + ". stacktrace: " + e.getLocalizedMessage(),
                    Level.SEVERE);
            e.printStackTrace();
            log.severe(ExceptionUtils.getStackTrace(e));
            System.exit(-1);
        }
        // enter main loop
        while (!shutdown) {
            if (REPORT)
                reportSend.add(report());
            println(report().toJSONString());

            // block until a connection is accepted
            println("Waiting for a connection");
            try {
                socket = serverSocket.accept();
                InetAddress inetaddr = socket.getInetAddress();

                println("Received connection from " + inetaddr);
                // get the message id, then call the appropriate message handler
                acceptMessage(socket);
                // close the socket
                socket.close();
            } catch (SocketTimeoutException e) {
                println("No connection: socket timed out", true);
            } catch (ConnectException e) {
                println("Caught ConnectException", true);
            } catch (SocketException e) {
                println("Caught SocketException", true);
            } catch (IOException e) {
                e.printStackTrace();
                log.severe(ExceptionUtils.getStackTrace(e));
            }

            // See if we have any messages to send
            if (msgsSend.size() > 0)
                println("Have " + msgsSend.size() + " messages to send");
            while (msgsSend.size() > 0) {
                MessageWrapper<AbstractMessage> currentMsg = msgsSend.remove();
                println("Current message: " + currentMsg.toString());
                NodeListEntry nle_dest = currentMsg.nle;
                String destID = currentMsg.destID;
                printMessageSize(currentMsg);
                currentMsg.msg.scheduleSend(nodeList, destID, nle_dest);
                println("Mailbox: Message of type " + currentMsg.msg.msgType + " with id# " + currentMsg.msg.msgID
                        + " scheduled for sending to " + nle_dest.toString());
            }
            // check if it's time to shut down (signaled from Control)
            if (shutdown) {
                // write a log message, then exit
                // serverSocket.close()
                println("Mailbox exited");
                return;
            }
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.severe(ExceptionUtils.getStackTrace(e));
            }
        }
    }

    public void printMessageSize(MessageWrapper<AbstractMessage> message) {
        try {
            ByteArrayOutputStream baostemp = new ByteArrayOutputStream();
            ObjectOutputStream oostemp = new ObjectOutputStream(baostemp);
            oostemp.writeObject(message.msg);
            oostemp.flush();
            println("Message is " + baostemp.size() + " bytes");
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    @Override
    public JSONObject report() {
        JSONObject props = new JSONObject();
        props.put("ctrlLen", ctrlMsgsRecv.size());
        props.put("evoLen", evolveMsgsRecv.size());
        props.put("outLen", msgsSend.size());
        props.put("timestamp", System.currentTimeMillis());
        JSONObject obj = new JSONObject();
        obj.put("mailbox", props);
        return obj;
    }

    /**
     * Accept a message from a socket. Adds the message to the appropriate queue
     */
    private void acceptMessage(Socket socket) {
        // get the source address
        String srcIP = socket.getInetAddress().getHostAddress();
        ObjectInputStream ois;
        try {
            InputStream is = socket.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            ois = new ObjectInputStream(bis);
            // now the Mailbox can block until it receives all message bytes
            // accept the message object
            AbstractMessage a = (AbstractMessage) ois.readObject();
            println("Received msg " + a.toString());
            // TODO MARK MESSAGE WITH OLD TIMESTAMP
            // save the srcIP for later. srcPort is already part of the message
            a.srcIP = srcIP;

            // cast to the appropriate message type
            if (a.msgType < MessageType.CONTROL) {
                // call the appropriate verifier, place the message in the queue
                // and call the appropriate message handler to reconstruct the
                // message over the network
                ControlMessage m = (ControlMessage) a;
                ctrlMsgsRecv.add(m);
            } else if (a.msgType < MessageType.EVOLVE) {
                EvolveMessage m = (EvolveMessage) a;
                evolveMsgsRecv.add(m);
            } else if (a.msgType < MessageType.DATA) {
                // TODO handle Data-specific messages (dataset/indexing
                // information, db webserver address, etc)
                throw new NoSuchMethodError("Handling DATA messages is not yet implemented");
            }
        } catch (StreamCorruptedException e) {
            println("Caught StreamCorruptedException: client must have terminated transmission", true);
        } catch (EOFException e) {
            println("Caught EOFException: client must have terminated transmission", true);
        } catch (IOException e) {
            log.log(Level.SEVERE, "Mailbox error: failed to accept message due to IOException", e);
            e.printStackTrace();
            log.severe(ExceptionUtils.getStackTrace(e));
        } catch (ClassNotFoundException e) {
            log.log(Level.SEVERE, "Mailbox error: failed to accept message due to ClassNotFoundException", e);
            e.printStackTrace();
            log.severe(ExceptionUtils.getStackTrace(e));
        }
    }

    public synchronized void shutdown() {
        shutdown = true;
        try {
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
            log.severe(ExceptionUtils.getStackTrace(e));
        }
    }

    /**
    * Obtain the number of bytes a serialized object occupies
     * 
     * @throws IOException
     */
    public static <N> int getSerializedSize(N n) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(n);
        oos.close();
        return out.toByteArray().length;
    }
}