0001: // ServletWrapper.java
0002: // $Id: ServletWrapper.java,v 1.78 2007/02/11 10:50:01 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.servlet;
0007:
0008: import java.io.File;
0009: import java.io.IOException;
0010: import java.io.OutputStream;
0011: import java.io.PrintStream;
0012: import java.io.PrintWriter;
0013:
0014: import java.util.Enumeration;
0015: import java.util.ArrayList;
0016: import java.util.NoSuchElementException;
0017:
0018: import javax.servlet.Servlet;
0019: import javax.servlet.ServletConfig;
0020: import javax.servlet.ServletContext;
0021: import javax.servlet.ServletException;
0022: import javax.servlet.SingleThreadModel;
0023: import javax.servlet.UnavailableException;
0024:
0025: import org.w3c.tools.timers.EventHandler;
0026: import org.w3c.tools.timers.EventManager;
0027:
0028: import org.w3c.util.ArrayDictionary;
0029: import org.w3c.util.EmptyEnumeration;
0030: import org.w3c.util.ThreadCache;
0031:
0032: import org.w3c.jigsaw.http.Reply;
0033: import org.w3c.jigsaw.http.Request;
0034: import org.w3c.jigsaw.http.httpd;
0035:
0036: import org.w3c.tools.resources.Attribute;
0037: import org.w3c.tools.resources.AttributeHolder;
0038: import org.w3c.tools.resources.AttributeRegistry;
0039: import org.w3c.tools.resources.FramedResource;
0040: import org.w3c.tools.resources.InvalidResourceException;
0041: import org.w3c.tools.resources.IntegerAttribute;
0042: import org.w3c.tools.resources.LongAttribute;
0043: import org.w3c.tools.resources.ObjectAttribute;
0044: import org.w3c.tools.resources.PropertiesAttribute;
0045: import org.w3c.tools.resources.Resource;
0046: import org.w3c.tools.resources.ResourceReference;
0047: import org.w3c.tools.resources.ServerInterface;
0048: import org.w3c.tools.resources.StringAttribute;
0049:
0050: import org.w3c.www.http.HTTP;
0051:
0052: /**
0053: * @author Alexandre Rafalovitch <alex@access.com.au>
0054: * @author Anselm Baird-Smith <abaird@w3.org>
0055: * @author Benoit Mahe <bmahe@w3.org>
0056: * @author Yves Lafon <ylafon@w3.org>
0057: */
0058:
0059: public class ServletWrapper extends FramedResource implements
0060: ServletConfig {
0061:
0062: static final ThreadCache threadCache = new ThreadCache(
0063: "servlet-runner");
0064:
0065: /**
0066: * The overall thread cache size
0067: */
0068: static int global = 0;
0069:
0070: /**
0071: * The instance amount of the cache size
0072: */
0073: private int local = 0;
0074:
0075: /**
0076: * Tunes the thread cache.
0077: *
0078: * @param capacity the required cache capacity of this instance
0079: */
0080: private final void tuneCache(int capacity) {
0081: int update = capacity - local;
0082: if (update != 0) {
0083: local = capacity;
0084: synchronized (threadCache) {
0085: global = Math.max(global + update, 0);
0086: threadCache.setCachesize(global);
0087: if (debug) {
0088: System.out.println("cacheupdate local=" + local
0089: + " global=" + global);
0090: }
0091: }
0092: }
0093: }
0094:
0095: static {
0096: threadCache.setCachesize(global);
0097: threadCache.setGrowAsNeeded(true);
0098: // threadCache.setIdleTimeout(86400000);
0099: threadCache.initialize();
0100: }
0101:
0102: protected class TimeoutManager implements EventHandler {
0103:
0104: private Object timer = null;
0105: private httpd server = null;
0106:
0107: /**
0108: * Handle timer events.
0109: * @param data The timer closure.
0110: * @param time The absolute time at which the event was triggered.
0111: * @see org.w3c.tools.timers.EventManager
0112: * @see org.w3c.tools.timers.EventHandler
0113: */
0114: public void handleTimerEvent(Object data, long time) {
0115: synchronized (this ) {
0116: timer = null;
0117: }
0118: // FIXME, each servlet instance available should have its
0119: // individual timeout manager.
0120: // Thus, resources could be released as required. This mechanism
0121: // would opt for a more fine grained servlet instance management.
0122: destroyServlet();
0123: }
0124:
0125: private synchronized void setTimer(long ms) {
0126: if (timer != null) {
0127: server.timer.recallTimer(timer);
0128: timer = null;
0129: }
0130: timer = server.timer.registerTimer(ms, this , null);
0131: }
0132:
0133: protected void restart() {
0134: start();
0135: }
0136:
0137: protected void start() {
0138: long timeout = getServletTimeout();
0139: if (timeout != -1)
0140: setTimer(timeout);
0141: }
0142:
0143: protected synchronized void stop() {
0144: if (timer != null) {
0145: server.timer.recallTimer(timer);
0146: timer = null;
0147: }
0148: }
0149:
0150: TimeoutManager(httpd server) {
0151: this .server = server;
0152: }
0153:
0154: }
0155:
0156: // used to pass the runner as a state
0157: public static final String RUNNER = "org.w3c.jigsaw.servlet.runner";
0158: // used to signal the end of the servlet in the Reply
0159: public static final String ENDED = "org.w3c.jigsaw.servlet.ended";
0160:
0161: protected TimeoutManager timeoutManager = null;
0162:
0163: // protected int connections = 0;
0164:
0165: protected final static boolean debug = false;
0166:
0167: /**
0168: * Attributes index - The servlet class name.
0169: */
0170: protected static int ATTR_SERVLET_CLASS = -1;
0171: /**
0172: * Attributes index - The servlet timeout
0173: */
0174: protected static int ATTR_SERVLET_TIMEOUT = -1;
0175: /**
0176: * Attributes index - The servlet maxinstances for single thread model
0177: * servlet instance pool size limitation, tk, 20.10.2001
0178: */
0179: protected static int ATTR_SERVLET_INSTANCEMAX = -1;
0180: /**
0181: * Attribute index - The init parameters for that servlet.
0182: */
0183: protected static int ATTR_PARAMETERS = 1;
0184: /**
0185: * Attribute index - Our parent-inherited servlet context.
0186: */
0187: protected static int ATTR_SERVLET_CONTEXT = -1;
0188: /**
0189: * Attribute index - Our parent-inherited session context.
0190: */
0191: protected static int ATTR_SESSION_CONTEXT = -1;
0192:
0193: static {
0194: Attribute a = null;
0195: Class cls = null;
0196: try {
0197: cls = Class
0198: .forName("org.w3c.jigsaw.servlet.ServletWrapper");
0199: } catch (Exception ex) {
0200: ex.printStackTrace();
0201: System.exit(0);
0202: }
0203: // The servlet class attribute.
0204: a = new StringAttribute("servlet-class", null,
0205: Attribute.EDITABLE | Attribute.MANDATORY);
0206: ATTR_SERVLET_CLASS = AttributeRegistry
0207: .registerAttribute(cls, a);
0208: // This servlet init parameters
0209: a = new PropertiesAttribute("servlet-parameters", null,
0210: Attribute.EDITABLE);
0211: ATTR_PARAMETERS = AttributeRegistry.registerAttribute(cls, a);
0212: // Our servlet context:
0213: a = new ObjectAttribute("servlet-context",
0214: "org.w3c.jigsaw.servlet.JigsawServletContext", null,
0215: Attribute.DONTSAVE);
0216: ATTR_SERVLET_CONTEXT = AttributeRegistry.registerAttribute(cls,
0217: a);
0218: // Our session context:
0219: a = new ObjectAttribute("session-context",
0220: "org.w3c.jigsaw.servlet.JigsawHttpSessionContext",
0221: null, Attribute.DONTSAVE);
0222: ATTR_SESSION_CONTEXT = AttributeRegistry.registerAttribute(cls,
0223: a);
0224: // The servlet timeout:
0225: a = new LongAttribute("servlet-timeout", null,
0226: Attribute.EDITABLE);
0227: ATTR_SERVLET_TIMEOUT = AttributeRegistry.registerAttribute(cls,
0228: a);
0229: // The servlet maxinstances:
0230: a = new IntegerAttribute("servlet-instancemax", null,
0231: Attribute.EDITABLE);
0232: ATTR_SERVLET_INSTANCEMAX = AttributeRegistry.registerAttribute(
0233: cls, a);
0234: }
0235:
0236: /**
0237: * Using a limited pool of servlet instances instead of a single
0238: * instance, tk, 22.10.2001
0239: * protected Servlet servlet = null;
0240: */
0241: protected final ServletPool servletPool = new ServletPool();
0242:
0243: /**
0244: * Is our servler initialized ?
0245: */
0246: protected boolean inited = false;
0247:
0248: /**
0249: * The Path where we can find the servlet class file.
0250: */
0251: public File getServletDirectory() {
0252: ResourceReference rr = getParent();
0253: if (rr != null) {
0254: try {
0255: Resource parent = rr.lock();
0256: if (parent.definesAttribute("directory"))
0257: return (File) parent.getValue("directory", null);
0258: } catch (InvalidResourceException ex) {
0259: ex.printStackTrace();
0260: } finally {
0261: rr.unlock();
0262: }
0263: }
0264: return null;
0265: }
0266:
0267: /**
0268: * Servlet stub implementation - Get an init parameter value.
0269: */
0270: public synchronized String getInitParameter(String string) {
0271: ArrayDictionary d = getServletParameters();
0272: String v = (d != null) ? (String) d.get(string) : null;
0273: return v;
0274: }
0275:
0276: /**
0277: * Servlet stub implementation - Get all init parameters.
0278: */
0279: public synchronized Enumeration getInitParameterNames() {
0280: // Convert init parameters to hashtable:
0281: ArrayDictionary d = getServletParameters();
0282: return (d != null) ? d.keys() : new EmptyEnumeration();
0283: }
0284:
0285: /**
0286: * Servlet stub implementation - Get that servlet context.
0287: */
0288: public ServletContext getServletContext() {
0289: ServletContext ctxt = (ServletContext) getValue(
0290: ATTR_SERVLET_CONTEXT, null);
0291: return ctxt;
0292: }
0293:
0294: public JigsawHttpSessionContext getSessionContext() {
0295: return (JigsawHttpSessionContext) getValue(
0296: ATTR_SESSION_CONTEXT, null);
0297: }
0298:
0299: protected long getServletTimeout() {
0300: long timeout = getLong(ATTR_SERVLET_TIMEOUT, 0);
0301: if (timeout == 0) {
0302: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
0303: timeout = ctxt.getServletTimeout();
0304: }
0305: return timeout;
0306: }
0307:
0308: protected int getInstanceMax() {
0309: // added for single thread model
0310: // servlet instance pool size limitation, tk, 20.10.2001
0311: int instancemax = getInt(ATTR_SERVLET_INSTANCEMAX, 0);
0312:
0313: if (instancemax == 0) {
0314: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
0315: instancemax = ctxt.getServletInstanceMax();
0316: }
0317: return instancemax;
0318: }
0319:
0320: protected void invalidateAllSession() {
0321: if (debug) {
0322: System.out.println("Invalidate All Session...");
0323: }
0324: JigsawHttpSessionContext ctxt = getSessionContext();
0325: Enumeration e = ctxt.getIds();
0326: while (e.hasMoreElements()) {
0327: ctxt.getSession((String) e.nextElement()).invalidate();
0328: }
0329: }
0330:
0331: /**
0332: * Check the servlet class, and try to initialize it.
0333: * @exception ClassNotFoundException if servlet class can't be found.
0334: * @exception ServletException if servlet can't be initialized.
0335: */
0336: protected void checkServlet() throws ClassNotFoundException,
0337: ServletException {
0338: synchronized (servletPool) {
0339: // tk, 20.10.2001, synchronized loading is necessary at
0340: // least for the non-SingleThreadModel
0341: boolean classmodified = getLocalServletLoader()
0342: .classChanged(getServletClass());
0343:
0344: if ((!inited) || classmodified ||
0345: // (servlet.getClass() !=
0346: // getLocalServletLoader().loadClass(getServletClass()))
0347: (servletPool.getLoadedClass() != getLocalServletLoader()
0348: .loadClass(getServletClass()))) {
0349: inited = launchServlet();
0350: }
0351: }
0352: }
0353:
0354: protected boolean isInited() {
0355: return inited;
0356: }
0357:
0358: /**
0359: * Check and eventually load the servlet we are wrapping.
0360: * This method was added for replacing getServlet() during access checks
0361: * in order to do perform checks without accessing a servlet instance.
0362: * @return true if and only if the sevlet has successfully been loaded
0363: */
0364: public boolean isServletLoaded() {
0365: try {
0366: checkServlet();
0367: } catch (Exception ex) {
0368: if (debug) {
0369: ex.printStackTrace();
0370: }
0371: }
0372: return inited;
0373: }
0374:
0375: /**
0376: * Helper class for managing a limited pool of servlet instances,
0377: * tk, 20.10.2001
0378: * For the SingleThreadModel instance are created as required and
0379: * retained up to a specified limit.
0380: * For the non-SingleThreadModel one instance only is created
0381: * (in accordance with the servlet spec).
0382: * The first instance initializes the pool with its pars-pro-toto
0383: * class attributes.
0384: */
0385: private class ServletPool implements EventHandler {
0386:
0387: // minimum wait time before tuning
0388: private static final int MINWAIT = 5000;
0389:
0390: // maximum delay for freeing instances
0391: private int idletime = MINWAIT;
0392:
0393: // earliest next tuning time (when decreasing)
0394: private long nexttime = -1L;
0395:
0396: // maximum pool size
0397: private int maximum = 0;
0398:
0399: // current pool capacity
0400: private int capacity = 0;
0401:
0402: // current pool usage level
0403: private int usage = 0;
0404:
0405: // list of servlet instances
0406: private final ArrayList pool = new ArrayList();
0407:
0408: // indicator for SingleThreadModel
0409: private boolean singleThreaded = false;
0410:
0411: // common class of the servlet pool
0412: private Class loadedClass = null;
0413:
0414: // the pool cleanup tuning event
0415: private Object cleanup = null;
0416:
0417: /**
0418: * Indicates, whether this pool needs to be initialized.
0419: */
0420: private final boolean requiresInitialization() {
0421: return ((!inited) || (capacity < 1) || (loadedClass == null));
0422: }
0423:
0424: /**
0425: * method for exporting the class common to all loaded servlet
0426: * instances
0427: * @return common loaded servlet class
0428: */
0429: protected Class getLoadedClass() {
0430: return loadedClass;
0431: }
0432:
0433: /**
0434: * method for adding a fresh servlet instance to the pool
0435: * (using external synchronization)
0436: * @param servlet the instance to be added to the pool
0437: * @return <strong>true</strong> if servlet was successfully added
0438: */
0439: protected boolean add(Servlet servlet) {
0440: if (requiresInitialization()) {
0441: singleThreaded = (servlet instanceof SingleThreadModel);
0442: if (singleThreaded)
0443: maximum = getInstanceMax();
0444: else
0445: maximum = 1;
0446: if (maximum > 0) {
0447: pool.ensureCapacity(maximum);
0448: }
0449: loadedClass = servlet.getClass();
0450: }
0451: if ((maximum < 1) || (capacity < maximum)) {
0452: boolean success = pool.add(servlet);
0453: capacity = pool.size();
0454: return success;
0455: } else
0456: return false;
0457: }
0458:
0459: /**
0460: * non-blocking method for removing a servlet instance from the
0461: * pool (using external synchronization)
0462: * @return a removed servlet instance or null if the pool is empty
0463: */
0464: protected Servlet remove() {
0465: if ((capacity > 0)
0466: && ((usage < 1) || (singleThreaded && (capacity > usage)))) {
0467: Servlet servlet = (Servlet) pool.remove(0);
0468: capacity = pool.size();
0469: if (capacity < 1) {
0470: loadedClass = null;
0471: }
0472: return servlet;
0473: } else
0474: return null;
0475: }
0476:
0477: /**
0478: * Uses a final timer for cleaning up the pool.
0479: *
0480: * @param wait wait interval for the next cleaning action (non-positive to stop cleaning)
0481: */
0482: protected void clean(long wait) {
0483: // do something for the end
0484: EventManager manager = ((httpd) getServer()).timer;
0485: if (cleanup != null) {
0486: manager.recallTimer(cleanup);
0487: }
0488: if (wait > 0L) {
0489: cleanup = manager.registerTimer(wait, this , null);
0490: }
0491: }
0492:
0493: /**
0494: * Tunes this pool (and also the thread cache).
0495: * <p>
0496: * This method implements a simple stragegy, which might
0497: * be replaced by a better one or just NOP. In other words,
0498: * the method has no functional importance apart from
0499: * managing resource efficiency.
0500: *
0501: * @param increasing indicates increasing usage
0502: */
0503: protected void tune(boolean increasing) {
0504: int capacity2 = (capacity >> 1);
0505: if (increasing) {
0506: if (usage > capacity2) {
0507: nexttime = -1L;
0508: if (usage >= capacity) {
0509: tuneCache(capacity);
0510: }
0511: }
0512: } else {
0513: if (usage < capacity2) {
0514: long now = System.currentTimeMillis();
0515: if (nexttime < 0) {
0516: nexttime = now + idletime;
0517: if (debug)
0518: System.out.println("considering usage="
0519: + usage + " next=" + capacity2
0520: + " delay=" + idletime);
0521: } else {
0522: long wait = nexttime - now;
0523: if (wait <= 0L) {
0524: if (singleThreaded) {
0525: for (int i = capacity; i > capacity2; i--) {
0526: Servlet unused = remove();
0527: if (unused != null) {
0528: ClassLoader loader = switchContext(unused);
0529: try {
0530: unused.destroy();
0531: } finally {
0532: resetContext(loader);
0533: }
0534: } else
0535: break;
0536: }
0537: } else
0538: capacity = capacity2;
0539: nexttime = -1L;
0540: tuneCache(capacity);
0541: if (debug)
0542: System.out.println("reducing usage="
0543: + usage
0544: + (singleThreaded ? " real"
0545: : " virtual")
0546: + " capacity="
0547: + capacity
0548: + " maximum="
0549: + maximum
0550: + " thread="
0551: + Thread.currentThread()
0552: .getName());
0553: }
0554: }
0555:
0556: if (usage < 1) {
0557: // buy an insurance
0558: if (debug)
0559: System.out.println("finalizing delay="
0560: + idletime);
0561: clean((idletime << 1));
0562: }
0563: }
0564: }
0565: }
0566:
0567: /**
0568: * Handle the cleanup event.
0569: *
0570: * @param data The timer closure.
0571: * @param time The absolute time at which the event was triggered.
0572: */
0573: public synchronized void handleTimerEvent(Object data, long time) {
0574: cleanup = null;
0575: if (usage < 1) {
0576: tune(false);
0577: }
0578: }
0579:
0580: /**
0581: * blocking method for accessing a servlet instance from the pool
0582: * @exception ServletException thrown if the pool is not properly
0583: * initialized
0584: */
0585: protected synchronized Servlet takeServlet()
0586: throws ServletException, IOException {
0587: if (requiresInitialization()) {
0588: throw new ServletException("Accessing servlet without"
0589: + " initialization.");
0590: }
0591: if (singleThreaded) {
0592: try {
0593: while (true) {
0594: if (usage < capacity) {
0595: Servlet servlet = (Servlet) pool
0596: .get(usage++);
0597: tune(true);
0598: if (debug)
0599: System.out.println("taking usage="
0600: + usage
0601: + " real capacity="
0602: + capacity
0603: + " maximum="
0604: + maximum
0605: + " servlet="
0606: + servlet.hashCode()
0607: + " thread="
0608: + Thread.currentThread()
0609: .getName());
0610: return servlet;
0611: } else {
0612: if ((maximum < 1) || (capacity < maximum)) {
0613: if (launchServlet(loadedClass)) {
0614: notifyAll();
0615: Thread.currentThread().yield();
0616: // give previous waiters a chance
0617: } else {
0618: if (debug)
0619: System.out
0620: .println("waiting thread="
0621: + Thread
0622: .currentThread()
0623: .getName());
0624: wait();
0625: }
0626: } else {
0627: if (debug)
0628: System.out
0629: .println("waiting thread="
0630: + Thread
0631: .currentThread()
0632: .getName());
0633: wait();
0634: }
0635: }
0636: }
0637: } catch (InterruptedException ex) {
0638: throw new IOException("Waiting for a free servlet"
0639: + " instance interrupted.");
0640: }
0641: } else {
0642: // One instance only is used in non single thread case
0643: // (cf. servlet api for details)
0644: usage++;
0645: capacity = Math.max(capacity, usage);
0646: Servlet servlet = (Servlet) pool.get(0);
0647: tune(true);
0648: if (debug)
0649: System.out.println("taking usage=" + usage
0650: + " virtual capacity=" + capacity
0651: + " maximum=" + maximum + " servlet="
0652: + servlet.hashCode() + " thread="
0653: + Thread.currentThread().getName());
0654: return servlet;
0655: }
0656: }
0657:
0658: /**
0659: * method for releasing a servlet instance into the pool after work
0660: * @param servlet the instance to be returned to the pool
0661: * @param duration the request duration
0662: * @exception ServletException thrown if pool is not properly
0663: * initialized
0664: */
0665: protected synchronized void releaseServlet(Servlet servlet,
0666: int duration) throws ServletException {
0667: if (requiresInitialization()) {
0668: throw new ServletException("Releasing servlet without"
0669: + " initialization.");
0670: }
0671: idletime = Math.max(idletime, duration);
0672: if (usage > 0) {
0673: if (singleThreaded) {
0674: pool.set(--usage, servlet);
0675: tune(false);
0676: if (debug)
0677: System.out.println("releasing usage=" + usage
0678: + " real capacity=" + capacity
0679: + " maximum=" + maximum + " servlet="
0680: + servlet.hashCode() + " thread="
0681: + Thread.currentThread().getName());
0682: notifyAll();
0683: } else {
0684: // In this case the servlet instance is shared, i.e.
0685: // we have a counting semaphore only.
0686: usage--;
0687: tune(false);
0688: if (debug)
0689: System.out.println("releasing usage=" + usage
0690: + " virtual capacity=" + capacity
0691: + " maximum=" + maximum + " servlet="
0692: + servlet.hashCode() + " thread="
0693: + Thread.currentThread().getName());
0694: }
0695: } else {
0696: throw new ServletException("Incorrect servlet release"
0697: + " occurred.");
0698: }
0699: }
0700:
0701: /**
0702: * method for referencing a servlet instance of the pool
0703: * This method was added for backward compatibility in order to
0704: * support the deprecated getServlet() method,
0705: * which is structurally not applicable to the pool mechanism, i.e.
0706: * it always returns null in
0707: * case of the SingleThreadModel (in accordance with the current
0708: * servlet spec)
0709: * @return a servlet reference or null
0710: */
0711: protected synchronized Servlet getRepresentative() {
0712: if (requiresInitialization()) {
0713: return null;
0714: } else {
0715: if (singleThreaded) {
0716: // FIXME, here we could also return pool[0],
0717: // which is defined but probably taken.
0718: // However, using pool[0] in a normal manner might
0719: // cause strange behavior
0720: // due to its single threaded design aspect.
0721: return null;
0722: } else {
0723: return (Servlet) pool.get(0);
0724: }
0725: }
0726: }
0727: }
0728:
0729: protected class ServletRunner implements Runnable, EventHandler {
0730: Servlet srServlet = null;
0731: JigsawHttpServletRequest srReq = null;
0732: JigsawHttpServletResponse srResp = null;
0733: Thread t = null;
0734: private Object stimer = null;
0735: private httpd server = (httpd) getServer();
0736:
0737: /**
0738: * Handle timer events.
0739: * @param data The timer closure.
0740: * @param time The absolute time at which the event was triggered.
0741: * @see org.w3c.tools.timers.EventManager
0742: * @see org.w3c.tools.timers.EventHandler
0743: */
0744: public void handleTimerEvent(Object data, long time) {
0745: signalTimeout();
0746: }
0747:
0748: protected void signalTimeout() {
0749: synchronized (this ) {
0750: stimer = null;
0751: // }
0752: // as the request timeouted, we interrupt the runner's thread
0753: // (as this thread may be the cause of the timeout)
0754: // synchronized (this) {
0755: if (t != null) {
0756: if (debug)
0757: System.out.println("Killing " + t);
0758: t.interrupt();
0759: }
0760: }
0761: }
0762:
0763: public void run() {
0764: synchronized (this ) {
0765: t = Thread.currentThread();
0766: }
0767: long start = System.currentTimeMillis();
0768: stimer = server.timer.registerTimer(server
0769: .getRequestTimeOut(), this , null);
0770: if (debug)
0771: System.out.println("running servlet="
0772: + srServlet.hashCode() + " thread="
0773: + Thread.currentThread().getName());
0774:
0775: // synchronization object
0776: Object o = null;
0777: try {
0778:
0779: Reply reply = srResp.getReply();
0780: if (reply != null) {
0781: o = reply
0782: .getState(JigsawHttpServletResponse.MONITOR);
0783: }
0784:
0785: ClassLoader loader = switchContext(srServlet);
0786: try {
0787: srServlet.service(srReq, srResp);
0788: } finally {
0789: resetContext(loader);
0790: }
0791:
0792: // processing done, release the servlet
0793: try {
0794: int duration = (int) Math.min(System
0795: .currentTimeMillis()
0796: - start, Integer.MAX_VALUE);
0797: servletPool.releaseServlet(srServlet, duration);
0798: } finally {
0799: srServlet = null;
0800: }
0801: // and remove the timer
0802: if (stimer != null) {
0803: server.timer.recallTimer(stimer);
0804: stimer = null;
0805: }
0806: srResp.flushStream(true);
0807: } catch (UnavailableException uex) {
0808: String message = null;
0809: srResp.setStatus(HTTP.SERVICE_UNAVAILABLE);
0810: if (uex.isPermanent()) {
0811: message = "<h2>The servlet is permanently "
0812: + "unavailable :</h2>" + "Details: <b>"
0813: + uex.getMessage() + "</b>";
0814: try {
0815: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,
0816: message);
0817: } catch (IOException ioex) {
0818: // not much to do now...
0819: }
0820: } else {
0821: int delay = uex.getUnavailableSeconds();
0822: if (delay > 0) {
0823: message = "<h2>The servlet is temporarily "
0824: + "unavailable :</h2>" + "Delay : "
0825: + delay
0826: + " seconds<br><br>Details: <b>"
0827: + uex.getMessage() + "</b>";
0828: srResp.getReply().setRetryAfter(delay);
0829: try {
0830: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,
0831: message);
0832: } catch (IOException ioex) {
0833: // not much to do now...
0834: }
0835: } else {
0836: message = "<h2>The servlet is temporarily "
0837: + "unavailable :</h2>" + "Details: <b>"
0838: + uex.getMessage() + "</b>";
0839: try {
0840: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,
0841: message);
0842: } catch (IOException ioex) {
0843: // not much to do now...
0844: }
0845: }
0846: }
0847: } catch (Exception ex) {
0848: if (debug) {
0849: ex.printStackTrace();
0850: }
0851: if (srResp.isStreamObtained()) {
0852: try {
0853: srResp.flushStream(false);
0854: OutputStream os = srResp.getRawOutputStream();
0855: if (os != null) {
0856: synchronized (os) {
0857: PrintWriter pw = new PrintWriter(os);
0858: ex.printStackTrace(pw);
0859: pw.flush();
0860: }
0861: }
0862: srResp.flushStream(true);
0863: } catch (IOException ioex) {
0864: }
0865: } else {
0866: try {
0867: srResp.sendError(HTTP.INTERNAL_SERVER_ERROR,
0868: "Servlet has thrown exception:"
0869: + ex.toString());
0870: } catch (IOException ioex) {
0871: // no stream to write on, fail silently
0872: }
0873: }
0874: } finally {
0875: if (stimer != null) {
0876: server.timer.recallTimer(stimer);
0877: stimer = null;
0878: }
0879: if (srServlet != null) {
0880: try {
0881: int duration = (int) Math.min(System
0882: .currentTimeMillis()
0883: - start, Integer.MAX_VALUE);
0884: servletPool.releaseServlet(srServlet, duration);
0885: } catch (ServletException ex) {
0886: // ignore
0887: }
0888: }
0889: // release the monitor waiting for the end of the reply setup
0890: if (o != null) {
0891: synchronized (o) {
0892: o.notifyAll();
0893: }
0894: }
0895: srServlet = null;
0896: srReq = null;
0897: // adds the END state
0898: Reply r = srResp.getReply();
0899: if (r != null) {
0900: r.setState(ENDED, new Object());
0901: }
0902: srResp = null;
0903: }
0904: }
0905:
0906: ServletRunner(Servlet servlet,
0907: JigsawHttpServletRequest request,
0908: JigsawHttpServletResponse response) {
0909: srServlet = servlet;
0910: srReq = request;
0911: srResp = response;
0912: }
0913: }
0914:
0915: protected void service(Request request, Reply reply)
0916: throws ServletException, IOException {
0917:
0918: JigsawHttpServletResponse jRes = null;
0919: JigsawHttpServletRequest jReq = null;
0920: /* modified due to servlet pooling, tk, 20.10.2001
0921: if (servlet instanceof SingleThreadModel) {
0922: synchronized (this) {
0923: jRes = new JigsawHttpServletResponse(request, reply);
0924: jReq = new JigsawHttpServletRequest(servlet,
0925: request,
0926: jRes,
0927: getSessionContext());
0928: jRes.setServletRequest(jReq);
0929: try {
0930: connections++;
0931: // FIXME we should reuse a thread rather than
0932: // reallocating one for every hit
0933: ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
0934: reply.setState(RUNNER, runner);
0935: runner.start();
0936: } finally {
0937: connections--;
0938: }
0939: }
0940: } else {
0941: */
0942: jRes = new JigsawHttpServletResponse(request, reply);
0943: Servlet servlet = servletPool.takeServlet();
0944: // accessing a fresh or sharable instance, tk, 21.10.2001
0945: // jReq = new JigsawHttpServletRequest(servlet,
0946: JigsawServletContext jco = (JigsawServletContext) getServletContext();
0947: jReq = new JigsawHttpServletRequest(servlet, jco, request,
0948: jRes, getSessionContext());
0949: jRes.setServletRequest(jReq);
0950: // try {
0951: // connections++;
0952: // FIXME we should reuse a thread rather than
0953: // reallocating one for every hit
0954: // reallocating one for every hit
0955: ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
0956: reply.setState(RUNNER, runner);
0957: threadCache.getThread(runner, true);
0958: // runner.start();
0959: // } finally {
0960: // connections--;
0961: // }
0962: /* } */
0963: timeoutManager.restart();
0964: }
0965:
0966: /**
0967: * Get the class name of the wrapped servlet.
0968: * @return The class name for the servlet if attribute is defined.
0969: * Otherwise the class name is deduced from the resource identifier.
0970: */
0971:
0972: public String getServletClass() {
0973: String sclass = getString(ATTR_SERVLET_CLASS, null);
0974: if (sclass == null) {
0975: String ident = getIdentifier();
0976: if (ident.endsWith(".class")) {
0977: sclass = ident;
0978: }
0979: }
0980: return sclass;
0981: }
0982:
0983: /**
0984: * Get the init parameters for our wrapped servlet.
0985: * @return An ArrayDictionary instance if the attribute is defined,
0986: * <strong>false</strong> otherwise.
0987: */
0988:
0989: public ArrayDictionary getServletParameters() {
0990: return (ArrayDictionary) getValue(ATTR_PARAMETERS, null);
0991: }
0992:
0993: protected void setValueOfSuperClass(int idx, Object value) {
0994: super .setValue(idx, value);
0995: }
0996:
0997: /**
0998: * Catch assignements to the servlet class name attribute.
0999: * <p>When a change to that attribute is detected, the servlet is
1000: * automatically reinitialized.
1001: */
1002:
1003: public void setValue(int idx, Object value) {
1004: super .setValue(idx, value);
1005: if (((idx == ATTR_SERVLET_CLASS) && (value != null))
1006: || (idx == ATTR_PARAMETERS)) {
1007: try {
1008: synchronized (servletPool) {
1009: // synchronization added, tk, 20.10.2001
1010: inited = launchServlet();
1011: }
1012: } catch (Exception ex) {
1013: String msg = ("unable to set servlet class \""
1014: + getServletClass() + "\" : " + ex.getMessage());
1015: getServer().errlog(msg);
1016: }
1017: }
1018: if (idx == ATTR_SERVLET_TIMEOUT) {
1019: timeoutManager.restart();
1020: }
1021: }
1022:
1023: /**
1024: * Destroy the servlet we are wrapping.
1025: */
1026: protected void destroyServlet() {
1027: // if ((servlet != null) && (connections < 1)) {
1028: RuntimeException rex = null;
1029: synchronized (servletPool) {
1030: Servlet servlet = servletPool.remove();
1031: while (servlet != null) {
1032: try {
1033: ClassLoader loader = switchContext(servlet);
1034: try {
1035: servlet.destroy();
1036: // servlet = null;
1037: } finally {
1038: resetContext(loader);
1039: }
1040: } catch (RuntimeException ex) {
1041: if (rex != null) {
1042: rex = ex;
1043: }
1044: } finally {
1045: servlet = servletPool.remove();
1046: }
1047: }
1048: inited = (servletPool.getLoadedClass() != null);
1049: if (!inited) {
1050: servletPool.clean(0L);
1051: tuneCache(0);
1052: }
1053: }
1054: if (rex != null)
1055: throw rex;
1056: // }
1057: }
1058:
1059: /**
1060: * Get the servlet we are wrapping.
1061: * @return A servlet instance, if the servlet is alredy running,
1062: * <strong>null</strong> otherwise.
1063: */
1064: public Servlet getServlet() {
1065: try {
1066: checkServlet();
1067: } catch (Exception ex) {
1068: if (debug)
1069: ex.printStackTrace();
1070: }
1071: return servletPool.getRepresentative();
1072: }
1073:
1074: /**
1075: * Sets the context classloader for the specified servlet class and
1076: * returns its predecessor or <code>null</code>.
1077: *
1078: * @return the previous context classloader if any
1079: */
1080: private static final ClassLoader switchContext(Servlet servlet) {
1081: if (servlet != null) {
1082: ClassLoader newContextClassLoader = servlet.getClass()
1083: .getClassLoader();
1084: Thread handler = Thread.currentThread();
1085: ClassLoader oldContextClassLoader = handler
1086: .getContextClassLoader();
1087: if (newContextClassLoader != oldContextClassLoader) {
1088: handler.setContextClassLoader(newContextClassLoader);
1089: return oldContextClassLoader;
1090: } else
1091: return null;
1092: } else
1093: return null;
1094: }
1095:
1096: /**
1097: * Resets the context classloader if applicable.
1098: *
1099: * @param loader the previous context classloader or <code>null</code>
1100: */
1101: private static final void resetContext(ClassLoader loader) {
1102: Thread.currentThread().setContextClassLoader(loader);
1103: }
1104:
1105: /**
1106: * Initialize our servlet from the given (loaded) class.
1107: * @param cls The servlet loaded main class.
1108: * @return A boolean, <strong>true</strong> if servlet was successfully
1109: * initialised, <strong>false</strong> otherwise.
1110: * @exception ServletException if servlet can't be initialized.
1111: */
1112:
1113: protected boolean launchServlet(Class cls) throws ServletException {
1114: if (debug) {
1115: System.out
1116: .println("launching Servlet: " + getServletName());
1117: }
1118: Servlet servlet = null;
1119: try {
1120: servlet = (Servlet) cls.newInstance();
1121: ClassLoader loader = switchContext(servlet);
1122: try {
1123: servlet.init((ServletConfig) this );
1124: } finally {
1125: resetContext(loader);
1126: }
1127: timeoutManager.restart();
1128: // modified for servlet pool and executed in a context
1129: // synchronized on the servlet pool, tk, 20.10.2001
1130: return servletPool.add(servlet);
1131: } catch (IllegalAccessException ex) {
1132: String msg = ("Illegal access during servlet instantiation, "
1133: + ex.getClass().getName() + ": " + ex.getMessage());
1134: if (debug) {
1135: ex.printStackTrace();
1136: }
1137: getServer().errlog(this , msg);
1138: // return false;
1139: throw new ServletException(msg, ex);
1140: } catch (InstantiationException iex) {
1141: String msg = ("unable to instantiate servlet, "
1142: + iex.getClass().getName() + ": " + iex
1143: .getMessage());
1144: if (debug) {
1145: iex.printStackTrace();
1146: }
1147: getServer().errlog(this , msg);
1148: // return false;
1149: throw new ServletException(msg, iex);
1150: } catch (ServletException nex) {
1151: String msg = ("Error while initializing servlet");
1152: getServer().errlog(this , msg);
1153: if (debug) {
1154: nex.printStackTrace();
1155: }
1156: getServer().errlog(this , msg);
1157: // return false;
1158: throw nex;
1159: } catch (Exception oex) {
1160: String msg = ("Error while loading servlet");
1161: getServer().errlog(this , msg);
1162: if (debug) {
1163: oex.printStackTrace();
1164: }
1165: getServer().errlog(this , msg);
1166: // return false;
1167: throw new ServletException(msg, oex);
1168: }
1169: }
1170:
1171: /**
1172: * Check if the Servletclass wrapped is a Servlet class without
1173: * initializing it. (not the same than checkServlet).
1174: * used by the ServletIndexer.
1175: * @see org.w3c.jigsaw.servlet.ServletIndexer
1176: * @return A boolean.
1177: */
1178: protected boolean isWrappingAServlet() {
1179: String clsname = getServletClass();
1180: if (clsname == null) {
1181: return false;
1182: }
1183: Class c = null;
1184: try {
1185: c = getLocalServletLoader().loadClass(clsname, true);
1186: Object o = c.newInstance();
1187: return (o instanceof Servlet);
1188: } catch (Exception ex) {
1189: return false;
1190: }
1191: }
1192:
1193: /**
1194: * Launch the servlet we are wrapping.
1195: * <p>This method either succeed, or the wrapper resource itself will fail
1196: * to initialize, acting as transparently as possible (in some sense).
1197: * @return A boolean, <strong>true</strong> if servlet launched.
1198: * @exception ClassNotFoundException if servlet class can't be found.
1199: * @exception ServletException if servlet can't be initialized.
1200: */
1201:
1202: protected boolean launchServlet() throws ClassNotFoundException,
1203: ServletException {
1204: // Get and check the servlet class:
1205: // if ( servlet != null )
1206: destroyServlet();
1207: if (inited) {
1208: String msg = "relaunching servlet failed due to incomplete \""
1209: + getServletClass() + "\" cleanup.";
1210: getServer().errlog(this , msg);
1211: return false;
1212: } else {
1213: // Load appropriate servlet class:
1214: String clsname = getServletClass();
1215: if (clsname == null) {
1216: getServer().errlog(this ,
1217: "no servlet class attribute" + " defined.");
1218: return false;
1219: } else {
1220: Class c = null;
1221: try {
1222: if (getLocalServletLoader().classChanged(clsname)) {
1223: createNewLocalServletLoader(true);
1224: invalidateAllSession();
1225: }
1226: c = getLocalServletLoader()
1227: .loadClass(clsname, true);
1228: } catch (ClassNotFoundException ex) {
1229: String msg = ("unable to find servlet class \""
1230: + getServletClass() + "\"");
1231: getServer().errlog(msg);
1232: // re throw the exception
1233: throw ex;
1234: }
1235: return (c != null) ? launchServlet(c) : false;
1236: }
1237: }
1238: }
1239:
1240: public boolean acceptUnload() {
1241: // return (servlet == null);
1242: return (!inited);
1243: }
1244:
1245: public void notifyUnload() {
1246: if (timeoutManager != null) {
1247: timeoutManager.stop();
1248: }
1249: destroyServlet();
1250: }
1251:
1252: /**
1253: * Get or create a suitable LocalServletLoader instance to load
1254: * that servlet.
1255: * @return A LocalServletLoader instance.
1256: */
1257: // singleton synchronized already performed at the servlet context.
1258: // protected synchronized AutoReloadServletLoader getLocalServletLoader() {
1259: protected AutoReloadServletLoader getLocalServletLoader() {
1260: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
1261: return ctxt.getLocalServletLoader();
1262: }
1263:
1264: protected AutoReloadServletLoader createNewLocalServletLoader(
1265: boolean keepold) {
1266: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
1267: return ctxt.createNewLocalServletLoader(keepold);
1268: }
1269:
1270: /**
1271: * Returns the name of this servlet instance.
1272: * The name may be provided via server administration, assigned in the
1273: * web application deployment descriptor, or for an unregistered (and thus
1274: * unnamed) servlet instance it will be the servlet's class name.
1275: * @return the name of the servlet instance
1276: */
1277: public String getServletName() {
1278: return getIdentifier();
1279: }
1280:
1281: /**
1282: * Initialize this servlet wrapper resource.
1283: * After the wrapper itself is inited, it performs the servlet
1284: * initialzation.
1285: * @param values The default attribute values.
1286: */
1287: public void initialize(Object values[]) {
1288: super .initialize(values);
1289: // connections = 0;
1290:
1291: if (getServletContext() != null) {
1292: timeoutManager = new TimeoutManager((httpd) getServer());
1293: timeoutManager.start();
1294: }
1295:
1296: try {
1297: registerFrameIfNone(
1298: "org.w3c.jigsaw.servlet.ServletWrapperFrame",
1299: "servlet-wrapper-frame");
1300: } catch (Exception ex) {
1301: ex.printStackTrace();
1302: }
1303: }
1304: }
|