/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2008-2010 Oracle and/or its affiliates. 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_1_1.html * or packager/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 packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [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.aio; import com.sun.grizzly.ConnectorHandler; import com.sun.grizzly.ContextTask; import com.sun.grizzly.Controller; import com.sun.grizzly.ProtocolChain; import com.sun.grizzly.ProtocolChainInstanceHandler; import com.sun.grizzly.util.Copyable; import com.sun.grizzly.util.State; import com.sun.grizzly.util.StateHolder; import java.io.IOException; import java.net.InetSocketAddress; import java.net.StandardSocketOption; import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.nio.channels.NetworkChannel; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; /** * Server side TCP support build on top of NIO.2 (AIO). * * @author Jeanfrancois Arcand */ public class TCPAIOHandler implements AIOHandler { private AsynchronousChannelGroup asyncChannelGroup; /** * 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; // timeout for read and writes. public final static long TIMEOUT_IN_SECONDS = 30; private long timeout = TIMEOUT_IN_SECONDS; /** * Server socket backlog. */ protected int ssBackLog = 4096; private AsynchronousServerSocketChannel listener; /** * Attributes, associated with the {@link AIOHandler} instance */ protected Map<String, Object> attributes; private int port = 8080; private AIOHandlerRunner aioHandlerRunner; private Controller controller; final CountDownLatch latch = new CountDownLatch(1); /** * This {@link AIOHandler} StateHolder, which is shared among * AIOHandler and its clones */ protected StateHolder<State> stateHolder = new StateHolder<State>(true); /** * The {@link ExecutorService} used by this instance. If null - * {@link Controller}'s {@link ExecutorService} will be used */ protected ExecutorService threadPool; /** * The ProtocolChainInstanceHandler used by this instance. If not set, and instance * of the DefaultInstanceHandler will be created. */ protected ProtocolChainInstanceHandler instanceHandler; /** * Flag, which shows whether shutdown was called for this {@link AIOHandler} */ protected AtomicBoolean isShutDown = new AtomicBoolean(false); public TCPAIOHandler(Controller controller) { this.controller = controller; } /** * A token decribing the protocol supported by an implementation of this * interface */ public Controller.Protocol protocol() { return Controller.Protocol.TCP; } public void initialize() throws IOException { listener = AsynchronousServerSocketChannel.open( asyncChannelGroup).bind(new InetSocketAddress(port),ssBackLog); } public void start() { try { isShutDown.set(false); initialize(); // accept connections listener.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { public void completed(final AsynchronousSocketChannel channel, Void attachment) { try { ProtocolChain protocolChain = controller.getProtocolChainInstanceHandler().poll(); AIOContext context = (AIOContext) controller.pollContext(); context.setAIOHandler(TCPAIOHandler.this); context.setChannel(channel); context.setProtocolChain(protocolChain); context.setAttribute("timeout", timeout); configureChannel(channel); ContextTask ct = new AIOContextTask(); ct.setContext(context); context.execute(ct,false); listener.accept(null, this); } catch (Throwable ex) { Controller.logger().log(Level.SEVERE, "completed", ex); } } public void failed(Throwable t, Void attachment) { if (Controller.logger().isLoggable(Level.FINEST)){ Controller.logger().log(Level.FINEST, "Pending Connection failed", attachment); } } public void cancelled(Void attachment) { if (Controller.logger().isLoggable(Level.FINEST)){ Controller.logger().log(Level.FINEST, "Pending Connection cancelled", attachment); } } }); } catch (Throwable t) { Controller.logger().log(Level.SEVERE, "Accept()", t); } } public AIOHandlerRunner getAioHandlerRunner() { return aioHandlerRunner; } public void setAioHandlerRunner(AIOHandlerRunner aioHandlerRunner) { this.aioHandlerRunner = aioHandlerRunner; } /** * {@inheritDoc} */ public void configureChannel(AsynchronousSocketChannel channel) throws IOException { if (linger >= 0) { channel.setOption(StandardSocketOption.SO_LINGER, linger); } channel.setOption(StandardSocketOption.TCP_NODELAY, tcpNoDelay); channel.setOption(StandardSocketOption.SO_REUSEADDR, reuseAddress); } /** * Closes {@link NetworkChannel} */ public void closeChannel(NetworkChannel channel) throws IOException { channel.close(); } /** * Set the <code>ProtocolChainInstanceHandler</code> to use for * creating instance of <code>ProtocolChain</code>. */ public void setProtocolChainInstanceHandler(ProtocolChainInstanceHandler instanceHandler) { this.instanceHandler = instanceHandler; } /** * Return the <code>ProtocolChainInstanceHandler</code> */ public ProtocolChainInstanceHandler getProtocolChainInstanceHandler() { return instanceHandler; } /** * {@inheritDoc} */ public ExecutorService getThreadPool() { return threadPool; } /** * {@inheritDoc} */ public void setThreadPool(ExecutorService threadPool) { this.threadPool = threadPool; } /** * {@inheritDoc} */ public void pause() { stateHolder.setState(State.PAUSED); } /** * {@inheritDoc} */ public void resume() { if (!State.PAUSED.equals(stateHolder.getState(false))) { throw new IllegalStateException("AIOHandler is not in PAUSED state, but: " + stateHolder.getState(false)); } stateHolder.setState(State.STARTED); } /** * {@inheritDoc} */ public StateHolder<State> getStateHolder() { return stateHolder; } /** * Shuntdown this instance by closing its Selector and associated channels. */ public void shutdown() { // If shutdown was called for this AIOHandler if (isShutDown.getAndSet(true)) { return; } stateHolder.setState(State.STOPPED); try { listener.close(); } catch (IOException ex) { Controller.logger().log(Level.SEVERE, "shutdown", ex); } } // ----------- 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<String, Object>(); } 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 <tt>key</tt>, null if <tt>key</tt> * does not exist in <tt>attributes</tt> */ public Object getAttribute(String key) { if (attributes == null) { return null; } return attributes.get(key); } /** * Set a <code>Map</code> of attribute name/value pairs. * Old <code>AttributeHolder</code> values will not be available. * Later changes of this <code>Map</code> will lead to changes to the current * <code>AttributeHolder</code>. * * @param attributes - map of name/value pairs */ public void setAttributes(Map<String, Object> attributes) { this.attributes = attributes; } /** * Return a <code>Map</code> of attribute name/value pairs. * Updates, performed on the returned <code>Map</code> will be reflected in * this <code>AttributeHolder</code> * * @return - <code>Map</code> of attribute name/value pairs */ public Map<String, Object> getAttributes() { return attributes; } public void releaseConnectorHandler(ConnectorHandler connectorHandler) { } public ConnectorHandler acquireConnectorHandler() { throw new UnsupportedOperationException("Not supported yet."); } public void copyTo(Copyable copy) { } public int getPort() { return port; } public void setPort(int port) { this.port = port; } /* 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 boolean isReuseAddress() { return reuseAddress; } public void setReuseAddress(boolean reuseAddress) { this.reuseAddress = reuseAddress; } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } public int getSsBackLog() { return ssBackLog; } public void setSsBackLog(int ssBackLog) { this.ssBackLog = ssBackLog; } public void setAynchronousChannelGroup(AsynchronousChannelGroup group) { asyncChannelGroup = group; } }