Java tutorial
/*********************************************************************************************************************** * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. **********************************************************************************************************************/ package eu.stratosphere.nephele.taskmanager.bytebuffered; import java.io.EOFException; 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.channels.SocketChannel; import java.util.ArrayDeque; import java.util.Iterator; import java.util.Queue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import eu.stratosphere.nephele.taskmanager.bufferprovider.BufferAvailabilityListener; import eu.stratosphere.nephele.taskmanager.transferenvelope.NoBufferAvailableException; import eu.stratosphere.util.StringUtils; public class IncomingConnectionThread extends Thread { private static final Log LOG = LogFactory.getLog(IncomingConnectionThread.class); private final ByteBufferedChannelManager byteBufferedChannelManager; private final Selector selector; private final Queue<SelectionKey> pendingReadEventSubscribeRequests = new ArrayDeque<SelectionKey>(); private final ServerSocketChannel listeningSocket; private static final class IncomingConnectionBufferAvailListener implements BufferAvailabilityListener { private final Queue<SelectionKey> pendingReadEventSubscribeRequests; private final SelectionKey key; private IncomingConnectionBufferAvailListener(final Queue<SelectionKey> pendingReadEventSubscribeRequests, final SelectionKey key) { this.pendingReadEventSubscribeRequests = pendingReadEventSubscribeRequests; this.key = key; } /** * {@inheritDoc} */ @Override public void bufferAvailable() { synchronized (this.pendingReadEventSubscribeRequests) { this.pendingReadEventSubscribeRequests.add(this.key); } } } public IncomingConnectionThread(ByteBufferedChannelManager byteBufferedChannelManager, boolean isListeningThread, InetSocketAddress listeningAddress) throws IOException { super("Incoming Connection Thread"); this.selector = Selector.open(); this.byteBufferedChannelManager = byteBufferedChannelManager; if (isListeningThread) { this.listeningSocket = ServerSocketChannel.open(); this.listeningSocket.configureBlocking(false); listeningSocket.register(this.selector, SelectionKey.OP_ACCEPT); this.listeningSocket.socket().bind(listeningAddress); LOG.debug("Listening on " + this.listeningSocket.socket().getLocalSocketAddress()); } else { this.listeningSocket = null; } } @Override public void run() { while (!this.isInterrupted()) { synchronized (this.pendingReadEventSubscribeRequests) { while (!this.pendingReadEventSubscribeRequests.isEmpty()) { final SelectionKey key = this.pendingReadEventSubscribeRequests.poll(); final IncomingConnection incomingConnection = (IncomingConnection) key.attachment(); final SocketChannel socketChannel = (SocketChannel) key.channel(); try { final SelectionKey newKey = socketChannel.register(this.selector, SelectionKey.OP_READ); newKey.attach(incomingConnection); } catch (ClosedChannelException e) { incomingConnection.reportTransmissionProblem(key, e); } } } try { this.selector.select(500); } catch (IOException e) { LOG.error(e); } final Iterator<SelectionKey> iter = this.selector.selectedKeys().iterator(); while (iter.hasNext()) { final SelectionKey key = iter.next(); iter.remove(); if (key.isValid()) { if (key.isReadable()) { doRead(key); } else if (key.isAcceptable()) { doAccept(key); } else { LOG.error("Unknown key: " + key); } } else { LOG.error("Received invalid key: " + key); } } } // Do cleanup, if necessary if (this.listeningSocket != null) { try { this.listeningSocket.close(); } catch (IOException ioe) { // Actually, we can ignore this exception LOG.debug(ioe); } } // Finally, close the selector try { this.selector.close(); } catch (IOException ioe) { LOG.debug(StringUtils.stringifyException(ioe)); } } private void doAccept(SelectionKey key) { SocketChannel clientSocket = null; try { clientSocket = this.listeningSocket.accept(); if (clientSocket == null) { LOG.error("Client socket is null"); return; } } catch (IOException ioe) { LOG.error(ioe); return; } final IncomingConnection incomingConnection = new IncomingConnection(this.byteBufferedChannelManager, clientSocket); SelectionKey clientKey = null; try { clientSocket.configureBlocking(false); clientKey = clientSocket.register(this.selector, SelectionKey.OP_READ); clientKey.attach(incomingConnection); } catch (IOException ioe) { incomingConnection.reportTransmissionProblem(clientKey, ioe); } } private void doRead(SelectionKey key) { final IncomingConnection incomingConnection = (IncomingConnection) key.attachment(); try { incomingConnection.read(); } catch (EOFException eof) { if (incomingConnection.isCloseUnexpected()) { final SocketChannel socketChannel = (SocketChannel) key.channel(); LOG.error("Connection from " + socketChannel.socket().getRemoteSocketAddress() + " was closed unexpectedly"); incomingConnection.reportTransmissionProblem(key, eof); } else { incomingConnection.closeConnection(key); } } catch (IOException ioe) { incomingConnection.reportTransmissionProblem(key, ioe); } catch (InterruptedException e) { // Nothing to do here } catch (NoBufferAvailableException e) { // There are no buffers available, unsubscribe from read event final SocketChannel socketChannel = (SocketChannel) key.channel(); try { final SelectionKey newKey = socketChannel.register(this.selector, 0); newKey.attach(incomingConnection); } catch (ClosedChannelException e1) { incomingConnection.reportTransmissionProblem(key, e1); } final BufferAvailabilityListener bal = new IncomingConnectionBufferAvailListener( this.pendingReadEventSubscribeRequests, key); if (!e.getBufferProvider().registerBufferAvailabilityListener(bal)) { // In the meantime, a buffer has become available again, subscribe to read event again try { final SelectionKey newKey = socketChannel.register(this.selector, SelectionKey.OP_READ); newKey.attach(incomingConnection); } catch (ClosedChannelException e1) { incomingConnection.reportTransmissionProblem(key, e1); } } } } }