org.coos.messaging.transport.NioTCPTransportManager Maven / Gradle / Ivy
/**
* COOS - Connected Objects Operating System (www.connectedobjects.org).
*
* Copyright (C) 2009 Telenor ASA and Tellu AS. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 program. If not, see .
*
* You may also contact one of the following for additional information:
* Telenor ASA, Snaroyveien 30, N-1331 Fornebu, Norway (www.telenor.no)
* Tellu AS, Hagalokkveien 13, N-1383 Asker, Norway (www.tellu.no)
*/
package org.coos.messaging.transport;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.coos.messaging.LinkManager;
import org.coos.messaging.util.Log;
import org.coos.messaging.util.LogFactory;
/**
* Nio tcp server implementation.
* @author Morten Versvik, Tellu AS
*
*/
public class NioTCPTransportManager extends DefaultChannelServer {
private static final String PROPERTY_LISTEN_PORT = "port";
private int listenPort = 15666;
private ServerSocket serverSocket;
private Map transports = new HashMap();
private static final Log logger = LogFactory.getLog(NioTCPTransportManager.class.getName());
private boolean running = true;
private Selector selector;
private List readyWriters = new ArrayList(100);
private List emptyWriters = new ArrayList(100);
private Object lock = "";
public NioTCPTransportManager() {
}
public NioTCPTransportManager(int listenPort, LinkManager linkManager) {
this.listenPort = listenPort;
setLinkManager(linkManager);
}
void readyWrite(SocketChannel sc) {
synchronized (lock) {
readyWriters.add(sc);
}
selector.wakeup();
}
void doneWrite(SocketChannel sc) {
synchronized (lock) {
emptyWriters.add(sc);
}
}
public int getListenPort() {
return listenPort;
}
ServerSocketChannel ssc;
public void start() throws Exception {
if (properties.get(PROPERTY_LISTEN_PORT) != null) {
listenPort = Integer.valueOf((String) properties.get(PROPERTY_LISTEN_PORT));
}
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
InetSocketAddress isa = new InetSocketAddress(listenPort);
ssc.socket().bind(isa, 100);
listenPort = isa.getPort();
logger.info(" Listening on port " + listenPort);
new Thread(new WorkerThread()).start();
}
public void stop() throws Exception {
if (running) {
running = false;
serverSocket.close();
stopTransports();
ssc.close();
}
}
public void stopTransports() throws Exception {
for (SocketChannel sc : transports.keySet()) {
sc.close();
}
transports.clear();
}
public void socketConnected(SocketChannel sc) {
logger.debug("Establishing transport to " + sc.socket().getInetAddress());
try {
// We should reduce the size of the TCP buffers or else we will
// easily run out of memory when accepting several thousands of
// connections
sc.socket().setReceiveBufferSize(2048);
sc.socket().setSendBufferSize(2048);
sc.configureBlocking(false);
NioTCPTransport ntt = new NioTCPTransport(this, selector, sc);
initializeChannel(ntt);
transports.put(sc, ntt);
} catch (IOException e) {
logger.error("Exception ignored", e);
}
}
public void socketDisconnected(SocketChannel sc) {
transports.remove(sc);
logger.debug("Closing transport to " + sc.socket().getInetAddress());
}
class WorkerThread implements Runnable {
WorkerThread() {
}
@Override
public void run() {
try {
selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e1) {
logger.error("Error opening selector!", e1);
}
while (running) {
try {
int selected = 0;
// Blocks waiting for events or selector.wakeup() when message is added to mailbox
selected = selector.select();
if (selected > 0) {
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) { // Add new connection
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socket = ssc.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
socketConnected(socket);
} else if (key.isReadable()) {
NioTCPTransport ntt = transports.get(key.channel());
try {
ntt.decodeFromSocket();
} catch (IOException e) {
logger.error("IOException " + e.toString() + " from socketchannel: " + key.channel());
}
}
if (key.isValid() && key.isWritable()) {
NioTCPTransport ntt = transports.get(key.channel());
ntt.handleWrite();
}
}
}
// Add/remove event notifications for writing to connections
synchronized (lock) {
for (SocketChannel sc : emptyWriters) {
SelectionKey sk = sc.keyFor(selector);
if (sk != null)
sk.interestOps(SelectionKey.OP_READ);
}
emptyWriters.clear();
for (SocketChannel sc : readyWriters) {
SelectionKey sk = sc.keyFor(selector);
if (sk != null)
sk.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
readyWriters.clear();
}
} catch (IOException e) {
logger.error("IOException ignored.", e);
e.printStackTrace();
} catch (Exception e) {
logger.error("Exception ignored.", e);
}
}
}
}
}