Package bgu.bio.com.reactor

Source Code of bgu.bio.com.reactor.Reactor

package bgu.bio.com.reactor;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import bgu.bio.com.protocol.ServerProtocolFactory;
import bgu.bio.io.file.properties.PropertiesUtils;

/**
* An implementation of the Reactor pattern.
*/
public class Reactor implements Runnable {

  private static final Logger logger = Logger.getLogger("bio.reactor");

  private final int _port;

  private final int _poolSize;

  private final ServerProtocolFactory _protocolFactory;

  private final TokenizerFactory _tokenizerFactory;

  private volatile boolean _shouldRun = true;

  private ReactorData _data;

  /**
   * Creates a new Reactor
   *
   * @param poolSize
   *            the number of WorkerThreads to include in the ThreadPool
   * @param port
   *            the port to bind the Reactor to
   * @param protocol
   *            the protocol factory to work with
   * @param tokenizer
   *            the tokenizer factory to work with
   * @throws IOException
   *             if some I/O problems arise during connection
   */
  public Reactor(int port, int poolSize, ServerProtocolFactory protocol,
      TokenizerFactory tokenizer) {
    _port = port;
    _poolSize = poolSize;
    _protocolFactory = protocol;
    _tokenizerFactory = tokenizer;
  }
 
  /**
   * Instantiates a new reactor with a UTF-8 char set.
   *
   * @param port the port
   * @param poolSize the pool size
   * @param protocol the protocol
   */
  public Reactor(int port, int poolSize, ServerProtocolFactory protocol){
    final Charset charset = Charset.forName("UTF-8");
    TokenizerFactory tokenizerMaker = new TokenizerFactory() {
      public StringMessageTokenizer create() {
        return new FixedSeparatorMessageTokenizer("\n", charset);
      }
    };
   
    _port = port;
    _poolSize = poolSize;
    _protocolFactory = protocol;
    _tokenizerFactory = tokenizerMaker;
  }

  /**
   * Create a non-blocking server socket channel and bind to to the Reactor
   * port
   */
  private ServerSocketChannel createServerSocket(int port) throws IOException {
    try {
      ServerSocketChannel ssChannel = ServerSocketChannel.open();
      ssChannel.configureBlocking(false);
      ssChannel.socket().bind(new InetSocketAddress(port));
      return ssChannel;
    } catch (IOException e) {
      logger.severe("Port " + port + " is busy");
      throw e;
    }
  }

  /**
   * Main operation of the Reactor:
   * <UL>
   * <LI>Uses the <CODE>Selector.select()</CODE> method to find new requests
   * from clients
   * <LI>For each request in the selection set:
   * <UL>
   * If it is <B>acceptable</B>, use the ConnectionAcceptor to accept it,
   * create a new ConnectionHandler for it register it to the Selector
   * <LI>If it is <B>readable</B>, use the ConnectionHandler to read it,
   * extract messages and insert them to the ThreadPool
   * </UL>
   */
  public void run() {
    // Create & start the ThreadPool
    ExecutorService executor = Executors.newFixedThreadPool(_poolSize);
    Selector selector = null;
    logger.setLevel(Level.SEVERE);
    ServerSocketChannel ssChannel = null;

    try {
      selector = Selector.open();
      ssChannel = createServerSocket(_port);
    } catch (IOException e) {
      logger.info("cannot create the selector -- server socket is busy?");
      return;
    }

    _data = new ReactorData(executor, selector, _protocolFactory,
        _tokenizerFactory);
    ConnectionAcceptor connectionAcceptor = new ConnectionAcceptor(
        ssChannel, _data);

    // Bind the server socket channel to the selector, with the new
    // acceptor as attachment

    try {
      ssChannel.register(selector, SelectionKey.OP_ACCEPT,
          connectionAcceptor);
    } catch (ClosedChannelException e) {
      logger.info("server channel seems to be closed!");
      return;
    }
    logger.info("Reactor is ready on port " + getPort());
    while (_shouldRun && selector.isOpen()) {
      // Wait for an event
      try {
        selector.select();
      } catch (IOException e) {
        logger.info("trouble with selector: " + e.getMessage());
        continue;
      }

      // Get list of selection keys with pending events
      Iterator<SelectionKey> it = selector.selectedKeys().iterator();

      // Process each key
      while (it.hasNext()) {
        // Get the selection key
        SelectionKey selKey = it.next();

        // Remove it from the list to indicate that it is being
        // processed. it.remove removes the last item returned by next.
        it.remove();

        // Check if it's a connection request
        if (selKey.isValid() && selKey.isAcceptable()) {
          logger.info("Accepting a connection");
          ConnectionAcceptor acceptor = (ConnectionAcceptor) selKey
              .attachment();
          try {
            acceptor.accept();
          } catch (IOException e) {
            logger.info("problem accepting a new connection: "
                + e.getMessage());
          }
          continue;
        }
        // Check if a message has been sent
        if (selKey.isValid() && selKey.isReadable()) {
          ConnectionHandler handler = (ConnectionHandler) selKey
              .attachment();
          logger.info("Channel is ready for reading");
          handler.read();
        }
        // Check if there are messages to send
        if (selKey.isValid() && selKey.isWritable()) {
          ConnectionHandler handler = (ConnectionHandler) selKey
              .attachment();
          logger.info("Channel is ready for writing");
          handler.write();
        }
      }
    }
    stopReactor();
  }

