1. /*
  2. * @(#)ConnectionTable.java 1.80 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.corba.se.internal.iiop;
  8. import java.io.*;
  9. import java.util.Hashtable;
  10. import java.util.Enumeration;
  11. import java.net.ServerSocket;
  12. import java.net.Socket;
  13. import java.net.InetAddress;
  14. import java.net.SocketException;
  15. import java.net.BindException;
  16. import java.net.ConnectException;
  17. import java.net.NoRouteToHostException;
  18. import org.omg.CORBA.COMM_FAILURE;
  19. import org.omg.CORBA.CompletionStatus;
  20. import com.sun.corba.se.connection.EndPointInfo;
  21. import com.sun.corba.se.connection.GetEndPointInfoAgainException;
  22. import com.sun.corba.se.internal.core.EndPoint;
  23. import com.sun.corba.se.internal.core.IOR;
  24. import com.sun.corba.se.internal.core.ServerGIOP;
  25. import com.sun.corba.se.internal.orbutil.MinorCodes;
  26. import com.sun.corba.se.internal.orbutil.ORBUtility;
  27. /**
  28. * One instance of this class is created per ORB object.
  29. */
  30. public class ConnectionTable {
  31. protected ORB orb;
  32. protected Hashtable connectionCache = new Hashtable();
  33. protected long globalCounter=0;
  34. private int MAX_SOCKET_RETRIES=5;
  35. protected ServerGIOP server;
  36. public ConnectionTable(ORB orb, ServerGIOP server) {
  37. this.orb = orb;
  38. this.server = server;
  39. }
  40. private void dprint(String msg) {
  41. ORBUtility.dprint(this, msg);
  42. }
  43. /**
  44. * Called only on client side.
  45. *
  46. * Get a connection from the connection table, or create a new one.
  47. *
  48. * @param ior The IOR whic is being invoked.
  49. * @return The connection, pre-existing or new.
  50. */
  51. public Connection getConnection(IOR ior)
  52. {
  53. return getConnection(ior, null);
  54. }
  55. private Connection getConnection(IOR ior, EndPointInfo endPointInfo)
  56. {
  57. endPointInfo =
  58. orb.getSocketFactory().getEndPointInfo(orb, ior, endPointInfo);
  59. EndPoint key;
  60. if (endPointInfo instanceof EndPointImpl) {
  61. key = (EndPointImpl) endPointInfo;
  62. } else {
  63. // Convert the implmentation to one of ours to make sure
  64. // we control hashCode and equals.
  65. key = new EndPointImpl(endPointInfo.getType(),
  66. endPointInfo.getPort(),
  67. endPointInfo.getHost());
  68. }
  69. if (orb.transportDebugFlag)
  70. dprint( "Client get called: host = " + key.getHostName() +
  71. " port = " + key.getPort() ) ;
  72. Connection c = null;
  73. // Create a new connection object. Even though Hashtable.get/put are
  74. // synchronized, we need to synchronize on "this" to keep the
  75. // get+put atomic and prevent two threads from creating connections
  76. // to the same destination at the same time.
  77. synchronized(this) {
  78. // Always recheck the entry condition after getting the lock !!
  79. c = (Connection) connectionCache.get(key);
  80. if (c != null) {
  81. if (orb.transportDebugFlag)
  82. dprint( "Returning connection " + c + " from table" ) ;
  83. return c;
  84. }
  85. }
  86. // Try to open a socket to the server
  87. int tries = 0;
  88. while (true) {
  89. try {
  90. Socket socket =
  91. orb.getSocketFactory().createSocket(endPointInfo);
  92. // set socket to disable Nagle's algorithm
  93. // (always send immediately)
  94. try {
  95. socket.setTcpNoDelay(true);
  96. } catch (Exception e) {
  97. }
  98. // Create an entry in the connection table
  99. synchronized( this ) {
  100. c = new IIOPConnection(orb, server, this, key);
  101. stampTime(c);
  102. connectionCache.put(key, c);
  103. c.setConnection(socket, this);
  104. if (orb.transportDebugFlag)
  105. dprint( "Creating new connection " + c ) ;
  106. }
  107. checkConnectionTable();
  108. break;
  109. } catch (GetEndPointInfoAgainException ex) {
  110. if( c != null ) {
  111. c.abortConnection();
  112. }
  113. deleteConn(key);
  114. return getConnection(ior, ex.getEndPointInfo());
  115. } catch (SocketException ex) {
  116. if (orb.transportDebugFlag)
  117. dprint( "SocketException " + ex +
  118. " while creating socket for new connection" ) ;
  119. if ( ex instanceof BindException
  120. || ex instanceof ConnectException
  121. || ex instanceof NoRouteToHostException )
  122. {
  123. if (orb.transportDebugFlag)
  124. dprint( "Serious error: aborting connection" ) ;
  125. throw new COMM_FAILURE(MinorCodes.CONNECT_FAILURE,
  126. CompletionStatus.COMPLETED_NO);
  127. }
  128. if (orb.transportDebugFlag)
  129. dprint( "Attempting resource cleanup and retry on socket creation" ) ;
  130. // Probably the system's file descriptor limit has been reached
  131. // Try cleaning up some connections and retry.
  132. if ((tries == MAX_SOCKET_RETRIES) || (!cleanUp())) {
  133. if (orb.transportDebugFlag)
  134. dprint( "Out of resources: aborting connection" ) ;
  135. throw new COMM_FAILURE(MinorCodes.CONNECT_FAILURE,
  136. CompletionStatus.COMPLETED_NO);
  137. }
  138. tries++;
  139. } catch ( Exception ex ) {
  140. if (orb.transportDebugFlag)
  141. dprint( "Exception " + ex +
  142. " while creating socket for new connection: aborting connection" ) ;
  143. if( c != null ) {
  144. c.abortConnection();
  145. }
  146. deleteConn(key);
  147. throw new COMM_FAILURE(MinorCodes.CONNECT_FAILURE,
  148. CompletionStatus.COMPLETED_NO);
  149. }
  150. }
  151. if (orb.transportDebugFlag)
  152. dprint( "Succesfully created socket for new connection" ) ;
  153. return c;
  154. }
  155. /**
  156. * Called only on server side when a connection has been accepted.
  157. * Always creates a new Connection instance, which starts the upcall.
  158. * @param sock The socket for the connection.
  159. * @param socketType The user defined type of socket (e.g., clear, ssl, etc).
  160. */
  161. public synchronized Connection getConnection(Socket sock,
  162. String socketType)
  163. {
  164. try {
  165. if (orb.transportDebugFlag)
  166. dprint( "Server getConnection(" + sock + ", " + socketType + ")");
  167. InputStream in = null;
  168. OutputStream out = null;
  169. try {
  170. in = sock.getInputStream();
  171. out = sock.getOutputStream();
  172. } catch ( Exception ex ) {
  173. throw new COMM_FAILURE(MinorCodes.CONNECT_FAILURE,
  174. CompletionStatus.COMPLETED_NO);
  175. }
  176. String host = sock.getInetAddress().getHostName();
  177. int port = sock.getPort();
  178. EndPoint key = new EndPointImpl(socketType, port, host);
  179. if (orb.transportDebugFlag)
  180. dprint( "host = " + host + " port = " + port ) ;
  181. // Create a new Connection instance, and start the upcall.
  182. // We explicitly give the input/output streams to IIOPConnection
  183. // so that there is the flexibility of substituting the streams
  184. // e.g. for handling multiple protocols later.
  185. Connection c = new IIOPConnection(orb, server, key, sock,
  186. in, out, this);
  187. connectionCache.put(key, c);
  188. stampTime(c);
  189. if (orb.transportDebugFlag)
  190. dprint( "Created connection " + c ) ;
  191. return c;
  192. } catch ( Exception ex ) {
  193. if (orb.transportDebugFlag)
  194. dprint( "Exception " + ex + " on creating connection" ) ;
  195. // We should not throw an exception back to the ListenerThread.
  196. // Just close the socket and return silently.
  197. try {
  198. sock.close();
  199. } catch ( Exception ex2 ) {}
  200. return null;
  201. }
  202. }
  203. /**
  204. * Delete a connection from the connection table.
  205. * @param key The Endpoint of the connection.
  206. * @see Connection
  207. */
  208. public synchronized void deleteConn(EndPoint key) {
  209. if (orb.transportDebugFlag)
  210. dprint( "DeleteConn called: host = " + key.getHostName() +
  211. " port = " + key.getPort() ) ;
  212. connectionCache.remove( key );
  213. }
  214. /**
  215. * CleanUp by discarding least recently used Connections that
  216. * are not busy
  217. */
  218. // private static final int LOW_WATER_MARK = 100;
  219. // On Solaris there seems to be a max of 256 connections per process.
  220. // private static final int HIGH_WATER_MARK = 248;
  221. // How many connections to clean at a time.
  222. // private static final int NCLEAN = 5;
  223. public boolean cleanUp()
  224. {
  225. if (orb.transportDebugFlag)
  226. dprint( "Cleanup called" ) ;
  227. if (connectionCache.size() < orb.getLowWaterMark()) {
  228. if (orb.transportDebugFlag)
  229. dprint( "Cleanup returns false: not enough connections open to start cleanup" ) ;
  230. return false;
  231. }
  232. for ( int i=0; i<orb.getNumberToReclaim(); i++ ) {
  233. Connection toClean = null;
  234. long lru = java.lang.Long.MAX_VALUE;
  235. Enumeration e = connectionCache.elements();
  236. // Find least recently used and not busy connection in cache
  237. while ( e.hasMoreElements() )
  238. {
  239. Connection c = (Connection) e.nextElement();
  240. if ( !c.isBusy() && c.timeStamp < lru )
  241. {
  242. toClean = c;
  243. lru = c.timeStamp;
  244. }
  245. }
  246. if ( toClean == null ) {
  247. if (orb.transportDebugFlag)
  248. dprint( "Cleanup returns false: all connections busy" ) ;
  249. return false;
  250. }
  251. // Clean connection
  252. try {
  253. if (orb.transportDebugFlag)
  254. dprint( "Cleanup is cleaning connection " + toClean ) ;
  255. toClean.cleanUp();
  256. } catch (Exception ex) {}
  257. }
  258. // XXX is necessary to do a GC to reclaim closed network connections ??
  259. // java.lang.System.gc();
  260. return true;
  261. }
  262. /**
  263. * Check if number of connections has exceeded high watermark
  264. */
  265. public void checkConnectionTable()
  266. {
  267. // note: Hashtable.size() is not synchronized
  268. if (connectionCache.size() > orb.getHighWaterMark())
  269. cleanUp();
  270. }
  271. // Need to worry about wrap around some day
  272. public synchronized void stampTime(Connection c)
  273. {
  274. // _REVISIT_ Need to worry about wrap around some day
  275. c.timeStamp = globalCounter++;
  276. }
  277. void destroyConnections()
  278. {
  279. // Shut down all connections
  280. Enumeration e = connectionCache.elements();
  281. while (e.hasMoreElements()) {
  282. Connection c = (Connection)e.nextElement();
  283. c.shutdown();
  284. }
  285. }
  286. public synchronized void print()
  287. {
  288. System.out.println("***ConnectionTable***");
  289. int siz = connectionCache.size();
  290. System.out.println(" SIZE=" + siz);
  291. if ( siz < 10 ) {
  292. Enumeration e = connectionCache.elements();
  293. while ( e.hasMoreElements() )
  294. {
  295. Connection c = (Connection) e.nextElement();
  296. c.print();
  297. }
  298. }
  299. }
  300. }