PageRenderTime 57ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/branches/socket-timeout/sgs-server/src/main/java/com/sun/sgs/impl/transport/tcp/TcpTransport.java

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