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