org.xsocket.Dispatcher Maven / Gradle / Ivy
/*
* Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xsocket.org/
*/
package org.xsocket;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* implementation of the {@link IDispatcher}
* All dispatcher methods are thread save.
*
*
This is a xSocket internal class and subject to change
*
* @author [email protected]
*/
public class Dispatcher implements IDispatcher {
private static final Logger LOG = Logger.getLogger(Dispatcher.class.getName());
private static final int SELECTOR_TIMEOUT = 5 * 1000;
private static final long TIMEOUT_SHUTDOWN_MILLIS = 5L * 1000L;
// is open flag
private volatile boolean isOpen = true;
// guard object for synchronizing
private final Object dispatcherThreadGuard = new Object();
// connection handling
private Selector selector = null;
// event handler
private IDispatcherEventHandler eventHandler = null;
// statistics
private long statisticsStartTime = System.currentTimeMillis();
private long handledRegistractions = 0;
private long handledReads = 0;
private long handledWrites = 0;
/**
* constructor
*
* @param eventHandler the assigned event handler
*/
public Dispatcher(IDispatcherEventHandler eventHandler) {
assert (eventHandler != null) : "null is not allowed for event handler ";
this.eventHandler = eventHandler;
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("dispatcher " + this.hashCode() + " has been created (eventHandler=" + eventHandler + ")");
}
try {
selector = Selector.open();
} catch (IOException ioe) {
String text = "exception occured while opening selector. Reason: " + ioe.toString();
LOG.severe(text);
throw new RuntimeException(text, ioe);
}
}
/**
* {@inheritDoc}
*/
public final IDispatcherEventHandler getEventHandler() {
return eventHandler;
}
/**
* {@inheritDoc}
*/
public void register(T handle, int ops) throws IOException {
assert (!handle.getChannel().isBlocking());
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("register handle " + handle);
}
synchronized (dispatcherThreadGuard) {
selector.wakeup();
handle.getChannel().register(selector, ops, handle);
eventHandler.onHandleRegisterEvent(handle);
}
handledRegistractions++;
}
/**
* {@inheritDoc}
*/
public void deregister(final T handle) throws IOException {
synchronized (dispatcherThreadGuard) {
selector.wakeup();
SelectionKey key = handle.getChannel().keyFor(selector);
if (key != null) {
if (key.isValid()) {
key.cancel();
}
}
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public final Set getRegistered() {
Set registered = new HashSet();
if (selector != null) {
SelectionKey[] selKeys = null;
synchronized (dispatcherThreadGuard) {
selector.wakeup();
Set keySet = selector.keys();
selKeys = keySet.toArray(new SelectionKey[keySet.size()]);
}
try {
for (SelectionKey key : selKeys) {
T handle = (T) key.attachment();
registered.add(handle);
}
} catch (Exception ignore) { }
}
return registered;
}
/**
* {@inheritDoc}
*/
public final void updateInterestSet(T handle, int ops) throws IOException {
SelectionKey key = handle.getChannel().keyFor(selector);
if (key != null) {
synchronized (dispatcherThreadGuard) {
if (key.isValid()) {
key.selector().wakeup();
// if (LOG.isLoggable(Level.FINER)) {
// LOG.finer("updating interest ops for " + handle + ". current value is " + printSelectionKeyValue(key.interestOps()));
// }
key.interestOps(ops);
// if (LOG.isLoggable(Level.FINE)) {
// LOG.fine("interest ops has been updated to " + printSelectionKeyValue(ops));
// }
} else {
throw new IOException("handle " + handle + " is invalid ");
}
}
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public final void run() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("selector listening ...");
}
while(isOpen) {
try {
// see http://developers.sun.com/learning/javaoneonline/2006/coreplatform/TS-1315.pdf
synchronized (dispatcherThreadGuard) {
/* suspend the dispatcher thread */
}
int eventCount = selector.select(SELECTOR_TIMEOUT);
// handle read write events
if (eventCount > 0) {
Set selectedEventKeys = selector.selectedKeys();
Iterator it = selectedEventKeys.iterator();
// handle read & write
while (it.hasNext()) {
SelectionKey eventKey = (SelectionKey) it.next();
it.remove();
T handle = (T) eventKey.attachment();
// read data
if (eventKey.isValid() && eventKey.isReadable()) {
// notify event handler
try {
eventHandler.onHandleReadableEvent(handle);
} catch (Exception e) {
LOG.warning("[" + Thread.currentThread().getName() + "] exception occured while handling readable event. Reason " + e.toString());
}
handledReads++;
}
// write data
if (eventKey.isValid() && eventKey.isWritable()) {
handledWrites++;
// notify event handler
try {
eventHandler.onHandleWriteableEvent(handle);
} catch (Exception e) {
LOG.warning("[" + Thread.currentThread().getName() + "] exception occured while handling writeable event. Reason " + e.toString());
}
}
}
}
} catch (Exception e) {
LOG.warning("[" + Thread.currentThread().getName() + "] exception occured while processing. Reason " + e.toString());
}
}
closeDispatcher();
}
@SuppressWarnings("unchecked")
private void closeDispatcher() {
LOG.fine("closing connections");
if (selector != null) {
try {
selector.close();
} catch (Exception e) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("error occured by close selector within tearDown " + e.toString());
}
}
}
}
/**
* {@inheritDoc}
*/
public void close() {
if (isOpen) {
if (selector != null) {
// initiate closing of open connections
Set openHandles = getRegistered();
int openConnections = openHandles.size();
for (T handle : openHandles) {
eventHandler.onDispatcherCloseEvent(handle);
}
// start closer thread
new Thread(new Closer(openConnections)).start();
}
}
}
/**
* check if this dispatcher is open
* @return true, if the disptacher is open
*/
public final boolean isOpen() {
return isOpen;
}
/**
* {@inheritDoc}
*/
public long getNumberOfHandledRegistrations() {
return handledRegistractions;
}
/**
* {@inheritDoc}
*/
public long getNumberOfHandledReads() {
return handledReads;
}
/**
* {@inheritDoc}
*/
public long getNumberOfHandledWrites() {
return handledWrites;
}
public void resetStatistics() {
statisticsStartTime = System.currentTimeMillis();
handledRegistractions = 0;
handledReads = 0;
handledWrites = 0;
}
protected long getStatisticsStartTime() {
return statisticsStartTime;
}
/*
private String printSelectionKeyValue(int key) {
StringBuilder sb = new StringBuilder();
if ((key & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
sb.append("OP_ACCEPT, ");
}
if ((key & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) {
sb.append("OP_CONNECT, ");
}
if ((key & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
sb.append("OP_WRITE, ");
}
if ((key & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
sb.append("OP_READ, ");
}
String txt = sb.toString();
txt = txt.trim();
if (txt.length() > 0) {
txt = txt.substring(0, txt.length() - 1);
}
return txt + " (" + key + ")";
}
*/
private class Closer implements Runnable {
private int openConnections = 0;
public Closer(int openConnections) {
this.openConnections = openConnections;
}
public void run() {
Thread.currentThread().setName("xDispatcherCloser");
long start = System.currentTimeMillis();
int terminatedConnections = 0;
do {
try {
Thread.sleep(100);
} catch (InterruptedException ignore) { }
if (System.currentTimeMillis() > (start + TIMEOUT_SHUTDOWN_MILLIS)) {
LOG.warning("shutdown timeout reached (" + DataConverter.toFormatedDuration(TIMEOUT_SHUTDOWN_MILLIS) + "). kill pending connections");
for (SelectionKey sk : selector.keys()) {
try {
terminatedConnections++;
sk.channel().close();
} catch (Exception ignore) { }
}
break;
}
} while (getRegistered().size() > 0);
isOpen = false;
// wake up selector, so that isRunning-loop can be terminated
selector.wakeup();
if ((openConnections > 0) || (terminatedConnections > 0)) {
if ((openConnections > 0) && (terminatedConnections > 0)) {
LOG.info((openConnections - terminatedConnections) + " connections has been closed properly, "
+ terminatedConnections + " connections has been terminate unclean");
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("dispatcher " + this.hashCode() + " has been closed (shutdown time = " + DataConverter.toFormatedDuration(System.currentTimeMillis() - start) + ")");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy