Annotation of java/classes/org/w3c/jigsaw/servlet/ServletWrapper.java, revision 1.58
1.1 abaird 1: // ServletWrapper.java
1.58 ! ylafon 2: // $Id: ServletWrapper.java,v 1.57 2000/12/21 16:57:25 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() {
1.58 ! ylafon 283: // synchronization object
! 284: Object o = null;
1.55 ylafon 285: try {
1.58 ! ylafon 286: Reply reply = srResp.getReply();
! 287: if (reply != null) {
! 288: o = reply.getState(JigsawHttpServletResponse.MONITOR);
! 289: }
1.55 ylafon 290: srServlet.service(srReq , srResp);
291: srResp.flushStream(true);
1.56 ylafon 292: } catch (UnavailableException uex) {
293: String message = null;
294: srResp.setStatus(HTTP.SERVICE_UNAVAILABLE);
295: if (uex.isPermanent()) {
296: message = "<h2>The servlet is permanently "+
297: "unavailable :</h2>"+
298: "Details: <b>"+uex.getMessage()+"</b>";
299: try {
300: srResp.sendError(HTTP.SERVICE_UNAVAILABLE, message);
301: } catch (IOException ioex) {
302: // not much to do now...
303: }
304: } else {
305: int delay = uex.getUnavailableSeconds();
306: if (delay > 0) {
307: message = "<h2>The servlet is temporarily "+
308: "unavailable :</h2>"+
309: "Delay : "+delay+
310: " seconds<br><br>Details: <b>"+
311: uex.getMessage()+"</b>";
312: srResp.getReply().setRetryAfter(delay);
313: try {
314: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
315: } catch (IOException ioex) {
316: // not much to do now...
317: }
318: } else {
319: message = "<h2>The servlet is temporarily "+
320: "unavailable :</h2>"+
321: "Details: <b>"+uex.getMessage()+
322: "</b>";
323: try {
324: srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
325: } catch (IOException ioex) {
326: // not much to do now...
327: }
328: }
329: }
1.55 ylafon 330: } catch (Exception ex) {
1.56 ylafon 331: try {
332: srResp.sendError(HTTP.INTERNAL_SERVER_ERROR,
333: "Servlet has thrown exception:" +
334: ex.toString());
335: } catch (IOException ioex) {
336: // no stream to write on, fail silently
337: }
338: } finally {
339: // release the monitor waiting for the end of the reply setup
1.58 ! ylafon 340: if (o != null) {
! 341: synchronized (o) {
! 342: o.notifyAll();
1.56 ylafon 343: }
344: }
1.55 ylafon 345: }
346: }
347:
348: ServletRunner(Servlet servlet,
349: JigsawHttpServletRequest request,
350: JigsawHttpServletResponse response) {
351: srServlet = servlet;
352: srReq = request;
353: srResp = response;
354: setName("ServletRunner");
355: }
356: }
357:
1.20 bmahe 358: protected void service(Request request, Reply reply)
359: throws ServletException, IOException
360: {
1.23 bmahe 361: JigsawHttpServletResponse jRes = null;
362: JigsawHttpServletRequest jReq = null;
363: if (servlet instanceof SingleThreadModel) {
364: synchronized (this) {
365: jRes = new JigsawHttpServletResponse(request, reply);
1.28 bmahe 366: jReq = new JigsawHttpServletRequest(servlet,
367: request,
368: jRes,
369: getSessionContext());
370: jRes.setServletRequest(jReq);
1.46 bmahe 371: try {
372: connections++;
1.55 ylafon 373: // FIXME we should reuse a thread rather than
374: // reallocating one for every hit
1.57 ylafon 375: ServletRunner runner;
376: runner = new ServletRunner(servlet, jReq, jRes);
377: reply.setState(RUNNER, runner);
378: runner.start();
1.46 bmahe 379: } finally {
380: connections--;
381: }
1.23 bmahe 382: }
383: } else {
1.31 bmahe 384: jRes = new JigsawHttpServletResponse(request, reply);
385: jReq = new JigsawHttpServletRequest(servlet,
386: request,
387: jRes,
388: getSessionContext());
389: jRes.setServletRequest(jReq);
1.46 bmahe 390: try {
391: connections++;
1.55 ylafon 392: // FIXME we should reuse a thread rather than
393: // reallocating one for every hit
1.57 ylafon 394: // reallocating one for every hit
395: ServletRunner runner;
396: runner = new ServletRunner(servlet, jReq, jRes);
397: reply.setState(RUNNER, runner);
398: runner.start();
1.46 bmahe 399: } finally {
400: connections--;
401: }
1.23 bmahe 402: }
1.40 bmahe 403: timeoutManager.restart();
1.20 bmahe 404: }
405:
406: /**
407: * Get the class name of the wrapped servlet.
408: * @return The class name for the servlet if attribute is defined.
409: * Otherwise the class name is deduced from the resource identifier.
410: */
411:
412: public String getServletClass()
413: {
414: String sclass = getString(ATTR_SERVLET_CLASS, null);
415: if (sclass == null) {
416: String ident = getIdentifier();
417: if (ident.endsWith(".class"))
418: sclass = ident;
419: }
420: return sclass;
421: }
422:
423: /**
424: * Get the init parameters for our wrapped servlet.
425: * @return An ArrayDictionary instance if the attribute is defined,
426: * <strong>false</strong> otherwise.
427: */
428:
429: public ArrayDictionary getServletParameters() {
430: return (ArrayDictionary) getValue(ATTR_PARAMETERS, null);
431: }
432:
1.26 bmahe 433: protected void setValueOfSuperClass(int idx, Object value) {
434: super.setValue(idx, value);
435: }
436:
1.20 bmahe 437: /**
438: * Catch assignements to the servlet class name attribute.
439: * <p>When a change to that attribute is detected, the servlet is
440: * automatically reinitialized.
441: */
442:
443: public void setValue(int idx, Object value) {
444: super.setValue(idx, value);
1.44 bmahe 445: if (((idx == ATTR_SERVLET_CLASS) && (value != null)) ||
446: (idx == ATTR_PARAMETERS)) {
1.36 bmahe 447: try {
448: inited = launchServlet();
449: } catch (Exception ex) {
450: String msg = ("unable to set servlet class \""+
451: getServletClass()+
452: "\" : "+
453: ex.getMessage());
454: getServer().errlog(msg);
455: }
1.40 bmahe 456: } if (idx == ATTR_SERVLET_TIMEOUT) {
457: timeoutManager.restart();
1.36 bmahe 458: }
1.20 bmahe 459: }
460:
461: /**
462: * Destroy the servlet we are wrapping.
463: */
464:
465: protected synchronized void destroyServlet() {
1.46 bmahe 466: if ((servlet != null) && (connections < 1)) {
1.20 bmahe 467: servlet.destroy();
468: servlet = null;
1.47 bmahe 469: inited = false;
1.20 bmahe 470: }
471: }
472:
473: /**
474: * Get the servlet we are wrapping.
475: * @return A servlet instance, if the servlet is alredy running,
476: * <strong>null</strong> otherwise.
477: */
478:
1.34 bmahe 479: public synchronized Servlet getServlet() {
1.36 bmahe 480: try {
481: checkServlet();
482: } catch (Exception ex) {
483: if (debug)
484: ex.printStackTrace();
485: }
1.20 bmahe 486: return servlet;
487: }
488:
489: /**
490: * Initialize our servlet from the given (loaded) class.
491: * @param cls The servlet loaded main class.
492: * @return A boolean, <strong>true</strong> if servlet was successfully
493: * initialised, <strong>false</strong> otherwise.
1.37 bmahe 494: * @exception ServletException if servlet can't be initialized.
1.20 bmahe 495: */
496:
1.36 bmahe 497: protected boolean launchServlet(Class cls)
498: throws ServletException
499: {
1.50 bmahe 500: if (debug) {
501: System.out.println("launching Servlet: "+getServletName());
502: }
1.20 bmahe 503: try {
504: servlet = (Servlet) cls.newInstance();
505: servlet.init((ServletConfig) this);
1.40 bmahe 506: timeoutManager.restart();
1.36 bmahe 507: } catch (IllegalAccessException ex) {
508: String msg = ("Illegal access during servlet instantiation, "+
1.34 bmahe 509: ex.getClass().getName()+": "+
1.20 bmahe 510: ex.getMessage());
511: if ( debug )
512: ex.printStackTrace();
513: getServer().errlog(this, msg);
514: return false;
1.36 bmahe 515: } catch (InstantiationException iex) {
516: String msg = ("unable to instantiate servlet, "+
517: iex.getClass().getName()+": "+
518: iex.getMessage());
519: if ( debug )
520: iex.printStackTrace();
521: getServer().errlog(this, msg);
522: return false;
1.20 bmahe 523: }
524: return (servlet != null) ;
1.32 bmahe 525: }
526:
527: /**
528: * Check if the Servletclass wrapped is a Servlet class without
1.36 bmahe 529: * initializing it. (not the same than checkServlet).
1.33 bmahe 530: * used by the ServletIndexer.
531: * @see org.w3c.jigsaw.servlet.ServletIndexer
1.32 bmahe 532: * @return A boolean.
533: */
534: protected boolean isWrappingAServlet() {
535: String clsname = getServletClass();
536: if ( clsname == null )
537: return false;
538: Class c = null;
539: try {
1.34 bmahe 540: c = getLocalServletLoader().loadClass(clsname, true);
1.32 bmahe 541: Object o = c.newInstance();
542: return (o instanceof Servlet);
543: } catch (Exception ex) {
544: return false;
545: }
1.20 bmahe 546: }
547:
548: /**
549: * Launch the servlet we are wrapping.
550: * <p>This method either succeed, or the wrapper resource itself will fail
551: * to initialize, acting as transparently as possible (in some sense).
552: * @return A boolean, <strong>true</strong> if servlet launched.
1.37 bmahe 553: * @exception ClassNotFoundException if servlet class can't be found.
554: * @exception ServletException if servlet can't be initialized.
1.20 bmahe 555: */
556:
1.36 bmahe 557: protected boolean launchServlet()
558: throws ClassNotFoundException, ServletException
559: {
1.20 bmahe 560: // Get and check the servlet class:
561: if ( servlet != null )
562: destroyServlet();
563: String clsname = getServletClass();
564: if ( clsname == null ) {
565: getServer().errlog(this, "no servlet class attribute defined.");
566: return false;
567: } else {
568: Class c = null;
569: try {
1.36 bmahe 570: if (getLocalServletLoader().classChanged(clsname)) {
1.34 bmahe 571: createNewLocalServletLoader(true);
1.36 bmahe 572: invalidateAllSession();
573: }
1.34 bmahe 574: c = getLocalServletLoader().loadClass(clsname, true);
1.20 bmahe 575: } catch (ClassNotFoundException ex) {
1.36 bmahe 576: String msg = ("unable to find servlet class \""+
577: getServletClass()+"\"");
1.20 bmahe 578: getServer().errlog(msg);
1.36 bmahe 579: // re throw the exception
580: throw ex;
1.20 bmahe 581: }
582: return (c != null) ? launchServlet(c) : false;
583: }
584: }
585:
1.40 bmahe 586: public boolean acceptUnload() {
587: return (servlet == null);
588: }
589:
1.20 bmahe 590: public void notifyUnload() {
1.42 bmahe 591: if (timeoutManager != null)
592: timeoutManager.stop();
1.20 bmahe 593: destroyServlet();
1.26 bmahe 594: }
595:
596: /**
597: * Get or create a suitable LocalServletLoader instance to load
598: * that servlet.
599: * @return A LocalServletLoader instance.
600: */
601:
1.34 bmahe 602: protected synchronized AutoReloadServletLoader getLocalServletLoader() {
603: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
604: return ctxt.getLocalServletLoader();
1.26 bmahe 605: }
606:
1.34 bmahe 607: protected
608: AutoReloadServletLoader createNewLocalServletLoader(boolean keepold)
1.26 bmahe 609: {
1.34 bmahe 610: JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
611: return ctxt.createNewLocalServletLoader(keepold);
1.20 bmahe 612: }
613:
614: /**
1.48 bmahe 615: * Returns the name of this servlet instance.
616: * The name may be provided via server administration, assigned in the
617: * web application deployment descriptor, or for an unregistered (and thus
618: * unnamed) servlet instance it will be the servlet's class name.
619: * @return the name of the servlet instance
620: */
621: public String getServletName() {
622: return getIdentifier();
623: }
624:
625: /**
1.20 bmahe 626: * Initialize this servlet wrapper resource.
627: * After the wrapper itself is inited, it performs the servlet
628: * initialzation.
629: * @param values The default attribute values.
630: */
631: public void initialize(Object values[]) {
632: super.initialize(values);
1.46 bmahe 633: connections = 0;
1.41 bmahe 634:
635: if (getServletContext() != null) {
636: timeoutManager = new TimeoutManager((httpd)getServer());
637: timeoutManager.start();
638: }
1.40 bmahe 639:
1.20 bmahe 640: try {
641: registerFrameIfNone("org.w3c.jigsaw.servlet.ServletWrapperFrame",
642: "servlet-wrapper-frame");
643: } catch (Exception ex) {
644: ex.printStackTrace();
645: }
1.40 bmahe 646:
1.18 bmahe 647: }
1.1 abaird 648:
649: }
650:
651:
Webmaster