  /**
   * Returns the listening port of the Reactor
   *
   * @return the listening port of the Reactor
   */
  public int getPort() {
    return _port;
  }

  /**
   * Stops the Reactor activity, including the Reactor thread and the Worker
   * Threads in the Thread Pool.
   */
  public synchronized void stopReactor() {
    if (!_shouldRun)
      return;
    _shouldRun = false;
    _data.getSelector().wakeup(); // Force select() to return
    _data.getExecutor().shutdown();
    try {
      _data.getExecutor().awaitTermination(2000, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      // Someone didn't have patience to wait for the executor pool to
      // close
      e.printStackTrace();
    }
  }

  public static void execute(int port, int poolSize,
      ServerProtocolFactory protocolMaker) {

    TokenizerFactory tokenizerMaker = new TokenizerFactory() {
      public StringMessageTokenizer create() {
        return new FixedSeparatorMessageTokenizer("\n", Charset
            .forName("UTF-8"));
      }
    };

    Reactor reactor = new Reactor(port, poolSize, protocolMaker,
        tokenizerMaker);
    Thread thread = new Thread(reactor);
    thread.start();
    logger.info("Reactor is ready on port " + reactor.getPort());
    try {
      thread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

 
  /**
   * Main program, used for demonstration purposes. Create and run a
   * Reactor-based server for the Echo protocol. Listening port number and
   * number of threads in the thread pool are read from the command line.
   */
  public static void main(final String args[]) {
    if (args.length != 1) {
      System.err.println("Usage: java Reactor <config file>");
      System.exit(1);
    }

    try {

      Properties props = new Properties();
      FileInputStream inStream = new FileInputStream(args[0]);
      props.load(inStream);
      inStream.close();

      ServerProtocolFactory protocolMaker = (ServerProtocolFactory) PropertiesUtils
          .instantiateFromProps(props.getProperty("protocol"),
              "protocol", props,
              ServerProtocolFactory.class);

      final Charset charset = Charset.forName(props.getProperty(
          "encoding", "UTF-8"));
      TokenizerFactory tokenizerMaker = new TokenizerFactory() {
        public StringMessageTokenizer create() {
          return new FixedSeparatorMessageTokenizer("\n", charset);
        }
      };

      int port = Integer.parseInt(props.getProperty("port", "57999"));
      int poolSize = Runtime.getRuntime().availableProcessors();
      if (props.containsKey("pool.size")){
        Integer.parseInt(props.getProperty("pool.size"));
      }

      Reactor reactor = new Reactor(port, poolSize, protocolMaker,
          tokenizerMaker);
      Thread thread = new Thread(reactor);
      thread.start();
      logger.info("Reactor is ready on port " + reactor.getPort());
      thread.join();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
TOP

Related Classes of bgu.bio.com.reactor.Reactor

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.