/**
 * MessageBroker is a class that demonstrates how to use the Chain of
 * Responsibility pattern ("Design Patterns", Gamma, Helm, Johnson,
 * Vlissides) to allow support for new message types to be dynamically
 * added to a running server.  The MessageBroker class is a simple
 * multithreaded server; clarity is emphasized at the expense of
 * fault-tolerance, performance, and other non-functional requirements.
 * <br>
 * This source code is copyright 2005 by Patrick May.  All
 * rights reserved.
 *
 * @author Patrick May (patrick@softwarematters.org)
 * @author &copy; 2005 Patrick May.  All rights reserved.
 * @version 1
 */

package org.softwarematters.example.MessageBroker;

import java.util.logging.Logger;

import java.net.ServerSocket;
import java.net.Socket;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;

public class MessageBroker implements Runnable
{
  private static final int DEFAULT_PORT = 8079;

  private static Logger logger_
    = Logger.getLogger(MessageBroker.class.getName());

  private ServerSocket serverSocket_ = null;
  private MessageHandlerFactory messageHandlerFactory_
    = new MessageHandlerFactory();

  /**
   * The default constructor for the MessageBroker class.
   */
  public MessageBroker()
    {
    this(DEFAULT_PORT);
    }


  /**
   * The full constructor for the MessageBroker class.
   *
   * @param port The port to listen on.
   */
  public MessageBroker(int port)
    {
    logger_.config("Initializing MessageBroker instance on port "
                   + port
                   + ".");

    try
      {
      serverSocket_ = new ServerSocket(port);
      }
    catch (IOException e)
      {
      serverSocket_ = null;
      logger_.severe("IOException when creating ServerSocket:  " + e);
      }
    }


  /**
   * Run the server.
   */
  public void run()
    {
    Socket socket = null;
    ClientHandler handler = null;

    if (serverSocket_ != null)
      while(true)
        {
        try
          {
          socket = serverSocket_.accept();
          handler = new ClientHandler(this,socket);
          new Thread(handler).start();
          }
        catch(IOException e)
          {
          logger_.severe("IOException on accept:  " + e);
          }
        }
    else
      logger_.severe("Server socket not initialized, terminating.");
    }


  /**
   * Add the specified class to the list of handlers.
   *
   * @param handlerName The name of the MessageHandler subtype to add.
   */
  public void addHandler(String handlerName) throws ClassNotFoundException
    {
    messageHandlerFactory_.addHandler(handlerName);
    }


  /**
   * ClientHandler is a class that encapsulates the processing of a
   * message received on a socket, using the Chain of Responsibility
   * pattern to select an appropriate message handling class.
   */
  class ClientHandler implements Runnable
    {
    private MessageBroker messageBroker_ = null;
    private Socket socket_ = null;

    /**
     * The full constructor for the ClientHandler class.
     *
     * @param messageBroker The MessageBroker that is constructing this
     *                      ClientHandler instance.
     * @param socket The socket to read from.
     */
    public ClientHandler(MessageBroker messageBroker,Socket socket)
      {
      messageBroker_ = messageBroker;
      socket_ = socket;
      }


    /**
     * Read and process the message.
     */
    public void run()
      {
      try
        {
        BufferedReader reader = new BufferedReader(
          new InputStreamReader(socket_.getInputStream()));
        String message = reader.readLine();
        logger_.info(message);

        MessageHandler handler
          = messageHandlerFactory_.getMessageHandler(message);
        if (handler != null)
          handler.handleMessage(
            new MessageHandlerContext(messageBroker_,
                                      socket_.getInputStream(),
                                      socket_.getOutputStream()));
        else
          logger_.severe("No message handler foound:  " + message);

        socket_.close();
        }
      catch(IOException e)
        {
        logger_.severe("IOException on read:  " + e);
        }
      }
    }  // end ClientHandler


  /**
   * A test harness for the MessageBroker class.
   *
   * @param args The command line arguments passed in.
   */
  public static void main(String args[])
    {
    if (args.length <= 1)
      {
      MessageBroker broker = null;

      if (args.length == 0)
        broker = new MessageBroker();
      else
        broker = new MessageBroker(Integer.parseInt(args[0]));

      try
        {
        broker.addHandler(
          "org.softwarematters.example.MessageBroker.AddHandlerMessageHandler");
        broker.run();
        }
      catch (ClassNotFoundException e)
        {
        System.out.println("Unable to load handler:  " + e.toString());
        }
      }
    else
      System.out.println(
        "usage:  java " + MessageBroker.class.getName() + " <port>");
    }  // end MessageBroker::main(String[])
}  // end MessageBroker

