001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings.session;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.wings.*;
018: import org.wings.script.JavaScriptListener;
019: import org.wings.event.ExitVetoException;
020: import org.wings.event.SRequestEvent;
021: import org.wings.externalizer.ExternalizeManager;
022: import org.wings.externalizer.ExternalizedResource;
023: import org.wings.io.*;
024: import org.wings.resource.*;
025: import org.wings.util.SStringBuilder;
026:
027: import javax.servlet.ServletConfig;
028: import javax.servlet.ServletContext;
029: import javax.servlet.ServletException;
030: import javax.servlet.ServletOutputStream;
031: import javax.servlet.http.*;
032:
033: import java.io.IOException;
034: import java.util.ArrayList;
035: import java.util.Collection;
036: import java.util.Enumeration;
037: import java.util.Locale;
038: import java.util.Arrays;
039: import java.util.StringTokenizer;
040:
041: /**
042: * The servlet engine creates for each user a new HttpSession. This
043: * HttpSession can be accessed by all Serlvets running in the engine. A
044: * WingServlet creates one wings SessionServlet per HTTPSession and stores
045: * it in its context.
046: * <p>As the SessionServlets acts as Wrapper for the WingsServlet, you can
047: * access from there as used the ServletContext and the HttpSession.
048: * Additionally the SessionServlet containts also the wingS-Session with
049: * all important services and the superordinated SFrame. To this SFrame all
050: * wings-Components and hence the complete application state is attached.
051: * The developer can access from any place via the SessionManager a
052: * reference to the wingS-Session. Additionally the SessionServlet
053: * provides access to the all containing HttpSession.
054: *
055: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
056: */
057: final class SessionServlet extends HttpServlet implements
058: HttpSessionBindingListener {
059: private final transient static Log log = LogFactory
060: .getLog(SessionServlet.class);
061:
062: /**
063: * The parent {@link WingServlet}
064: */
065: protected transient HttpServlet parent = this ;
066:
067: /**
068: * The session.
069: */
070: private transient/* --- ATTENTION! This disable session serialization! */Session session;
071:
072: private boolean firstRequest = true;
073:
074: /** Refer to comment in {@link #doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)} */
075: private String exitSessionWorkaround;
076:
077: private Boolean clientDebuggingEnabled = null;
078:
079: /**
080: * Default constructor.
081: */
082: protected SessionServlet() {
083: }
084:
085: /**
086: * Sets the parent servlet contianint this wings session
087: * servlet (WingsServlet, delegating its requests to the SessionServlet).
088: */
089: protected final void setParent(HttpServlet p) {
090: if (p != null)
091: parent = p;
092: }
093:
094: public final Session getSession() {
095: return session;
096: }
097:
098: /**
099: * Overrides the session set for setLocaleFromHeader by a request parameter.
100: * Hence you can force the wings session to adopt the clients Locale.
101: */
102: public final void setLocaleFromHeader(String[] args) {
103: if (args == null)
104: return;
105:
106: for (int i = 0; i < args.length; i++) {
107: try {
108: getSession().setLocaleFromHeader(
109: Boolean.valueOf(args[i]).booleanValue());
110: } catch (Exception e) {
111: log.error("setLocaleFromHeader", e);
112: }
113: }
114: }
115:
116: /**
117: * The Locale of the current wings session servlet is determined by
118: * the locale transmitted by the browser. The request parameter
119: * <PRE>LocaleFromHeader</PRE> can override the behaviour
120: * of a wings session servlet to adopt the clients browsers Locale.
121: *
122: * @param req The request to determine the local from.
123: */
124: protected final void handleLocale(HttpServletRequest req) {
125: setLocaleFromHeader(req.getParameterValues("LocaleFromHeader"));
126:
127: if (getSession().getLocaleFromHeader())
128: getSession().determineLocale();
129: }
130:
131: // jetzt kommen alle Servlet Methoden, die an den parent deligiert
132: // werden
133:
134: public ServletContext getServletContext() {
135: if (parent != this )
136: return parent.getServletContext();
137: else
138: return super .getServletContext();
139: }
140:
141: public String getInitParameter(String name) {
142: if (parent != this )
143: return parent.getInitParameter(name);
144: else
145: return super .getInitParameter(name);
146: }
147:
148: public Enumeration getInitParameterNames() {
149: if (parent != this )
150: return parent.getInitParameterNames();
151: else
152: return super .getInitParameterNames();
153: }
154:
155: /**
156: * Delegates log messages to the according WingsServlet or alternativly
157: * to the HttpServlet logger.
158: *
159: * @param msg The logmessage
160: */
161: public void log(String msg) {
162: if (parent != this )
163: parent.log(msg);
164: else
165: super .log(msg);
166: }
167:
168: public String getServletInfo() {
169: if (parent != this )
170: return parent.getServletInfo();
171: else
172: return super .getServletInfo();
173: }
174:
175: public ServletConfig getServletConfig() {
176: if (parent != this )
177: return parent.getServletConfig();
178: else
179: return super .getServletConfig();
180: }
181:
182: // bis hierhin
183:
184: /**
185: * init
186: */
187: public final void init(ServletConfig config,
188: HttpServletRequest request, HttpServletResponse response)
189: throws ServletException {
190: try {
191: session = new Session();
192: SessionManager.setSession(session);
193:
194: // set request.url in session, if used in constructor of wings main classs
195: //if (request.isRequestedSessionIdValid()) {
196: // this will fire an event, if the encoding has changed ..
197: session.setProperty("request.url", new RequestURL("",
198: getSessionEncoding(response)));
199: //}
200:
201: session.init(config, request, response);
202:
203: try {
204: String mainClassName = config
205: .getInitParameter("wings.mainclass");
206: Class mainClass = null;
207: try {
208: mainClass = Class.forName(mainClassName, true,
209: Thread.currentThread()
210: .getContextClassLoader());
211: } catch (ClassNotFoundException e) {
212: // fallback, in case the servlet container fails to set the
213: // context class loader.
214: mainClass = Class.forName(mainClassName);
215: }
216: mainClass.newInstance();
217: } catch (Exception ex) {
218: log.fatal("could not load wings.mainclass: "
219: + config.getInitParameter("wings.mainclass"),
220: ex);
221: throw new ServletException(ex);
222: }
223: } finally {
224: // The session was set by the constructor. After init we
225: // expect that only doPost/doGet is called, which set the
226: // session also. So remove it here.
227: SessionManager.removeSession();
228: }
229: }
230:
231: /**
232: * this method references to
233: * {@link #doGet(HttpServletRequest, HttpServletResponse)}
234: */
235: public final void doPost(HttpServletRequest req,
236: HttpServletResponse res) throws IOException {
237: //value chosen to limit denial of service
238: if (req.getContentLength() > getSession().getMaxContentLength() * 1024) {
239: res.setContentType("text/html");
240: ServletOutputStream out = res.getOutputStream();
241: out
242: .println("<html><head><meta http-equiv=\"expires\" content=\"0\"><title>Too big</title></head>");
243: out.println("<body><h1>Error - content length > "
244: + getSession().getMaxContentLength() + "k");
245: out.println("</h1></body></html>");
246: } else {
247: doGet(req, res);
248: }
249: // sollte man den obigen Block nicht durch folgende Zeile ersetzen?
250: //throw new RuntimeException("this method must never be called!");
251: // bsc: Wieso?
252: }
253:
254: /**
255: * Verarbeitet Informationen vom Browser:
256: * <UL>
257: * <LI> setzt Locale
258: * <LI> Dispatch Get Parameter
259: * <LI> feuert Form Events
260: * </UL>
261: * Ist synchronized, damit nur ein Frame gleichzeitig bearbeitet
262: * werden kann.
263: */
264: public final synchronized void doGet(HttpServletRequest req,
265: HttpServletResponse response) {
266: // Special case: You double clicked i.e. a "logout button"
267: // First request arrives, second is on hold. First invalidates session and sends redirect as response,
268: // but browser ignores and expects response in second request. But second request has longer a valid session.
269: if (session == null) {
270: try {
271: response
272: .sendRedirect(exitSessionWorkaround != null ? exitSessionWorkaround
273: : "");
274: return;
275: } catch (IOException e) {
276: log
277: .info("Session exit workaround failed to to IOException (triple click?)");
278: }
279: }
280:
281: SessionManager.setSession(session);
282: session.setServletRequest(req);
283: session.setServletResponse(response);
284:
285: session.fireRequestEvent(SRequestEvent.REQUEST_START);
286:
287: // in case, the previous thread did not clean up.
288: SForm.clearArmedComponents();
289:
290: Device outputDevice = null;
291:
292: ReloadManager reloadManager = session.getReloadManager();
293:
294: try {
295: /*
296: * The tomcat 3.x has a bug, in that it does not encode the URL
297: * sometimes. It does so, when there is a cookie, containing some
298: * tomcat sessionid but that is invalid (because, for instance,
299: * we restarted the tomcat in-between).
300: * [I can't think of this being the correct behaviour, so I assume
301: * it is a bug. ]
302: *
303: * So we have to workaround this here: if we actually got the
304: * session id from a cookie, but it is not valid, we don't do
305: * the encodeURL() here: we just leave the requestURL as it is
306: * in the properties .. and this is url-encoded, since
307: * we had set it up in the very beginning of this session
308: * with URL-encoding on (see WingServlet::newSession()).
309: *
310: * Vice versa: if the requestedSessionId is valid, then we can
311: * do the encoding (which then does URL-encoding or not, depending
312: * whether the servlet engine detected a cookie).
313: * (hen)
314: */
315: RequestURL requestURL = null;
316: if (req.isRequestedSessionIdValid()) {
317: requestURL = new RequestURL("",
318: getSessionEncoding(response));
319: // this will fire an event, if the encoding has changed ..
320: session.setProperty("request.url", requestURL);
321: }
322:
323: if (log.isDebugEnabled()) {
324: log.debug("Request URL: " + requestURL);
325: log.debug("HTTP header:");
326: for (Enumeration en = req.getHeaderNames(); en
327: .hasMoreElements();) {
328: String header = (String) en.nextElement();
329: log.debug(" " + header + ": "
330: + req.getHeader(header));
331: }
332: }
333: handleLocale(req);
334:
335: // The externalizer is able to handle static and dynamic resources
336: ExternalizeManager extManager = getSession()
337: .getExternalizeManager();
338: String pathInfo = req.getPathInfo(); // Note: Websphere returns <code>null</code> here!
339: if (pathInfo != null && pathInfo.length() > 0) {
340: // strip of leading /
341: pathInfo = pathInfo.substring(1);
342: }
343: if (log.isDebugEnabled())
344: log.debug("pathInfo: " + pathInfo);
345:
346: // If we have no path info, or the special '_' path info (that should be explained
347: // somewhere, Holger), then we deliver the top-level frame of this application.
348: String externalizeIdentifier = null;
349: if (pathInfo == null || pathInfo.length() == 0
350: || "_".equals(pathInfo) || firstRequest) {
351: externalizeIdentifier = retrieveCurrentRootFrameResource()
352: .getId();
353: firstRequest = false;
354: } else {
355: externalizeIdentifier = pathInfo;
356: }
357:
358: // Retrieve externalized resource
359: ExternalizedResource extInfo = extManager
360: .getExternalizedResource(externalizeIdentifier);
361:
362: // Special case handling: We request a .html resource of a session which is not accessible.
363: // This happens some times and leads to a 404, though it should not be possible.
364: if (extInfo == null && pathInfo != null
365: && pathInfo.endsWith(".html")) {
366: log
367: .info("Found a request to an invalid .html during a valid session. Redirecting to root frame.");
368: response
369: .sendRedirect(retrieveCurrentRootFrameResource()
370: .getURL().toString());
371: return;
372: }
373:
374: if (extInfo != null
375: && extInfo.getObject() instanceof UpdateResource) {
376: reloadManager.setUpdateMode(true);
377: } else {
378: reloadManager.setUpdateMode(false);
379: }
380:
381: // Prior to dispatching the actual events we have to detect
382: // their epoch and inform the dispatcher which will then be
383: // able to check if the request is valid and processed. If
384: // this is not the case, we force a complete page reload.
385: session.getDispatcher().setEventEpoch(
386: req.getParameter("event_epoch"));
387:
388: Enumeration en = req.getParameterNames();
389: final Cookie[] cookies = req.getCookies();
390: final Collection<Cookie> cookiesToDispatch = new ArrayList<Cookie>();
391:
392: // handle debug.cookie - read it every time.
393: session.removeProperty("debug.cookie");
394: if (cookies != null) {
395: //handle cookies
396: for (int i = 0; i < cookies.length; i++) {
397: Cookie cookie = cookies[i];
398: String paramName = cookie.getName();
399:
400: if ("DEBUG".equals(paramName)
401: && clientDebuggingEnabled()) {
402: // Cookies have a limited length, therefore we copy
403: // them trustingly into the session.
404:
405: // Use a Tokenizer for performance.
406: StringTokenizer tokenizer = new StringTokenizer(
407: cookie.getValue(), ":");
408: String[] values = new String[tokenizer
409: .countTokens()];
410: for (int j = 0; j < values.length; j++) {
411: values[j] = tokenizer.nextToken();
412: }
413: session.setProperty("debug.cookie", values);
414: } else {
415: cookiesToDispatch.add(cookie);
416: }
417: }
418: }
419:
420: // are there parameters/low level events to dispatch
421: if (en.hasMoreElements()) {
422: // only fire DISPATCH_START if we have parameters to dispatch
423: session.fireRequestEvent(SRequestEvent.DISPATCH_START);
424:
425: if (cookiesToDispatch != null) {
426: //dispatch cookies
427: for (Cookie cookie : cookiesToDispatch) {
428: String paramName = cookie.getName();
429: String value = cookie.getValue();
430:
431: if (log.isDebugEnabled())
432: log.debug("dispatching cookie " + paramName
433: + " = " + value);
434:
435: session.getDispatcher().dispatch(paramName,
436: new String[] { value });
437: }
438: }
439:
440: if (log.isDebugEnabled()) {
441: log.debug("Parameters:");
442: for (Enumeration e = req.getParameterNames(); e
443: .hasMoreElements();) {
444: String paramName = (String) e.nextElement();
445: SStringBuilder param = new SStringBuilder();
446: param.append(" ").append(paramName).append(
447: ": ");
448: final String[] values = req
449: .getParameterValues(paramName);
450: param.append(values != null ? Arrays
451: .toString(values) : "null");
452: log.debug(param);
453: }
454: }
455:
456: while (en.hasMoreElements()) {
457: String paramName = (String) en.nextElement();
458: String[] values = req.getParameterValues(paramName);
459:
460: // We do not need to dispatch the event epoch since it is already
461: // handled a few lines above. Furthermore we will not dispatch any
462: // names that start with an '_' (e.g. _xhrId or parts of XCalendar).
463: if (paramName.equals("event_epoch")
464: || paramName.startsWith("_")) {
465: continue;
466: }
467:
468: String value = values[0];
469:
470: // Split the values of the event trigger
471: if (paramName.equals("event_trigger")) {
472: int pos = value.indexOf('|');
473: paramName = value.substring(0, pos);
474: values = new String[] { value
475: .substring(pos + 1) };
476: }
477:
478: // Handle form submit via default button
479: if (paramName.equals("default_button")) {
480: if (value.equals("undefined")) {
481: continue;
482: } else {
483: paramName = values[0];
484: values = new String[] { "1" };
485: }
486: }
487:
488: if (log.isDebugEnabled())
489: log.debug("dispatching " + paramName + " = "
490: + Arrays.asList(values));
491:
492: session.getDispatcher().dispatch(paramName, values);
493: }
494:
495: SForm.fireEvents();
496:
497: // only fire DISPATCH DONE if we have parameters to dispatch
498: session.fireRequestEvent(SRequestEvent.DISPATCH_DONE);
499: }
500:
501: session.fireRequestEvent(SRequestEvent.PROCESS_REQUEST);
502: session.getDispatcher().invokeRunnables();
503:
504: // if the user chose to exit the session as a reaction on an
505: // event, we got an URL to redirect after the session.
506: /*
507: * where is the right place?
508: * The right place is
509: * - _after_ we processed the events
510: * (e.g. the 'Pressed Exit-Button'-event or gave
511: * the user the chance to exit this session in the custom
512: * processRequest())
513: * - but _before_ the rendering of the page,
514: * because otherwise an redirect won't work, since we must
515: * not have sent anything to the output stream).
516: */
517: if (session.getExitAddress() != null) {
518:
519: try {
520: session.firePrepareExit();
521: session.fireRequestEvent(SRequestEvent.REQUEST_END);
522:
523: String redirectAddress;
524: if (session.getExitAddress().length() > 0) {
525: // redirect to user requested URL.
526: redirectAddress = session.getExitAddress();
527: } else {
528: // redirect to a fresh session.
529: redirectAddress = req.getRequestURL()
530: .toString();
531: }
532: req.getSession().invalidate(); // calls destroy implicitly
533: response.sendRedirect(redirectAddress);
534: exitSessionWorkaround = redirectAddress;
535: return;
536: } catch (ExitVetoException ex) {
537: session.exit(null);
538: } // end of try-catch
539: }
540:
541: if (session.getRedirectAddress() != null) {
542: handleRedirect(response);
543: return;
544: }
545:
546: reloadManager.notifyCGs();
547: reloadManager.invalidateFrames();
548:
549: // TODO ResourceMapper
550: ResourceMapper mapper = session.getResourceMapper();
551: if (extInfo == null && mapper != null) {
552: Resource res = mapper.mapResource(req.getPathInfo());
553: if (res != null) {
554: extInfo = extManager.getExternalizedResource(res
555: .getId());
556: }
557: }
558:
559: if (extInfo != null) {
560: outputDevice = DeviceFactory.createDevice(extInfo);
561: session.fireRequestEvent(SRequestEvent.DELIVER_START,
562: extInfo);
563:
564: long startTime = System.currentTimeMillis();
565: extManager.deliver(extInfo, response, outputDevice);
566: long endTime = System.currentTimeMillis();
567: log
568: .debug("------------------------- Time needed for rendering: "
569: + (endTime - startTime)
570: + " ms -------------------------\n");
571:
572: session.fireRequestEvent(SRequestEvent.DELIVER_DONE,
573: extInfo);
574: } else {
575: handleUnknownResourceRequested(req, response);
576: }
577:
578: } catch (Throwable e) {
579: log.error("Uncaught Exception", e);
580: handleException(response, e);
581: } finally {
582: if (session != null) {
583: session.fireRequestEvent(SRequestEvent.REQUEST_END);
584: }
585:
586: if (outputDevice != null) {
587: try {
588: outputDevice.close();
589: } catch (Exception e) {
590: }
591: }
592:
593: /*
594: * the session might be null due to destroy().
595: */
596: if (session != null) {
597: reloadManager.clear();
598: session.setServletRequest(null);
599: session.setServletResponse(null);
600: }
601:
602: // make sure that the session association to the thread is removed
603: // from the SessionManager
604: SessionManager.removeSession();
605: SForm.clearArmedComponents();
606: }
607: }
608:
609: /**
610: * @return
611: */
612: private boolean clientDebuggingEnabled() {
613: if (clientDebuggingEnabled == null) {
614: clientDebuggingEnabled = "true".equals((String) session
615: .getProperty("wings.client.debug")) ? Boolean.TRUE
616: : Boolean.FALSE;
617: }
618: return clientDebuggingEnabled.booleanValue();
619: }
620:
621: /**
622: * Searches the current session for the root HTML frame and returns the Resource
623: * representing this root HTML frame (i.e. for you to retrieve the externalizer id
624: * via <code>getId()</code>-method).
625: * @return Resource of the root HTML frame
626: */
627: private Resource retrieveCurrentRootFrameResource()
628: throws ServletException {
629: log.debug("delivering default frame");
630:
631: if (session.getFrames().size() == 0)
632: throw new ServletException("no frame visible");
633:
634: // get the first frame from the set and walk up the hierarchy.
635: // this should work in most cases. if there are more than one
636: // toplevel frames, the developer has to care about the resource
637: // ids anyway ..
638: SFrame defaultFrame = (SFrame) session.getFrames().iterator()
639: .next();
640: while (defaultFrame.getParent() != null)
641: defaultFrame = (SFrame) defaultFrame.getParent();
642:
643: return defaultFrame.getDynamicResource(ReloadResource.class);
644: }
645:
646: private void handleRedirect(HttpServletResponse response)
647: throws IOException {
648: try {
649: ReloadManager reloadManager = session.getReloadManager();
650: if (reloadManager.isUpdateMode()) {
651: String script = "wingS.request.sendRedirect(\""
652: + session.getRedirectAddress() + "\");";
653: session.getScriptManager().addScriptListener(
654: new JavaScriptListener(null, null, script));
655: /*
656: Resource root = retrieveCurrentRootFrameResource();
657: ExternalizedResource externalizedResource = session.getExternalizeManager().getExternalizedResource(root.getId());
658: session.fireRequestEvent(SRequestEvent.DELIVER_START, externalizedResource);
659:
660: String encoding = session.getCharacterEncoding();
661: response.setContentType("text/xml; charset=" + encoding);
662: ServletOutputStream out = response.getOutputStream();
663: Device outputDevice = new ServletDevice(out);
664: UpdateResource.writeHeader(outputDevice);
665: UpdateResource.writeUpdate(outputDevice, "wingS.request.sendRedirect(\"" + session.getRedirectAddress() + "\");");
666: UpdateResource.writeFooter(outputDevice);
667: outputDevice.flush();
668:
669: session.fireRequestEvent(SRequestEvent.DELIVER_DONE, externalizedResource);
670: session.fireRequestEvent(SRequestEvent.REQUEST_END);
671:
672: reloadManager.clear();
673: session.setServletRequest(null);
674: session.setServletResponse(null);
675: SessionManager.removeSession();
676: SForm.clearArmedComponents();
677: */
678: } else {
679: response.sendRedirect(session.getRedirectAddress());
680: }
681: } catch (Exception e) {
682: log.warn(e.getMessage(), e);
683: } finally {
684: session.setRedirectAddress(null);
685: }
686: }
687:
688: /**
689: * In case of an error, display an error page to the user. This is only
690: * done if there is a properties <code>wings.error.handler</code> defined
691: * in the web.xml file. If the property is present, the following steps
692: * are performed:
693: * <li> Load the class named by the value of that property, using the
694: * current thread's context class loader,
695: * <li> Instantiate that class using its zero-argument constructor,
696: * <li> Cast the instance to ExceptionHandler,
697: * <li> Invoke the handler's <tt>handle</tt> method, passing it the
698: * <tt>thrown</tt> argument that was passed to this method.
699: * </ol>
700: *
701: * @see DefaultExceptionHandler
702: * @param response the HTTP Response to use
703: * @param thrown the Exception to report
704: */
705: protected void handleException(HttpServletResponse response,
706: Throwable thrown) {
707: try {
708: String className = (String) session
709: .getProperty("wings.error.handler");
710: if (className == null)
711: className = DefaultExceptionHandler.class.getName();
712:
713: ClassLoader classLoader = Thread.currentThread()
714: .getContextClassLoader();
715: Class clazz = classLoader.loadClass(className);
716: ExceptionHandler exceptionHandler = (ExceptionHandler) clazz
717: .newInstance();
718:
719: StringBuilderDevice device = new StringBuilderDevice(4096);
720: exceptionHandler.handle(device, thrown);
721: Resource resource = new StringResource(device.toString(),
722: "html", "text/html");
723: String url = session.getExternalizeManager().externalize(
724: resource);
725:
726: ReloadManager reloadManager = session.getReloadManager();
727: reloadManager.notifyCGs();
728:
729: session.fireRequestEvent(SRequestEvent.DISPATCH_DONE);
730: session.fireRequestEvent(SRequestEvent.PROCESS_REQUEST);
731:
732: if (reloadManager.isUpdateMode()) {
733: Resource root = retrieveCurrentRootFrameResource();
734: ExternalizedResource externalizedResource = session
735: .getExternalizeManager()
736: .getExternalizedResource(root.getId());
737: session.fireRequestEvent(SRequestEvent.DELIVER_START,
738: externalizedResource);
739:
740: String encoding = session.getCharacterEncoding();
741: response
742: .setContentType("text/xml; charset=" + encoding);
743: ServletOutputStream out = response.getOutputStream();
744: Device outputDevice = new ServletDevice(out, encoding);
745: UpdateResource.writeHeader(outputDevice);
746: UpdateResource.writeUpdate(outputDevice,
747: "wingS.request.sendRedirect(\"" + url + "\");");
748: UpdateResource.writeFooter(outputDevice);
749: outputDevice.flush();
750:
751: session.fireRequestEvent(SRequestEvent.DELIVER_DONE,
752: externalizedResource);
753: session.fireRequestEvent(SRequestEvent.REQUEST_END);
754:
755: reloadManager.clear();
756: session.setServletRequest(null);
757: session.setServletResponse(null);
758: SessionManager.removeSession();
759: SForm.clearArmedComponents();
760: } else {
761: // TODO FIXME: This redirect is in most times too late. Redirect works only if no byte
762: // has yet been sent to the client (dispatch phase)
763: // Won't work if exception occurred during rendering phase
764: // Solution: Provide / send javascript code to redirect.
765: response.sendRedirect(url);
766: }
767: } catch (Exception e) {
768: log.warn(e.getMessage(), thrown);
769: }
770: }
771:
772: /**
773: * This method is called, whenever a Resource is requested whose
774: * name is not known within this session.
775: *
776: * @param req the causing HttpServletRequest
777: * @param res the HttpServletResponse of this request
778: */
779: protected void handleUnknownResourceRequested(
780: HttpServletRequest req, HttpServletResponse res)
781: throws IOException {
782: res.setStatus(HttpServletResponse.SC_NOT_FOUND);
783: res.setContentType("text/html");
784: res.getOutputStream().println(
785: "<h1>404 Not Found</h1>Unknown Resource Requested: "
786: + req.getPathInfo());
787: }
788:
789: /* HttpSessionBindingListener */
790: public void valueBound(HttpSessionBindingEvent event) {
791: }
792:
793: /* HttpSessionBindingListener */
794: public void valueUnbound(HttpSessionBindingEvent event) {
795: destroy();
796: }
797:
798: /**
799: * get the Session Encoding, that is appended to each URL.
800: * Basically, this is response.encodeURL(""), but unfortuntatly, this
801: * empty encoding isn't supported by Tomcat 4.x anymore.
802: */
803: public static String getSessionEncoding(HttpServletResponse response) {
804: if (response == null)
805: return "";
806: // encode dummy non-empty URL.
807: return response.encodeURL("foo").substring(3);
808: }
809:
810: /**
811: * Destroy and cleanup the session servlet.
812: */
813: public void destroy() {
814: log.info("destroy called");
815: try {
816: if (session != null) {
817: // Session is needed on destroying the session
818: SessionManager.setSession(session);
819: session.destroy();
820: }
821:
822: // hint the gc.
823: parent = null;
824: session = null;
825: } catch (Exception ex) {
826: log.error("destroy", ex);
827: } finally {
828: SessionManager.removeSession();
829: }
830: }
831:
832: /**
833: * A check if this session servlet seems to be alive or is i.e. invalid because it
834: * was deserialized.
835: *
836: * @return <code>true</code>, if this session servlet seems to be valid and alive.
837: */
838: public boolean isValid() {
839: return session != null && parent != null;
840: }
841: }
|