--- /dev/null
+// SocketClientFactory.java\r
+// $Id: SocketClientFactory.java,v 1.2 2010/06/15 12:56:07 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.http.socket ;\r
+\r
+import java.io.IOException;\r
+import java.io.PrintStream;\r
+\r
+import java.net.InetAddress;\r
+import java.net.ServerSocket;\r
+import java.net.Socket;\r
+import java.net.URL;\r
+\r
+import org.w3c.jigsaw.http.Client;\r
+import org.w3c.jigsaw.http.ClientFactory;\r
+import org.w3c.jigsaw.http.httpd;\r
+\r
+import org.w3c.jigsaw.config.PropertySet;\r
+\r
+import org.w3c.util.LRUAble;\r
+import org.w3c.util.LRUList;\r
+import org.w3c.util.ObservableProperties;\r
+import org.w3c.util.PropertyMonitoring;\r
+import org.w3c.util.Status;\r
+import org.w3c.util.SyncLRUList;\r
+import org.w3c.util.ThreadCache;\r
+\r
+class DebugThread extends Thread {\r
+ SocketClientFactory pool = null;\r
+\r
+ public void run() {\r
+ while ( true ) {\r
+ try {\r
+ sleep(1000*10);\r
+ // Display some client statistics:\r
+ SocketClientState cs = null;\r
+ cs = (SocketClientState) pool.freeList.getHead();\r
+ while (cs != null) {\r
+ System.out.println(cs.client\r
+ + " reqcount="\r
+ + cs.client.getRequestCount()\r
+ + ", bindcount="\r
+ + cs.client.getBindCount());\r
+ cs = (SocketClientState)pool.freeList.getNext((LRUAble)cs);\r
+ }\r
+ System.out.println("freeCount ="+pool.freeCount);\r
+ System.out.println("idleCount ="+pool.idleCount);\r
+ System.out.println("totalCount="+pool.clientCount);\r
+ System.out.println("estimCount="+pool.clientEstim);\r
+ System.out.println("Average: "+pool.loadavg);\r
+ } catch (Exception ex) {\r
+ ex.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+\r
+ DebugThread(SocketClientFactory pool) {\r
+ this.pool = pool;\r
+ setPriority(Thread.MAX_PRIORITY);\r
+ }\r
+}\r
+\r
+/**\r
+ * The client pool is a kind of client factory.\r
+ * Each time the server gets a new connection, it calls the client pool\r
+ * to bound a client object (newly created or spared) to handle it.\r
+ */\r
+\r
+public class SocketClientFactory implements ClientFactory, PropertyMonitoring,\r
+ Status {\r
+\r
+ private static final boolean debug = false;\r
+ private static final boolean debugstats = false;\r
+ private static final boolean debugthread = false;\r
+\r
+ public static final int MINSPARE_FREE = 5;\r
+ public static final int MAXSPARE_FREE = 10;\r
+ public static final int MAXSPARE_IDLE = 20;\r
+ public static final int MAXTHREADS = 4;//Commented by Jeff Huang 40\r
+ public static final int MAXCLIENTS = 32;\r
+ public static final int IDLETO = 10000;\r
+\r
+ public static final int AVG_LIGHT = 1;\r
+ public static final int AVG_NORMAL = 2;\r
+ public static final int AVG_HIGH = 3;\r
+ public static final int AVG_DEAD = 4;\r
+\r
+ // FIXME doc\r
+ public final static String \r
+ MINSPARE_FREE_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.minFree";\r
+ // FIXME doc\r
+ public final static String \r
+ MAXSPARE_FREE_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxFree";\r
+ // FIXME doc\r
+ public final static String \r
+ MAXSPARE_IDLE_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxIdle";\r
+ // FIXME doc\r
+ public final static String \r
+ MAXTHREADS_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxThreads";\r
+ // FIXME doc\r
+ public final static String\r
+ MAXCLIENTS_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxClients";\r
+ // FIXME doc\r
+ public final static String\r
+ IDLETO_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.idleTimeout";\r
+ // FIXME doc\r
+ public final static String\r
+ BINDADDR_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.bindAddress";\r
+ // FIXME doc\r
+ public final static String\r
+ TIMEOUT_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.timeout";\r
+\r
+ int minFree = 0;\r
+ int maxFree = 0;\r
+ int maxIdle = 0;\r
+ int maxClients = 0;\r
+ InetAddress bindAddr = null;\r
+ int timeout = 0;\r
+\r
+ int count = 0 ; // number of created clients.\r
+ httpd server = null ;\r
+ ObservableProperties props = null ;\r
+ int busyCount = 0 ; // number of busy clients.\r
+\r
+ LRUList idleList = null;\r
+ LRUList freeList = null;\r
+\r
+ SocketClientState csList = null;\r
+\r
+ int idleCount = 0 ;\r
+ int freeCount = 0 ;\r
+ int clientCount = 0 ;\r
+ int clientEstim = 0 ;\r
+\r
+ ThreadCache threadcache = null;\r
+\r
+ int loadavg = AVG_LIGHT;\r
+\r
+ boolean alive = true;\r
+\r
+ /**\r
+ * Give the status of this class as a partial HTML text which will be added\r
+ * into a block level element\r
+ * @return a String, the generated HTML\r
+ */\r
+ public String getHTMLStatus() {\r
+ int idle = 0;\r
+ int free = 0;\r
+ int used = 0;\r
+ int bndc = 0;\r
+ int reqc = 0;\r
+ int bnd, req;\r
+ StringBuffer sb = new StringBuffer();\r
+ SocketClientState cs = null;\r
+ StringBuffer sb1 = null;\r
+ if (debugstats) {\r
+ sb1 = new StringBuffer();\r
+ }\r
+ // used clients\r
+ cs = csList;\r
+ if (debugstats) {\r
+ sb1.append("<table border=\"1\" class=\"idle\">\n"\r
+ + "<caption>Used Clients"\r
+ +"</caption><tr><th>Id</th><th>BindCount</th>"\r
+ +"<th>ReqCount</th><th>Diff</th>"\r
+ + "<th>BoundTo</th><th>URI</th></tr>\n");\r
+ }\r
+ while (cs != null) {\r
+ if (cs.status == SocketClientState.C_BUSY) {\r
+ InetAddress ia = cs.client.getInetAddress();\r
+ bnd = cs.client.getBindCount();\r
+ req = cs.client.getRequestCount();\r
+ if (debugstats) {\r
+ sb1.append ("<tr><td>");\r
+ sb1.append(cs.id);\r
+ sb1.append("</td><td>");\r
+ sb1.append(bnd);\r
+ sb1.append("</td><td>");\r
+ sb1.append(req);\r
+ sb1.append("</td><td>");\r
+ sb1.append(req - bnd);\r
+ sb1.append("</td><td>");\r
+ if (ia == null) {\r
+ sb1.append("Unbound");\r
+ } else {\r
+ sb1.append(\r
+ cs.client.getInetAddress().getHostAddress());\r
+ }\r
+ sb1.append("</td><td>");\r
+ if (cs.client.currentURI == null) {\r
+ sb1.append('-');\r
+ } else {\r
+ String u = cs.client.currentURI.toString();\r
+ for (int i = 0 ; i < u.length() ; i++) {\r
+ char ch = u.charAt(i) ;\r
+ switch (ch) {\r
+ case '<': sb1.append ("<") ; break ;\r
+ case '>': sb1.append (">") ; break ;\r
+ case '&': sb1.append ("&") ; break ;\r
+ default: sb1.append (ch) ; break;\r
+ }\r
+ }\r
+ }\r
+ sb1.append("</td></tr>\n");\r
+ }\r
+ used++;\r
+ bndc += bnd;\r
+ reqc += req;\r
+ }\r
+ cs = cs.csnext;\r
+ }\r
+ if (debugstats) {\r
+ sb1.append("</table>\n");\r
+ }\r
+ // idle clients\r
+ cs = (SocketClientState) idleList.getHead();\r
+ if (debugstats) {\r
+ sb1.append("<table border=\"1\" class=\"idle\">\n<caption>Idle "\r
+ +"Clients</caption><tr><th>Id<th>BindCount<th>ReqCount"\r
+ +"<th>Diff<th>BoundTo</tr>\n");\r
+ }\r
+ while (cs != null) {\r
+ InetAddress ia = cs.client.getInetAddress();\r
+ idle++;\r
+ bnd = cs.client.getBindCount(); \r
+ req = cs.client.getRequestCount(); \r
+ if (debugstats) {\r
+ sb1.append ("<tr><td>" +cs.id+ "<td>"+ bnd \r
+ + "<td>" + req + "<td>" + (req - bnd) + "<td>" + \r
+ ((ia == null) ? "Unbound" : \r
+ cs.client.getInetAddress().getHostAddress()) + \r
+ "</tr>\n" ); \r
+ }\r
+ bndc += bnd; \r
+ reqc += req; \r
+ cs = (SocketClientState)idleList.getNext(cs);\r
+ }\r
+ if (debugstats) {\r
+ sb1.append("</table>\n");\r
+ }\r
+ // free clients\r
+ cs = (SocketClientState) freeList.getHead();\r
+ if (debugstats) {\r
+ sb1.append("<table border=\"1\" class=\"idle\">\n"\r
+ + "<caption>Free Clients"\r
+ + "</caption><tr><th>Id<th>BindCount<th>ReqCount<th>"\r
+ + "Diff</tr>\n");\r
+ }\r
+ while (cs != null) {\r
+ free++;\r
+ bnd = cs.client.getBindCount(); \r
+ req = cs.client.getRequestCount(); \r
+ if (debugstats) {\r
+ sb1.append ("<tr><td>" + cs.id + "<td>"+ bnd\r
+ + "<td>" + req + "<td>" + (req - bnd) + "\n");\r
+ }\r
+ bndc += bnd;\r
+ reqc += req;\r
+ cs = (SocketClientState)freeList.getNext(cs);\r
+ }\r
+ if (debugstats) {\r
+ sb1.append("</table>\n");\r
+ }\r
+ \r
+ // stats\r
+ sb.append("<table border class=\"thread\">\n<caption>Thread counts"\r
+ + "</caption><tr><th>free<th>idle<th>used"\r
+ + "<th>estim<th>total<th>Load</tr>");\r
+ sb.append("<tr><td>");\r
+ sb.append(freeCount);\r
+ sb.append('(');\r
+ sb.append(free);\r
+ sb.append(")</td><td>");\r
+ sb.append(idleCount);\r
+ sb.append('(');\r
+ sb.append(idle);\r
+ sb.append(")</td><td>");\r
+ sb.append(clientCount - freeCount - idleCount);\r
+ sb.append('(');\r
+ sb.append(used);\r
+ sb.append(")</td><td>");\r
+ sb.append(clientEstim);\r
+ sb.append("</td><td>");\r
+ sb.append(clientCount);\r
+ sb.append("</td><td>");\r
+ sb.append(loadavg);\r
+ sb.append("</td></tr></table>\n");\r
+ // usage stats\r
+ sb.append("<table border class=\"usage\">\n<caption>Usage</caption>"\r
+ + "<tr><th>ReqCount<th>BindCount<th>Diff</tr>\n<tr><td>");\r
+ sb.append(reqc);\r
+ sb.append("</td><td>");\r
+ sb.append(bndc);\r
+ sb.append("</td><td>");\r
+ sb.append(reqc -bndc);\r
+ sb.append("</td></tr></table>\n");\r
+ if (debugstats) {\r
+ sb.append(sb1);\r
+ }\r
+\r
+ if (debugstats) {\r
+ cs = csList;\r
+ sb.append("<table border=\"1\" class=\"idle\">\n<caption>General"\r
+ + " Status</caption><tr><th>Id<th>Client<th>"\r
+ + "Status<th>marked<th>Thread</tr>\n");\r
+ while (cs != null) {\r
+ sb.append ("<tr><td>" +cs.id+ "<td>" + \r
+ ((cs.client == null) ? "None" : "bound") +\r
+ "<td>" );\r
+ switch (cs.status) {\r
+ case SocketClientState.C_IDLE:\r
+ sb.append ("Idle");\r
+ break;\r
+ case SocketClientState.C_BUSY:\r
+ sb.append ("Busy");\r
+ break;\r
+ case SocketClientState.C_FREE:\r
+ sb.append ("Free");\r
+ break;\r
+ case SocketClientState.C_KILL:\r
+ sb.append ("Kill");\r
+ break;\r
+ case SocketClientState.C_FIN:\r
+ sb.append ("Fin");\r
+ break;\r
+ }\r
+ sb.append ("<td>" + cs.marked);\r
+ if (cs.client != null) {\r
+ sb.append("<td>" + cs.client.thread +"</tr>\n");\r
+ } else {\r
+ sb.append("<td>No CLient</tr>\n");\r
+ }\r
+ cs = cs.csnext;\r
+ }\r
+ sb.append("</table>\n");\r
+ }\r
+ return sb.toString();\r
+ }\r
+\r
+ /**\r
+ * Some property have changed, update our setting.\r
+ * @param name The name of the property that has changed.\r
+ * @return A boolean, <strong>true</strong> if we updated ourself \r
+ * successfully.\r
+ */\r
+ public boolean propertyChanged (String name) {\r
+ httpd s = server;\r
+ if ( name.equals(MINSPARE_FREE_P) ) {\r
+ minFree = props.getInteger(MINSPARE_FREE_P, minFree);\r
+ } else if ( name.equals(MAXSPARE_FREE_P) ) {\r
+ maxFree = props.getInteger(MAXSPARE_FREE_P, maxFree);\r
+ } else if ( name.equals(MAXSPARE_IDLE_P) ) {\r
+ maxIdle = props.getInteger(MAXSPARE_IDLE_P, maxIdle);\r
+ } else if ( name.equals(MAXTHREADS_P) ) {\r
+ int maxThreads = props.getInteger(MAXTHREADS_P, -1);\r
+ if ( maxThreads > 0 )\r
+ threadcache.setCachesize(maxThreads);\r
+ } else if ( name.equals(IDLETO_P) ) {\r
+ int idleto = props.getInteger(IDLETO_P, -1);\r
+ if ( idleto > 0 ) {\r
+ threadcache.setIdleTimeout(idleto);\r
+ }\r
+ } else if ( name.equals(MAXCLIENTS_P) ) {\r
+ int newmax = props.getInteger(MAXCLIENTS_P, -1);\r
+ if ( newmax > maxClients ) {\r
+ for (int i = maxClients-newmax; --i >= 0; )\r
+ addClient(true);\r
+ } else if ( newmax > 0 ) {\r
+ maxClients = newmax;\r
+ }\r
+ } else if (name.equals(BINDADDR_P)) {\r
+ try {\r
+ bindAddr = InetAddress.getByName(props.getString(BINDADDR_P,\r
+ null));\r
+ } catch (Exception ex) {\r
+ // nothing\r
+ }\r
+ } else if (name.equals(TIMEOUT_P)) {\r
+ timeout = props.getInteger(IDLETO_P, timeout);\r
+ }\r
+ return true ;\r
+ }\r
+\r
+\r
+\r
+ /**\r
+ * Remove this client state from the glohbal client list.\r
+ * @param cs The client state to remove from the list.\r
+ */\r
+\r
+ protected synchronized void deleteClient(SocketClientState cs) {\r
+ synchronized (csList) {\r
+ if ( cs.csprev == null ) {\r
+ csList = cs.csnext;\r
+ } else if ( cs.csnext == null ) {\r
+ cs.csprev.csnext = null;\r
+ } else {\r
+ cs.csprev.csnext = cs.csnext;\r
+ cs.csnext.csprev = cs.csprev;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Factory for creating a new client for this pool.\r
+ * @param server the target http daemon \r
+ * @param state the client state holder\r
+ * @return a new socket client\r
+ */\r
+ protected SocketClient createClient(httpd server, \r
+ SocketClientState state) {\r
+ return new SocketClient(server, this, state);\r
+ }\r
+ \r
+\r
+ /**\r
+ * Create a new client for this pool.\r
+ * @param free A boolean, if <strong>true</strong> the client is inserted\r
+ * straight into the free list, otherwise, it is not plugged into any\r
+ * list.\r
+ * @return A SocketClientState instance, if creation of a new client was\r
+ * allowed, <strong>null</strong> if no more clients could be created.\r
+ */\r
+\r
+ protected synchronized SocketClientState addClient (boolean free) {\r
+ // Create a new client. \r
+ csList = new SocketClientState(csList);\r
+ SocketClientState cs = csList;\r
+ SocketClient client = createClient(server, cs);\r
+ cs.client = client;\r
+ clientCount++;\r
+ clientEstim++;\r
+ // Plug into free LRU if required:\r
+ if ( free ) {\r
+ cs.status = SocketClientState.C_FREE;\r
+ freeList.toHead(cs);\r
+ freeCount++;\r
+ }\r
+ return cs ;\r
+ }\r
+\r
+ /**\r
+ * We are not using synchronized functions to speed up things, \r
+ * but it is sometime useful to check that clients are not in a bad\r
+ * shape\r
+ */\r
+ private final void checkDeadClients() {\r
+ SocketClientState cs = null;\r
+ cs = csList;\r
+ boolean check = true;\r
+ int idlecount = 0;\r
+ int freecount = 0;\r
+ int clientcount = 0;\r
+\r
+ while (cs != null) {\r
+ if (cs.client != null) {\r
+ clientcount++;\r
+ switch(cs.status) {\r
+ case SocketClientState.C_BUSY:\r
+ if (cs.client.thread == null) {\r
+ if (cs.marked) {\r
+ if ( clientEstim <= maxClients ) {\r
+ cs.marked = false;\r
+ ++freeCount;\r
+ updateLoadAverage();\r
+ freeList.toHead(cs);\r
+ cs.status = SocketClientState.C_FREE;\r
+ cs.client.done = true;\r
+ } \r
+ check = false;\r
+ } else {\r
+ cs.marked = true;\r
+ }\r
+ }\r
+ break;\r
+ case SocketClientState.C_FREE:\r
+ freecount++;\r
+ cs.marked = false;\r
+ break;\r
+ default:\r
+ cs.marked = false;\r
+ break;\r
+ }\r
+ }\r
+ cs = cs.csnext;\r
+ }\r
+ // sanity check\r
+ if (freecount != freeCount) {\r
+ cs = (SocketClientState) idleList.getHead();\r
+ while (cs != null) {\r
+ idlecount++;\r
+ cs = (SocketClientState)idleList.getNext(cs);\r
+ }\r
+ cs = (SocketClientState) freeList.getHead();\r
+ freecount = 0;\r
+ while (cs != null) {\r
+ freecount++;\r
+ cs = (SocketClientState) freeList.getNext(cs);\r
+ }\r
+ freeCount = freecount;\r
+ idleCount = idlecount;\r
+ } \r
+ }\r
+\r
+ /**\r
+ * Update our idea of the current load.\r
+ * The one important invariant here, is that whenever the free list\r
+ * becomes empty, then the load average should be equals to\r
+ * <strong>AVG_DEAD</strong>. This ensures that the server will start \r
+ * dropping connections.\r
+ *\r
+ */\r
+ private final void updateLoadAverage() {\r
+ int oldavg = loadavg;\r
+ if ( freeCount >= maxFree ) {\r
+ loadavg = AVG_LIGHT;\r
+ } else if ((freeCount >= minFree) || (idleCount >= maxIdle)) {\r
+ if ((loadavg = AVG_NORMAL) < oldavg) {\r
+ server.thread.setPriority(Thread.MAX_PRIORITY);\r
+ }\r
+ } else if ( freeCount > 0 ) {\r
+ /* idleCount < MINSPARE_IDLE */\r
+ if ((loadavg = AVG_HIGH) > oldavg) {\r
+ // first ensure the state is sane\r
+// checkDeadClients();\r
+ server.thread.setPriority(server.getClientThreadPriority()-2);\r
+ }\r
+ } else {\r
+ loadavg = AVG_DEAD;\r
+ }\r
+ }\r
+ \r
+ private final synchronized void incrClientCount() {\r
+ ++clientCount;\r
+ ++clientEstim;\r
+ updateLoadAverage();\r
+ }\r
+\r
+ private final synchronized void decrClientCount() {\r
+ --clientCount;\r
+ updateLoadAverage();\r
+ }\r
+\r
+ private final synchronized boolean incrFreeCount() {\r
+ if ( clientEstim > maxClients ) {\r
+ clientEstim--;\r
+ return false;\r
+ }\r
+ ++freeCount;\r
+ updateLoadAverage();\r
+ return true;\r
+ }\r
+\r
+ private final synchronized boolean decrFreeCount() {\r
+ if ( freeCount > 0 ) {\r
+ --freeCount;\r
+ updateLoadAverage();\r
+ return true;\r
+ } else {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ private final synchronized boolean incrIdleCount() {\r
+ if ((loadavg > AVG_HIGH) || (idleCount + 1 >= maxIdle))\r
+ return false;\r
+ ++idleCount;\r
+ updateLoadAverage();\r
+ return true;\r
+ }\r
+\r
+ private final synchronized boolean decrIdleCount() {\r
+ if ( idleCount > 0 ) {\r
+ --idleCount;\r
+ updateLoadAverage();\r
+ return true;\r
+ } else {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Removes an idle client from the list, updates only the idle list\r
+ * as the free count has already be accessed\r
+ * @param the socket client to remove from the idle list\r
+ */\r
+ protected boolean idleClientRemove(SocketClient client) {\r
+ // If the client pool has shut down, exit straight:\r
+ if ( ! alive )\r
+ return false;\r
+ SocketClientState cs = client.state;\r
+ synchronized (csList) {\r
+ switch(cs.status) {\r
+ case SocketClientState.C_IDLE:\r
+ decrIdleCount();\r
+ idleList.remove(cs);\r
+ cs.status = SocketClientState.C_FREE;\r
+ break;\r
+ case SocketClientState.C_KILL:\r
+ case SocketClientState.C_BUSY:\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ \r
+ /**\r
+ * Notify that this client has finished with its connection.\r
+ * If the pool wants the client to be freed (because it has too many of \r
+ * them), it makes the client kill itself (which will trigger a call to\r
+ * the clientFinished method, were enventual cleanup is performed).\r
+ * @param client The client that is done with its connection.\r
+ */\r
+\r
+ protected boolean clientConnectionFinished (SocketClient client) {\r
+ // If the client pool has shut down, exit straight:\r
+ if ( ! alive )\r
+ return false;\r
+ SocketClientState cs = client.state;\r
+ synchronized (csList) { \r
+ switch(cs.status) {\r
+ case SocketClientState.C_IDLE:\r
+ decrIdleCount();\r
+ idleList.remove(cs);\r
+ break;\r
+ case SocketClientState.C_BUSY:\r
+ case SocketClientState.C_KILL:\r
+ break;\r
+ case SocketClientState.C_FREE:\r
+ // already freeed?\r
+ if (client.done) {\r
+ client.done = false;\r
+ return true;\r
+ }\r
+ default:\r
+ break;\r
+ }\r
+ if (incrFreeCount()) {\r
+ if ( debug )\r
+ System.out.println(client+": now free.");\r
+ cs.status = SocketClientState.C_FREE;\r
+ freeList.toHead(cs);\r
+ return true;\r
+ } else {\r
+ if ( debug )\r
+ System.out.println(client+": terminate.");\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Notify that this client has been killed.\r
+ * @param client The client that has terminate.\r
+ */\r
+\r
+ protected void clientFinished (SocketClient client) {\r
+ // If we're not alive any more, skip:\r
+ if ( ! alive )\r
+ return;\r
+ SocketClientState cs = client.state;\r
+ synchronized (csList) {\r
+ if ( debug )\r
+ System.out.println(client+": finished "+cs.status);\r
+ // Otherwise, perform the job:\r
+ switch(cs.status) {\r
+ case SocketClientState.C_IDLE:\r
+ break;\r
+ case SocketClientState.C_FREE:\r
+ decrFreeCount();\r
+ freeList.remove(cs);\r
+ break;\r
+ case SocketClientState.C_BUSY:\r
+ default:\r
+ String msg = (client \r
+ + ": finished with unknown status "\r
+ + cs.status);\r
+ server.errlog(msg);\r
+ break;\r
+ }\r
+ cs.status = SocketClientState.C_FIN;\r
+ decrClientCount();\r
+ deleteClient(cs);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * The client notifies the pool that is has been activated.\r
+ * The client state object is updated to unmark the client as idle.\r
+ * <p>This method needs not be synchronized, as it affect only the client\r
+ * state, <em>not</em> the client list.\r
+ * @param client The activated client.\r
+ */\r
+\r
+ protected void notifyUse(SocketClient client) {\r
+ if ( debug )\r
+ System.out.println(client+": used.");\r
+ SocketClientState cs = client.state;\r
+ synchronized (csList) {\r
+ if (cs.status == SocketClientState.C_IDLE) {\r
+ decrIdleCount();\r
+ idleList.remove(cs);\r
+ }\r
+ cs.status = SocketClientState.C_BUSY;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * The client notifies the pool that it enters idle state.\r
+ * <p>This method needs not be synchronized, as it affect only the client\r
+ * state, <em>not</em> the client list.\r
+ * @param client The client that is going to be idle.\r
+ */\r
+\r
+ protected boolean notifyIdle(SocketClient client) {\r
+ SocketClientState cs = client.state;\r
+ if ( alive ) {\r
+ synchronized (csList) {\r
+ if ( incrIdleCount() ) {\r
+ if ( debug ) \r
+ System.out.println(client+": idle, keep-alive.");\r
+ cs.status = SocketClientState.C_IDLE;\r
+ idleList.toHead(cs);\r
+ return true;\r
+ } else {\r
+ if ( debug )\r
+ System.out.println(client+": idle, closed.");\r
+ // Kill some old idle connections, give a chance for next:\r
+ int killsome = Math.max((maxFree - freeCount), \r
+ (maxIdle - idleCount));\r
+ killSomeClients((killsome > 0) ? killsome : 1);\r
+ // And give it a change if the load is not too high\r
+ if ( incrIdleCount() ) {\r
+ if ( debug ) \r
+ System.out.println(client+": idle, keep-alive.");\r
+ cs.status = SocketClientState.C_IDLE;\r
+ idleList.toHead(cs);\r
+ return true; \r
+ } \r
+ return false;\r
+ }\r
+ }\r
+ } else {\r
+ if ( debug )\r
+ System.out.println(client+": idle (dead), closed.");\r
+ // Kill some old idle connections, give a chance for next:\r
+ int killsome = Math.max((maxFree - freeCount),\r
+ (maxIdle - idleCount));\r
+ killSomeClients((killsome > 0) ? killsome : 1);\r
+ return false;\r
+ }\r
+ }\r
+\r
+ protected void killSomeClients(int howmany) {\r
+ int count = (howmany > 0) ? howmany : Math.max((maxFree - freeCount),\r
+ (maxIdle - idleCount));\r
+ if (debug) {\r
+ System.out.println("Killing :" + howmany);\r
+ }\r
+ while ( --count >= 0 ) {\r
+ SocketClientState cs = (SocketClientState) idleList.removeTail();\r
+ if ( cs != null ) {\r
+ synchronized (csList) {\r
+ if (cs.status == SocketClientState.C_IDLE) {\r
+ if ( debug )\r
+ System.out.println(cs.client + \r
+ ": kill (some-client).");\r
+ decrIdleCount();\r
+ cs.status = SocketClientState.C_KILL;\r
+ cs.client.unbind();\r
+ }\r
+ }\r
+ } else {\r
+ break;\r
+ }\r
+ // if the load falls back to normal operation, we are all set\r
+ if ((freeCount > minFree) && (idleCount < maxIdle)) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ final protected void killSomeClients() {\r
+ killSomeClients(-1);\r
+ }\r
+\r
+ protected void run(SocketClient client) {\r
+ if ( debug )\r
+ System.out.println(client+": warming up...");\r
+ boolean threaded = threadcache.getThread(client, true);\r
+ if ( debug )\r
+ System.out.println(client+": threaded="+threaded);\r
+ }\r
+\r
+ /**\r
+ * Handle the given connection.\r
+ * Find a free client, bind it to the given socket, and run it. If we\r
+ * have reached our maximum allowed number of clients, kill some\r
+ * connections.\r
+ * <p>A client enters the LRU list (and become a candidate for kill) only\r
+ * after it has handle one request (or if some timeout expires). This is\r
+ * performed by the first call to <code>notifyUse</code> which will\r
+ * silently insert the client into the LRU if it was not there already.\r
+ * <p>This client pool does a lot of nice thinigs, but could probably be\r
+ * implemented in a much better way (while keeping the features it has).\r
+ * Contention arond the pool is probably concern number 1 of performances.\r
+ * @param socket The connection to handle.\r
+ */\r
+\r
+ public void handleConnection (Socket socket) {\r
+ if ( debug )\r
+ System.out.println("new connection.");\r
+ SocketClientState cs = null;\r
+ switch(loadavg) {\r
+ case AVG_LIGHT:\r
+ // Free list is non empty, be fast:\r
+ if ( decrFreeCount() ) {\r
+ cs = (SocketClientState) freeList.removeTail();\r
+ if (cs == null) {\r
+ while (!incrFreeCount()) {\r
+ } \r
+ } \r
+ }\r
+ break;\r
+ case AVG_NORMAL:\r
+ case AVG_HIGH:\r
+ // Free list is non empty, but we try killing a client:\r
+ killSomeClients();\r
+ if ( decrFreeCount() ) {\r
+ cs = (SocketClientState) freeList.removeTail();\r
+ if (cs == null) {\r
+ while (!incrFreeCount()) {\r
+ } \r
+ }\r
+ }\r
+ break;\r
+ case AVG_DEAD:\r
+ break;\r
+ }\r
+ if ( debug )\r
+ System.out.println("load "+loadavg\r
+ + ", client="\r
+ + ((cs != null) \r
+ ? cs.client.toString()\r
+ : "unbound"));\r
+ // At this point, we do have a free client, bind it:\r
+ if ( cs != null ) {\r
+ if ( debug ) \r
+ System.out.println(cs.client+": bound.");\r
+ cs.status = SocketClientState.C_BUSY;\r
+ cs.client.bind(socket);\r
+ } else {\r
+ if ( debug )\r
+ System.out.println("*** connection refused (overloaded).");\r
+ try {\r
+ socket.close();\r
+ } catch (IOException ex) {\r
+ }\r
+ server.errlog(socket.getInetAddress()+" refused (overloaded).");\r
+ }\r
+ return;\r
+ }\r
+\r
+ protected synchronized void killClients(boolean force) {\r
+ alive = false;\r
+ // Kill all clients (first shot):\r
+ SocketClientState cs = csList;\r
+ while ((cs != null) && (cs.client != null)) {\r
+ synchronized (csList) {\r
+ // Only if a client is idely read'ing its socket, we close it\r
+ cs.client.kill(cs.status==SocketClientState.C_IDLE);\r
+ }\r
+ cs = cs.csnext;\r
+ }\r
+ // Kill all clients (second shot):\r
+ // Some client may be in transition during first shot, second shot\r
+ // really kills everything.\r
+ try {\r
+ Thread.sleep(5000);\r
+ } catch (Exception ex) {\r
+ }\r
+ cs = csList;\r
+ while ((cs != null) && (cs.client != null)) {\r
+ synchronized (csList) {\r
+ cs.client.kill(true);\r
+ }\r
+ cs = cs.csnext;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Shutdown the client pool. \r
+ * If force is <strong>true</strong>, kill all running clients right\r
+ * now, otherwise, wait for them to terminate gracefully, and return\r
+ * when done.\r
+ * @param force Should we interrupt running clients.\r
+ */\r
+\r
+ public void shutdown (boolean force) {\r
+ // First stage: kill all clients (synchronized)\r
+ killClients(force) ;\r
+ // Second stage (unsynchronized), join all client threads\r
+ SocketClientState cs = csList;\r
+ while ((cs != null) && (cs.client != null)) {\r
+ if ( debug )\r
+ System.out.println(cs.client+": join."); \r
+ cs.client.join();\r
+ cs = cs.csnext;\r
+ }\r
+ // Some cleanup (helps the GC, and make sure everything is free)\r
+ props.unregisterObserver(this);\r
+ props = null;\r
+ csList = null;\r
+ freeList = null;\r
+ idleList = null;\r
+ server = null;\r
+ }\r
+\r
+ /**\r
+ * Create the master socket for this client factory.\r
+ * @exception IOException If some IO error occurs while creating the\r
+ * server socket.\r
+ * @return A ServerSocket instance.\r
+ */\r
+\r
+ public ServerSocket createServerSocket() \r
+ throws IOException\r
+ {\r
+ // using maxCLient in the backlog is safe, but an overkill :)\r
+ if (bindAddr == null) {\r
+ return new ServerSocket (server.getPort(),\r
+ Math.max(128, maxClients));\r
+ } else {\r
+ return new ServerSocket (server.getPort(), \r
+ Math.max(128, maxClients),\r
+ bindAddr);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Initialize the raw, client socket factory.\r
+ * @param server The server context we are attached to.\r
+ */\r
+\r
+ public void initialize(httpd server) {\r
+ // Initialize instance variables:\r
+ this.server = server ;\r
+ this.props = server.getProperties() ;\r
+ this.props.registerObserver (this) ;\r
+ // Register our property sheet:\r
+ PropertySet set = new SocketConnectionProp("SocketConnectionProp"\r
+ , server);\r
+ server.registerPropertySet(set);\r
+ // Initialize parameters from properties:\r
+ this.minFree = props.getInteger(MINSPARE_FREE_P, MINSPARE_FREE);\r
+ this.maxFree = props.getInteger(MAXSPARE_FREE_P, MAXSPARE_FREE);\r
+ this.maxIdle = props.getInteger(MAXSPARE_IDLE_P, MAXSPARE_IDLE);\r
+ this.maxClients = props.getInteger(MAXCLIENTS_P, MAXCLIENTS);\r
+ this.timeout = props.getInteger(TIMEOUT_P, IDLETO);\r
+ String bindAddrName = props.getString(BINDADDR_P, null);\r
+ if (bindAddrName != null) {\r
+ try {\r
+ bindAddr = InetAddress.getByName(bindAddrName);\r
+ } catch (Exception ex) {\r
+ // nothing, fallback to default\r
+ }\r
+ }\r
+ // Create the LRU lists:\r
+ idleList = new SyncLRUList();\r
+ freeList = new SyncLRUList();\r
+ // Create the full client list:\r
+ csList = new SocketClientState();\r
+ // Create all our clients:\r
+ for (int i = 0 ; i < maxClients ; i++) {\r
+ if ( addClient(true) == null )\r
+ throw new RuntimeException (this.getClass().getName()\r
+ + "[construstructor]"\r
+ + ": unable to create clients.");\r
+ }\r
+ // Create the thread cache:\r
+ threadcache = new ThreadCache(server.getIdentifier() + \r
+ "-socket-clients");\r
+ threadcache.setCachesize(props.getInteger(MAXTHREADS_P, MAXTHREADS));\r
+ threadcache.setThreadPriority(server.getClientThreadPriority());\r
+ threadcache.setIdleTimeout(props.getInteger(IDLETO_P,IDLETO));\r
+ threadcache.setGrowAsNeeded(true);\r
+ threadcache.initialize();\r
+ // Start the debugging thread, if needed:\r
+ if ( debugthread ) {\r
+ new DebugThread(this).start();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Empty constructor for dynamic class instantiation.\r
+ */\r
+\r
+ public SocketClientFactory() {\r
+ }\r
+\r
+}\r