0001: // SocketClientFactory.java
0002: // $Id: SocketClientFactory.java,v 1.48 2004/08/30 16:04:44 ylafon Exp $
0003: // (c) COPYRIGHT MIT and INRIA, 1996.
0004: // Please first read the full copyright statement in file COPYRIGHT.html
0005:
0006: package org.w3c.jigsaw.http.socket;
0007:
0008: import java.io.IOException;
0009: import java.io.PrintStream;
0010:
0011: import java.net.InetAddress;
0012: import java.net.ServerSocket;
0013: import java.net.Socket;
0014: import java.net.URL;
0015:
0016: import org.w3c.jigsaw.http.Client;
0017: import org.w3c.jigsaw.http.ClientFactory;
0018: import org.w3c.jigsaw.http.httpd;
0019:
0020: import org.w3c.jigsaw.config.PropertySet;
0021:
0022: import org.w3c.util.LRUAble;
0023: import org.w3c.util.LRUList;
0024: import org.w3c.util.ObservableProperties;
0025: import org.w3c.util.PropertyMonitoring;
0026: import org.w3c.util.Status;
0027: import org.w3c.util.SyncLRUList;
0028: import org.w3c.util.ThreadCache;
0029:
0030: class DebugThread extends Thread {
0031: SocketClientFactory pool = null;
0032:
0033: public void run() {
0034: while (true) {
0035: try {
0036: sleep(1000 * 10);
0037: // Display some client statistics:
0038: SocketClientState cs = null;
0039: cs = (SocketClientState) pool.freeList.getHead();
0040: while (cs != null) {
0041: System.out
0042: .println(cs.client + " reqcount="
0043: + cs.client.getRequestCount()
0044: + ", bindcount="
0045: + cs.client.getBindCount());
0046: cs = (SocketClientState) pool.freeList
0047: .getNext((LRUAble) cs);
0048: }
0049: System.out.println("freeCount =" + pool.freeCount);
0050: System.out.println("idleCount =" + pool.idleCount);
0051: System.out.println("totalCount=" + pool.clientCount);
0052: System.out.println("estimCount=" + pool.clientEstim);
0053: System.out.println("Average: " + pool.loadavg);
0054: } catch (Exception ex) {
0055: ex.printStackTrace();
0056: }
0057: }
0058: }
0059:
0060: DebugThread(SocketClientFactory pool) {
0061: this .pool = pool;
0062: setPriority(Thread.MAX_PRIORITY);
0063: }
0064: }
0065:
0066: /**
0067: * The client pool is a kind of client factory.
0068: * Each time the server gets a new connection, it calls the client pool
0069: * to bound a client object (newly created or spared) to handle it.
0070: */
0071:
0072: public class SocketClientFactory implements ClientFactory,
0073: PropertyMonitoring, Status {
0074:
0075: private static final boolean debug = false;
0076: private static final boolean debugstats = false;
0077: private static final boolean debugthread = false;
0078:
0079: public static final int MINSPARE_FREE = 5;
0080: public static final int MAXSPARE_FREE = 10;
0081: public static final int MAXSPARE_IDLE = 20;
0082: public static final int MAXTHREADS = 40;
0083: public static final int MAXCLIENTS = 32;
0084: public static final int IDLETO = 10000;
0085:
0086: public static final int AVG_LIGHT = 1;
0087: public static final int AVG_NORMAL = 2;
0088: public static final int AVG_HIGH = 3;
0089: public static final int AVG_DEAD = 4;
0090:
0091: // FIXME doc
0092: public final static String MINSPARE_FREE_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.minFree";
0093: // FIXME doc
0094: public final static String MAXSPARE_FREE_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxFree";
0095: // FIXME doc
0096: public final static String MAXSPARE_IDLE_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxIdle";
0097: // FIXME doc
0098: public final static String MAXTHREADS_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxThreads";
0099: // FIXME doc
0100: public final static String MAXCLIENTS_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.maxClients";
0101: // FIXME doc
0102: public final static String IDLETO_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.idleTimeout";
0103: // FIXME doc
0104: public final static String BINDADDR_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.bindAddress";
0105: // FIXME doc
0106: public final static String TIMEOUT_P = "org.w3c.jigsaw.http.socket.SocketClientFactory.timeout";
0107:
0108: int minFree = 0;
0109: int maxFree = 0;
0110: int maxIdle = 0;
0111: int maxClients = 0;
0112: InetAddress bindAddr = null;
0113: int timeout = 0;
0114:
0115: int count = 0; // number of created clients.
0116: httpd server = null;
0117: ObservableProperties props = null;
0118: int busyCount = 0; // number of busy clients.
0119:
0120: LRUList idleList = null;
0121: LRUList freeList = null;
0122:
0123: SocketClientState csList = null;
0124:
0125: int idleCount = 0;
0126: int freeCount = 0;
0127: int clientCount = 0;
0128: int clientEstim = 0;
0129:
0130: ThreadCache threadcache = null;
0131:
0132: int loadavg = AVG_LIGHT;
0133:
0134: boolean alive = true;
0135:
0136: /**
0137: * Give the status of this class as a partial HTML text which will be added
0138: * into a block level element
0139: * @return a String, the generated HTML
0140: */
0141: public String getHTMLStatus() {
0142: int idle = 0;
0143: int free = 0;
0144: int used = 0;
0145: int bndc = 0;
0146: int reqc = 0;
0147: int bnd, req;
0148: StringBuffer sb = new StringBuffer();
0149: SocketClientState cs = null;
0150: StringBuffer sb1 = null;
0151: if (debugstats) {
0152: sb1 = new StringBuffer();
0153: }
0154: // used clients
0155: cs = csList;
0156: if (debugstats) {
0157: sb1.append("<table border=\"1\" class=\"idle\">\n"
0158: + "<caption>Used Clients"
0159: + "</caption><tr><th>Id</th><th>BindCount</th>"
0160: + "<th>ReqCount</th><th>Diff</th>"
0161: + "<th>BoundTo</th><th>URI</th></tr>\n");
0162: }
0163: while (cs != null) {
0164: if (cs.status == SocketClientState.C_BUSY) {
0165: InetAddress ia = cs.client.getInetAddress();
0166: bnd = cs.client.getBindCount();
0167: req = cs.client.getRequestCount();
0168: if (debugstats) {
0169: sb1.append("<tr><td>");
0170: sb1.append(cs.id);
0171: sb1.append("</td><td>");
0172: sb1.append(bnd);
0173: sb1.append("</td><td>");
0174: sb1.append(req);
0175: sb1.append("</td><td>");
0176: sb1.append(req - bnd);
0177: sb1.append("</td><td>");
0178: if (ia == null) {
0179: sb1.append("Unbound");
0180: } else {
0181: sb1.append(cs.client.getInetAddress()
0182: .getHostAddress());
0183: }
0184: sb1.append("</td><td>");
0185: if (cs.client.currentURI == null) {
0186: sb1.append('-');
0187: } else {
0188: String u = cs.client.currentURI.toString();
0189: for (int i = 0; i < u.length(); i++) {
0190: char ch = u.charAt(i);
0191: switch (ch) {
0192: case '<':
0193: sb1.append("<");
0194: break;
0195: case '>':
0196: sb1.append(">");
0197: break;
0198: case '&':
0199: sb1.append("&");
0200: break;
0201: default:
0202: sb1.append(ch);
0203: break;
0204: }
0205: }
0206: }
0207: sb1.append("</td></tr>\n");
0208: }
0209: used++;
0210: bndc += bnd;
0211: reqc += req;
0212: }
0213: cs = cs.csnext;
0214: }
0215: if (debugstats) {
0216: sb1.append("</table>\n");
0217: }
0218: // idle clients
0219: cs = (SocketClientState) idleList.getHead();
0220: if (debugstats) {
0221: sb1
0222: .append("<table border=\"1\" class=\"idle\">\n<caption>Idle "
0223: + "Clients</caption><tr><th>Id<th>BindCount<th>ReqCount"
0224: + "<th>Diff<th>BoundTo</tr>\n");
0225: }
0226: while (cs != null) {
0227: InetAddress ia = cs.client.getInetAddress();
0228: idle++;
0229: bnd = cs.client.getBindCount();
0230: req = cs.client.getRequestCount();
0231: if (debugstats) {
0232: sb1.append("<tr><td>"
0233: + cs.id
0234: + "<td>"
0235: + bnd
0236: + "<td>"
0237: + req
0238: + "<td>"
0239: + (req - bnd)
0240: + "<td>"
0241: + ((ia == null) ? "Unbound" : cs.client
0242: .getInetAddress().getHostAddress())
0243: + "</tr>\n");
0244: }
0245: bndc += bnd;
0246: reqc += req;
0247: cs = (SocketClientState) idleList.getNext(cs);
0248: }
0249: if (debugstats) {
0250: sb1.append("</table>\n");
0251: }
0252: // free clients
0253: cs = (SocketClientState) freeList.getHead();
0254: if (debugstats) {
0255: sb1
0256: .append("<table border=\"1\" class=\"idle\">\n"
0257: + "<caption>Free Clients"
0258: + "</caption><tr><th>Id<th>BindCount<th>ReqCount<th>"
0259: + "Diff</tr>\n");
0260: }
0261: while (cs != null) {
0262: free++;
0263: bnd = cs.client.getBindCount();
0264: req = cs.client.getRequestCount();
0265: if (debugstats) {
0266: sb1.append("<tr><td>" + cs.id + "<td>" + bnd + "<td>"
0267: + req + "<td>" + (req - bnd) + "\n");
0268: }
0269: bndc += bnd;
0270: reqc += req;
0271: cs = (SocketClientState) freeList.getNext(cs);
0272: }
0273: if (debugstats) {
0274: sb1.append("</table>\n");
0275: }
0276:
0277: // stats
0278: sb
0279: .append("<table border class=\"thread\">\n<caption>Thread counts"
0280: + "</caption><tr><th>free<th>idle<th>used"
0281: + "<th>estim<th>total<th>Load</tr>");
0282: sb.append("<tr><td>");
0283: sb.append(freeCount);
0284: sb.append('(');
0285: sb.append(free);
0286: sb.append(")</td><td>");
0287: sb.append(idleCount);
0288: sb.append('(');
0289: sb.append(idle);
0290: sb.append(")</td><td>");
0291: sb.append(clientCount - freeCount - idleCount);
0292: sb.append('(');
0293: sb.append(used);
0294: sb.append(")</td><td>");
0295: sb.append(clientEstim);
0296: sb.append("</td><td>");
0297: sb.append(clientCount);
0298: sb.append("</td><td>");
0299: sb.append(loadavg);
0300: sb.append("</td></tr></table>\n");
0301: // usage stats
0302: sb
0303: .append("<table border class=\"usage\">\n<caption>Usage</caption>"
0304: + "<tr><th>ReqCount<th>BindCount<th>Diff</tr>\n<tr><td>");
0305: sb.append(reqc);
0306: sb.append("</td><td>");
0307: sb.append(bndc);
0308: sb.append("</td><td>");
0309: sb.append(reqc - bndc);
0310: sb.append("</td></tr></table>\n");
0311: if (debugstats) {
0312: sb.append(sb1);
0313: }
0314:
0315: if (debugstats) {
0316: cs = csList;
0317: sb
0318: .append("<table border=\"1\" class=\"idle\">\n<caption>General"
0319: + " Status</caption><tr><th>Id<th>Client<th>"
0320: + "Status<th>marked<th>Thread</tr>\n");
0321: while (cs != null) {
0322: sb.append("<tr><td>" + cs.id + "<td>"
0323: + ((cs.client == null) ? "None" : "bound")
0324: + "<td>");
0325: switch (cs.status) {
0326: case SocketClientState.C_IDLE:
0327: sb.append("Idle");
0328: break;
0329: case SocketClientState.C_BUSY:
0330: sb.append("Busy");
0331: break;
0332: case SocketClientState.C_FREE:
0333: sb.append("Free");
0334: break;
0335: case SocketClientState.C_KILL:
0336: sb.append("Kill");
0337: break;
0338: case SocketClientState.C_FIN:
0339: sb.append("Fin");
0340: break;
0341: }
0342: sb.append("<td>" + cs.marked);
0343: if (cs.client != null) {
0344: sb.append("<td>" + cs.client.thread + "</tr>\n");
0345: } else {
0346: sb.append("<td>No CLient</tr>\n");
0347: }
0348: cs = cs.csnext;
0349: }
0350: sb.append("</table>\n");
0351: }
0352: return sb.toString();
0353: }
0354:
0355: /**
0356: * Some property have changed, update our setting.
0357: * @param name The name of the property that has changed.
0358: * @return A boolean, <strong>true</strong> if we updated ourself
0359: * successfully.
0360: */
0361: public boolean propertyChanged(String name) {
0362: httpd s = server;
0363: if (name.equals(MINSPARE_FREE_P)) {
0364: minFree = props.getInteger(MINSPARE_FREE_P, minFree);
0365: } else if (name.equals(MAXSPARE_FREE_P)) {
0366: maxFree = props.getInteger(MAXSPARE_FREE_P, maxFree);
0367: } else if (name.equals(MAXSPARE_IDLE_P)) {
0368: maxIdle = props.getInteger(MAXSPARE_IDLE_P, maxIdle);
0369: } else if (name.equals(MAXTHREADS_P)) {
0370: int maxThreads = props.getInteger(MAXTHREADS_P, -1);
0371: if (maxThreads > 0)
0372: threadcache.setCachesize(maxThreads);
0373: } else if (name.equals(IDLETO_P)) {
0374: int idleto = props.getInteger(IDLETO_P, -1);
0375: if (idleto > 0) {
0376: threadcache.setIdleTimeout(idleto);
0377: }
0378: } else if (name.equals(MAXCLIENTS_P)) {
0379: int newmax = props.getInteger(MAXCLIENTS_P, -1);
0380: if (newmax > maxClients) {
0381: for (int i = maxClients - newmax; --i >= 0;)
0382: addClient(true);
0383: } else if (newmax > 0) {
0384: maxClients = newmax;
0385: }
0386: } else if (name.equals(BINDADDR_P)) {
0387: try {
0388: bindAddr = InetAddress.getByName(props.getString(
0389: BINDADDR_P, null));
0390: } catch (Exception ex) {
0391: // nothing
0392: }
0393: } else if (name.equals(TIMEOUT_P)) {
0394: timeout = props.getInteger(IDLETO_P, timeout);
0395: }
0396: return true;
0397: }
0398:
0399: /**
0400: * Remove this client state from the glohbal client list.
0401: * @param cs The client state to remove from the list.
0402: */
0403:
0404: protected synchronized void deleteClient(SocketClientState cs) {
0405: synchronized (csList) {
0406: if (cs.csprev == null) {
0407: csList = cs.csnext;
0408: } else if (cs.csnext == null) {
0409: cs.csprev.csnext = null;
0410: } else {
0411: cs.csprev.csnext = cs.csnext;
0412: cs.csnext.csprev = cs.csprev;
0413: }
0414: }
0415: }
0416:
0417: /**
0418: * Factory for creating a new client for this pool.
0419: * @param server the target http daemon
0420: * @param state the client state holder
0421: * @return a new socket client
0422: */
0423: protected SocketClient createClient(httpd server,
0424: SocketClientState state) {
0425: return new SocketClient(server, this , state);
0426: }
0427:
0428: /**
0429: * Create a new client for this pool.
0430: * @param free A boolean, if <strong>true</strong> the client is inserted
0431: * straight into the free list, otherwise, it is not plugged into any
0432: * list.
0433: * @return A SocketClientState instance, if creation of a new client was
0434: * allowed, <strong>null</strong> if no more clients could be created.
0435: */
0436:
0437: protected synchronized SocketClientState addClient(boolean free) {
0438: // Create a new client.
0439: csList = new SocketClientState(csList);
0440: SocketClientState cs = csList;
0441: SocketClient client = createClient(server, cs);
0442: cs.client = client;
0443: clientCount++;
0444: clientEstim++;
0445: // Plug into free LRU if required:
0446: if (free) {
0447: cs.status = SocketClientState.C_FREE;
0448: freeList.toHead(cs);
0449: freeCount++;
0450: }
0451: return cs;
0452: }
0453:
0454: /**
0455: * We are not using synchronized functions to speed up things,
0456: * but it is sometime useful to check that clients are not in a bad
0457: * shape
0458: */
0459: private final void checkDeadClients() {
0460: SocketClientState cs = null;
0461: cs = csList;
0462: boolean check = true;
0463: int idlecount = 0;
0464: int freecount = 0;
0465: int clientcount = 0;
0466:
0467: while (cs != null) {
0468: if (cs.client != null) {
0469: clientcount++;
0470: switch (cs.status) {
0471: case SocketClientState.C_BUSY:
0472: if (cs.client.thread == null) {
0473: if (cs.marked) {
0474: if (clientEstim <= maxClients) {
0475: cs.marked = false;
0476: ++freeCount;
0477: updateLoadAverage();
0478: freeList.toHead(cs);
0479: cs.status = SocketClientState.C_FREE;
0480: cs.client.done = true;
0481: }
0482: check = false;
0483: } else {
0484: cs.marked = true;
0485: }
0486: }
0487: break;
0488: case SocketClientState.C_FREE:
0489: freecount++;
0490: cs.marked = false;
0491: break;
0492: default:
0493: cs.marked = false;
0494: break;
0495: }
0496: }
0497: cs = cs.csnext;
0498: }
0499: // sanity check
0500: if (freecount != freeCount) {
0501: cs = (SocketClientState) idleList.getHead();
0502: while (cs != null) {
0503: idlecount++;
0504: cs = (SocketClientState) idleList.getNext(cs);
0505: }
0506: cs = (SocketClientState) freeList.getHead();
0507: freecount = 0;
0508: while (cs != null) {
0509: freecount++;
0510: cs = (SocketClientState) freeList.getNext(cs);
0511: }
0512: freeCount = freecount;
0513: idleCount = idlecount;
0514: }
0515: }
0516:
0517: /**
0518: * Update our idea of the current load.
0519: * The one important invariant here, is that whenever the free list
0520: * becomes empty, then the load average should be equals to
0521: * <strong>AVG_DEAD</strong>. This ensures that the server will start
0522: * dropping connections.
0523: *
0524: */
0525: private final void updateLoadAverage() {
0526: int oldavg = loadavg;
0527: if (freeCount >= maxFree) {
0528: loadavg = AVG_LIGHT;
0529: } else if ((freeCount >= minFree) || (idleCount >= maxIdle)) {
0530: if ((loadavg = AVG_NORMAL) < oldavg) {
0531: server.thread.setPriority(Thread.MAX_PRIORITY);
0532: }
0533: } else if (freeCount > 0) {
0534: /* idleCount < MINSPARE_IDLE */
0535: if ((loadavg = AVG_HIGH) > oldavg) {
0536: // first ensure the state is sane
0537: // checkDeadClients();
0538: server.thread.setPriority(server
0539: .getClientThreadPriority() - 2);
0540: }
0541: } else {
0542: loadavg = AVG_DEAD;
0543: }
0544: }
0545:
0546: private final synchronized void incrClientCount() {
0547: ++clientCount;
0548: ++clientEstim;
0549: updateLoadAverage();
0550: }
0551:
0552: private final synchronized void decrClientCount() {
0553: --clientCount;
0554: updateLoadAverage();
0555: }
0556:
0557: private final synchronized boolean incrFreeCount() {
0558: if (clientEstim > maxClients) {
0559: clientEstim--;
0560: return false;
0561: }
0562: ++freeCount;
0563: updateLoadAverage();
0564: return true;
0565: }
0566:
0567: private final synchronized boolean decrFreeCount() {
0568: if (freeCount > 0) {
0569: --freeCount;
0570: updateLoadAverage();
0571: return true;
0572: } else {
0573: return false;
0574: }
0575: }
0576:
0577: private final synchronized boolean incrIdleCount() {
0578: if ((loadavg > AVG_HIGH) || (idleCount + 1 >= maxIdle))
0579: return false;
0580: ++idleCount;
0581: updateLoadAverage();
0582: return true;
0583: }
0584:
0585: private final synchronized boolean decrIdleCount() {
0586: if (idleCount > 0) {
0587: --idleCount;
0588: updateLoadAverage();
0589: return true;
0590: } else {
0591: return false;
0592: }
0593: }
0594:
0595: /**
0596: * Removes an idle client from the list, updates only the idle list
0597: * as the free count has already be accessed
0598: * @param the socket client to remove from the idle list
0599: */
0600: protected boolean idleClientRemove(SocketClient client) {
0601: // If the client pool has shut down, exit straight:
0602: if (!alive)
0603: return false;
0604: SocketClientState cs = client.state;
0605: synchronized (csList) {
0606: switch (cs.status) {
0607: case SocketClientState.C_IDLE:
0608: decrIdleCount();
0609: idleList.remove(cs);
0610: cs.status = SocketClientState.C_FREE;
0611: break;
0612: case SocketClientState.C_KILL:
0613: case SocketClientState.C_BUSY:
0614: default:
0615: break;
0616: }
0617: }
0618: return true;
0619: }
0620:
0621: /**
0622: * Notify that this client has finished with its connection.
0623: * If the pool wants the client to be freed (because it has too many of
0624: * them), it makes the client kill itself (which will trigger a call to
0625: * the clientFinished method, were enventual cleanup is performed).
0626: * @param client The client that is done with its connection.
0627: */
0628:
0629: protected boolean clientConnectionFinished(SocketClient client) {
0630: // If the client pool has shut down, exit straight:
0631: if (!alive)
0632: return false;
0633: SocketClientState cs = client.state;
0634: synchronized (csList) {
0635: switch (cs.status) {
0636: case SocketClientState.C_IDLE:
0637: decrIdleCount();
0638: idleList.remove(cs);
0639: break;
0640: case SocketClientState.C_BUSY:
0641: case SocketClientState.C_KILL:
0642: break;
0643: case SocketClientState.C_FREE:
0644: // already freeed?
0645: if (client.done) {
0646: client.done = false;
0647: return true;
0648: }
0649: default:
0650: break;
0651: }
0652: if (incrFreeCount()) {
0653: if (debug)
0654: System.out.println(client + ": now free.");
0655: cs.status = SocketClientState.C_FREE;
0656: freeList.toHead(cs);
0657: return true;
0658: } else {
0659: if (debug)
0660: System.out.println(client + ": terminate.");
0661: return false;
0662: }
0663: }
0664: }
0665:
0666: /**
0667: * Notify that this client has been killed.
0668: * @param client The client that has terminate.
0669: */
0670:
0671: protected void clientFinished(SocketClient client) {
0672: // If we're not alive any more, skip:
0673: if (!alive)
0674: return;
0675: SocketClientState cs = client.state;
0676: synchronized (csList) {
0677: if (debug)
0678: System.out.println(client + ": finished " + cs.status);
0679: // Otherwise, perform the job:
0680: switch (cs.status) {
0681: case SocketClientState.C_IDLE:
0682: break;
0683: case SocketClientState.C_FREE:
0684: decrFreeCount();
0685: freeList.remove(cs);
0686: break;
0687: case SocketClientState.C_BUSY:
0688: default:
0689: String msg = (client
0690: + ": finished with unknown status " + cs.status);
0691: server.errlog(msg);
0692: break;
0693: }
0694: cs.status = SocketClientState.C_FIN;
0695: decrClientCount();
0696: deleteClient(cs);
0697: }
0698: }
0699:
0700: /**
0701: * The client notifies the pool that is has been activated.
0702: * The client state object is updated to unmark the client as idle.
0703: * <p>This method needs not be synchronized, as it affect only the client
0704: * state, <em>not</em> the client list.
0705: * @param client The activated client.
0706: */
0707:
0708: protected void notifyUse(SocketClient client) {
0709: if (debug)
0710: System.out.println(client + ": used.");
0711: SocketClientState cs = client.state;
0712: synchronized (csList) {
0713: if (cs.status == SocketClientState.C_IDLE) {
0714: decrIdleCount();
0715: idleList.remove(cs);
0716: }
0717: cs.status = SocketClientState.C_BUSY;
0718: }
0719: }
0720:
0721: /**
0722: * The client notifies the pool that it enters idle state.
0723: * <p>This method needs not be synchronized, as it affect only the client
0724: * state, <em>not</em> the client list.
0725: * @param client The client that is going to be idle.
0726: */
0727:
0728: protected boolean notifyIdle(SocketClient client) {
0729: SocketClientState cs = client.state;
0730: if (alive) {
0731: synchronized (csList) {
0732: if (incrIdleCount()) {
0733: if (debug)
0734: System.out.println(client
0735: + ": idle, keep-alive.");
0736: cs.status = SocketClientState.C_IDLE;
0737: idleList.toHead(cs);
0738: return true;
0739: } else {
0740: if (debug)
0741: System.out.println(client + ": idle, closed.");
0742: // Kill some old idle connections, give a chance for next:
0743: int killsome = Math.max((maxFree - freeCount),
0744: (maxIdle - idleCount));
0745: killSomeClients((killsome > 0) ? killsome : 1);
0746: // And give it a change if the load is not too high
0747: if (incrIdleCount()) {
0748: if (debug)
0749: System.out.println(client
0750: + ": idle, keep-alive.");
0751: cs.status = SocketClientState.C_IDLE;
0752: idleList.toHead(cs);
0753: return true;
0754: }
0755: return false;
0756: }
0757: }
0758: } else {
0759: if (debug)
0760: System.out.println(client + ": idle (dead), closed.");
0761: // Kill some old idle connections, give a chance for next:
0762: int killsome = Math.max((maxFree - freeCount),
0763: (maxIdle - idleCount));
0764: killSomeClients((killsome > 0) ? killsome : 1);
0765: return false;
0766: }
0767: }
0768:
0769: protected void killSomeClients(int howmany) {
0770: int count = (howmany > 0) ? howmany : Math.max(
0771: (maxFree - freeCount), (maxIdle - idleCount));
0772: if (debug) {
0773: System.out.println("Killing :" + howmany);
0774: }
0775: while (--count >= 0) {
0776: SocketClientState cs = (SocketClientState) idleList
0777: .removeTail();
0778: if (cs != null) {
0779: synchronized (csList) {
0780: if (cs.status == SocketClientState.C_IDLE) {
0781: if (debug)
0782: System.out.println(cs.client
0783: + ": kill (some-client).");
0784: decrIdleCount();
0785: cs.status = SocketClientState.C_KILL;
0786: cs.client.unbind();
0787: }
0788: }
0789: } else {
0790: break;
0791: }
0792: // if the load falls back to normal operation, we are all set
0793: if ((freeCount > minFree) && (idleCount < maxIdle)) {
0794: break;
0795: }
0796: }
0797: }
0798:
0799: final protected void killSomeClients() {
0800: killSomeClients(-1);
0801: }
0802:
0803: protected void run(SocketClient client) {
0804: if (debug)
0805: System.out.println(client + ": warming up...");
0806: boolean threaded = threadcache.getThread(client, true);
0807: if (debug)
0808: System.out.println(client + ": threaded=" + threaded);
0809: }
0810:
0811: /**
0812: * Handle the given connection.
0813: * Find a free client, bind it to the given socket, and run it. If we
0814: * have reached our maximum allowed number of clients, kill some
0815: * connections.
0816: * <p>A client enters the LRU list (and become a candidate for kill) only
0817: * after it has handle one request (or if some timeout expires). This is
0818: * performed by the first call to <code>notifyUse</code> which will
0819: * silently insert the client into the LRU if it was not there already.
0820: * <p>This client pool does a lot of nice thinigs, but could probably be
0821: * implemented in a much better way (while keeping the features it has).
0822: * Contention arond the pool is probably concern number 1 of performances.
0823: * @param socket The connection to handle.
0824: */
0825:
0826: public void handleConnection(Socket socket) {
0827: if (debug)
0828: System.out.println("new connection.");
0829: SocketClientState cs = null;
0830: switch (loadavg) {
0831: case AVG_LIGHT:
0832: // Free list is non empty, be fast:
0833: if (decrFreeCount()) {
0834: cs = (SocketClientState) freeList.removeTail();
0835: if (cs == null) {
0836: while (!incrFreeCount()) {
0837: }
0838: }
0839: }
0840: break;
0841: case AVG_NORMAL:
0842: case AVG_HIGH:
0843: // Free list is non empty, but we try killing a client:
0844: killSomeClients();
0845: if (decrFreeCount()) {
0846: cs = (SocketClientState) freeList.removeTail();
0847: if (cs == null) {
0848: while (!incrFreeCount()) {
0849: }
0850: }
0851: }
0852: break;
0853: case AVG_DEAD:
0854: break;
0855: }
0856: if (debug)
0857: System.out
0858: .println("load "
0859: + loadavg
0860: + ", client="
0861: + ((cs != null) ? cs.client.toString()
0862: : "unbound"));
0863: // At this point, we do have a free client, bind it:
0864: if (cs != null) {
0865: if (debug)
0866: System.out.println(cs.client + ": bound.");
0867: cs.status = SocketClientState.C_BUSY;
0868: cs.client.bind(socket);
0869: } else {
0870: if (debug)
0871: System.out
0872: .println("*** connection refused (overloaded).");
0873: try {
0874: socket.close();
0875: } catch (IOException ex) {
0876: }
0877: server.errlog(socket.getInetAddress()
0878: + " refused (overloaded).");
0879: }
0880: return;
0881: }
0882:
0883: protected synchronized void killClients(boolean force) {
0884: alive = false;
0885: // Kill all clients (first shot):
0886: SocketClientState cs = csList;
0887: while ((cs != null) && (cs.client != null)) {
0888: synchronized (csList) {
0889: // Only if a client is idely read'ing its socket, we close it
0890: cs.client.kill(cs.status == SocketClientState.C_IDLE);
0891: }
0892: cs = cs.csnext;
0893: }
0894: // Kill all clients (second shot):
0895: // Some client may be in transition during first shot, second shot
0896: // really kills everything.
0897: try {
0898: Thread.sleep(5000);
0899: } catch (Exception ex) {
0900: }
0901: cs = csList;
0902: while ((cs != null) && (cs.client != null)) {
0903: synchronized (csList) {
0904: cs.client.kill(true);
0905: }
0906: cs = cs.csnext;
0907: }
0908: }
0909:
0910: /**
0911: * Shutdown the client pool.
0912: * If force is <strong>true</strong>, kill all running clients right
0913: * now, otherwise, wait for them to terminate gracefully, and return
0914: * when done.
0915: * @param force Should we interrupt running clients.
0916: */
0917:
0918: public void shutdown(boolean force) {
0919: // First stage: kill all clients (synchronized)
0920: killClients(force);
0921: // Second stage (unsynchronized), join all client threads
0922: SocketClientState cs = csList;
0923: while ((cs != null) && (cs.client != null)) {
0924: if (debug)
0925: System.out.println(cs.client + ": join.");
0926: cs.client.join();
0927: cs = cs.csnext;
0928: }
0929: // Some cleanup (helps the GC, and make sure everything is free)
0930: props.unregisterObserver(this );
0931: props = null;
0932: csList = null;
0933: freeList = null;
0934: idleList = null;
0935: server = null;
0936: }
0937:
0938: /**
0939: * Create the master socket for this client factory.
0940: * @exception IOException If some IO error occurs while creating the
0941: * server socket.
0942: * @return A ServerSocket instance.
0943: */
0944:
0945: public ServerSocket createServerSocket() throws IOException {
0946: // using maxCLient in the backlog is safe, but an overkill :)
0947: if (bindAddr == null) {
0948: return new ServerSocket(server.getPort(), Math.max(128,
0949: maxClients));
0950: } else {
0951: return new ServerSocket(server.getPort(), Math.max(128,
0952: maxClients), bindAddr);
0953: }
0954: }
0955:
0956: /**
0957: * Initialize the raw, client socket factory.
0958: * @param server The server context we are attached to.
0959: */
0960:
0961: public void initialize(httpd server) {
0962: // Initialize instance variables:
0963: this .server = server;
0964: this .props = server.getProperties();
0965: this .props.registerObserver(this );
0966: // Register our property sheet:
0967: PropertySet set = new SocketConnectionProp(
0968: "SocketConnectionProp", server);
0969: server.registerPropertySet(set);
0970: // Initialize parameters from properties:
0971: this .minFree = props.getInteger(MINSPARE_FREE_P, MINSPARE_FREE);
0972: this .maxFree = props.getInteger(MAXSPARE_FREE_P, MAXSPARE_FREE);
0973: this .maxIdle = props.getInteger(MAXSPARE_IDLE_P, MAXSPARE_IDLE);
0974: this .maxClients = props.getInteger(MAXCLIENTS_P, MAXCLIENTS);
0975: this .timeout = props.getInteger(TIMEOUT_P, IDLETO);
0976: String bindAddrName = props.getString(BINDADDR_P, null);
0977: if (bindAddrName != null) {
0978: try {
0979: bindAddr = InetAddress.getByName(bindAddrName);
0980: } catch (Exception ex) {
0981: // nothing, fallback to default
0982: }
0983: }
0984: // Create the LRU lists:
0985: idleList = new SyncLRUList();
0986: freeList = new SyncLRUList();
0987: // Create the full client list:
0988: csList = new SocketClientState();
0989: // Create all our clients:
0990: for (int i = 0; i < maxClients; i++) {
0991: if (addClient(true) == null)
0992: throw new RuntimeException(this .getClass().getName()
0993: + "[construstructor]"
0994: + ": unable to create clients.");
0995: }
0996: // Create the thread cache:
0997: threadcache = new ThreadCache(server.getIdentifier()
0998: + "-socket-clients");
0999: threadcache.setCachesize(props.getInteger(MAXTHREADS_P,
1000: MAXTHREADS));
1001: threadcache.setThreadPriority(server.getClientThreadPriority());
1002: threadcache.setIdleTimeout(props.getInteger(IDLETO_P, IDLETO));
1003: threadcache.setGrowAsNeeded(true);
1004: threadcache.initialize();
1005: // Start the debugging thread, if needed:
1006: if (debugthread) {
1007: new DebugThread(this ).start();
1008: }
1009: }
1010:
1011: /**
1012: * Empty constructor for dynamic class instantiation.
1013: */
1014:
1015: public SocketClientFactory() {
1016: }
1017:
1018: }
|