Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / jigsaw / http / socket / SocketClientFactory.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/http/socket/SocketClientFactory.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/http/socket/SocketClientFactory.java
new file mode 100644 (file)
index 0000000..a9f90bf
--- /dev/null
@@ -0,0 +1,1004 @@
+// 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 ("&lt;") ; break ;\r
+                           case '>': sb1.append ("&gt;") ; break ;\r
+                           case '&': sb1.append ("&amp;") ; 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