All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.grizzly.TCPConnectorHandler Maven / Gradle / Ivy

There is a newer version: 10.0-b28
Show 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.AsyncQueueDataProcessor;
import com.sun.grizzly.async.AsyncQueueReadable;
import com.sun.grizzly.async.AsyncReadCallbackHandler;
import com.sun.grizzly.async.AsyncReadCondition;
import com.sun.grizzly.async.AsyncWriteCallbackHandler;
import com.sun.grizzly.async.AsyncQueueWritable;
import com.sun.grizzly.util.InputReader;
import com.sun.grizzly.util.OutputWriter;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
/**
 * Non blocking TCP Connector Handler. The recommended way to use this class
 * is by creating an external Controller and share the same SelectorHandler
 * instance.
 * 

* Recommended * ----------- *


 * Controller controller = new Controller();
 * // new TCPSelectorHandler(true) means the Selector will be used only
 * // for client operation (OP_READ, OP_WRITE, OP_CONNECT).
 * TCPSelectorHandler tcpSelectorHandler = new TCPSelectorHandler(true);
 * controller.setSelectorHandler(tcpSelectorHandler);
 * TCPConnectorHandler tcpConnectorHandler = new TCPConnectorHandler();
 * tcpConnectorHandler.connect(localhost,port, new CallbackHandler(){...},
 *                             tcpSelectorHandler);
 * TCPConnectorHandler tcpConnectorHandler2 = new TCPConnectorHandler();
 * tcpConnectorHandler2.connect(localhost,port, new CallbackHandler(){...},
 *                             tcpSelectorHandler);
 * 

* Not recommended (but still works) * --------------------------------- *


 * TCPConnectorHandler tcpConnectorHandler = new TCPConnectorHandler();
 * tcpConnectorHandler.connect(localhost,port);
 * 

* * Internally, a new Controller will be created everytime connect(localhost,port) * is invoked, which has an impact on performance. * * @author Jeanfrancois Arcand */ public class TCPConnectorHandler implements ConnectorHandler, AsyncQueueWritable, AsyncQueueReadable { /** * default TCP channel connection timeout in milliseconds */ private static final int DEFAULT_CONNECTION_TIMEOUT = 30 * 1000; /** * The underlying TCPSelectorHandler used to mange SelectionKeys. */ protected TCPSelectorHandler selectorHandler; /** * A {@link CallbackHandler} handler invoked by the TCPSelectorHandler * when a non blocking operation is ready to be processed. */ private CallbackHandler callbackHandler; /** * A blocking {@link InputStream} that use a pool of Selector * to execute a blocking read operation. */ private InputReader inputStream; /** * The connection's SocketChannel. */ private SocketChannel socketChannel; /** * Is the connection established. */ private volatile boolean isConnected; /** * The internal Controller used (in case not specified). */ private Controller controller; /** * IsConnected Latch related */ private CountDownLatch isConnectedLatch; /** * Are we creating a controller every run. */ private boolean isStandalone = false; /** * The socket tcpDelay. * * Default value for tcpNoDelay. */ protected boolean tcpNoDelay = true; /** * The socket reuseAddress */ protected boolean reuseAddress = true; /** * The socket linger. */ protected int linger = -1; /** * Connection timeout is milliseconds */ protected int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; /** * Connect to hostname:port. When an aysnchronous event happens (e.g * OP_READ or OP_WRITE), the {@link Controller} will invoke * the CallBackHandler. * @param remoteAddress remote address to connect * @param callbackHandler the handler invoked by its associated {@link SelectorHandler} when * a non blocking operation is ready to be handled. When null, all * read and write operation will be delegated to the default * {@link ProtocolChain} and its list of {@link ProtocolFilter} * . When null, this {@link ConnectorHandler} will create an instance of {@link DefaultCallbackHandler}. * @throws java.io.IOException */ public void connect(SocketAddress remoteAddress, CallbackHandler callbackHandler) throws IOException { connect(remoteAddress,null,callbackHandler); } /** * Connect to hostname:port. When an aysnchronous event happens (e.g * OP_READ or OP_WRITE), the {@link Controller} will invoke * the CallBackHandler. * @param remoteAddress remote address to connect * @param localAddress local address to bind * @param callbackHandler the handler invoked by its associated {@link SelectorHandler} when * a non blocking operation is ready to be handled. When null, all * read and write operation will be delegated to the default * {@link ProtocolChain} and its list of {@link ProtocolFilter} * . When null, this {@link ConnectorHandler} will create an instance of {@link DefaultCallbackHandler}. * @throws java.io.IOException */ public void connect(SocketAddress remoteAddress, SocketAddress localAddress, CallbackHandler callbackHandler) throws IOException { if (controller == null){ throw new IllegalStateException("Controller cannot be null"); } connect(remoteAddress,localAddress,callbackHandler, (TCPSelectorHandler)controller.getSelectorHandler(protocol())); } /** * Connect to hostname:port. When an aysnchronous event happens (e.g * OP_READ or OP_WRITE), the {@link Controller} will invoke * the CallBackHandler. * @param remoteAddress remote address to connect * @param callbackHandler the handler invoked by its associated {@link SelectorHandler} when * a non blocking operation is ready to be handled. When null, all * read and write operation will be delegated to the default * {@link ProtocolChain} and its list of {@link ProtocolFilter} * . When null, this {@link ConnectorHandler} will create an instance of {@link DefaultCallbackHandler}. * @param selectorHandler an instance of SelectorHandler. * @throws java.io.IOException */ public void connect(SocketAddress remoteAddress, CallbackHandler callbackHandler, TCPSelectorHandler selectorHandler) throws IOException { connect(remoteAddress,null,callbackHandler,selectorHandler); } /** * Connect to hostname:port. When an aysnchronous event happens (e.g * OP_READ or OP_WRITE), the {@link Controller} will invoke * the CallBackHandler. * @param remoteAddress remote address to connect * @param localAddress local address to bin * @param callbackHandler the handler invoked by its associated {@link SelectorHandler} when * a non blocking operation is ready to be handled. When null, all * read and write operation will be delegated to the default * {@link ProtocolChain} and its list of {@link ProtocolFilter} * . When null, this {@link ConnectorHandler} will create an instance of {@link DefaultCallbackHandler}. * @param selectorHandler an instance of SelectorHandler. * @throws java.io.IOException */ public void connect(SocketAddress remoteAddress, SocketAddress localAddress, CallbackHandler callbackHandler, TCPSelectorHandler selectorHandler) throws IOException { if (isConnected){ throw new AlreadyConnectedException(); } if (controller == null){ throw new IllegalStateException("Controller cannot be null"); } if (selectorHandler == null){ throw new IllegalStateException("SelectorHandler cannot be null"); } this.selectorHandler = selectorHandler; if (callbackHandler == null){ callbackHandler = new DefaultCallbackHandler(this); } else { this.callbackHandler = callbackHandler; } // Wait for the onConnect to be invoked. isConnectedLatch = new CountDownLatch(1); selectorHandler.connect(remoteAddress,localAddress,callbackHandler); inputStream = new InputReader(); try { isConnectedLatch.await(connectionTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { throw new IOException(ex.getMessage()); } } /** * Connect to hostname:port. Internally an instance of Controller and * its default SelectorHandler will be created everytime this method is * called. This method should be used only and only if no external * Controller has been initialized. * @param remoteAddress remote address to connect * @throws java.io.IOException */ public void connect(SocketAddress remoteAddress) throws IOException { connect(remoteAddress,(SocketAddress)null); } /** * Connect to hostname:port. Internally an instance of Controller and * its default SelectorHandler will be created everytime this method is * called. This method should be used only and only if no external * Controller has been initialized. * @param remoteAddress remote address to connect * @throws java.io.IOException * @param localAddress local address to bin */ public void connect(SocketAddress remoteAddress, SocketAddress localAddress) throws IOException { if (isConnected){ throw new AlreadyConnectedException(); } if (controller == null){ isStandalone = true; controller = new Controller(); controller.setSelectorHandler(new TCPSelectorHandler(true)); DefaultPipeline pipeline = new DefaultPipeline(); pipeline.initPipeline(); pipeline.startPipeline(); controller.setPipeline(pipeline); final CountDownLatch latch = new CountDownLatch(1); controller.addStateListener(new ControllerStateListenerAdapter() { @Override public void onReady() { latch.countDown(); } @Override public void onException(Throwable e) { latch.countDown(); } }); callbackHandler = new DefaultCallbackHandler(this,false); new Thread(controller, "GrizzlyTCPConnectorHandler-Controller").start(); try { latch.await(); } catch (InterruptedException ex) { } } if (null == callbackHandler) { callbackHandler = new DefaultCallbackHandler(this); } connect(remoteAddress,localAddress,callbackHandler, (TCPSelectorHandler)controller.getSelectorHandler(protocol())); } /** * Read bytes. If blocking is set to true, a pool of temporary * {@link Selector} will be used to read bytes. * @param byteBuffer The byteBuffer to store bytes. * @param blocking true if a a pool of temporary Selector * is required to handle a blocking read. * @return number of bytes read * @throws java.io.IOException */ public long read(ByteBuffer byteBuffer, boolean blocking) throws IOException { if (!isConnected){ throw new NotYetConnectedException(); } SelectionKey key = socketChannel.keyFor(selectorHandler.getSelector()); if (blocking){ inputStream.setSelectionKey(key); return inputStream.read(byteBuffer); } else { if (callbackHandler == null){ throw new IllegalStateException ("Non blocking read needs a CallbackHandler"); } int nRead = socketChannel.read(byteBuffer); if (nRead == 0){ selectorHandler.register(key,SelectionKey.OP_READ); } return nRead; } } /** * Writes bytes. If blocking is set to true, a pool of temporary * {@link Selector} will be used to writes bytes. * @param byteBuffer The byteBuffer to write. * @param blocking true if a a pool of temporary Selector * is required to handle a blocking write. * @return number of bytes written * @throws java.io.IOException */ public long write(ByteBuffer byteBuffer, boolean blocking) throws IOException { if (!isConnected){ throw new NotYetConnectedException(); } if (blocking){ return OutputWriter.flushChannel(socketChannel,byteBuffer); } else { if (callbackHandler == null){ throw new IllegalStateException ("Non blocking write needs a CallbackHandler"); } SelectionKey key = socketChannel.keyFor(selectorHandler.getSelector()); int nWrite = 1; int totalWriteBytes = 0; while (nWrite > 0 && byteBuffer.hasRemaining()){ nWrite = socketChannel.write(byteBuffer); totalWriteBytes += nWrite; } if (totalWriteBytes == 0 && byteBuffer.hasRemaining()){ selectorHandler.register(key,SelectionKey.OP_WRITE); } return totalWriteBytes; } } /** * {@inheritDoc} */ public void readFromAsyncQueue(ByteBuffer buffer, AsyncReadCallbackHandler callbackHandler) throws IOException { readFromAsyncQueue(buffer, callbackHandler, null); } /** * {@inheritDoc} */ public void readFromAsyncQueue(ByteBuffer buffer, AsyncReadCallbackHandler callbackHandler, AsyncReadCondition condition) throws IOException { readFromAsyncQueue(buffer, callbackHandler, condition, null); } /** * {@inheritDoc} */ public void readFromAsyncQueue(ByteBuffer buffer, AsyncReadCallbackHandler callbackHandler, AsyncReadCondition condition, AsyncQueueDataProcessor readPostProcessor) throws IOException { selectorHandler.getAsyncQueueReader().read( socketChannel.keyFor(selectorHandler.getSelector()), buffer, callbackHandler, condition, readPostProcessor); } /** * {@inheritDoc} */ public void writeToAsyncQueue(ByteBuffer buffer) throws IOException { writeToAsyncQueue(buffer, null); } /** * {@inheritDoc} */ public void writeToAsyncQueue(ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler) throws IOException { writeToAsyncQueue(buffer, callbackHandler, null); } /** * {@inheritDoc} */ public void writeToAsyncQueue(ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler, AsyncQueueDataProcessor writePreProcessor) throws IOException { writeToAsyncQueue(buffer, callbackHandler, writePreProcessor, false); } /** * {@inheritDoc} */ public void writeToAsyncQueue(ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler, AsyncQueueDataProcessor writePreProcessor, boolean isCloneByteBuffer) throws IOException { selectorHandler.getAsyncQueueWriter().write( socketChannel.keyFor(selectorHandler.getSelector()), buffer, callbackHandler, writePreProcessor, isCloneByteBuffer); } /** * {@inheritDoc} */ public void writeToAsyncQueue(SocketAddress dstAddress, ByteBuffer buffer) throws IOException { writeToAsyncQueue(dstAddress, buffer, null); } /** * {@inheritDoc} */ public void writeToAsyncQueue(SocketAddress dstAddress, ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler) throws IOException { writeToAsyncQueue(dstAddress, buffer, callbackHandler, null); } /** * {@inheritDoc} */ public void writeToAsyncQueue(SocketAddress dstAddress, ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler, AsyncQueueDataProcessor writePreProcessor) throws IOException { writeToAsyncQueue(dstAddress, buffer, callbackHandler, writePreProcessor, false); } /** * {@inheritDoc} */ public void writeToAsyncQueue(SocketAddress dstAddress, ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler, AsyncQueueDataProcessor writePreProcessor, boolean isCloneByteBuffer) throws IOException { selectorHandler.getAsyncQueueWriter().write( socketChannel.keyFor(selectorHandler.getSelector()), dstAddress, buffer, callbackHandler, writePreProcessor, isCloneByteBuffer); } /** * Close the underlying connection. */ public void close() throws IOException{ if (socketChannel != null){ if (selectorHandler != null){ SelectionKey key = socketChannel.keyFor(selectorHandler.getSelector()); if (key == null) return; selectorHandler.getSelectionKeyHandler().cancel(key); } else { socketChannel.close(); } } if (controller != null && isStandalone){ controller.stop(); controller = null; } isStandalone = false; isConnected = false; connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; } /** * Finish handling the OP_CONNECT interest ops. * @param key - a {@link SelectionKey} */ public void finishConnect(SelectionKey key) throws IOException{ try{ if (Controller.logger().isLoggable(Level.FINE)) { Controller.logger().log(Level.FINE, "Finish connect"); } socketChannel = (SocketChannel)key.channel(); socketChannel.finishConnect(); isConnected = socketChannel.isConnected(); configureChannel(socketChannel); if (Controller.logger().isLoggable(Level.FINE)) { Controller.logger().log(Level.FINE, "isConnected: " + isConnected); } } catch (IOException ex){ throw ex; } finally { isConnectedLatch.countDown(); } } /** * {@inheritDoc} */ public void configureChannel(SelectableChannel channel) throws IOException{ Socket socket = ((SocketChannel) channel).socket(); try{ if(linger >= 0 ) { socket.setSoLinger( true, linger); } } catch (SocketException ex){ Controller.logger().log(Level.WARNING, "setSoLinger exception ",ex); } try{ socket.setTcpNoDelay(tcpNoDelay); } catch (SocketException ex){ Controller.logger().log(Level.WARNING, "setTcpNoDelay exception ",ex); } try{ socket.setReuseAddress(reuseAddress); } catch (SocketException ex){ Controller.logger().log(Level.WARNING, "setReuseAddress exception ",ex); } } /** * A token decribing the protocol supported by an implementation of this * interface * @return this {@link ConnectorHandler}'s protocol */ public Controller.Protocol protocol(){ return Controller.Protocol.TCP; } /** * Is the underlying SocketChannel connected. * @return true if connected, otherwise false */ public boolean isConnected(){ return isConnected && socketChannel.isOpen(); } /** * Return the {@link Controller} * @return the {@link Controller} */ public Controller getController() { return controller; } /** * Set the {@link Controller} to use with this instance. * @param controller the {@link Controller} to use with this instance. */ public void setController(Controller controller) { this.controller = controller; } /** * Return the current {@link SocketChannel} used. * @return the current {@link SocketChannel} used. */ public SelectableChannel getUnderlyingChannel() { return socketChannel; } /** * Set the {@link SocketChannel}. * @param the {@link SocketChannel} to use. */ protected void setUnderlyingChannel(SocketChannel socketChannel){ this.socketChannel = socketChannel; } /** * Return the {@link CallbackHandler}. * @return the {@link CallbackHandler}. */ public CallbackHandler getCallbackHandler() { return callbackHandler; } /** * Set the {@link CallbackHandler}. * @param callbackHandler the {@link CallbackHandler}. */ public void setCallbackHandler(CallbackHandler callbackHandler) { this.callbackHandler = callbackHandler; } /** * Return the associated {@link SelectorHandler}. * @return the associated {@link SelectorHandler}. */ public TCPSelectorHandler getSelectorHandler() { return selectorHandler; } /** * 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 disabled (set to false). * * Disabled by default since enabling tcpNoDelay for most applications * can cause packets to appear to arrive in a fragmented fashion where it * takes multiple OP_READ events (i.e. multiple calls to read small * messages). The common behaviour seen when this occurs is that often times * a small number of bytes, as small as 1 byte at a time is read per OP_READ * event dispatch. This results in a large number of system calls to * read(), system calls to enable and disable interest ops and potentially * a large number of thread context switches between a thread doing the * Select(ing) and a worker thread doing the read. * * The Connector side should also set tcpNoDelay the same as it is set here * whenever possible. */ public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; } /** * @see java.net.Socket#setLinger() * @return the linger value. */ public int getLinger() { return linger; } /** * @see java.net.Socket#setLinger() */ public void setLinger(int linger) { this.linger = linger; } /** * Get TCP channel connection timeout in milliseconds * @return TCP channel connection timeout in milliseconds */ public int getConnectionTimeout() { return connectionTimeout; } /** * Set TCP channel connection timeout in milliseconds * @param connectionTimeout TCP channel connection timeout in milliseconds */ public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } /** * @see java.net.Socket#setReuseAddress() */ public boolean isReuseAddress() { return reuseAddress; } /** * @see java.net.Socket#setReuseAddress() */ public void setReuseAddress(boolean reuseAddress) { this.reuseAddress = reuseAddress; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy