KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > session > SessionServlet


1 /*
2  * $Id: SessionServlet.java,v 1.14 2005/05/27 15:11:38 neurolabs Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings.session;
15
16
17 import org.apache.commons.logging.Log;
18 import org.apache.commons.logging.LogFactory;
19 import org.wings.*;
20 import org.wings.event.ExitVetoException;
21 import org.wings.event.SRequestEvent;
22 import org.wings.externalizer.ExternalizeManager;
23 import org.wings.externalizer.ExternalizedResource;
24 import org.wings.io.Device;
25 import org.wings.io.DeviceFactory;
26 import org.wings.io.ServletDevice;
27 import org.wings.resource.DynamicCodeResource;
28
29 import javax.servlet.ServletConfig JavaDoc;
30 import javax.servlet.ServletContext JavaDoc;
31 import javax.servlet.ServletException JavaDoc;
32 import javax.servlet.ServletOutputStream JavaDoc;
33 import javax.servlet.http.*;
34 import java.io.IOException JavaDoc;
35 import java.io.PrintWriter JavaDoc;
36 import java.io.StringWriter JavaDoc;
37 import java.util.Enumeration JavaDoc;
38 import java.util.Locale JavaDoc;
39 import java.util.Arrays JavaDoc;
40
41 /**
42  * The servlet engine creates for each user a new HttpSession. This
43  * HttpSession can be accessed by all Serlvets running in the engine. A
44  * WingServlet creates one wings SessionServlet per HTTPSession and stores
45  * it in its context.
46  * As the SessionServlets acts as Wrapper for the WingsServlet, you can
47  * access from there as used the ServletContext and the HttpSession.
48  * Additionally the SessionServlet containts also the wingS-Session with
49  * all important services and the superordinated SFrame. To this SFrame all
50  * wings-Components and hence the complete application state is attached.
51  * The developer can access from any place via the SessionManager a
52  * reference to the wingS-Session. Additionally the SessionServlet
53  * provides access to the all containing HttpSession.
54  *
55  * @author <a HREF="mailto:haaf@mercatis.de">Armin Haaf</a>
56  * @version $Revision: 1.14 $
57  */

