Annotation of java/classes/org/w3c/jigsaw/servlet/ServletWrapper.java, revision 1.67
1.1 abaird 1: // ServletWrapper.java
1.67 ! ylafon 2: // $Id: ServletWrapper.java,v 1.66 2002/09/16 18:36:56 ylafon Exp $
1.1 abaird 3: // (c) COPYRIGHT MIT and INRIA, 1996.
4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
1.18 bmahe 6: package org.w3c.jigsaw.servlet;
1.1 abaird 7:
1.51 ylafon 8: import java.io.File;
9: import java.io.IOException;
1.64 ylafon 10: import java.io.OutputStream;
1.51 ylafon 11: import java.io.PrintStream;
1.64 ylafon 12: import java.io.PrintWriter;
1.51 ylafon 13:
14: import java.util.Enumeration;
1.59 ylafon 15: import java.util.LinkedList;
16: import java.util.NoSuchElementException;
1.51 ylafon 17:
18: import javax.servlet.Servlet;
19: import javax.servlet.ServletConfig;
20: import javax.servlet.ServletContext;
21: import javax.servlet.ServletException;
22: import javax.servlet.SingleThreadModel;
1.56 ylafon 23: import javax.servlet.UnavailableException;
1.51 ylafon 24:
25: import org.w3c.tools.timers.EventHandler;
26: import org.w3c.tools.timers.EventManager;
27:
28: import org.w3c.util.ArrayDictionary;
29: import org.w3c.util.EmptyEnumeration;
1.62 ylafon 30: import org.w3c.util.ThreadCache;
1.51 ylafon 31:
32: import org.w3c.jigsaw.http.Reply;
33: import org.w3c.jigsaw.http.Request;
34: import org.w3c.jigsaw.http.httpd;
35:
36: import org.w3c.tools.resources.Attribute;
37: import org.w3c.tools.resources.AttributeHolder;
38: import org.w3c.tools.resources.AttributeRegistry;
39: import org.w3c.tools.resources.FramedResource;
40: import org.w3c.tools.resources.InvalidResourceException;
1.59 ylafon 41: import org.w3c.tools.resources.IntegerAttribute;
1.51 ylafon 42: import org.w3c.tools.resources.LongAttribute;
43: import org.w3c.tools.resources.ObjectAttribute;
44: import org.w3c.tools.resources.PropertiesAttribute;
45: import org.w3c.tools.resources.Resource;
46: import org.w3c.tools.resources.ResourceReference;
47: import org.w3c.tools.resources.ServerInterface;
48: import org.w3c.tools.resources.StringAttribute;
1.1 abaird 49:
1.56 ylafon 50: import org.w3c.www.http.HTTP;
51:
1.2 abaird 52: /**
53: * @author Alexandre Rafalovitch <alex@access.com.au>
54: * @author Anselm Baird-Smith <abaird@w3.org>
1.18 bmahe 55: * @author Benoit Mahe <bmahe@w3.org>
1.2 abaird 56: */
57:
1.40 bmahe 58: public class ServletWrapper extends FramedResource
59: implements ServletConfig
1.1 abaird 60: {
61:
1.62 ylafon 62: static ThreadCache threadCache = null;
63:
64: static {
65: threadCache = new ThreadCache("servlet-runner");
66: threadCache.setCachesize(5);
67: threadCache.setGrowAsNeeded(true);
68: threadCache.setIdleTimeout(86400000);
69: threadCache.initialize();
70: }
71:
1.40 bmahe 72: protected class TimeoutManager implements EventHandler {
73:
74: private Object timer = null;
75: private httpd server = null;
76:
77: /**
78: * Handle timer events.
79: * @param data The timer closure.
80: * @param time The absolute time at which the event was triggered.
81: * @see org.w3c.tools.timers.EventManager
82: * @see org.w3c.tools.timers.EventHandler
83: */
84: public synchronized void handleTimerEvent(Object data, long time) {
85: timer = null;
1.60 ylafon 86: // FIXME, each servlet instance available should have its
87: // individual timeout manager.
88: // Thus, resources could be released as required. This mechanism
89: // would opt for a more fine grained servlet instance management.
1.40 bmahe 90: destroyServlet();
91: }
92:
93: private synchronized void setTimer(long ms) {
94: if ( timer != null ) {
95: server.timer.recallTimer(timer) ;
96: timer = null ;
97: }
98: timer = server.timer.registerTimer(ms, this, null);
99: }
100:
101: protected void restart() {
102: start();
103: }
104:
105: protected void start() {
1.45 bmahe 106: long timeout = getServletTimeout();
107: if (timeout != -1)
1.59 ylafon 108: setTimer(timeout);
1.40 bmahe 109: }
110:
111: protected synchronized void stop() {
112: if ( timer != null ) {
113: server.timer.recallTimer(timer) ;
114: timer = null;
115: }
116: }
117:
118: TimeoutManager(httpd server) {
119: this.server = server;
120: }
121:
122: }
123:
1.57 ylafon 124: public static final String RUNNER = "org.w3c.jigsaw.servlet.runner";
125:
1.40 bmahe 126: protected TimeoutManager timeoutManager = null;
127:
1.60 ylafon 128: // protected int connections = 0;
1.46 bmahe 129:
1.40 bmahe 130: protected final static boolean debug = false;
1.9 bmahe 131:
1.20 bmahe 132: /**
133: * Attributes index - The servlet class name.
134: */
135: protected static int ATTR_SERVLET_CLASS = -1 ;
136: /**
1.40 bmahe 137: * Attributes index - The servlet timeout
138: */
139: protected static int ATTR_SERVLET_TIMEOUT = -1 ;
140: /**
1.60 ylafon 141: * Attributes index - The servlet maxinstances for single thread model
142: * servlet instance pool size limitation, tk, 20.10.2001
1.59 ylafon 143: */
144: protected static int ATTR_SERVLET_INSTANCEMAX = -1 ;
145: /**
1.20 bmahe 146: * Attribute index - The init parameters for that servlet.
147: */
148: protected static int ATTR_PARAMETERS = 1;
149: /**
150: * Attribute index - Our parent-inherited servlet context.
151: */
152: protected static int ATTR_SERVLET_CONTEXT = -1;
153: /**
1.28 bmahe 154: * Attribute index - Our parent-inherited session context.
155: */
156: protected static int ATTR_SESSION_CONTEXT = -1;
1.20 bmahe 157:
158: static {
159: Attribute a = null ;
160: Class cls = null ;
161: try {
162: cls = Class.forName("org.w3c.jigsaw.servlet.ServletWrapper") ;
163: } catch (Exception ex) {
164: ex.printStackTrace();
165: System.exit(0);
166: }
167: // The servlet class attribute.
168: a = new StringAttribute("servlet-class"
1.1 abaird 169: , null
1.20 bmahe 170: , Attribute.EDITABLE | Attribute.MANDATORY) ;
171: ATTR_SERVLET_CLASS = AttributeRegistry.registerAttribute(cls, a) ;
172: // This servlet init parameters
173: a = new PropertiesAttribute("servlet-parameters"
174: , null
175: , Attribute.EDITABLE);
176: ATTR_PARAMETERS = AttributeRegistry.registerAttribute(cls, a);
177: // Our servlet context:
178: a = new ObjectAttribute("servlet-context",
1.24 bmahe 179: "org.w3c.jigsaw.servlet.JigsawServletContext",
1.20 bmahe 180: null,
181: Attribute.DONTSAVE);
182: ATTR_SERVLET_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
1.28 bmahe 183: // Our session context:
184: a = new ObjectAttribute("session-context",
1.60 ylafon 185: "org.w3c.jigsaw.servlet.JigsawHttpSessionContext",
186: null,
187: Attribute.DONTSAVE);
1.28 bmahe 188: ATTR_SESSION_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
1.40 bmahe 189: // The servlet timeout:
190: a = new LongAttribute("servlet-timeout",
191: null,
192: Attribute.EDITABLE);
193: ATTR_SERVLET_TIMEOUT = AttributeRegistry.registerAttribute(cls, a);
1.59 ylafon 194: // The servlet maxinstances:
195: a = new IntegerAttribute("servlet-instancemax",
1.60 ylafon 196: null,
197: Attribute.EDITABLE);
1.59 ylafon 198: ATTR_SERVLET_INSTANCEMAX = AttributeRegistry.registerAttribute(cls, a);
1.20 bmahe 199: }
1.51 ylafon 200:
1.59 ylafon 201: /**
1.60 ylafon 202: * Using a limited pool of servlet instances instead of a single
203: * instance, tk, 22.10.2001
1.59 ylafon 204: * protected Servlet servlet = null;
1.20 bmahe 205: */
1.59 ylafon 206: protected ServletPool servletPool = new ServletPool();
207:
1.20 bmahe 208: /**
1.59 ylafon 209: * Is our servler initialized ?
1.20 bmahe 210: */
211: protected boolean inited = false;
212:
213: /**
214: * The Path where we can find the servlet class file.
215: */
216: public File getServletDirectory() {
1.25 bmahe 217: ResourceReference rr = getParent();
218: if (rr != null) {
219: try {
220: Resource parent = rr.lock();
221: if (parent.definesAttribute("directory"))
222: return (File) parent.getValue("directory", null);
223: } catch(InvalidResourceException ex) {
224: ex.printStackTrace();
225: } finally {
226: rr.unlock();
227: }
228: }
229: return null;
1.20 bmahe 230: }
231:
232: /**
233: * Servlet stub implementation - Get an init parameter value.
234: */
235: public synchronized String getInitParameter(String string) {
236: ArrayDictionary d = getServletParameters();
237: String v = (d != null) ? (String) d.get(string) : null;
238: return v;
239: }
240:
241: /**
242: * Servlet stub implementation - Get all init parameters.
243: */
244: public synchronized Enumeration getInitParameterNames() {
245: // Convert init parameters to hashtable:
246: ArrayDictionary d = getServletParameters();
247: return (d != null) ? d.keys() : new EmptyEnumeration();
248: }
249:
250: /**
251: * Servlet stub implementation - Get that servlet context.
252: */
253: public ServletContext getServletContext() {
1.52 bmahe 254: ServletContext ctxt =
255: (ServletContext) getValue(ATTR_SERVLET_CONTEXT, null);
256: return ctxt;
1.20 bmahe 257: }
258:
1.28 bmahe 259: public JigsawHttpSessionContext getSessionContext() {
260: return (JigsawHttpSessionContext) getValue(ATTR_SESSION_CONTEXT, null);
261: }
262:
1.40 bmahe 263: protected long getServletTimeout() {
1.45 bmahe 264: long timeout = getLong(ATTR_SERVLET_TIMEOUT, 0);
265: if (timeout == 0) {
1.40 bmahe 266: JigsawServletContext ctxt =
267: (JigsawServletContext) getServletContext();
268: timeout = ctxt.getServletTimeout();
269: }
270: return timeout;
271: }
272:
1.60 ylafon 273: protected int getInstanceMax() {
274: // added for single thread model
275: // servlet instance pool size limitation, tk, 20.10.2001
1.59 ylafon 276: int instancemax = getInt(ATTR_SERVLET_INSTANCEMAX, 1);
277: if (instancemax < 1) {
278: JigsawServletContext ctxt =
279: (JigsawServletContext) getServletContext();
280: instancemax = ctxt.getServletInstanceMax();
281: }
1.60 ylafon 282: if (instancemax < 1) {
283: return 1;
284: } else {
285: return instancemax;
286: }
1.59 ylafon 287: }
288:
1.36 bmahe 289: protected void invalidateAllSession() {
1.60 ylafon 290: if (debug) {
1.36 bmahe 291: System.out.println("Invalidate All Session...");
1.60 ylafon 292: }
1.36 bmahe 293: JigsawHttpSessionContext ctxt = getSessionContext();
294: Enumeration enum = ctxt.getIds();
1.60 ylafon 295: while (enum.hasMoreElements()) {
1.36 bmahe 296: ctxt.getSession((String)enum.nextElement()).invalidate();
1.60 ylafon 297: }
1.36 bmahe 298: }
299:
1.37 bmahe 300: /**
301: * Check the servlet class, ans try to initialize it.
302: * @exception ClassNotFoundException if servlet class can't be found.
303: * @exception ServletException if servlet can't be initialized.
304: */
1.36 bmahe 305: protected void checkServlet()
306: throws ClassNotFoundException, ServletException
1.60 ylafon 307: {
308: synchronized(servletPool) {
309: // tk, 20.10.2001, synchronized loading is necessary at
310: // least for the non-SingleThreadModel
311: boolean classmodified =
312: getLocalServletLoader().classChanged(getServletClass());
313:
314: if ((!inited) ||
315: classmodified ||
316: // (servlet.getClass() !=
317: // getLocalServletLoader().loadClass(getServletClass()))
318: (servletPool.getLoadedClass() !=
319: getLocalServletLoader().loadClass(getServletClass()))
320: ) {
321: inited = launchServlet();
322: }
1.36 bmahe 323: }
1.60 ylafon 324: }
1.20 bmahe 325:
326: protected boolean isInited() {
327: return inited;
328: }
329:
1.59 ylafon 330: /**
1.60 ylafon 331: * Check and eventually load the servlet we are wrapping.
332: * This method was added for replacing getServlet() during access checks
333: * in order to do perform checks without accessing a servlet instance.
334: * @return true if and only if the sevlet has successfully been loaded
335: */
336: public boolean isServletLoaded() {
337: try {
338: checkServlet();
339: } catch (Exception ex) {
340: if (debug) {
341: ex.printStackTrace();
342: }
343: }
344: return inited;
345: }
346:
347: /**
348: * Helper class for managing a limited pool of servlet instances,
349: * tk, 20.10.2001
350: * For the SingleThreadModel instance are created as required and
351: * retained up to a specified limit.
352: * For the non-SingleThreadModel one instance only is created
353: * (in accordance with the servlet spec).
354: * The first instance initializes the pool with its pars-pro-toto
355: * class attributes.
1.59 ylafon 356: */
357: private class ServletPool {
358:
1.60 ylafon 359: // maximum pool size
360: private int maximum = 1;
361:
1.59 ylafon 362: // current pool capacity
363: private int capacity = 0;
364:
1.60 ylafon 365: // current pool usage level
366: private int usage = 0;
367:
1.59 ylafon 368: // list of servlet instances
1.60 ylafon 369: private Servlet[] pool = null;
1.59 ylafon 370:
371: // indicator for SingleThreadModel
372: private boolean singleThreaded = false;
373:
374: // common class of the servlet pool
375: private Class loadedClass = null;
376:
1.60 ylafon 377:
1.59 ylafon 378: /**
1.60 ylafon 379: * method for exporting the class common to all loaded servlet
380: * instances
1.59 ylafon 381: * @return common loaded servlet class
382: */
383: protected Class getLoadedClass() {
384: return loadedClass;
385: }
386:
387: /**
1.60 ylafon 388: * method for adding a fresh servlet instance to the pool
389: * (using external synchronization)
1.59 ylafon 390: * @param servlet the instance to be added to the pool
391: */
392: protected void add(Servlet servlet) {
1.60 ylafon 393: if ((pool == null)||(loadedClass == null)) {
394: singleThreaded = (servlet instanceof SingleThreadModel);
395: if (singleThreaded) maximum = getInstanceMax();
396: else maximum = 1;
397: loadedClass = servlet.getClass();
398: pool = new Servlet[maximum];
399: }
400: if (capacity < maximum) {
401: pool[capacity++] = servlet;
1.59 ylafon 402: }
403: }
404:
405: /**
1.60 ylafon 406: * non-blocking method for removing a servlet instance from the
407: * pool (using external synchronization)
1.59 ylafon 408: * @return a removed servlet instance or null if the pool is empty
409: */
410: protected Servlet remove() {
1.60 ylafon 411: if (capacity > usage) {
412: Servlet servlet = pool[--capacity];
413: if (capacity < 1) {
414: pool = null;
415: loadedClass = null;
416: singleThreaded = false;
417: maximum = 1;
418: }
419: return servlet;
420: }
421: else return null;
1.59 ylafon 422: }
423:
424: /**
425: * blocking method for accessing a servlet instance from the pool
1.60 ylafon 426: * @exception ServletException thrown if the pool is not properly
427: * initialized
1.59 ylafon 428: */
429: protected synchronized Servlet takeServlet() throws ServletException {
1.60 ylafon 430: if ((!inited)||(capacity < 1)||
431: (pool == null)||(loadedClass == null)) {
432: throw new ServletException("Accessing servlet without"+
433: " initialization.");
1.59 ylafon 434: }
435: if (singleThreaded) {
1.60 ylafon 436: try {
437: while (true) {
438: if (usage < capacity) {
439: return pool[usage++];
440: } else {
441: if (capacity < maximum) {
442: if (launchServlet(loadedClass)) {
443: notifyAll();
444: Thread.currentThread().yield();
445: // give previous waiters a chance
446: } else {
447: wait();
448: }
449: } else {
450: wait();
451: }
452: }
453: }
454: }
455: catch (InterruptedException ex) {
456: throw new ServletException("Waiting for a free servlet"+
457: " instance interrupted.");
458: }
459: } else {
460: // One instance only is used in non single thread case
461: // (cf. servlet api for details)
462: usage++;
463: return pool[0];
1.59 ylafon 464: }
465: }
466:
467: /**
468: * method for releasing a servlet instance into the pool after work
1.60 ylafon 469: * @param servlet the instance to be returned to the pool
470: * @exception ServletException thrown if pool is not properly
471: * initialized
1.59 ylafon 472: */
1.60 ylafon 473: protected synchronized void releaseServlet(Servlet servlet)
474: throws ServletException
475: {
476: if ((!inited)||(capacity < 1)||(pool == null)||
477: (loadedClass == null)) {
478: throw new ServletException("Releasing servlet without"+
479: " initialization.");
1.59 ylafon 480: }
1.60 ylafon 481: if (usage > 0) {
482: if (singleThreaded) {
483: pool[--usage] = servlet;
484: notifyAll();
485: } else {
486: // In this case the servlet instance is shared, i.e.
487: // we have a counting semaphore only.
488: usage--;
489: }
490: } else {
491: throw new ServletException("Incorrect servlet release"+
492: " occurred.");
493: }
1.59 ylafon 494: }
495:
496: /**
497: * method for referencing a servlet instance of the pool
1.60 ylafon 498: * This method was added for backward compatibility in order to
499: * support the deprecated getServlet() method,
500: * which is structurally not applicable to the pool mechanism, i.e.
501: * it always returns null in
502: * case of the SingleThreadModel (in accordance with the current
503: * servlet spec)
1.59 ylafon 504: * @return a servlet reference or null
505: */
506: protected synchronized Servlet getRepresentative() {
1.60 ylafon 507: if ((!inited)||(capacity < 1)||(loadedClass == null)) {
508: return null;
509: } else {
510: if (singleThreaded) {
511: // FIXME, here we could also return pool[0],
512: // which is defined but probably taken.
513: // However, using pool[0] in a normal manner might
514: // cause strange behavior
515: // due to its single threaded design aspect.
516: return null;
517: } else {
518: return pool[0];
519: }
1.59 ylafon 520: }
521: }
1.60 ylafon 522: }
1.59 ylafon 523:
1.62 ylafon 524: private class ServletRunner implements Runnable {
1.61 ylafon 525: Servlet srServlet = null;
526: JigsawHttpServletRequest srReq = null;
527: JigsawHttpServletResponse srResp = null;
1.55 ylafon 528:
529: public void run() {
1.62 ylafon 530: // name modified in order to keep track of who is running
531: // which instance, tk, 21.10.2001
532: Thread t = Thread.currentThread();
533: String ts = t.getName();
534: t.setName("ServletRunner<" + ts +":"+ srServlet.hashCode() + ">");
1.58 ylafon 535: // synchronization object
536: Object o = null;
1.55 ylafon 537: try {
1.58 ylafon 538: Reply reply = srResp.getReply();
539: if (reply != null) {
540: o = reply.getState(JigsawHttpServletResponse.MONITOR);
541: }
1.55 ylafon 542: srServlet.service(srReq , srResp);
1.60 ylafon 543: try {
544: servletPool.releaseServlet(srServlet);
1.67 ! ylafon 545: } finally {
1.60 ylafon 546: srServlet = null;
547: }
1.55 ylafon 548: srResp.flushStream(true);
1.56 ylafon 549: } catch (UnavailableException uex) {
550: String message = null;
551: srResp.setStatus(HTTP.SERVICE_UNAVAILABLE);
552: if (uex.isPermanent()) {
553: message = "<h2>The servlet is permanently "+
554: "unavailable :</h2>"+
555: "Details: <b>"+uex.getMessage()+"</b>";
556: try {
557: srResp.sendError(HTTP.SERVICE_UNAVAILABLE, message);
558: } catch (IOException ioex) {
559: // not much to do now...
560: }
561: } else {
562: int delay = uex.getUnavailableSeconds();
563: if (delay > 0) {
564: message = "<h2>The servlet is temporarily "+
565: "unavailable :</h2>"+
566: "Delay : "+delay+
567: " seconds<br><br>Details: <b>"+
568: uex.getMessage()+"</b>";
569: srResp.getReply().setRetryAfter(delay);
570: try {
571: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
572: } catch (IOException ioex) {
573: // not much to do now...
574: }
575: } else {
576: message = "<h2>The servlet is temporarily "+
577: "unavailable :</h2>"+
578: "Details: <b>"+uex.getMessage()+
579: "</b>";
580: try {
581: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
582: } catch (IOException ioex) {
583: // not much to do now...
584: }
585: }
586: }
1.55 ylafon 587: } catch (Exception ex) {
1.65 ylafon 588: if (debug) {
589: ex.printStackTrace();
590: }
1.63 ylafon 591: if (srResp.isStreamObtained()) {
592: try {
1.64 ylafon 593: srResp.flushStream(false);
1.66 ylafon 594: OutputStream os = srResp.getRawOutputStream();
1.64 ylafon 595: if (os != null) {
1.66 ylafon 596: synchronized (os) {
597: PrintWriter pw = new PrintWriter(os);
598: ex.printStackTrace(pw);
599: pw.flush();
600: }
1.64 ylafon 601: }
1.63 ylafon 602: srResp.flushStream(true);
603: } catch (IOException ioex) {}
604: } else {
605: try {
606: srResp.sendError(HTTP.INTERNAL_SERVER_ERROR,
607: "Servlet has thrown exception:" +
608: ex.toString());
609: } catch (IOException ioex) {
610: // no stream to write on, fail silently
611: }
1.56 ylafon 612: }
613: } finally {
1.60 ylafon 614: if (srServlet != null) {
615: try {
616: servletPool.releaseServlet(srServlet);
617: }
618: catch (ServletException ex) {
619: // ignore
620: }
621: }
1.56 ylafon 622: // release the monitor waiting for the end of the reply setup
1.58 ylafon 623: if (o != null) {
624: synchronized (o) {
625: o.notifyAll();
1.56 ylafon 626: }
627: }
1.62 ylafon 628: // revert name to original value
629: t.setName(ts);
1.64 ylafon 630: srServlet = null;
631: srReq = null;
632: srResp = null;
1.55 ylafon 633: }
634: }
635:
636: ServletRunner(Servlet servlet,
637: JigsawHttpServletRequest request,
638: JigsawHttpServletResponse response) {
639: srServlet = servlet;
640: srReq = request;
641: srResp = response;
1.62 ylafon 642: }
1.55 ylafon 643: }
644:
1.20 bmahe 645: protected void service(Request request, Reply reply)
646: throws ServletException, IOException
1.60 ylafon 647: {
648: JigsawHttpServletResponse jRes = null;
649: JigsawHttpServletRequest jReq = null;
650: /* modified due to servlet pooling, tk, 20.10.2001
651: if (servlet instanceof SingleThreadModel) {
652: synchronized (this) {
653: jRes = new JigsawHttpServletResponse(request, reply);
654: jReq = new JigsawHttpServletRequest(servlet,
655: request,
656: jRes,
657: getSessionContext());
658: jRes.setServletRequest(jReq);
659: try {
660: connections++;
661: // FIXME we should reuse a thread rather than
662: // reallocating one for every hit
663: ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
664: reply.setState(RUNNER, runner);
665: runner.start();
666: } finally {
667: connections--;
668: }
669: }
670: } else {
671: */
1.31 bmahe 672: jRes = new JigsawHttpServletResponse(request, reply);
1.60 ylafon 673: Servlet servlet = servletPool.takeServlet();
674: // accessing a fresh or sharable instance, tk, 21.10.2001
1.66 ylafon 675: // jReq = new JigsawHttpServletRequest(servlet,
676: JigsawServletContext jco =
677: (JigsawServletContext) getServletContext();
1.31 bmahe 678: jReq = new JigsawHttpServletRequest(servlet,
1.66 ylafon 679: jco,
1.31 bmahe 680: request,
681: jRes,
682: getSessionContext());
683: jRes.setServletRequest(jReq);
1.60 ylafon 684: // try {
685: // connections++;
686: // FIXME we should reuse a thread rather than
687: // reallocating one for every hit
688: // reallocating one for every hit
689: ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
690: reply.setState(RUNNER, runner);
1.62 ylafon 691: threadCache.getThread(runner, true);
692: // runner.start();
1.60 ylafon 693: // } finally {
694: // connections--;
695: // }
696: /* } */
697: timeoutManager.restart();
698: }
1.20 bmahe 699:
700: /**
701: * Get the class name of the wrapped servlet.
702: * @return The class name for the servlet if attribute is defined.
703: * Otherwise the class name is deduced from the resource identifier.
704: */
705:
706: public String getServletClass()
1.60 ylafon 707: {
708: String sclass = getString(ATTR_SERVLET_CLASS, null);
709: if (sclass == null) {
710: String ident = getIdentifier();
711: if (ident.endsWith(".class")) {
712: sclass = ident;
713: }
714: }
715: return sclass;
1.20 bmahe 716: }
717:
718: /**
719: * Get the init parameters for our wrapped servlet.
720: * @return An ArrayDictionary instance if the attribute is defined,
721: * <strong>false</strong> otherwise.
722: */
723:
724: public ArrayDictionary getServletParameters() {
725: return (ArrayDictionary) getValue(ATTR_PARAMETERS, null);
726: }
727:
1.26 bmahe 728: protected void setValueOfSuperClass(int idx, Object value) {
729: super.setValue(idx, value);
730: }
731:
1.20 bmahe 732: /**
733: * Catch assignements to the servlet class name attribute.
734: * <p>When a change to that attribute is detected, the servlet is
735: * automatically reinitialized.
736: */
737:
738: public void setValue(int idx, Object value) {
739: super.setValue(idx, value);
1.44 bmahe 740: if (((idx == ATTR_SERVLET_CLASS) && (value != null)) ||
741: (idx == ATTR_PARAMETERS)) {
1.36 bmahe 742: try {
1.60 ylafon 743: synchronized(servletPool) {
744: // synchronization added, tk, 20.10.2001
1.59 ylafon 745: inited = launchServlet();
746: }
1.36 bmahe 747: } catch (Exception ex) {
748: String msg = ("unable to set servlet class \""+
749: getServletClass()+
750: "\" : "+
751: ex.getMessage());
752: getServer().errlog(msg);
753: }
1.40 bmahe 754: } if (idx == ATTR_SERVLET_TIMEOUT) {
755: timeoutManager.restart();
1.36 bmahe 756: }
1.20 bmahe 757: }
758:
759: /**
760: * Destroy the servlet we are wrapping.
761: */
1.60 ylafon 762: protected void destroyServlet() {
1.59 ylafon 763: // if ((servlet != null) && (connections < 1)) {
1.60 ylafon 764: synchronized(servletPool) {
765: Servlet servlet = servletPool.remove();
766: while (servlet != null) {
767: servlet.destroy();
768: // servlet = null;
769: servlet = servletPool.remove();
770: }
771: inited = (servletPool.getLoadedClass() != null);
1.20 bmahe 772: }
1.60 ylafon 773: // }
1.20 bmahe 774: }
775:
776: /**
777: * Get the servlet we are wrapping.
778: * @return A servlet instance, if the servlet is alredy running,
779: * <strong>null</strong> otherwise.
780: */
1.60 ylafon 781: public Servlet getServlet() {
1.36 bmahe 782: try {
783: checkServlet();
784: } catch (Exception ex) {
785: if (debug)
786: ex.printStackTrace();
787: }
1.59 ylafon 788: return servletPool.getRepresentative();
1.20 bmahe 789: }
1.59 ylafon 790:
1.20 bmahe 791: /**
792: * Initialize our servlet from the given (loaded) class.
793: * @param cls The servlet loaded main class.
794: * @return A boolean, <strong>true</strong> if servlet was successfully
795: * initialised, <strong>false</strong> otherwise.
1.37 bmahe 796: * @exception ServletException if servlet can't be initialized.
1.20 bmahe 797: */
798:
1.36 bmahe 799: protected boolean launchServlet(Class cls)
800: throws ServletException
1.60 ylafon 801: {
802: if (debug) {
803: System.out.println("launching Servlet: "+getServletName());
804: }
805: Servlet servlet = null;
806: try {
807: servlet = (Servlet) cls.newInstance();
808: servlet.init((ServletConfig) this);
809: timeoutManager.restart();
810: } catch (IllegalAccessException ex) {
811: String msg = ("Illegal access during servlet instantiation, "+
812: ex.getClass().getName()+": "+
813: ex.getMessage());
814: if ( debug ) {
815: ex.printStackTrace();
816: }
817: getServer().errlog(this, msg);
818: return false;
819: } catch (InstantiationException iex) {
820: String msg = ("unable to instantiate servlet, "+
821: iex.getClass().getName()+": "+
822: iex.getMessage());
823: if ( debug ) {
824: iex.printStackTrace();
825: }
826: getServer().errlog(this, msg);
827: return false;
1.67 ! ylafon 828: } catch (Exception oex) {
! 829: String msg = ("Error while loading servlet");
! 830: getServer().errlog(this, msg);
! 831: if ( debug ) {
! 832: oex.printStackTrace();
! 833: }
1.60 ylafon 834: }
835: // modified for servlet pool and executed in a context
836: // synchronized on the servlet pool, tk, 20.10.2001
837: if (servlet != null) {
838: servletPool.add(servlet);
839: return true;
840: } else {
841: return false;
842: }
1.50 bmahe 843: }
1.32 bmahe 844:
845: /**
846: * Check if the Servletclass wrapped is a Servlet class without
1.36 bmahe 847: * initializing it. (not the same than checkServlet).
1.33 bmahe 848: * used by the ServletIndexer.
849: * @see org.w3c.jigsaw.servlet.ServletIndexer
1.32 bmahe 850: * @return A boolean.
851: */
852: protected boolean isWrappingAServlet() {
853: String clsname = getServletClass();
1.60 ylafon 854: if ( clsname == null ) {
1.32 bmahe 855: return false;
1.60 ylafon 856: }
1.32 bmahe 857: Class c = null;
858: try {
1.34 bmahe 859: c = getLocalServletLoader().loadClass(clsname, true);
1.32 bmahe 860: Object o = c.newInstance();
861: return (o instanceof Servlet);
862: } catch (Exception ex) {
863: return false;
864: }
1.20 bmahe 865: }
866:
867: /**
868: * Launch the servlet we are wrapping.
869: * <p>This method either succeed, or the wrapper resource itself will fail
870: * to initialize, acting as transparently as possible (in some sense).
871: * @return A boolean, <strong>true</strong> if servlet launched.
1.37 bmahe 872: * @exception ClassNotFoundException if servlet class can't be found.
873: * @exception ServletException if servlet can't be initialized.
1.20 bmahe 874: */
875:
1.36 bmahe 876: protected boolean launchServlet()
877: throws ClassNotFoundException, ServletException
1.60 ylafon 878: {
879: // Get and check the servlet class:
880: // if ( servlet != null )
1.20 bmahe 881: destroyServlet();
1.60 ylafon 882: if (inited) {
883: String msg = "relaunching servlet failed due to incomplete \""
884: + getServletClass() + "\" cleanup.";
885: getServer().errlog(this, msg);
886: return false;
887: } else {
888: // Load appropriate servlet class:
889: String clsname = getServletClass();
890: if ( clsname == null ) {
891: getServer().errlog(this, "no servlet class attribute"+
892: " defined.");
893: return false;
894: } else {
895: Class c = null;
896: try {
897: if (getLocalServletLoader().classChanged(clsname)) {
898: createNewLocalServletLoader(true);
899: invalidateAllSession();
900: }
901: c = getLocalServletLoader().loadClass(clsname, true);
902: } catch (ClassNotFoundException ex) {
903: String msg = ("unable to find servlet class \""+
904: getServletClass()+"\"");
905: getServer().errlog(msg);
906: // re throw the exception
907: throw ex;
908: }
909: return (c != null) ? launchServlet(c) : false;
910: }
1.20 bmahe 911: }
912: }
1.59 ylafon 913:
1.60 ylafon 914: public boolean acceptUnload() {
1.59 ylafon 915: // return (servlet == null);
1.60 ylafon 916: return (!inited);
1.40 bmahe 917: }
1.59 ylafon 918:
1.20 bmahe 919: public void notifyUnload() {
1.60 ylafon 920: if (timeoutManager != null) {
1.42 bmahe 921: timeoutManager.stop();
1.60 ylafon 922: }
1.20 bmahe 923: destroyServlet();
1.26 bmahe 924: }
925:
926: /**
927: * Get or create a suitable LocalServletLoader instance to load
928: * that servlet.
929: * @return A LocalServletLoader instance.
930: */
1.60 ylafon 931: // singleton synchronized already performed at the servlet context.
932: // protected synchronized AutoReloadServletLoader getLocalServletLoader() {
933: protected AutoReloadServletLoader getLocalServletLoader() {
1.34 bmahe 934: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
935: return ctxt.getLocalServletLoader();
1.26 bmahe 936: }
937:
1.60 ylafon 938: protected AutoReloadServletLoader createNewLocalServletLoader(boolean
939: keepold)
1.26 bmahe 940: {
1.60 ylafon 941: JigsawServletContext ctxt = (JigsawServletContext)
942: getServletContext();
1.34 bmahe 943: return ctxt.createNewLocalServletLoader(keepold);
1.20 bmahe 944: }
945:
946: /**
1.48 bmahe 947: * Returns the name of this servlet instance.
948: * The name may be provided via server administration, assigned in the
949: * web application deployment descriptor, or for an unregistered (and thus
950: * unnamed) servlet instance it will be the servlet's class name.
951: * @return the name of the servlet instance
952: */
953: public String getServletName() {
954: return getIdentifier();
955: }
956:
957: /**
1.20 bmahe 958: * Initialize this servlet wrapper resource.
959: * After the wrapper itself is inited, it performs the servlet
960: * initialzation.
961: * @param values The default attribute values.
962: */
963: public void initialize(Object values[]) {
964: super.initialize(values);
1.60 ylafon 965: // connections = 0;
1.41 bmahe 966:
967: if (getServletContext() != null) {
968: timeoutManager = new TimeoutManager((httpd)getServer());
969: timeoutManager.start();
970: }
1.40 bmahe 971:
1.20 bmahe 972: try {
973: registerFrameIfNone("org.w3c.jigsaw.servlet.ServletWrapperFrame",
974: "servlet-wrapper-frame");
975: } catch (Exception ex) {
976: ex.printStackTrace();
977: }
1.18 bmahe 978: }
1.1 abaird 979: }
Webmaster