/tags/sgs-0.9.9.3/sgs-server/src/main/java/com/sun/sgs/impl/transport/tcp/TcpTransport.java
Java | 359 lines | 209 code | 44 blank | 106 comment | 24 complexity | 084186b20b7c5b763f71b93103e1874b MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0
- /*
- * Copyright 2007-2009 Sun Microsystems, Inc.
- *
- * This file is part of Project Darkstar Server.
- *
- * Project Darkstar Server is free software: you can redistribute it
- * and/or modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation and
- * distributed hereunder to you.
- *
- * Project Darkstar Server 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package com.sun.sgs.impl.transport.tcp;
- import com.sun.sgs.app.Delivery;
- import com.sun.sgs.impl.sharedutil.LoggerWrapper;
- import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
- import com.sun.sgs.nio.channels.AsynchronousChannelGroup;
- import com.sun.sgs.nio.channels.AsynchronousServerSocketChannel;
- import com.sun.sgs.nio.channels.AsynchronousSocketChannel;
- import com.sun.sgs.nio.channels.CompletionHandler;
- import com.sun.sgs.nio.channels.IoFuture;
- import com.sun.sgs.nio.channels.spi.AsynchronousChannelProvider;
- import com.sun.sgs.transport.ConnectionHandler;
- import com.sun.sgs.transport.Transport;
- import com.sun.sgs.transport.TransportDescriptor;
- import java.io.IOException;
- import java.net.InetAddress;
- import java.net.InetSocketAddress;
- import java.util.Properties;
- import java.util.concurrent.CancellationException;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * Implementation of a TCP {@link Transport}.
- * The {@link #TcpTransport constructor} supports the following
- * properties: <p>
- *
- * <dl style="margin-left: 1em">
- *
- * <dt> <i>Property:</i> <code><b>
- * {@value #LISTEN_HOST_PROPERTY}
- * </b></code><br>
- * <i>Default:</i> Listen on all network interfaces
- *
- * <dd style="padding-top: .5em">Specifies the network address the transport
- * will listen on.<p>
- *
- * <dt> <i>Property:</i> <code><b>
- * {@value #LISTEN_PORT_PROPERTY}
- * </b></code><br>
- * <i>Default:</i> {@value #DEFAULT_PORT}<br>
- *
- * <dd style="padding-top: .5em">
- * Specifies the network port that the transport instance will listen on.
- * The value must be between 1 and 65535.<p>
- *
- * <dt> <i>Property:</i> <code><b>
- * {@value #ACCEPTOR_BACKLOG_PROPERTY}
- * </b></code><br>
- * <i>Default:</i> 0<br>
- *
- * <dd style="padding-top: .5em">
- * Specifies the acceptor backlog. This value is passed as the second
- * argument to the
- * {@link AsynchronousServerSocketChannel#bind(SocketAddress,int)
- * AsynchronousServerSocketChannel.bind} method.
- * </dl> <p>
- */
- public class TcpTransport implements Transport {
-
- private static final String PKG_NAME = "com.sun.sgs.impl.transport.tcp";
-
- private static final LoggerWrapper logger =
- new LoggerWrapper(Logger.getLogger(PKG_NAME));
-
- /**
- * The server listen address property.
- * This is the host interface we are listening on. Default is listen
- * on all interfaces.
- */
- public static final String LISTEN_HOST_PROPERTY =
- PKG_NAME + ".listen.address";
-
- /** The name of the server port property. */
- public static final String LISTEN_PORT_PROPERTY =
- PKG_NAME + ".listen.port";
- /** The default port: {@value #DEFAULT_PORT}. */
- public static final int DEFAULT_PORT = 62964;
-
- /** The listen address. */
- final InetSocketAddress listenAddress;
-
- /** The name of the acceptor backlog property. */
- public static final String ACCEPTOR_BACKLOG_PROPERTY =
- PKG_NAME + ".acceptor.backlog";
- /** The default acceptor backlog (<= 0 means default). */
- private static final int DEFAULT_ACCEPTOR_BACKLOG = 0;
- /** The acceptor backlog. */
- private final int acceptorBacklog;
-
- /** The async channel group for this service. */
- private final AsynchronousChannelGroup asyncChannelGroup;
- /** The acceptor for listening for new connections. */
- volatile AsynchronousServerSocketChannel acceptor;
- /** The currently-active accept operation, or {@code null} if none. */
- volatile IoFuture<?, ?> acceptFuture = null;
- /** The acceptor listener. */
- private AcceptorListener acceptorListener = null;
-
- /** The transport descriptor */
- private final TcpDescriptor descriptor;
- /**
- * Constructs an instance of this class with the specified properties.
- *
- * @param properties transport properties
- */
- public TcpTransport(Properties properties) {
-
- if (properties == null) {
- throw new NullPointerException("properties is null");
- }
- logger.log(Level.CONFIG,
- "Creating TCP transport with properties:{0}",
- properties);
-
- PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
- acceptorBacklog = wrappedProps.getIntProperty(ACCEPTOR_BACKLOG_PROPERTY,
- DEFAULT_ACCEPTOR_BACKLOG);
-
- String host = properties.getProperty(LISTEN_HOST_PROPERTY);
- int port = wrappedProps.getIntProperty(LISTEN_PORT_PROPERTY,
- DEFAULT_PORT, 1, 65535);
- try {
- // If no host address is supplied, default to listen on all
- // interfaces on the local host.
- //
- listenAddress =
- host == null ?
- new InetSocketAddress(port) :
- new InetSocketAddress(host, port);
-
- descriptor =
- new TcpDescriptor(host == null ?
- InetAddress.getLocalHost().getHostName() :
- host,
- listenAddress.getPort());
- AsynchronousChannelProvider provider =
- AsynchronousChannelProvider.provider();
- asyncChannelGroup =
- provider.openAsynchronousChannelGroup(
- Executors.newCachedThreadPool());
- acceptor =
- provider.openAsynchronousServerSocketChannel(asyncChannelGroup);
- try {
- acceptor.bind(listenAddress, acceptorBacklog);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG,
- "acceptor bound to host: {0} port:{1,number,#}",
- descriptor.hostName,
- descriptor.listeningPort);
- }
- } catch (Exception e) {
- logger.logThrow(Level.WARNING, e,
- "acceptor failed to listen on {0}",
- listenAddress);
- try {
- acceptor.close();
- } catch (IOException ioe) {
- logger.logThrow(Level.WARNING, ioe,
- "problem closing acceptor");
- }
- throw e;
- }
- } catch (Exception e) {
- if (logger.isLoggable(Level.CONFIG)) {
- logger.logThrow(Level.CONFIG, e,
- "Failed to create TCP transport");
- }
- shutdown();
- throw new RuntimeException(e);
- }
- }
-
- /* -- implement Transport -- */
-
- /** {@inheritDoc} */
- public TransportDescriptor getDescriptor() {
- return descriptor;
- }
-
- /** {@inheritDoc} */
- public Delivery getDelivery() {
- return Delivery.RELIABLE;
- }
-
- /** {@inheritDoc} */
- public synchronized void accept(ConnectionHandler handler) {
- if (handler == null) {
- throw new NullPointerException("null handler");
- } else if (!acceptor.isOpen()) {
- throw new IllegalStateException("transport has been shutdown");
- }
-
- if (acceptorListener != null) {
- throw new IllegalStateException("accept already called");
- }
- acceptorListener = new AcceptorListener(handler);
-
- assert acceptFuture == null;
- acceptFuture = acceptor.accept(acceptorListener);
- logger.log(Level.CONFIG, "transport accepting connections");
- }
- /** {@inheritDoc} */
- public synchronized void shutdown() {
- final IoFuture<?, ?> future = acceptFuture;
- acceptFuture = null;
-
- if (future != null) {
- future.cancel(true);
- }
-
- if (acceptor != null && acceptor.isOpen()) {
- try {
- acceptor.close();
- } catch (IOException e) {
- logger.logThrow(Level.FINEST, e, "closing acceptor throws");
- // swallow exception
- }
- }
- if (asyncChannelGroup != null && !asyncChannelGroup.isShutdown()) {
- asyncChannelGroup.shutdown();
- boolean groupShutdownCompleted = false;
- try {
- groupShutdownCompleted =
- asyncChannelGroup.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- logger.logThrow(Level.FINEST, e,
- "shutdown async group interrupted");
- Thread.currentThread().interrupt();
- }
- if (!groupShutdownCompleted) {
- logger.log(Level.WARNING, "forcing async group shutdown");
- try {
- asyncChannelGroup.shutdownNow();
- } catch (IOException e) {
- logger.logThrow(Level.FINEST, e,
- "shutdown async group throws");
- // swallow exception
- }
- }
- logger.log(Level.FINEST, "transport shutdown");
- }
- }
- /**
- * Closes the current acceptor and opens a new one, binding it to the
- * listen address specified during construction. This method is
- * invoked if a problem occurs handling a new connection or initiating
- * another accept request on the current acceptor.
- *
- * @throws IOException if the async channel group is shutdown, or
- * a problem occurs creating the new acceptor or binding it to
- * the listen address
- */
- private synchronized void restart()
- throws IOException
- {
- if (asyncChannelGroup.isShutdown()) {
- throw new IOException("channel group is shutdown");
- }
-
- try {
- acceptor.close();
- } catch (IOException ex) {
- logger.logThrow(Level.FINEST, ex,
- "exception closing acceptor during restart");
- }
- acceptor = AsynchronousChannelProvider.provider().
- openAsynchronousServerSocketChannel(asyncChannelGroup);
-
- acceptor.bind(listenAddress, acceptorBacklog);
- }
-
- /** A completion handler for accepting connections. */
- private class AcceptorListener
- implements CompletionHandler<AsynchronousSocketChannel, Void>
- {
- /** The connection handler. */
- private final ConnectionHandler connectionHandler;
- /**
- * Constructs an instance with the specified {@code connectionHandler}.
- *
- * @param connectionHandler a connection handler
- */
- AcceptorListener(ConnectionHandler connectionHandler) {
- this.connectionHandler = connectionHandler;
- }
-
- /** Handle new connection or report failure. */
- public void completed(IoFuture<AsynchronousSocketChannel, Void> result)
- {
- try {
- try {
- AsynchronousSocketChannel newChannel = result.getNow();
- logger.log(Level.FINER, "Accepted {0}", newChannel);
- connectionHandler.newConnection(newChannel);
- // Resume accepting connections
- acceptFuture = acceptor.accept(this);
- } catch (ExecutionException e) {
- throw (e.getCause() == null) ? e : e.getCause();
- }
- } catch (CancellationException e) {
- logger.logThrow(Level.FINE, e, "acceptor cancelled");
- //ignore
- } catch (Throwable e) {
- logger.logThrow(Level.SEVERE, e,
- "acceptor error on {0}", listenAddress);
- try {
- restart();
-
- // Resume accepting connections on new acceptor
- acceptFuture = acceptor.accept(this);
- } catch (IOException ioe) {
- logger.logThrow(Level.FINEST, ioe,
- "exception during restart");
- shutdown();
- connectionHandler.shutdown();
- }
- }
- }
- }
- }