58 final class SessionServlet
59         extends HttpServlet
60         implements HttpSessionBindingListener {
61     private final transient static Log log = LogFactory.getLog(SessionServlet.class);
62
63     /**
64      * The parent WingsServlet
65      */

66     protected transient HttpServlet parent = this;
67
68     /**
69      * This should be a resource ..
70      */

71     protected String JavaDoc errorTemplateFile;
72
73     /**
74      * The session.
75      */

76     private Session session;
77
78     private boolean firstRequest = true;
79
80     /**
81      * Default constructor-
82      */

83     protected SessionServlet() {
84     }
85
86     /**
87      * Sets the parent servlet contianint this wings session
88      * servlet (WingsServlet, delegating its requests to the SessionServlet).
89      */

90     protected final void setParent(HttpServlet p) {
91         if (p != null) parent = p;
92     }
93
94     public final Session getSession() {
95         return session;
96     }
97
98     /**
99      * Overrides the session set for setLocaleFromHeader by a request
100      * parameter.
101      * Hence you can force the wings session to adopt the clients Locale.
102      */

103     public final void setLocaleFromHeader(String JavaDoc[] args) {
104         if (args == null)
105             return;
106
107         for (int i = 0; i < args.length; i++) {
108             try {
109                 getSession().setLocaleFromHeader(Boolean.valueOf(args[i]).booleanValue());
110             } catch (Exception JavaDoc 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             for (Enumeration JavaDoc en = req.getLocales(); en.hasMoreElements();) {
129                 Locale JavaDoc locale = (Locale JavaDoc) en.nextElement();
130                 try {
131                     getSession().setLocale(locale);
132                     return;
133                 } catch (Exception JavaDoc ex) {
134                     log.warn("locale not supported " + locale);
135                 } // end of try-catch
136
} // end of for ()
137
}
138     }
139
140     // jetzt kommen alle Servlet Methoden, die an den parent deligiert
141
// werden
142

143
144     public ServletContext JavaDoc getServletContext() {
145         if (parent != this)
146             return parent.getServletContext();
147         else
148             return super.getServletContext();
149     }
150
151
152     public String JavaDoc getInitParameter(String JavaDoc name) {
153         if (parent != this)
154             return parent.getInitParameter(name);
155         else
156             return super.getInitParameter(name);
157     }
158
159
160     public Enumeration JavaDoc getInitParameterNames() {
161         if (parent != this)
162             return parent.getInitParameterNames();
163         else
164             return super.getInitParameterNames();
165     }
166
167     /**
168      * Delegates log messages to the according WingsServlet or alternativley
169      * to the HttpServlet logger.
170      *
171      * @param msg The logmessage
172      */

173     public void log(String JavaDoc msg) {
174         if (parent != this)
175             parent.log(msg);
176         else
177             super.log(msg);
178     }
179
180
181     public String JavaDoc getServletInfo() {
182         if (parent != this)
183             return parent.getServletInfo();
184         else
185             return super.getServletInfo();
186     }
187
188
189     public ServletConfig JavaDoc getServletConfig() {
190         if (parent != this)
191             return parent.getServletConfig();
192         else
193             return super.getServletConfig();
194     }
195
196     // bis hierhin
197

198     /**
199      * The error template which should be presented on any uncaught Exceptions can be set
200      * via a property <code>wings.error.template</code> in the web.xml file.
201      *
202      * @throws ServletException
203      */

204     protected void initErrorTemplate(ServletConfig JavaDoc config) throws ServletException JavaDoc {
205         if (errorTemplateFile == null) {
206             errorTemplateFile = config.getInitParameter("wings.error.template");
207         }
208     }
209
210     /**
211      * init
212      */

213     public final void init(ServletConfig JavaDoc config,
214                            HttpServletRequest request,
215                            HttpServletResponse response) throws ServletException JavaDoc {
216         try {
217             initErrorTemplate(config);
218
219             session = new Session();
220             SessionManager.setSession(session);
221
222             // set request.url in session, if used in constructor of wings main classs
223
if (request.isRequestedSessionIdValid()) {
224                 // this will fire an event, if the encoding has changed ..
225
((PropertyService) session).setProperty("request.url",
226                         new RequestURL("", getSessionEncoding(response)));
227             }
228
229             session.init(config, request);
230
231
232             try {
233                 String JavaDoc mainClassName = config.getInitParameter("wings.mainclass");
234                 Class JavaDoc mainClass = null;
235                 try {
236                     mainClass = Class.forName(mainClassName, true,
237                             Thread.currentThread()
238                             .getContextClassLoader());
239                 } catch (ClassNotFoundException JavaDoc e) {
240                     // fallback, in case the servlet container fails to set the
241
// context class loader.
242
mainClass = Class.forName(mainClassName);
243                 }
244                 Object JavaDoc main = mainClass.newInstance();
245             } catch (Exception JavaDoc ex) {
246                 log.fatal("could not load wings.mainclass: " +
247                         config.getInitParameter("wings.mainclass"), ex);
248                 throw new ServletException JavaDoc(ex);
249             }
250         } finally {
251             // The session was set by the constructor. After init we
252
// expect that only doPost/doGet is called, which set the
253
// session also. So remove it here.
254
SessionManager.removeSession();
255         }
256     }
257
258     /**
259      * this method references to
260      * {@link #doGet(HttpServletRequest, HttpServletResponse)}
261      */

262     public final void doPost(HttpServletRequest req, HttpServletResponse res)
263             throws ServletException JavaDoc, IOException JavaDoc {
264         //value chosen to limit denial of service
265
if (req.getContentLength() > getSession().getMaxContentLength() * 1024) {
266             res.setContentType("text/html");
267             ServletOutputStream JavaDoc out = res.getOutputStream();
268             out.println("<html><head><title>Too big</title></head>");
269             out.println("<body><h1>Error - content length &gt; " +
270                     getSession().getMaxContentLength() + "k");
271             out.println("</h1></body></html>");
272         } else {
273             doGet(req, res);
274         }
275         // sollte man den obigen Block nicht durch folgende Zeile ersetzen?
276
//throw new RuntimeException("this method must never be called!");
277
// bsc: Wieso?
278
}
279
280
281     /**
282      * Verarbeitet Informationen vom Browser:
283      * <UL>
284      * <LI> setzt Locale
285      * <LI> Dispatch Get Parameter
286      * <LI> feuert Form Events
287      * </UL>
288      * Ist synchronized, damit nur ein Frame gleichzeitig bearbeitet
289      * werden kann.
290      *
291      * @ deprecated -- bsc: warum? und was aendern?
292      */

293     public final synchronized void doGet(HttpServletRequest req,
294                                          HttpServletResponse response)
295             throws ServletException JavaDoc, IOException JavaDoc {
296         SessionManager.setSession(session);
297         session.setServletRequest(req);
298         session.setServletResponse(response);
299
300         session.fireRequestEvent(SRequestEvent.REQUEST_START);
301
302         // in case, the previous thread did not clean up.
303
SForm.clearArmedComponents();
304
305         Device outputDevice = null;
306
307         try {
308             /*
309              * The tomcat 3.x has a bug, in that it does not encode the URL
310              * sometimes. It does so, when there is a cookie, containing some
311              * tomcat sessionid but that is invalid (because, for instance,
312              * we restarted the tomcat in-between).
313              * [I can't think of this being the correct behaviour, so I assume
314              * it is a bug. ]
315              *
316              * So we have to workaround this here: if we actually got the
317              * session id from a cookie, but it is not valid, we don't do
318              * the encodeURL() here: we just leave the requestURL as it is
319              * in the properties .. and this is url-encoded, since
320              * we had set it up in the very beginning of this session
321              * with URL-encoding on (see WingServlet::newSession()).
322              *
323              * Vice versa: if the requestedSessionId is valid, then we can
324              * do the encoding (which then does URL-encoding or not, depending
325              * whether the servlet engine detected a cookie).
326              * (hen)
327              */

328             RequestURL requestURL = null;
329             if (req.isRequestedSessionIdValid()) {
330                 requestURL = new RequestURL("", getSessionEncoding(response));
331                 // this will fire an event, if the encoding has changed ..
332
((PropertyService) session).setProperty("request.url",
333                         requestURL);
334             }
335
336             if (log.isDebugEnabled()) {
337                 log.debug("RequestURL: " + requestURL);
338                 log.debug("\nHEADER:");
339                 for (Enumeration JavaDoc en = req.getHeaderNames(); en.hasMoreElements();) {
340                     String JavaDoc header = (String JavaDoc) en.nextElement();
341                     log.debug(" " + header + ": " + req.getHeader(header));
342                 }
343             }
344             handleLocale(req);
345
346             Enumeration JavaDoc en = req.getParameterNames();
347             Cookie[] cookies = req.getCookies();
348
349             // are there parameters/low level events to dispatch
350
if (en.hasMoreElements()) {
351                 // only fire DISPATCH_START if we have parameters to dispatch
352
session.fireRequestEvent(SRequestEvent.DISPATCH_START);
353
354                 for (int i = 0; i < cookies.length; i++) {
355                     Cookie cookie = cookies[i];
356                     String JavaDoc paramName = (String JavaDoc) cookie.getName();
357                     String JavaDoc value = cookie.getValue();
358
359                     if (log.isDebugEnabled())
360                         log.debug("dispatching cookie " + paramName + " = " + value);
361
362                     session.getDispatcher().dispatch(paramName, new String JavaDoc[] { value });
363                 }
364                 while (en.hasMoreElements()) {
365                     String JavaDoc paramName = (String JavaDoc) en.nextElement();
366                     String JavaDoc[] value = req.getParameterValues(paramName);
367
368                     if (log.isDebugEnabled())
369                         log.debug("dispatching " + paramName + " = " + Arrays.asList(value));
370
371                     session.getDispatcher().dispatch(paramName, value);
372                 }
373
374                 SForm.fireEvents();
375             
376                 // only fire DISPATCH DONE if we have parameters to dispatch
377
session.fireRequestEvent(SRequestEvent.DISPATCH_DONE);
378             }
379
380             session.fireRequestEvent(SRequestEvent.PROCESS_REQUEST);
381             
382             
383             // if the user chose to exit the session as a reaction on an
384
// event, we got an URL to redirect after the session.
385
/*
386              * where is the right place?
387              * The right place is
388              * - _after_ we processed the events
389              * (e.g. the 'Pressed Exit-Button'-event or gave
390              * the user the chance to exit this session in the custom
391              * processRequest())
392              * - but _before_ the rendering of the page,
393              * because otherwise an redirect won't work, since we must
394              * not have sent anything to the output stream).
395              */

396             if (session.getExitAddress() != null) {
397
398                 try {
399                     session.firePrepareExit();
400
401                     String JavaDoc redirectAddress;
402                     if (session.getExitAddress().length() > 0) {
403                         // redirect to user requested URL.
404
redirectAddress = session.getExitAddress();
405                     } else {
406                         // redirect to a fresh session.
407
redirectAddress = req.getRequestURL().toString();
408                     }
409                     req.getSession().invalidate(); // calls destroy implicitly
410
response.sendRedirect(redirectAddress);
411
412                     return;
413                 } catch (ExitVetoException ex) {
414                     session.exit(null);
415                 } // end of try-catch
416
}
417
418             if (session.getRedirectAddress() != null) {
419                 // redirect to user requested URL.
420
response.sendRedirect(session.getRedirectAddress());
421                 session.setRedirectAddress(null);
422                 return;
423             }
424             
425             // invalidate frames and resources
426
getSession().getReloadManager().invalidateResources();
427             
428             // deliver resource. The
429
// externalizer is able to handle static and dynamic resources
430
ExternalizeManager extManager = getSession().getExternalizeManager();
431             String JavaDoc pathInfo = req.getPathInfo().substring(1);
432             log.debug("pathInfo: " + pathInfo);
433
434             /*
435              * if we have no path info, or the special '_' path info
436              * (that should be explained somewhere, Holger), then we
437              * deliver the toplevel Frame of this application.
438              */

439             String JavaDoc externalizeIdentifier = null;
440             if (pathInfo == null
441                     || pathInfo.length() == 0
442                     || "_".equals(pathInfo)
443                     || firstRequest) {
444                 log.debug("delivering default frame");
445
446                 if (session.getFrames().size() == 0)
447                     throw new ServletException JavaDoc("no frame visible");
448                 
449                 // get the first frame from the set and walk up the hierarchy.
450
// this should work in most cases. if there are more than one
451
// toplevel frames, the developer has to care about the resource
452
// ids anyway ..
453
SFrame defaultFrame = (SFrame) session.getFrames().iterator().next();
454                 while (defaultFrame.getParent() != null)
455                     defaultFrame = (SFrame) defaultFrame.getParent();
456
457                 Resource resource = defaultFrame.getDynamicResource(DynamicCodeResource.class);
458                 externalizeIdentifier = resource.getId();
459
460                 firstRequest = false;
461             } else {
462                 externalizeIdentifier = pathInfo;
463             }
464             
465             // externalized this resource.
466
ExternalizedResource extInfo = extManager
467                     .getExternalizedResource(externalizeIdentifier);
468             if (extInfo != null) {
469                 outputDevice = DeviceFactory.createDevice(extInfo);
470                 //outputDevice = createOutputDevice(req, response, extInfo);
471

472                 session.fireRequestEvent(SRequestEvent.DELIVER_START, extInfo);
473
474                 extManager.deliver(extInfo, response, outputDevice);
475
476                 session.fireRequestEvent(SRequestEvent.DELIVER_DONE, extInfo);
477             } else {
478                 handleUnknownResourceRequested(req, response);
479             }
480
481         } catch (Throwable JavaDoc e) {
482             log.fatal("exception: ", e);
483             handleException(req, response, e);
484         } finally {
485             if (session != null) {
486                 session.fireRequestEvent(SRequestEvent.REQUEST_END);
487             }
488
489             if (outputDevice != null) {
490                 try {
491                     outputDevice.close();
492                 } catch (Exception JavaDoc e) {
493                 }
494             }
495
496             /*
497              * the session might be null due to destroy().
498              */

499             if (session != null) {
500                 session.getReloadManager().clear();
501                 session.setServletRequest(null);
502                 session.setServletResponse(null);
503             }
504
505
506             // make sure that the session association to the thread is removed
507
// from the SessionManager
508
SessionManager.removeSession();
509             SForm.clearArmedComponents();
510         }
511     }
512
513     /**
514      * create a Device that is used to deliver the content, that is
515      * session specific.
516      * The default
517      * implementation just creates a ServletDevice. You can override this
518      * method to decide yourself what happens to the output. You might, for
519      * instance, write some device, that logs the output for debugging
520      * purposes, or one that creates a gziped output stream to transfer
521      * data more efficiently. You get the request and response as well as
522      * the ExternalizedResource to decide, what kind of device you want to create.
523      * You can rely on the fact, that extInfo is not null.
524      * Further, you can rely on the fact, that noting has been written yet
525      * to the output, so that you can set you own set of Headers.
526      *
527      * @param request the HttpServletRequest that is answered
528      * @param response the HttpServletResponse.
529      * @param extInfo the externalized info of the resource about to be
530      * delivered.
531      */

532     protected Device createOutputDevice(HttpServletRequest request,
533                                         HttpServletResponse response,
534                                         ExternalizedResource extInfo)
535             throws IOException JavaDoc {
536         return new ServletDevice(response.getOutputStream());
537     }
538
539
540
541     // Exception Handling
542

543     private SFrame errorFrame;
544
545     private SLabel errorStackTraceLabel;
546     private SLabel errorMessageLabel;
547
548     protected void handleException(HttpServletRequest req,
549                                    HttpServletResponse res, Throwable JavaDoc e) {
550         try {
551             if (errorFrame == null) {
552                 errorFrame = new SFrame();
553                 /*
554                  * if we don't have an errorTemplateFile defined, then this
555                  * will throw an Exception, so the StackTrace is NOT exposed
556                  * to the user (may be security relevant)
557                  */

558                 errorFrame.getContentPane().
559                         setLayout(new STemplateLayout(errorTemplateFile));
560
561                 errorStackTraceLabel = new SLabel();
562                 errorFrame.getContentPane().add(errorStackTraceLabel,
563                         "EXCEPTION_STACK_TRACE");
564
565                 errorMessageLabel = new SLabel();
566                 errorFrame.getContentPane().add(errorMessageLabel,
567                         "EXCEPTION_MESSAGE");
568             }
569
570             res.setContentType("text/html");
571             ServletOutputStream JavaDoc out = res.getOutputStream();
572             errorStackTraceLabel.setText(getStackTraceString(e));
573             errorMessageLabel.setText(e.getMessage());
574             errorFrame.write(new ServletDevice(out));
575         } catch (Exception JavaDoc ex) {
576         }
577     }
578
579     /**
580      * This method is called, whenever a Resource is requested whose
581      * name is not known within this session.
582      *
583      * @param req the causing HttpServletRequest
584      * @param res the HttpServletResponse of this request
585      */

586     protected void handleUnknownResourceRequested(HttpServletRequest req,
587                                                   HttpServletResponse res)
588             throws IOException JavaDoc {
589         res.setStatus(HttpServletResponse.SC_NOT_FOUND);
590         res.setContentType("text/html");
591         res.getOutputStream().println("<h1>404 Not Found</h1>Unknown Resource Requested");
592     }
593
594     /**
595      * --- HttpSessionBindingListener --- *
596      */

597
598
599     public void valueBound(HttpSessionBindingEvent event) {
600     }
601
602
603     public void valueUnbound(HttpSessionBindingEvent event) {
604         destroy();
605     }
606
607     /**
608      * get the Session Encoding, that is appended to each URL.
609      * Basically, this is response.encodeURL(""), but unfortuntatly, this
610      * empty encoding isn't supported by Tomcat 4.x anymore.
611      */

612     public static String JavaDoc getSessionEncoding(HttpServletResponse response) {
613         if (response == null) return "";
614         // encode dummy non-empty URL.
615
String JavaDoc enc = response.encodeURL("foo").substring(3);
616         return enc;
617     }
618
619     public void destroy() {
620         log.info("destroy called");
621
622         // Session is needed on destroying the session
623
SessionManager.setSession(session);
624         try {
625             // hint the gc.
626
setParent(null);
627             session.destroy();
628             session = null;
629             errorFrame = null;
630             errorStackTraceLabel = null;
631             errorMessageLabel = null;
632         } catch (Exception JavaDoc ex) {
633             log.error("destroy", ex);
634         } finally {
635             SessionManager.removeSession();
636         }
637     }
638
639     private String JavaDoc getStackTraceString(Throwable JavaDoc e) {
640         StringWriter JavaDoc stringWriter = new StringWriter JavaDoc();
641         PrintWriter JavaDoc printWriter = new PrintWriter JavaDoc(stringWriter);
642         stringWriter.getBuffer().setLength(0);
643         e.printStackTrace(printWriter);
644         return stringWriter.toString();
645     }
646 }
647
648
649
Popular Tags