com.sun.grizzly.TCPSelectorHandler Maven / Gradle / Ivy
The newest version!
/* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can obtain * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. * Sun designates this particular file as subject to the "Classpath" exception * as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License * Header, with the fields enclosed by brackets [] replaced by your own * identifying information: "Portions Copyrighted [year] * [name of copyright owner]" * * Contributor(s): * * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. * */ package com.sun.grizzly; import com.sun.grizzly.async.AsyncQueueReader; import com.sun.grizzly.async.AsyncQueueReaderContextTask; import com.sun.grizzly.async.AsyncQueueWriter; import com.sun.grizzly.async.TCPAsyncQueueWriter; import com.sun.grizzly.async.AsyncQueueWriterContextTask; import com.sun.grizzly.async.TCPAsyncQueueReader; import com.sun.grizzly.util.CallbackHandlerSelectionKeyAttachment; import com.sun.grizzly.util.Cloner; import com.sun.grizzly.util.Copyable; import com.sun.grizzly.util.SelectionKeyAttachment; import com.sun.grizzly.util.SelectionKeyOP; import com.sun.grizzly.util.SelectionKeyOP.ConnectSelectionKeyOP; import com.sun.grizzly.util.State; import com.sun.grizzly.util.StateHolder; import java.io.IOException; import java.net.BindException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; /** * A SelectorHandler handles all java.nio.channels.Selector operations. * One or more instance of a Selector are handled by SelectorHandler. * The logic for processing of SelectionKey interest (OP_ACCEPT,OP_READ, etc.) * is usually defined using an instance of SelectorHandler. * * This class represents a TCP implementation of a SelectorHandler. * This class first bind a ServerSocketChannel to a TCP port and then start * waiting for NIO events. * * @author Jeanfrancois Arcand */ public class TCPSelectorHandler implements SelectorHandler { /** * The ConnectorInstanceHandler used to return a new or pooled * ConnectorHandler */ protected ConnectorInstanceHandler connectorInstanceHandler; /** * The list of {@link SelectionKeyOP} to register next time the * Selector.select is invoked. */ protected ConcurrentLinkedQueue
*/ protected CallableopToRegister; /** * The socket tcpDelay. * * Default value for tcpNoDelay is disabled (set to true). */ protected boolean tcpNoDelay = true; /** * The socket reuseAddress */ protected boolean reuseAddress = true; /** * The socket linger. */ protected int linger = -1; /** * The socket time out */ protected int socketTimeout = -1; protected Logger logger; /** * The server socket time out */ protected int serverTimeout = 0; /** * The inet address to use when binding. */ protected InetAddress inet; /** * The default TCP port. */ protected int port = 18888; /** * The ServerSocket instance. */ protected ServerSocket serverSocket; /** * The ServerSocketChannel. */ protected ServerSocketChannel serverSocketChannel; /** * The single Selector. */ protected Selector selector; /** * The Selector time out. */ protected long selectTimeout = 1000L; /** * Server socket backlog. */ protected int ssBackLog = 4096; /** * Is this used for client only or client/server operation. */ protected Role role = Role.CLIENT_SERVER; /** * The SelectionKeyHandler associated with this SelectorHandler. */ protected SelectionKeyHandler selectionKeyHandler; /** * The ProtocolChainInstanceHandler used by this instance. If not set, and instance * of the DefaultInstanceHandler will be created. */ protected ProtocolChainInstanceHandler instanceHandler; /** * The {@link Pipeline} used by this instance. If null - * {@link Controller}'s {@link Pipeline} will be used */ protected Pipeline pipeline; /** * {@link AsyncQueueWriter} */ protected AsyncQueueWriter asyncQueueWriter; /** * {@link AsyncQueueWriter} */ protected AsyncQueueReader asyncQueueReader; /** * Attributes, associated with the {@link SelectorHandler} instance */ protected Map attributes; /** * This {@link SelectorHandler} StateHolder, which is shared among * SelectorHandler and its clones */ protected StateHolder stateHolder = new StateHolder (true); /** * Flag, which shows whether shutdown was called for this {@link SelectorHandler} */ protected AtomicBoolean isShutDown = new AtomicBoolean(false); public TCPSelectorHandler(){ this(Role.CLIENT_SERVER); } /** * Create a TCPSelectorHandler only used with ConnectorHandler. * * @param isClient true if this SelectorHandler is only used * to handle ConnectorHandler. */ public TCPSelectorHandler(boolean isClient) { this(boolean2Role(isClient)); } /** * Create a TCPSelectorHandler only used with ConnectorHandler. * * @param role the TCPSelectorHandler {@link Role} */ public TCPSelectorHandler(Role role) { this.role = role; logger = Controller.logger(); } public void copyTo(Copyable copy) { TCPSelectorHandler copyHandler = (TCPSelectorHandler) copy; copyHandler.selector = selector; if (selectionKeyHandler != null) { copyHandler.setSelectionKeyHandler(Cloner.clone(selectionKeyHandler)); } copyHandler.attributes = attributes; copyHandler.selectTimeout = selectTimeout; copyHandler.serverTimeout = serverTimeout; copyHandler.inet = inet; copyHandler.port = port; copyHandler.ssBackLog = ssBackLog; copyHandler.tcpNoDelay = tcpNoDelay; copyHandler.linger = linger; copyHandler.socketTimeout = socketTimeout; copyHandler.logger = logger; copyHandler.reuseAddress = reuseAddress; copyHandler.connectorInstanceHandler = connectorInstanceHandler; copyHandler.stateHolder = stateHolder; } /** * Return the set of SelectionKey registered on this Selector. */ public Set keys(){ if (selector != null){ return selector.keys(); } else { throw new IllegalStateException("Selector is not created!"); } } /** * Is the Selector open. */ public boolean isOpen(){ if (selector != null){ return selector.isOpen(); } else { return false; } } /** * Before invoking {@link Selector#select}, make sure the {@link ServerSocketChannel} * has been created. If true, then register all {@link SelectionKey} to the {@link Selector}. * @param ctx {@link Context} */ public void preSelect(Context ctx) throws IOException { initOpRegistriesIfRequired(); if (asyncQueueReader == null) { asyncQueueReader = new TCPAsyncQueueReader(this); } if (asyncQueueWriter == null) { asyncQueueWriter = new TCPAsyncQueueWriter(this); } if (selector == null){ try { isShutDown.set(false); connectorInstanceHandler = new ConnectorInstanceHandler. ConcurrentQueueDelegateCIH( getConnectorInstanceHandlerDelegate()); // Create the socket listener selector = Selector.open(); if (role != Role.CLIENT){ serverSocketChannel = ServerSocketChannel.open(); serverSocket = serverSocketChannel.socket(); serverSocket.setReuseAddress(reuseAddress); if ( inet == null){ serverSocket.bind(new InetSocketAddress(port),ssBackLog); } else { serverSocket.bind(new InetSocketAddress(inet,port),ssBackLog); } serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } ctx.getController().notifyReady(); } catch (SocketException ex){ throw new BindException(ex.getMessage() + ": " + port + "=" + this); } if (role != Role.CLIENT){ serverSocket.setSoTimeout(serverTimeout); } } else { processPendingOperations(ctx); } } protected void processPendingOperations(Context ctx) throws IOException { if (!opToRegister.isEmpty()) { SelectionKeyOP operation; Iterator opIterator = opToRegister.iterator(); while(opIterator.hasNext()) { operation = opIterator.next(); opIterator.remove(); if ((operation.getOp() & SelectionKey.OP_CONNECT) != 0) { onConnectOp(ctx, (SelectionKeyOP.ConnectSelectionKeyOP) operation); } else { if ((operation.getOp() & SelectionKey.OP_READ) != 0) { onReadOp(operation); } if ((operation.getOp() & SelectionKey.OP_WRITE) != 0) { onWriteOp(operation); } } SelectionKeyOP.releaseSelectionKeyOP(operation); } } } /** * Handle new OP_READ ops. */ protected void onReadOp(SelectionKeyOP selectionKeyOp) throws ClosedChannelException { SelectionKey key = selectionKeyOp.getSelectionKey(); if (key != null) { selectionKeyHandler.register(key, SelectionKey.OP_READ); } else { selectionKeyHandler.register(selectionKeyOp.getChannel(), SelectionKey.OP_READ); } } /** * Handle new OP_WRITE ops. */ protected void onWriteOp(SelectionKeyOP selectionKeyOp) throws ClosedChannelException { SelectionKey key = selectionKeyOp.getSelectionKey(); if (key != null) { selectionKeyHandler.register(key, SelectionKey.OP_WRITE); } else { selectionKeyHandler.register(selectionKeyOp.getChannel(), SelectionKey.OP_WRITE); } } /** * Handle new OP_CONNECT ops. */ protected void onConnectOp(Context ctx, SelectionKeyOP.ConnectSelectionKeyOP selectionKeyOp) throws IOException { SocketAddress remoteAddress = selectionKeyOp.getRemoteAddress(); SocketAddress localAddress = selectionKeyOp.getLocalAddress(); CallbackHandler callbackHandler = selectionKeyOp.getCallbackHandler(); SocketChannel socketChannel = SocketChannel.open(); socketChannel.socket().setReuseAddress(reuseAddress); if (localAddress != null) { socketChannel.socket().bind(localAddress); } socketChannel.configureBlocking(false); SelectionKey key = socketChannel.register(selector, SelectionKey.OP_CONNECT); key.attach(CallbackHandlerSelectionKeyAttachment.create(key, callbackHandler)); boolean isConnected; try { isConnected = socketChannel.connect(remoteAddress); } catch(Exception e) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Exception occured when tried to connect socket", e); } // set isConnected to true to let callback handler to know about the problem happened isConnected = true; } // if channel was connected immediately or exception occured if (isConnected) { onConnectInterest(key, ctx); } } /** * Execute the Selector.select(...) operations. * @param ctx {@link Context} * @return {@link Set} of {@link SelectionKey} */ public Set select(Context ctx) throws IOException{ selector.select(selectTimeout); return selector.selectedKeys(); } /** * Invoked after Selector.select(). * @param ctx {@link Context} */ public void postSelect(Context ctx) { Set readyKeys = keys(); if (readyKeys.isEmpty()){ return; } if (isOpen()) { selectionKeyHandler.expire(readyKeys.iterator()); } } /** * Register a SelectionKey to this Selector. */ public void register(SelectionKey key, int ops) { if (key == null) { throw new NullPointerException("SelectionKey parameter is null"); } SelectionKeyOP keyOP = SelectionKeyOP.aquireSelectionKeyOP(ops); keyOP.setSelectionKey(key); keyOP.setOp(ops); initOpRegistriesIfRequired(); opToRegister.offer(keyOP); selector.wakeup(); } public void register(SelectableChannel channel, int ops) { if (channel == null) { throw new NullPointerException("SelectableChannel parameter is null"); } SelectionKeyOP keyOP = SelectionKeyOP.aquireSelectionKeyOP(ops); keyOP.setChannel(channel); keyOP.setOp(ops); initOpRegistriesIfRequired(); opToRegister.offer(keyOP); selector.wakeup(); } /** * Register a CallBackHandler to this Selector. * * @param remoteAddress remote address to connect * @param localAddress local address to bin * @param callBackHandler {@link CallbackHandler} * @throws java.io.IOException */ protected void connect(SocketAddress remoteAddress, SocketAddress localAddress, CallbackHandler callBackHandler) throws IOException { SelectionKeyOP.ConnectSelectionKeyOP keyOP = (ConnectSelectionKeyOP) SelectionKeyOP.aquireSelectionKeyOP(SelectionKey.OP_CONNECT); keyOP.setOp(SelectionKey.OP_CONNECT); keyOP.setRemoteAddress(remoteAddress); keyOP.setLocalAddress(localAddress); keyOP.setCallbackHandler(callBackHandler); initOpRegistriesIfRequired(); opToRegister.offer(keyOP); selector.wakeup(); } /** * {@inheritDoc} */ public void pause() { stateHolder.setState(State.PAUSED); } /** * {@inheritDoc} */ public void resume() { if (!State.PAUSED.equals(stateHolder.getState(false))) { throw new IllegalStateException("SelectorHandler is not in PAUSED state, but: " + stateHolder.getState(false)); } stateHolder.setState(State.STARTED); } /** * {@inheritDoc} */ public StateHolder getStateHolder() { return stateHolder; } /** * Shuntdown this instance by closing its Selector and associated channels. */ public void shutdown() { // If shutdown was called for this SelectorHandler if (isShutDown.getAndSet(true)) return; stateHolder.setState(State.STOPPED); if (selector != null) { try { for (SelectionKey selectionKey : selector.keys()) { selectionKeyHandler.close(selectionKey); } } catch (ClosedSelectorException e) { // If Selector is already closed - OK } catch (ConcurrentModificationException e) { // Someone else works with keys. Create copy Object[] keys = selector.keys().toArray(); for (Object selectionKey : keys) { selectionKeyHandler.close((SelectionKey) selectionKey); } } } try{ if (serverSocket != null) serverSocket.close(); } catch (Throwable ex){ Controller.logger().log(Level.SEVERE, "serverSocket.close",ex); } try{ if (serverSocketChannel != null) serverSocketChannel.close(); } catch (Throwable ex){ Controller.logger().log(Level.SEVERE, "serverSocketChannel.close",ex); } try{ if (selector != null) selector.close(); } catch (Throwable ex){ Controller.logger().log(Level.SEVERE, "selector.close",ex); } if (asyncQueueReader != null) { asyncQueueReader.close(); asyncQueueReader = null; } if (asyncQueueWriter != null) { asyncQueueWriter.close(); asyncQueueWriter = null; } attributes = null; } /** * {@inheritDoc} */ public SelectableChannel acceptWithoutRegistration(SelectionKey key) throws IOException { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept(); return channel; } /** * Handle OP_ACCEPT. * @param ctx {@link Context} * @return always returns false */ public boolean onAcceptInterest(SelectionKey key, Context ctx) throws IOException{ SelectableChannel channel = acceptWithoutRegistration(key); if (channel != null) { configureChannel(channel); SelectionKey readKey = channel.register(selector, SelectionKey.OP_READ); readKey.attach(System.currentTimeMillis()); } return false; } /** * Handle OP_READ. * @param ctx {@link Context} * @param key {@link SelectionKey} * @return false if handled by a {@link CallbackHandler}, otherwise true */ public boolean onReadInterest(final SelectionKey key,final Context ctx) throws IOException{ // disable OP_READ on key before doing anything else key.interestOps(key.interestOps() & (~SelectionKey.OP_READ)); Object attach = SelectionKeyAttachment.getAttachment(key); if (asyncQueueReader.isAsyncQueueReaderEnabledFor(key)) { final Context context = pollContext(ctx, key, Context.OpType.OP_READ); invokeAsyncQueueReader(context); return false; } else if (attach instanceof CallbackHandler){ final Context context = pollContext(ctx, key, Context.OpType.OP_READ); invokeCallbackHandler((CallbackHandler) attach, context); return false; } else { return true; } } /** * Handle OP_WRITE. * * @param key {@link SelectionKey} * @param ctx {@link Context} */ public boolean onWriteInterest(final SelectionKey key,final Context ctx) throws IOException{ // disable OP_WRITE on key before doing anything else key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE)); Object attach; if (asyncQueueWriter.hasReadyAsyncWriteData(key)) { final Context context = pollContext(ctx, key, Context.OpType.OP_WRITE); invokeAsyncQueueWriter(context); return false; } else if ((attach = SelectionKeyAttachment.getAttachment(key)) instanceof CallbackHandler){ final Context context = pollContext(ctx, key, Context.OpType.OP_WRITE); invokeCallbackHandler((CallbackHandler) attach, context); return false; } else { return true; } } /** * Handle OP_CONNECT. * @param key {@link SelectionKey} * @param ctx {@link Context} */ public boolean onConnectInterest(final SelectionKey key, Context ctx) throws IOException{ try { // disable OP_CONNECT on key before doing anything else key.interestOps(key.interestOps() & (~SelectionKey.OP_CONNECT)); // No OP_READ nor OP_WRITE allowed yet. key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE)); key.interestOps(key.interestOps() & (~SelectionKey.OP_READ)); } catch(CancelledKeyException e) { // Even if key was cancelled - we need to notify CallBackHandler if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "CancelledKeyException occured when tried to change key interests", e); } } Object attach = SelectionKeyAttachment.getAttachment(key); if (attach instanceof CallbackHandler){ final Context context = pollContext(ctx, key, Context.OpType.OP_CONNECT); invokeCallbackHandler((CallbackHandler) attach, context); } return false; } /** * Invoke a CallbackHandler via a Context instance. * @param context {@link Context} * @throws java.io.IOException */ protected void invokeCallbackHandler(CallbackHandler callbackHandler, Context context) throws IOException { IOEvent ioEvent = new IOEvent.DefaultIOEvent (context); context.setIOEvent(ioEvent); // Added because of incompatibility with Grizzly 1.6.0 context.setSelectorHandler(this); try { CallbackHandlerContextTask task = CallbackHandlerContextTask.poll(); task.setCallBackHandler(callbackHandler); boolean isRunInSeparateThread = true; if (callbackHandler instanceof CallbackHandlerDescriptor) { isRunInSeparateThread = ((CallbackHandlerDescriptor) callbackHandler). isRunInSeparateThread(context.getCurrentOpType()); } context.execute(task, isRunInSeparateThread); } catch (PipelineFullException ex) { throw new IOException(ex.getMessage()); } } /** * Invoke a {@link AsyncQueueReader} * @param context {@link Context} * @throws java.io.IOException */ protected void invokeAsyncQueueReader(Context context) throws IOException { AsyncQueueReaderContextTask task = AsyncQueueReaderContextTask.poll(); task.setAsyncQueueReader(asyncQueueReader); try { context.execute(task); } catch (PipelineFullException ex) { throw new IOException(ex.getMessage()); } } /** * Invoke a {@link AsyncQueueWriter} * @param context {@link Context} * @throws java.io.IOException */ protected void invokeAsyncQueueWriter(Context context) throws IOException { AsyncQueueWriterContextTask task = AsyncQueueWriterContextTask.poll(); task.setAsyncQueueWriter(asyncQueueWriter); try { context.execute(task); } catch (PipelineFullException ex) { throw new IOException(ex.getMessage()); } } /** * Return an instance of the default {@link ConnectorHandler}, * which is the {@link TCPConnectorHandler} * @return {@link ConnectorHandler} */ public ConnectorHandler acquireConnectorHandler(){ if (selector == null || !selector.isOpen()){ throw new IllegalStateException("SelectorHandler not yet started"); } ConnectorHandler connectorHandler = connectorInstanceHandler.acquire(); connectorHandler.setController(Controller.getHandlerController(this)); return connectorHandler; } /** * Release a ConnectorHandler. */ public void releaseConnectorHandler(ConnectorHandler connectorHandler){ connectorInstanceHandler.release(connectorHandler); } /** * A token decribing the protocol supported by an implementation of this * interface */ public Controller.Protocol protocol(){ return Controller.Protocol.TCP; } // ------------------------------------------------------ Utils ----------// /** * Initializes {@link SelectionKey} operation registries */ protected void initOpRegistriesIfRequired() { if (opToRegister == null){ opToRegister = new ConcurrentLinkedQueue (); } } /** * {@inheritDoc} */ public void configureChannel(SelectableChannel channel) throws IOException{ Socket socket = ((SocketChannel) channel).socket(); channel.configureBlocking(false); if (!channel.isOpen()){ return; } try{ if(socketTimeout >= 0 ) { socket.setSoTimeout(socketTimeout); } } catch (SocketException ex){ if (logger.isLoggable(Level.FINE)){ logger.log(Level.FINE, "setSoTimeout exception ",ex); } } try{ if(linger >= 0 ) { socket.setSoLinger( true, linger); } } catch (SocketException ex){ if (logger.isLoggable(Level.FINE)){ logger.log(Level.FINE, "setSoLinger exception ",ex); } } try{ socket.setTcpNoDelay(tcpNoDelay); } catch (SocketException ex){ if (logger.isLoggable(Level.FINE)){ logger.log(Level.FINE, "setTcpNoDelay exception ",ex); } } try{ socket.setReuseAddress(reuseAddress); } catch (SocketException ex){ if (logger.isLoggable(Level.FINE)){ logger.log(Level.FINE, "setReuseAddress exception ",ex); } } } // ------------------------------------------------------ Properties -----// public final Selector getSelector() { return selector; } public final void setSelector(Selector selector) { this.selector = selector; } /** * {@inheritDoc} */ public AsyncQueueReader getAsyncQueueReader() { return asyncQueueReader; } /** * {@inheritDoc} */ public AsyncQueueWriter getAsyncQueueWriter() { return asyncQueueWriter; } public long getSelectTimeout() { return selectTimeout; } public void setSelectTimeout(long selectTimeout) { this.selectTimeout = selectTimeout; } public int getServerTimeout() { return serverTimeout; } public void setServerTimeout(int serverTimeout) { this.serverTimeout = serverTimeout; } public InetAddress getInet() { return inet; } public void setInet(InetAddress inet) { this.inet = inet; } /** * Gets this {@link SelectorHandler} current role. * TCPSelectorHandler could act as client, which corresponds to * {@link Role#CLIENT} or client-server, which corresponds * to the {@link Role#CLIENT_SERVER} * * @return the {@link Role} */ public Role getRole() { return role; } /** * Sets this {@link SelectorHandler} current role. * TCPSelectorHandler could act as client, which corresponds to * {@link Role#CLIENT} or client-server, which corresponds * to the {@link Role#CLIENT_SERVER} * * @param role the {@link Role} */ public void setRole(Role role) { this.role = role; } /** * Returns port number {@link SelectorHandler} is listening on * Similar to getPort()
, but getting port number directly from * connection ({@link ServerSocket}, {@link DatagramSocket}). * So if default port number 0 was set during initialization, thengetPort()
* will return 0, but getPortLowLevel() will * return port number assigned by OS. * * @return port number or -1 if {@link SelectorHandler} was not initialized for accepting connections. */ public int getPortLowLevel() { if (serverSocket != null) { return serverSocket.getLocalPort(); } return -1; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getSsBackLog() { return ssBackLog; } public void setSsBackLog(int ssBackLog) { this.ssBackLog = ssBackLog; } /** * Return the tcpNoDelay value used by the underlying accepted Sockets. * * Also see setTcpNoDelay(boolean tcpNoDelay) */ public boolean isTcpNoDelay() { return tcpNoDelay; } /** * Enable (true) or disable (false) the underlying Socket's * tcpNoDelay. * * Default value for tcpNoDelay is enabled (set to true), as according to * the performance tests, it performs better for most cases. * * The Connector side should also set tcpNoDelay the same as it is set here * whenever possible. */ public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; } public int getLinger() { return linger; } public void setLinger(int linger) { this.linger = linger; } public int getSocketTimeout() { return socketTimeout; } public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; } public Logger getLogger() { return logger; } public void setLogger(Logger logger) { this.logger = logger; } public boolean isReuseAddress() { return reuseAddress; } public void setReuseAddress(boolean reuseAddress) { this.reuseAddress = reuseAddress; } /** * {@inheritDoc} */ public Pipeline pipeline(){ return pipeline; } /** * {@inheritDoc} */ public void setPipeline(Pipeline pipeline){ this.pipeline = pipeline; } /** * {@inheritDoc} */ public Class getPreferredSelectionKeyHandler() { return DefaultSelectionKeyHandler.class; } /** * Get the SelectionKeyHandler associated with this SelectorHandler. */ public SelectionKeyHandler getSelectionKeyHandler() { return selectionKeyHandler; } /** * Set SelectionKeyHandler associated with this SelectorHandler. */ public void setSelectionKeyHandler(SelectionKeyHandler selectionKeyHandler) { this.selectionKeyHandler = selectionKeyHandler; this.selectionKeyHandler.setSelectorHandler(this); } /** * Set the {@link ProtocolChainInstanceHandler} to use for * creating instance of {@link ProtocolChain}. */ public void setProtocolChainInstanceHandler(ProtocolChainInstanceHandler instanceHandler){ this.instanceHandler = instanceHandler; } /** * Return the {@link ProtocolChainInstanceHandler} */ public ProtocolChainInstanceHandler getProtocolChainInstanceHandler(){ return instanceHandler; } /** * {@inheritDoc} */ public void closeChannel(SelectableChannel channel) { // channel could be either SocketChannel or ServerSocketChannel if (channel instanceof SocketChannel) { Socket socket = ((SocketChannel) channel).socket(); try { if (!socket.isInputShutdown()) socket.shutdownInput(); } catch (IOException ex){ ; } try { if (!socket.isOutputShutdown()) socket.shutdownOutput(); } catch (IOException ex){ ; } try{ socket.close(); } catch (IOException ex){ ; } } try{ channel.close(); } catch (IOException ex){ ; // LOG ME } if (asyncQueueReader != null) { asyncQueueReader.onClose(channel); } if (asyncQueueWriter != null) { asyncQueueWriter.onClose(channel); } } /** * Polls {@link Context} from pool and initializes it. * * @param serverContext {@link Controller} context * @param key {@link SelectionKey} * @return {@link Context} */ protected Context pollContext(final Context serverContext, final SelectionKey key, final Context.OpType opType) { ProtocolChain protocolChain = instanceHandler != null ? instanceHandler.poll() : serverContext.getController().getProtocolChainInstanceHandler().poll(); final Context context = serverContext.getController().pollContext(key, opType); context.setSelectionKey(key); context.setSelectorHandler(this); context.setAsyncQueueReader(asyncQueueReader); context.setAsyncQueueWriter(asyncQueueWriter); context.setProtocolChain(protocolChain); return context; } //--------------- ConnectorInstanceHandler ----------------------------- /** * Returnfactory object, which knows how * to create {@link ConnectorInstanceHandler} corresponding to the protocol * @return factory getConnectorInstanceHandlerDelegate() { return new Callable () { public ConnectorHandler call() throws Exception { return new TCPConnectorHandler(); } }; } // ----------- AttributeHolder interface implementation ----------- // /** * Remove a key/value object. * Method is not thread safe * * @param key - name of an attribute * @return attribute which has been removed */ public Object removeAttribute(String key) { if (attributes == null) return null; return attributes.remove(key); } /** * Set a key/value object. * Method is not thread safe * * @param key - name of an attribute * @param value - value of named attribute */ public void setAttribute(String key, Object value) { if (attributes == null) { attributes = new HashMap (); } attributes.put(key, value); } /** * Return an object based on a key. * Method is not thread safe * * @param key - name of an attribute * @return - attribute value for the key, null if key * does not exist in attributes */ public Object getAttribute(String key) { if (attributes == null) return null; return attributes.get(key); } /** * Set a {@link Map} of attribute name/value pairs. * Old {@link AttributeHolder} values will not be available. * Later changes of this {@link Map} will lead to changes to the current * {@link AttributeHolder}. * * @param attributes - map of name/value pairs */ public void setAttributes(Map attributes) { this.attributes = attributes; } /** * Return a {@link Map} of attribute name/value pairs. * Updates, performed on the returned {@link Map} will be reflected in * this {@link AttributeHolder} * * @return - {@link Map} of attribute name/value pairs */ public Map getAttributes() { return attributes; } /** * Returns the {@link Role}, depending on isClient value * @param isClient true>tt>, if this SelectorHandler works in * the client mode, or false otherwise. * @return {@link Role} */ protected static Role boolean2Role(boolean isClient) { if (isClient) return Role.CLIENT; return Role.CLIENT_SERVER; } }