PageRenderTime 67ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/sgs-0.9.9.7/sgs-server/src/main/java/com/sun/sgs/impl/transport/tcp/TcpTransport.java

https://github.com/Cyberqat/Red-Dwarf
Java | 359 lines | 209 code | 44 blank | 106 comment | 24 complexity | 084186b20b7c5b763f71b93103e1874b MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0
  1. /*
  2. * Copyright 2007-2009 Sun Microsystems, Inc.
  3. *
  4. * This file is part of Project Darkstar Server.
  5. *
  6. * Project Darkstar Server is free software: you can redistribute it
  7. * and/or modify it under the terms of the GNU General Public License
  8. * version 2 as published by the Free Software Foundation and
  9. * distributed hereunder to you.
  10. *
  11. * Project Darkstar Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sun.sgs.impl.transport.tcp;
  20. import com.sun.sgs.app.Delivery;
  21. import com.sun.sgs.impl.sharedutil.LoggerWrapper;
  22. import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
  23. import com.sun.sgs.nio.channels.AsynchronousChannelGroup;
  24. import com.sun.sgs.nio.channels.AsynchronousServerSocketChannel;
  25. import com.sun.sgs.nio.channels.AsynchronousSocketChannel;
  26. import com.sun.sgs.nio.channels.CompletionHandler;
  27. import com.sun.sgs.nio.channels.IoFuture;
  28. import com.sun.sgs.nio.channels.spi.AsynchronousChannelProvider;
  29. import com.sun.sgs.transport.ConnectionHandler;
  30. import com.sun.sgs.transport.Transport;
  31. import com.sun.sgs.transport.TransportDescriptor;
  32. import java.io.IOException;
  33. import java.net.InetAddress;
  34. import java.net.InetSocketAddress;
  35. import java.util.Properties;
  36. import java.util.concurrent.CancellationException;
  37. import java.util.concurrent.ExecutionException;
  38. import java.util.concurrent.Executors;
  39. import java.util.concurrent.TimeUnit;
  40. import java.util.logging.Level;
  41. import java.util.logging.Logger;
  42. /**
  43. * Implementation of a TCP {@link Transport}.
  44. * The {@link #TcpTransport constructor} supports the following
  45. * properties: <p>
  46. *
  47. * <dl style="margin-left: 1em">
  48. *
  49. * <dt> <i>Property:</i> <code><b>
  50. * {@value #LISTEN_HOST_PROPERTY}
  51. * </b></code><br>
  52. * <i>Default:</i> Listen on all network interfaces
  53. *
  54. * <dd style="padding-top: .5em">Specifies the network address the transport
  55. * will listen on.<p>
  56. *
  57. * <dt> <i>Property:</i> <code><b>
  58. * {@value #LISTEN_PORT_PROPERTY}
  59. * </b></code><br>
  60. * <i>Default:</i> {@value #DEFAULT_PORT}<br>
  61. *
  62. * <dd style="padding-top: .5em">
  63. * Specifies the network port that the transport instance will listen on.
  64. * The value must be between 1 and 65535.<p>
  65. *
  66. * <dt> <i>Property:</i> <code><b>
  67. * {@value #ACCEPTOR_BACKLOG_PROPERTY}
  68. * </b></code><br>
  69. * <i>Default:</i> 0<br>
  70. *
  71. * <dd style="padding-top: .5em">
  72. * Specifies the acceptor backlog. This value is passed as the second
  73. * argument to the
  74. * {@link AsynchronousServerSocketChannel#bind(SocketAddress,int)
  75. * AsynchronousServerSocketChannel.bind} method.
  76. * </dl> <p>
  77. */
  78. public class TcpTransport implements Transport {
  79. private static final String PKG_NAME = "com.sun.sgs.impl.transport.tcp";
  80. private static final LoggerWrapper logger =
  81. new LoggerWrapper(Logger.getLogger(PKG_NAME));
  82. /**
  83. * The server listen address property.
  84. * This is the host interface we are listening on. Default is listen
  85. * on all interfaces.
  86. */
  87. public static final String LISTEN_HOST_PROPERTY =
  88. PKG_NAME + ".listen.address";
  89. /** The name of the server port property. */
  90. public static final String LISTEN_PORT_PROPERTY =
  91. PKG_NAME + ".listen.port";
  92. /** The default port: {@value #DEFAULT_PORT}. */
  93. public static final int DEFAULT_PORT = 62964;
  94. /** The listen address. */
  95. final InetSocketAddress listenAddress;
  96. /** The name of the acceptor backlog property. */
  97. public static final String ACCEPTOR_BACKLOG_PROPERTY =
  98. PKG_NAME + ".acceptor.backlog";
  99. /** The default acceptor backlog (&lt;= 0 means default). */
  100. private static final int DEFAULT_ACCEPTOR_BACKLOG = 0;
  101. /** The acceptor backlog. */
  102. private final int acceptorBacklog;
  103. /** The async channel group for this service. */
  104. private final AsynchronousChannelGroup asyncChannelGroup;
  105. /** The acceptor for listening for new connections. */
  106. volatile AsynchronousServerSocketChannel acceptor;
  107. /** The currently-active accept operation, or {@code null} if none. */
  108. volatile IoFuture<?, ?> acceptFuture = null;
  109. /** The acceptor listener. */
  110. private AcceptorListener acceptorListener = null;
  111. /** The transport descriptor */
  112. private final TcpDescriptor descriptor;
  113. /**
  114. * Constructs an instance of this class with the specified properties.
  115. *
  116. * @param properties transport properties
  117. */
  118. public TcpTransport(Properties properties) {
  119. if (properties == null) {
  120. throw new NullPointerException("properties is null");
  121. }
  122. logger.log(Level.CONFIG,
  123. "Creating TCP transport with properties:{0}",
  124. properties);
  125. PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
  126. acceptorBacklog = wrappedProps.getIntProperty(ACCEPTOR_BACKLOG_PROPERTY,
  127. DEFAULT_ACCEPTOR_BACKLOG);
  128. String host = properties.getProperty(LISTEN_HOST_PROPERTY);
  129. int port = wrappedProps.getIntProperty(LISTEN_PORT_PROPERTY,
  130. DEFAULT_PORT, 1, 65535);
  131. try {
  132. // If no host address is supplied, default to listen on all
  133. // interfaces on the local host.
  134. //
  135. listenAddress =
  136. host == null ?
  137. new InetSocketAddress(port) :
  138. new InetSocketAddress(host, port);
  139. descriptor =
  140. new TcpDescriptor(host == null ?
  141. InetAddress.getLocalHost().getHostName() :
  142. host,
  143. listenAddress.getPort());
  144. AsynchronousChannelProvider provider =
  145. AsynchronousChannelProvider.provider();
  146. asyncChannelGroup =
  147. provider.openAsynchronousChannelGroup(
  148. Executors.newCachedThreadPool());
  149. acceptor =
  150. provider.openAsynchronousServerSocketChannel(asyncChannelGroup);
  151. try {
  152. acceptor.bind(listenAddress, acceptorBacklog);
  153. if (logger.isLoggable(Level.CONFIG)) {
  154. logger.log(Level.CONFIG,
  155. "acceptor bound to host: {0} port:{1,number,#}",
  156. descriptor.hostName,
  157. descriptor.listeningPort);
  158. }
  159. } catch (Exception e) {
  160. logger.logThrow(Level.WARNING, e,
  161. "acceptor failed to listen on {0}",
  162. listenAddress);
  163. try {
  164. acceptor.close();
  165. } catch (IOException ioe) {
  166. logger.logThrow(Level.WARNING, ioe,
  167. "problem closing acceptor");
  168. }
  169. throw e;
  170. }
  171. } catch (Exception e) {
  172. if (logger.isLoggable(Level.CONFIG)) {
  173. logger.logThrow(Level.CONFIG, e,
  174. "Failed to create TCP transport");
  175. }
  176. shutdown();
  177. throw new RuntimeException(e);
  178. }
  179. }
  180. /* -- implement Transport -- */
  181. /** {@inheritDoc} */
  182. public TransportDescriptor getDescriptor() {
  183. return descriptor;
  184. }
  185. /** {@inheritDoc} */
  186. public Delivery getDelivery() {
  187. return Delivery.RELIABLE;
  188. }
  189. /** {@inheritDoc} */
  190. public synchronized void accept(ConnectionHandler handler) {
  191. if (handler == null) {
  192. throw new NullPointerException("null handler");
  193. } else if (!acceptor.isOpen()) {
  194. throw new IllegalStateException("transport has been shutdown");
  195. }
  196. if (acceptorListener != null) {
  197. throw new IllegalStateException("accept already called");
  198. }
  199. acceptorListener = new AcceptorListener(handler);
  200. assert acceptFuture == null;
  201. acceptFuture = acceptor.accept(acceptorListener);
  202. logger.log(Level.CONFIG, "transport accepting connections");
  203. }
  204. /** {@inheritDoc} */
  205. public synchronized void shutdown() {
  206. final IoFuture<?, ?> future = acceptFuture;
  207. acceptFuture = null;
  208. if (future != null) {
  209. future.cancel(true);
  210. }
  211. if (acceptor != null && acceptor.isOpen()) {
  212. try {
  213. acceptor.close();
  214. } catch (IOException e) {
  215. logger.logThrow(Level.FINEST, e, "closing acceptor throws");
  216. // swallow exception
  217. }
  218. }
  219. if (asyncChannelGroup != null && !asyncChannelGroup.isShutdown()) {
  220. asyncChannelGroup.shutdown();
  221. boolean groupShutdownCompleted = false;
  222. try {
  223. groupShutdownCompleted =
  224. asyncChannelGroup.awaitTermination(1, TimeUnit.SECONDS);
  225. } catch (InterruptedException e) {
  226. logger.logThrow(Level.FINEST, e,
  227. "shutdown async group interrupted");
  228. Thread.currentThread().interrupt();
  229. }
  230. if (!groupShutdownCompleted) {
  231. logger.log(Level.WARNING, "forcing async group shutdown");
  232. try {
  233. asyncChannelGroup.shutdownNow();
  234. } catch (IOException e) {
  235. logger.logThrow(Level.FINEST, e,
  236. "shutdown async group throws");
  237. // swallow exception
  238. }
  239. }
  240. logger.log(Level.FINEST, "transport shutdown");
  241. }
  242. }
  243. /**
  244. * Closes the current acceptor and opens a new one, binding it to the
  245. * listen address specified during construction. This method is
  246. * invoked if a problem occurs handling a new connection or initiating
  247. * another accept request on the current acceptor.
  248. *
  249. * @throws IOException if the async channel group is shutdown, or
  250. * a problem occurs creating the new acceptor or binding it to
  251. * the listen address
  252. */
  253. private synchronized void restart()
  254. throws IOException
  255. {
  256. if (asyncChannelGroup.isShutdown()) {
  257. throw new IOException("channel group is shutdown");
  258. }
  259. try {
  260. acceptor.close();
  261. } catch (IOException ex) {
  262. logger.logThrow(Level.FINEST, ex,
  263. "exception closing acceptor during restart");
  264. }
  265. acceptor = AsynchronousChannelProvider.provider().
  266. openAsynchronousServerSocketChannel(asyncChannelGroup);
  267. acceptor.bind(listenAddress, acceptorBacklog);
  268. }
  269. /** A completion handler for accepting connections. */
  270. private class AcceptorListener
  271. implements CompletionHandler<AsynchronousSocketChannel, Void>
  272. {
  273. /** The connection handler. */
  274. private final ConnectionHandler connectionHandler;
  275. /**
  276. * Constructs an instance with the specified {@code connectionHandler}.
  277. *
  278. * @param connectionHandler a connection handler
  279. */
  280. AcceptorListener(ConnectionHandler connectionHandler) {
  281. this.connectionHandler = connectionHandler;
  282. }
  283. /** Handle new connection or report failure. */
  284. public void completed(IoFuture<AsynchronousSocketChannel, Void> result)
  285. {
  286. try {
  287. try {
  288. AsynchronousSocketChannel newChannel = result.getNow();
  289. logger.log(Level.FINER, "Accepted {0}", newChannel);
  290. connectionHandler.newConnection(newChannel);
  291. // Resume accepting connections
  292. acceptFuture = acceptor.accept(this);
  293. } catch (ExecutionException e) {
  294. throw (e.getCause() == null) ? e : e.getCause();
  295. }
  296. } catch (CancellationException e) {
  297. logger.logThrow(Level.FINE, e, "acceptor cancelled");
  298. //ignore
  299. } catch (Throwable e) {
  300. logger.logThrow(Level.SEVERE, e,
  301. "acceptor error on {0}", listenAddress);
  302. try {
  303. restart();
  304. // Resume accepting connections on new acceptor
  305. acceptFuture = acceptor.accept(this);
  306. } catch (IOException ioe) {
  307. logger.logThrow(Level.FINEST, ioe,
  308. "exception during restart");
  309. shutdown();
  310. connectionHandler.shutdown();
  311. }
  312. }
  313. }
  314. }
  315. }