Annotation of java/classes/org/w3c/jigsaw/servlet/ServletWrapper.java, revision 1.57
1.1 abaird 1: // ServletWrapper.java
1.57 ! ylafon 2: // $Id: ServletWrapper.java,v 1.56 2000/11/03 10:32:03 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;
13:
14: import javax.servlet.Servlet;
15: import javax.servlet.ServletConfig;
16: import javax.servlet.ServletContext;
17: import javax.servlet.ServletException;
18: import javax.servlet.SingleThreadModel;
1.56 ylafon 19: import javax.servlet.UnavailableException;
1.51 ylafon 20:
21: import org.w3c.tools.timers.EventHandler;
22: import org.w3c.tools.timers.EventManager;
23:
24: import org.w3c.util.ArrayDictionary;
25: import org.w3c.util.EmptyEnumeration;
26:
27: import org.w3c.jigsaw.http.Reply;
28: import org.w3c.jigsaw.http.Request;
29: import org.w3c.jigsaw.http.httpd;
30:
31: import org.w3c.tools.resources.Attribute;
32: import org.w3c.tools.resources.AttributeHolder;
33: import org.w3c.tools.resources.AttributeRegistry;
34: import org.w3c.tools.resources.FramedResource;
35: import org.w3c.tools.resources.InvalidResourceException;
36: import org.w3c.tools.resources.LongAttribute;
37: import org.w3c.tools.resources.ObjectAttribute;
38: import org.w3c.tools.resources.PropertiesAttribute;
39: import org.w3c.tools.resources.Resource;
40: import org.w3c.tools.resources.ResourceReference;
41: import org.w3c.tools.resources.ServerInterface;
42: import org.w3c.tools.resources.StringAttribute;
1.1 abaird 43:
1.56 ylafon 44: import org.w3c.www.http.HTTP;
45:
1.2 abaird 46: /**
47: * @author Alexandre Rafalovitch <alex@access.com.au>
48: * @author Anselm Baird-Smith <abaird@w3.org>
1.18 bmahe 49: * @author Benoit Mahe <bmahe@w3.org>
1.2 abaird 50: */
51:
1.40 bmahe 52: public class ServletWrapper extends FramedResource
53: implements ServletConfig
1.1 abaird 54: {
55:
1.40 bmahe 56: protected class TimeoutManager implements EventHandler {
57:
58: private Object timer = null;
59: private httpd server = null;
60:
61: /**
62: * Handle timer events.
63: * @param data The timer closure.
64: * @param time The absolute time at which the event was triggered.
65: * @see org.w3c.tools.timers.EventManager
66: * @see org.w3c.tools.timers.EventHandler
67: */
68: public synchronized void handleTimerEvent(Object data, long time) {
69: timer = null;
70: destroyServlet();
71: }
72:
73: private synchronized void setTimer(long ms) {
74: if ( timer != null ) {
75: server.timer.recallTimer(timer) ;
76: timer = null ;
77: }
78: timer = server.timer.registerTimer(ms, this, null);
79: }
80:
81: protected void restart() {
82: start();
83: }
84:
85: protected void start() {
1.45 bmahe 86: long timeout = getServletTimeout();
87: if (timeout != -1)
88: setTimer(getServletTimeout());
1.40 bmahe 89: }
90:
91: protected synchronized void stop() {
92: if ( timer != null ) {
93: server.timer.recallTimer(timer) ;
94: timer = null;
95: }
96: }
97:
98: TimeoutManager(httpd server) {
99: this.server = server;
100: }
101:
102: }
103:
1.57 ! ylafon 104: public static final String RUNNER = "org.w3c.jigsaw.servlet.runner";
! 105:
1.40 bmahe 106: protected TimeoutManager timeoutManager = null;
107:
1.46 bmahe 108: protected int connections = 0;
109:
1.40 bmahe 110: protected final static boolean debug = false;
1.9 bmahe 111:
1.20 bmahe 112: /**
113: * Attributes index - The servlet class name.
114: */
115: protected static int ATTR_SERVLET_CLASS = -1 ;
116: /**
1.40 bmahe 117: * Attributes index - The servlet timeout
118: */
119: protected static int ATTR_SERVLET_TIMEOUT = -1 ;
120: /**
1.20 bmahe 121: * Attribute index - The init parameters for that servlet.
122: */
123: protected static int ATTR_PARAMETERS = 1;
124: /**
125: * Attribute index - Our parent-inherited servlet context.
126: */
127: protected static int ATTR_SERVLET_CONTEXT = -1;
128: /**
1.28 bmahe 129: * Attribute index - Our parent-inherited session context.
130: */
131: protected static int ATTR_SESSION_CONTEXT = -1;
1.20 bmahe 132:
133: static {
134: Attribute a = null ;
135: Class cls = null ;
136: try {
137: cls = Class.forName("org.w3c.jigsaw.servlet.ServletWrapper") ;
138: } catch (Exception ex) {
139: ex.printStackTrace();
140: System.exit(0);
141: }
142: // The servlet class attribute.
143: a = new StringAttribute("servlet-class"
1.1 abaird 144: , null
1.20 bmahe 145: , Attribute.EDITABLE | Attribute.MANDATORY) ;
146: ATTR_SERVLET_CLASS = AttributeRegistry.registerAttribute(cls, a) ;
147: // This servlet init parameters
148: a = new PropertiesAttribute("servlet-parameters"
149: , null
150: , Attribute.EDITABLE);
151: ATTR_PARAMETERS = AttributeRegistry.registerAttribute(cls, a);
152: // Our servlet context:
153: a = new ObjectAttribute("servlet-context",
1.24 bmahe 154: "org.w3c.jigsaw.servlet.JigsawServletContext",
1.20 bmahe 155: null,
156: Attribute.DONTSAVE);
157: ATTR_SERVLET_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
1.28 bmahe 158: // Our session context:
159: a = new ObjectAttribute("session-context",
160: "org.w3c.jigsaw.servlet.JigsawHttpSessionContext",
161: null,
162: Attribute.DONTSAVE);
163: ATTR_SESSION_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
1.40 bmahe 164: // The servlet timeout:
165: a = new LongAttribute("servlet-timeout",
166: null,
167: Attribute.EDITABLE);
168: ATTR_SERVLET_TIMEOUT = AttributeRegistry.registerAttribute(cls, a);
169:
1.20 bmahe 170: }
1.51 ylafon 171:
1.20 bmahe 172: /**
173: * The servlet wrapped within that Jigsaw resource.
174: */
175: protected Servlet servlet = null;
176: /**
177: * Is out servler initialized ?
178: */
179: protected boolean inited = false;
180:
181: /**
182: * The Path where we can find the servlet class file.
183: */
184: public File getServletDirectory() {
1.25 bmahe 185: ResourceReference rr = getParent();
186: if (rr != null) {
187: try {
188: Resource parent = rr.lock();
189: if (parent.definesAttribute("directory"))
190: return (File) parent.getValue("directory", null);
191: } catch(InvalidResourceException ex) {
192: ex.printStackTrace();
193: } finally {
194: rr.unlock();
195: }
196: }
197: return null;
1.20 bmahe 198: }
199:
200: /**
201: * Servlet stub implementation - Get an init parameter value.
202: */
203:
204: public synchronized String getInitParameter(String string) {
205: ArrayDictionary d = getServletParameters();
206: String v = (d != null) ? (String) d.get(string) : null;
207: return v;
208: }
209:
210: /**
211: * Servlet stub implementation - Get all init parameters.
212: */
213:
214: public synchronized Enumeration getInitParameterNames() {
215: // Convert init parameters to hashtable:
216: ArrayDictionary d = getServletParameters();
217: return (d != null) ? d.keys() : new EmptyEnumeration();
218: }
219:
220: /**
221: * Servlet stub implementation - Get that servlet context.
222: */
223:
224: public ServletContext getServletContext() {
1.52 bmahe 225: ServletContext ctxt =
226: (ServletContext) getValue(ATTR_SERVLET_CONTEXT, null);
227: return ctxt;
1.20 bmahe 228: }
229:
1.28 bmahe 230: public JigsawHttpSessionContext getSessionContext() {
231: return (JigsawHttpSessionContext) getValue(ATTR_SESSION_CONTEXT, null);
232: }
233:
1.40 bmahe 234: protected long getServletTimeout() {
1.45 bmahe 235: long timeout = getLong(ATTR_SERVLET_TIMEOUT, 0);
236: if (timeout == 0) {
1.40 bmahe 237: JigsawServletContext ctxt =
238: (JigsawServletContext) getServletContext();
239: timeout = ctxt.getServletTimeout();
240: }
241: return timeout;
242: }
243:
1.36 bmahe 244: protected void invalidateAllSession() {
245: if (debug)
246: System.out.println("Invalidate All Session...");
247: JigsawHttpSessionContext ctxt = getSessionContext();
248: Enumeration enum = ctxt.getIds();
249: while (enum.hasMoreElements())
250: ctxt.getSession((String)enum.nextElement()).invalidate();
251: }
252:
1.37 bmahe 253: /**
254: * Check the servlet class, ans try to initialize it.
255: * @exception ClassNotFoundException if servlet class can't be found.
256: * @exception ServletException if servlet can't be initialized.
257: */
1.36 bmahe 258: protected void checkServlet()
259: throws ClassNotFoundException, ServletException
260: {
261: boolean classmodified =
262: getLocalServletLoader().classChanged(getServletClass());
263:
1.47 bmahe 264: if ((! inited) ||
265: classmodified ||
1.36 bmahe 266: (servlet.getClass() !=
267: getLocalServletLoader().loadClass(getServletClass())))
268: {
269: inited = launchServlet();
270: }
1.20 bmahe 271: }
272:
273: protected boolean isInited() {
274: return inited;
275: }
276:
1.55 ylafon 277: private class ServletRunner extends Thread {
278: Servlet srServlet;
279: JigsawHttpServletRequest srReq;
280: JigsawHttpServletResponse srResp;
281:
282: public void run() {
283: try {
284: srServlet.service(srReq , srResp);
285: srResp.flushStream(true);
1.56 ylafon 286: } catch (UnavailableException uex) {
287: String message = null;
288: srResp.setStatus(HTTP.SERVICE_UNAVAILABLE);
289: if (uex.isPermanent()) {
290: message = "<h2>The servlet is permanently "+
291: "unavailable :</h2>"+
292: "Details: <b>"+uex.getMessage()+"</b>";
293: try {
294: srResp.sendError(HTTP.SERVICE_UNAVAILABLE, message);
295: } catch (IOException ioex) {
296: // not much to do now...
297: }
298: } else {
299: int delay = uex.getUnavailableSeconds();
300: if (delay > 0) {
301: message = "<h2>The servlet is temporarily "+
302: "unavailable :</h2>"+
303: "Delay : "+delay+
304: " seconds<br><br>Details: <b>"+
305: uex.getMessage()+"</b>";
306: srResp.getReply().setRetryAfter(delay);
307: try {
308: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
309: } catch (IOException ioex) {
310: // not much to do now...
311: }
312: } else {
313: message = "<h2>The servlet is temporarily "+
314: "unavailable :</h2>"+
315: "Details: <b>"+uex.getMessage()+
316: "</b>";
317: try {
318: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
319: } catch (IOException ioex) {
320: // not much to do now...
321: }
322: }
323: }
1.55 ylafon 324: } catch (Exception ex) {
1.56 ylafon 325: try {
326: srResp.sendError(HTTP.INTERNAL_SERVER_ERROR,
327: "Servlet has thrown exception:" +
328: ex.toString());
329: } catch (IOException ioex) {
330: // no stream to write on, fail silently
331: }
332: } finally {
333: // release the monitor waiting for the end of the reply setup
334: Reply reply = srResp.getReply();
335: if (reply != null) {
336: Object o = reply.getState(
337: JigsawHttpServletResponse.MONITOR);
338: if (o != null) {
339: synchronized (o) {
340: o.notifyAll();
341: }
342: }
343: }
1.55 ylafon 344: }
345: }
346:
347: ServletRunner(Servlet servlet,
348: JigsawHttpServletRequest request,
349: JigsawHttpServletResponse response) {
350: srServlet = servlet;
351: srReq = request;
352: srResp = response;
353: setName("ServletRunner");
354: }
355: }
356:
1.20 bmahe 357: protected void service(Request request, Reply reply)
358: throws ServletException, IOException
359: {
1.23 bmahe 360: JigsawHttpServletResponse jRes = null;
361: JigsawHttpServletRequest jReq = null;
362: if (servlet instanceof SingleThreadModel) {
363: synchronized (this) {
364: jRes = new JigsawHttpServletResponse(request, reply);
1.28 bmahe 365: jReq = new JigsawHttpServletRequest(servlet,
366: request,
367: jRes,
368: getSessionContext());
369: jRes.setServletRequest(jReq);
1.46 bmahe 370: try {
371: connections++;
1.55 ylafon 372: // FIXME we should reuse a thread rather than
373: // reallocating one for every hit
1.57 ! ylafon 374: ServletRunner runner;
! 375: runner = new ServletRunner(servlet, jReq, jRes);
! 376: reply.setState(RUNNER, runner);
! 377: runner.start();
1.46 bmahe 378: } finally {
379: connections--;
380: }
1.23 bmahe 381: }
382: } else {
1.31 bmahe 383: jRes = new JigsawHttpServletResponse(request, reply);
384: jReq = new JigsawHttpServletRequest(servlet,
385: request,
386: jRes,
387: getSessionContext());
388: jRes.setServletRequest(jReq);
1.46 bmahe 389: try {
390: connections++;
1.55 ylafon 391: // FIXME we should reuse a thread rather than
392: // reallocating one for every hit
1.57 ! ylafon 393: // reallocating one for every hit
! 394: ServletRunner runner;
! 395: runner = new ServletRunner(servlet, jReq, jRes);
! 396: reply.setState(RUNNER, runner);
! 397: runner.start();
1.46 bmahe 398: } finally {
399: connections--;
400: }
1.23 bmahe 401: }
1.40 bmahe 402: timeoutManager.restart();
1.20 bmahe 403: }
404:
405: /**
406: * Get the class name of the wrapped servlet.
407: * @return The class name for the servlet if attribute is defined.
408: * Otherwise the class name is deduced from the resource identifier.
409: */
410:
411: public String getServletClass()
412: {
413: String sclass = getString(ATTR_SERVLET_CLASS, null);
414: if (sclass == null) {
415: String ident = getIdentifier();
416: if (ident.endsWith(".class"))
417: sclass = ident;
418: }
419: return sclass;
420: }
421:
422: /**
423: * Get the init parameters for our wrapped servlet.
424: * @return An ArrayDictionary instance if the attribute is defined,
425: * <strong>false</strong> otherwise.
426: */
427:
428: public ArrayDictionary getServletParameters() {
429: return (ArrayDictionary) getValue(ATTR_PARAMETERS, null);
430: }
431:
1.26 bmahe 432: protected void setValueOfSuperClass(int idx, Object value) {
433: super.setValue(idx, value);
434: }
435:
1.20 bmahe 436: /**
437: * Catch assignements to the servlet class name attribute.
438: * <p>When a change to that attribute is detected, the servlet is
439: * automatically reinitialized.
440: */
441:
442: public void setValue(int idx, Object value) {
443: super.setValue(idx, value);
1.44 bmahe 444: if (((idx == ATTR_SERVLET_CLASS) && (value != null)) ||
445: (idx == ATTR_PARAMETERS)) {
1.36 bmahe 446: try {
447: inited = launchServlet();
448: } catch (Exception ex) {
449: String msg = ("unable to set servlet class \""+
450: getServletClass()+
451: "\" : "+
452: ex.getMessage());
453: getServer().errlog(msg);
454: }
1.40 bmahe 455: } if (idx == ATTR_SERVLET_TIMEOUT) {
456: timeoutManager.restart();
1.36 bmahe 457: }
1.20 bmahe 458: }
459:
460: /**
461: * Destroy the servlet we are wrapping.
462: */
463:
464: protected synchronized void destroyServlet() {
1.46 bmahe 465: if ((servlet != null) && (connections < 1)) {
1.20 bmahe 466: servlet.destroy();
467: servlet = null;
1.47 bmahe 468: inited = false;
1.20 bmahe 469: }
470: }
471:
472: /**
473: * Get the servlet we are wrapping.
474: * @return A servlet instance, if the servlet is alredy running,
475: * <strong>null</strong> otherwise.
476: */
477:
1.34 bmahe 478: public synchronized Servlet getServlet() {
1.36 bmahe 479: try {
480: checkServlet();
481: } catch (Exception ex) {
482: if (debug)
483: ex.printStackTrace();
484: }
1.20 bmahe 485: return servlet;
486: }
487:
488: /**
489: * Initialize our servlet from the given (loaded) class.
490: * @param cls The servlet loaded main class.
491: * @return A boolean, <strong>true</strong> if servlet was successfully
492: * initialised, <strong>false</strong> otherwise.
1.37 bmahe 493: * @exception ServletException if servlet can't be initialized.
1.20 bmahe 494: */
495:
1.36 bmahe 496: protected boolean launchServlet(Class cls)
497: throws ServletException
498: {
1.50 bmahe 499: if (debug) {
500: System.out.println("launching Servlet: "+getServletName());
501: }
1.20 bmahe 502: try {
503: servlet = (Servlet) cls.newInstance();
504: servlet.init((ServletConfig) this);
1.40 bmahe 505: timeoutManager.restart();
1.36 bmahe 506: } catch (IllegalAccessException ex) {
507: String msg = ("Illegal access during servlet instantiation, "+
1.34 bmahe 508: ex.getClass().getName()+": "+
1.20 bmahe 509: ex.getMessage());
510: if ( debug )
511: ex.printStackTrace();
512: getServer().errlog(this, msg);
513: return false;
1.36 bmahe 514: } catch (InstantiationException iex) {
515: String msg = ("unable to instantiate servlet, "+
516: iex.getClass().getName()+": "+
517: iex.getMessage());
518: if ( debug )
519: iex.printStackTrace();
520: getServer().errlog(this, msg);
521: return false;
1.20 bmahe 522: }
523: return (servlet != null) ;
1.32 bmahe 524: }
525:
526: /**
527: * Check if the Servletclass wrapped is a Servlet class without
1.36 bmahe 528: * initializing it. (not the same than checkServlet).
1.33 bmahe 529: * used by the ServletIndexer.
530: * @see org.w3c.jigsaw.servlet.ServletIndexer
1.32 bmahe 531: * @return A boolean.
532: */
533: protected boolean isWrappingAServlet() {
534: String clsname = getServletClass();
535: if ( clsname == null )
536: return false;
537: Class c = null;
538: try {
1.34 bmahe 539: c = getLocalServletLoader().loadClass(clsname, true);
1.32 bmahe 540: Object o = c.newInstance();
541: return (o instanceof Servlet);
542: } catch (Exception ex) {
543: return false;
544: }
1.20 bmahe 545: }
546:
547: /**
548: * Launch the servlet we are wrapping.
549: * <p>This method either succeed, or the wrapper resource itself will fail
550: * to initialize, acting as transparently as possible (in some sense).
551: * @return A boolean, <strong>true</strong> if servlet launched.
1.37 bmahe 552: * @exception ClassNotFoundException if servlet class can't be found.
553: * @exception ServletException if servlet can't be initialized.
1.20 bmahe 554: */
555:
1.36 bmahe 556: protected boolean launchServlet()
557: throws ClassNotFoundException, ServletException
558: {
1.20 bmahe 559: // Get and check the servlet class:
560: if ( servlet != null )
561: destroyServlet();
562: String clsname = getServletClass();
563: if ( clsname == null ) {
564: getServer().errlog(this, "no servlet class attribute defined.");
565: return false;
566: } else {
567: Class c = null;
568: try {
1.36 bmahe 569: if (getLocalServletLoader().classChanged(clsname)) {
1.34 bmahe 570: createNewLocalServletLoader(true);
1.36 bmahe 571: invalidateAllSession();
572: }
1.34 bmahe 573: c = getLocalServletLoader().loadClass(clsname, true);
1.20 bmahe 574: } catch (ClassNotFoundException ex) {
1.36 bmahe 575: String msg = ("unable to find servlet class \""+
576: getServletClass()+"\"");
1.20 bmahe 577: getServer().errlog(msg);
1.36 bmahe 578: // re throw the exception
579: throw ex;
1.20 bmahe 580: }
581: return (c != null) ? launchServlet(c) : false;
582: }
583: }
584:
1.40 bmahe 585: public boolean acceptUnload() {
586: return (servlet == null);
587: }
588:
1.20 bmahe 589: public void notifyUnload() {
1.42 bmahe 590: if (timeoutManager != null)
591: timeoutManager.stop();
1.20 bmahe 592: destroyServlet();
1.26 bmahe 593: }
594:
595: /**
596: * Get or create a suitable LocalServletLoader instance to load
597: * that servlet.
598: * @return A LocalServletLoader instance.
599: */
600:
1.34 bmahe 601: protected synchronized AutoReloadServletLoader getLocalServletLoader() {
602: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
603: return ctxt.getLocalServletLoader();
1.26 bmahe 604: }
605:
1.34 bmahe 606: protected
607: AutoReloadServletLoader createNewLocalServletLoader(boolean keepold)
1.26 bmahe 608: {
1.34 bmahe 609: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
610: return ctxt.createNewLocalServletLoader(keepold);
1.20 bmahe 611: }
612:
613: /**
1.48 bmahe 614: * Returns the name of this servlet instance.
615: * The name may be provided via server administration, assigned in the
616: * web application deployment descriptor, or for an unregistered (and thus
617: * unnamed) servlet instance it will be the servlet's class name.
618: * @return the name of the servlet instance
619: */
620: public String getServletName() {
621: return getIdentifier();
622: }
623:
624: /**
1.20 bmahe 625: * Initialize this servlet wrapper resource.
626: * After the wrapper itself is inited, it performs the servlet
627: * initialzation.
628: * @param values The default attribute values.
629: */
630: public void initialize(Object values[]) {
631: super.initialize(values);
1.46 bmahe 632: connections = 0;
1.41 bmahe 633:
634: if (getServletContext() != null) {
635: timeoutManager = new TimeoutManager((httpd)getServer());
636: timeoutManager.start();
637: }
1.40 bmahe 638:
1.20 bmahe 639: try {
640: registerFrameIfNone("org.w3c.jigsaw.servlet.ServletWrapperFrame",
641: "servlet-wrapper-frame");
642: } catch (Exception ex) {
643: ex.printStackTrace();
644: }
1.40 bmahe 645:
1.18 bmahe 646: }
1.1 abaird 647:
648: }
649:
650:
Webmaster