View Javadoc

1   package org.apache.turbine.services.rundata;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  
30  import javax.naming.Context;
31  import javax.servlet.ServletConfig;
32  import javax.servlet.ServletContext;
33  import javax.servlet.http.HttpServletRequest;
34  import javax.servlet.http.HttpServletResponse;
35  import javax.servlet.http.HttpSession;
36  
37  import org.apache.commons.lang.StringUtils;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.ecs.Document;
41  import org.apache.ecs.Element;
42  import org.apache.ecs.StringElement;
43  import org.apache.fulcrum.mimetype.MimeTypeService;
44  import org.apache.fulcrum.parser.CookieParser;
45  import org.apache.fulcrum.parser.ParameterParser;
46  import org.apache.fulcrum.pool.Recyclable;
47  import org.apache.turbine.Turbine;
48  import org.apache.turbine.TurbineConstants;
49  import org.apache.turbine.om.security.User;
50  import org.apache.turbine.pipeline.DefaultPipelineData;
51  import org.apache.turbine.services.ServiceManager;
52  import org.apache.turbine.services.TurbineServices;
53  import org.apache.turbine.services.template.TurbineTemplate;
54  import org.apache.turbine.util.FormMessages;
55  import org.apache.turbine.util.ServerData;
56  import org.apache.turbine.util.SystemError;
57  import org.apache.turbine.util.security.AccessControlList;
58  import org.apache.turbine.util.template.TemplateInfo;
59  
60  /**
61   * DefaultTurbineRunData is the default implementation of the
62   * TurbineRunData interface, which is distributed by the Turbine
63   * RunData service, if another implementation is not defined in
64   * the default or specified RunData configuration.
65   * TurbineRunData is an extension to RunData, which
66   * is an interface to run-rime information that is passed
67   * within Turbine. This provides the threading mechanism for the
68   * entire system because multiple requests can potentially come in
69   * at the same time.  Thus, there is only one RunData implementation
70   * for each request that is being serviced.
71   *
72   * <p>DefaultTurbineRunData implements the Recyclable interface making
73   * it possible to pool its instances for recycling.
74   *
75   * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
76   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
77   * @author <a href="mailto:bhoeneis@ee.ethz.ch">Bernie Hoeneisen</a>
78   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
79   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
80   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
81   * @version $Id: DefaultTurbineRunData.java 1066938 2011-02-03 20:14:53Z ludwig $
82   */
83  public class DefaultTurbineRunData
84          extends DefaultPipelineData
85          implements TurbineRunData, Recyclable
86  {
87      /**
88       * The disposed flag.
89       */
90      private boolean disposed;
91  
92      /** The default locale. */
93      private static Locale defaultLocale = null;
94  
95      /** The default charset. */
96      private static String defaultCharSet = null;
97  
98      /** A reference to the GET/POST data parser. */
99      private ParameterParser parameters;
100 
101     /** A reference to a cookie parser. */
102     public CookieParser cookies;
103 
104     /** The servlet request interface. */
105     private HttpServletRequest req;
106 
107     /** The servlet response interface. */
108     private HttpServletResponse res;
109 
110     /** The servlet configuration. */
111     private ServletConfig config;
112 
113     /**
114      * The servlet context information.
115      * Note that this is from the "Turbine" Servlet context.
116      */
117     private ServletContext servletContext;
118 
119     /** The access control list. */
120     private AccessControlList acl;
121 
122     /** Determines if there is information in the document or not. */
123     private boolean pageSet;
124 
125     /** This creates an ECS Document. */
126     private Document page;
127 
128     /** Cached action name to execute for this request. */
129     private String action;
130 
131     /** This is the layout that the page will use to render the screen. */
132     private String layout;
133 
134     /** Cached screen name to execute for this request. */
135     private String screen;
136 
137     /** The character encoding of template files. */
138     private String templateEncoding;
139 
140     /** Information used by a Template system (such as Velocity/JSP). */
141     private TemplateInfo templateInfo;
142 
143     /** This is where output messages from actions should go. */
144     private StringElement message;
145 
146     /**
147      * This is a dedicated message class where output messages from
148      * actions should go.
149      */
150     private FormMessages messages;
151 
152     /** The user object. */
153     private User user;
154 
155     /** This is what will build the <title></title> of the document. */
156     private String title;
157 
158     /** Determines if there is information in the outputstream or not. */
159     private boolean outSet;
160 
161     /**
162      * Cache the output stream because it can be used in many
163      * different places.
164      */
165     private PrintWriter out;
166 
167     /** The locale. */
168     private Locale locale;
169 
170     /** The HTTP charset. */
171     private String charSet;
172 
173     /** The HTTP content type to return. */
174     private String contentType = "text/html";
175 
176     /** If this is set, also set the status code to 302. */
177     private String redirectURI;
178 
179     /** The HTTP status code to return. */
180     private int statusCode = HttpServletResponse.SC_OK;
181 
182     /** This is a List to hold critical system errors. */
183     private List<SystemError> errors = new ArrayList<SystemError>();
184 
185     /** JNDI Contexts. */
186     private Map<String, Context> jndiContexts;
187 
188     /** Holds ServerData (basic properties) about this RunData object. */
189     private ServerData serverData;
190 
191     /** @see #getRemoteAddr() */
192     private String remoteAddr;
193 
194     /** @see #getRemoteHost() */
195     private String remoteHost;
196 
197     /** @see #getUserAgent() */
198     private String userAgent;
199 
200     /** A holder for stack trace. */
201     private String stackTrace;
202 
203     /** A holder ofr stack trace exception. */
204     private Throwable stackTraceException;
205 
206     /**
207      * Put things here and they will be shown on the default Error
208      * screen.  This is great for debugging variable values when an
209      * exception is thrown.
210      */
211     private Map<String, Object> debugVariables = new HashMap<String, Object>();
212 
213     /** Logging */
214     private static Log log = LogFactory.getLog(DefaultTurbineRunData.class);
215 
216     /**
217      * Attempts to get the User object from the session.  If it does
218      * not exist, it returns null.
219      *
220      * @param session An HttpSession.
221      * @return A User.
222      */
223     public static User getUserFromSession(HttpSession session)
224     {
225         try
226         {
227             return (User) session.getAttribute(User.SESSION_KEY);
228         }
229         catch (ClassCastException e)
230         {
231             return null;
232         }
233     }
234 
235     /**
236      * Allows one to invalidate the user in a session.
237      *
238      * @param session An HttpSession.
239      * @return True if user was invalidated.
240      */
241     public static boolean removeUserFromSession(HttpSession session)
242     {
243         try
244         {
245             session.removeAttribute(User.SESSION_KEY);
246         }
247         catch (Exception e)
248         {
249             return false;
250         }
251         return true;
252     }
253 
254     /**
255      * Gets the default locale defined by properties named
256      * "locale.default.lang" and "locale.default.country".
257      *
258      * This changed from earlier Turbine versions that you can
259      * rely on getDefaultLocale() to never return null.
260      *
261      * @return A Locale object.
262      */
263     protected static Locale getDefaultLocale()
264     {
265         if (defaultLocale == null)
266         {
267             /* Get the default locale and cache it in a static variable. */
268             String lang = Turbine.getConfiguration()
269                 .getString(TurbineConstants.LOCALE_DEFAULT_LANGUAGE_KEY,
270                     TurbineConstants.LOCALE_DEFAULT_LANGUAGE_DEFAULT);
271 
272             String country = Turbine.getConfiguration()
273                 .getString(TurbineConstants.LOCALE_DEFAULT_COUNTRY_KEY,
274                     TurbineConstants.LOCALE_DEFAULT_COUNTRY_DEFAULT);
275 
276 
277             // We ensure that lang and country is never null
278             defaultLocale =  new Locale(lang, country);
279         }
280         return defaultLocale;
281     }
282 
283     /**
284      * Gets the default charset defined by a property named
285      * "locale.default.charset" or by the specified locale.
286      * If the specified locale is null, the default locale is applied.
287      *
288      * @return the name of the default charset or null.
289      */
290     protected String getDefaultCharSet()
291     {
292         log.debug("getDefaultCharSet()");
293 
294         if (defaultCharSet == null)
295         {
296             /* Get the default charset and cache it in a static variable. */
297             defaultCharSet = Turbine.getConfiguration()
298                 .getString(TurbineConstants.LOCALE_DEFAULT_CHARSET_KEY,
299                     TurbineConstants.LOCALE_DEFAULT_CHARSET_DEFAULT);
300             log.debug("defaultCharSet = " + defaultCharSet + " (From Properties)");
301         }
302 
303         String charset = defaultCharSet;
304 
305         if (StringUtils.isEmpty(charset))
306         {
307             log.debug("charset is empty!");
308             /* Default charset isn't specified, get the locale specific one. */
309             Locale locale = this.locale;
310             if (locale == null)
311             {
312                 locale = getDefaultLocale();
313                 log.debug("Locale was null, is now " + locale + " (from getDefaultLocale())");
314             }
315 
316             log.debug("Locale is " + locale);
317 
318             if (!locale.equals(Locale.US))
319             {
320                 log.debug("We don't have US Locale!");
321                 ServiceManager serviceManager = TurbineServices.getInstance();
322 				MimeTypeService mimeTypeService=null;
323                 try {
324 					mimeTypeService= (MimeTypeService)serviceManager.getService(MimeTypeService.ROLE);
325                 }
326                 catch (Exception e){
327                     throw new RuntimeException(e);
328                 }
329                 charset = mimeTypeService.getCharSet(locale);
330 
331                 log.debug("Charset now " + charset);
332             }
333         }
334 
335         log.debug("Returning default Charset of " + charset);
336         return charset;
337     }
338 
339     /**
340      * Constructs a run data object.
341      */
342     public DefaultTurbineRunData()
343     {
344         super();
345         recycle();
346     }
347 
348     /**
349      * Recycles the object by removing its disposed flag.
350      */
351     public void recycle()
352     {
353         disposed = false;
354     }
355 
356     /**
357      * Disposes a run data object.
358      */
359     public void dispose()
360     {
361         parameters = null;
362         cookies = null;
363         req = null;
364         res = null;
365         config = null;
366         servletContext = null;
367         acl = null;
368         pageSet = false;
369         page = null;
370         action = null;
371         layout = null;
372         screen = null;
373         templateEncoding = null;
374         templateInfo = null;
375         message = null;
376         messages = null;
377         user = null;
378         title = null;
379         outSet = false;
380         out = null;
381         locale = null;
382         charSet = null;
383         contentType = "text/html";
384         redirectURI = null;
385         statusCode = HttpServletResponse.SC_OK;
386         errors.clear();
387         jndiContexts = null;
388         serverData = null;
389         remoteAddr = null;
390         remoteHost = null;
391         userAgent = null;
392         stackTrace = null;
393         stackTraceException = null;
394         debugVariables.clear();
395     }
396 
397     // ***************************************
398     // Implementation of the RunData interface
399     // ***************************************
400 
401     /**
402      * Gets the parameters.
403      *
404      * @return a parameter parser.
405      */
406     public ParameterParser getParameters()
407     {
408         // Parse the parameters first, if not yet done.
409         if ((this.parameters != null) &&
410                 (this.parameters.getRequest() != this.req))
411         {
412             this.parameters.setRequest(this.req);
413         }
414         return this.parameters;
415     }
416 
417     /**
418      * Gets the cookies.
419      *
420      * @return a cookie parser.
421      */
422     public CookieParser getCookies()
423     {
424         // Parse the cookies first, if not yet done.
425         if ((this.cookies != null) &&
426                 (this.cookies.getRequest() != getRequest()))
427         {
428             this.cookies.setData(getRequest(), getResponse());
429         }
430         return this.cookies;
431     }
432 
433     /**
434      * Gets the servlet request.
435      *
436      * @return the request.
437      */
438     public HttpServletRequest getRequest()
439     {
440         return this.req;
441     }
442 
443     /**
444      * Gets the servlet response.
445      *
446      * @return the response.
447      */
448     public HttpServletResponse getResponse()
449     {
450         return this.res;
451     }
452 
453     /**
454      * Gets the servlet session information.
455      *
456      * @return the session.
457      */
458     public HttpSession getSession()
459     {
460         return getRequest().getSession();
461     }
462 
463     /**
464      * Gets the servlet configuration used during servlet init.
465      *
466      * @return the configuration.
467      */
468     public ServletConfig getServletConfig()
469     {
470         return this.config;
471     }
472 
473     /**
474      * Gets the servlet context used during servlet init.
475      *
476      * @return the context.
477      */
478     public ServletContext getServletContext()
479     {
480         return this.servletContext;
481     }
482 
483     /**
484      * Gets the access control list.
485      *
486      * @return the access control list.
487      */
488     public AccessControlList getACL()
489     {
490         return acl;
491     }
492 
493     /**
494      * Sets the access control list.
495      *
496      * @param acl an access control list.
497      */
498     public void setACL(AccessControlList acl)
499     {
500         this.acl = acl;
501     }
502 
503     /**
504      * Checks to see if the page is set.
505      *
506      * @return true if the page is set.
507      * @deprecated no replacement planned, ECS is no longer a requirement
508      */
509     @Deprecated
510     public boolean isPageSet()
511     {
512         return pageSet;
513     }
514 
515     /**
516      * Gets the page.
517      *
518      * @return a document.
519      * @deprecated no replacement planned, ECS is no longer a requirement
520      */
521     @Deprecated
522     public Document getPage()
523     {
524         pageSet = true;
525         if (this.page == null)
526         {
527             this.page = new Document();
528         }
529         return this.page;
530     }
531 
532     /**
533      * Whether or not an action has been defined.
534      *
535      * @return true if an action has been defined.
536      */
537     public boolean hasAction()
538     {
539         return (StringUtils.isNotEmpty(this.action)
540           && !this.action.equalsIgnoreCase("null"));
541     }
542 
543     /**
544      * Gets the action. It returns an empty string if null so
545      * that it is easy to do conditionals on it based on the
546      * equalsIgnoreCase() method.
547      *
548      * @return a string, "" if null.
549      */
550     public String getAction()
551     {
552         return (hasAction() ? this.action : "");
553     }
554 
555     /**
556      * Sets the action for the request.
557      *
558      * @param action a atring.
559      */
560     public void setAction(String action)
561     {
562         this.action = action;
563     }
564 
565     /**
566      * If the Layout has not been defined by the screen then set the
567      * layout to be "DefaultLayout".  The screen object can also
568      * override this method to provide intelligent determination of
569      * the Layout to execute.  You can also define that logic here as
570      * well if you want it to apply on a global scale.  For example,
571      * if you wanted to allow someone to define layout "preferences"
572      * where they could dynamicially change the layout for the entire
573      * site.
574      *
575      * @return a string.
576      */
577 
578     public String getLayout()
579     {
580         if (this.layout == null)
581         {
582             /*
583              * This will return something if the template
584              * services are running. If we get nothing we
585              * will fall back to the ECS layout.
586              */
587             layout = TurbineTemplate.getDefaultLayoutName(this);
588 
589             if (layout == null)
590             {
591                 layout = "DefaultLayout";
592             }
593         }
594 
595         return this.layout;
596     }
597 
598     /**
599      * Set the layout for the request.
600      *
601      * @param layout a string.
602      */
603     public void setLayout(String layout)
604     {
605         this.layout = layout;
606     }
607 
608     /**
609      * Convenience method for a template info that
610      * returns the layout template being used.
611      *
612      * @return a string.
613      */
614     public String getLayoutTemplate()
615     {
616         return getTemplateInfo().getLayoutTemplate();
617     }
618 
619     /**
620      * Modifies the layout template for the screen. This convenience
621      * method allows for a layout to be modified from within a
622      * template. For example;
623      *
624      *    $data.setLayoutTemplate("NewLayout.vm")
625      *
626      * @param layout a layout template.
627      */
628     public void setLayoutTemplate(String layout)
629     {
630         getTemplateInfo().setLayoutTemplate(layout);
631     }
632 
633     /**
634      * Whether or not a screen has been defined.
635      *
636      * @return true if a screen has been defined.
637      */
638     public boolean hasScreen()
639     {
640         return StringUtils.isNotEmpty(this.screen);
641     }
642 
643     /**
644      * Gets the screen to execute.
645      *
646      * @return a string.
647      */
648     public String getScreen()
649     {
650         return (hasScreen() ? this.screen : "");
651     }
652 
653     /**
654      * Sets the screen for the request.
655      *
656      * @param screen a string.
657      */
658     public void setScreen(String screen)
659     {
660         this.screen = screen;
661     }
662 
663     /**
664      * Convenience method for a template info that
665      * returns the name of the template being used.
666      *
667      * @return a string.
668      */
669     public String getScreenTemplate()
670     {
671         return getTemplateInfo().getScreenTemplate();
672     }
673 
674     /**
675      * Sets the screen template for the request. For
676      * example;
677      *
678      *    $data.setScreenTemplate("NewScreen.vm")
679      *
680      * @param screen a screen template.
681      */
682     public void setScreenTemplate(String screen)
683     {
684         getTemplateInfo().setScreenTemplate(screen);
685     }
686 
687     /**
688      * Gets the character encoding to use for reading template files.
689      *
690      * @return the template encoding or null if not specified.
691      */
692     public String getTemplateEncoding()
693     {
694         return templateEncoding;
695     }
696 
697     /**
698      * Sets the character encoding to use for reading template files.
699      *
700      * @param encoding the template encoding.
701      */
702     public void setTemplateEncoding(String encoding)
703     {
704         templateEncoding = encoding;
705     }
706 
707     /**
708      * Gets the template info. Creates a new one if needed.
709      *
710      * @return a template info.
711      */
712     public TemplateInfo getTemplateInfo()
713     {
714         if (templateInfo == null)
715         {
716             templateInfo = new TemplateInfo(this);
717         }
718         return templateInfo;
719     }
720 
721     /**
722      * Whether or not a message has been defined.
723      *
724      * @return true if a message has been defined.
725      */
726     public boolean hasMessage()
727     {
728         return (this.message != null)
729             && StringUtils.isNotEmpty(this.message.toString());
730     }
731 
732     /**
733      * Gets the results of an action or another message
734      * to be displayed as a string.
735      *
736      * @return a string.
737      */
738     public String getMessage()
739     {
740         return (this.message == null ? null : this.message.toString());
741     }
742 
743     /**
744      * Sets the message for the request as a string.
745      *
746      * @param msg a string.
747      */
748     public void setMessage(String msg)
749     {
750         this.message = new StringElement(msg);
751     }
752 
753     /**
754      * Adds the string to message. If message has prior messages from
755      * other actions or screens, this method can be used to chain them.
756      *
757      * @param msg a string.
758      */
759     public void addMessage(String msg)
760     {
761         addMessage(new StringElement(msg));
762     }
763 
764     /**
765      * Gets the results of an action or another message
766      * to be displayed as an ECS string element.
767      *
768      * @return a string element.
769      */
770     public StringElement getMessageAsHTML()
771     {
772         return this.message;
773     }
774 
775     /**
776      * Sets the message for the request as an ECS element.
777      *
778      * @param msg an element.
779      */
780     public void setMessage(Element msg)
781     {
782         this.message = new StringElement(msg);
783     }
784 
785     /**
786      * Adds the ECS element to message. If message has prior messages from
787      * other actions or screens, this method can be used to chain them.
788      *
789      * @param msg an element.
790      */
791     public void addMessage(Element msg)
792     {
793         if (msg != null)
794         {
795             if (message != null)
796             {
797                 message.addElement(msg);
798             }
799             else
800             {
801                 message = new StringElement(msg);
802             }
803         }
804     }
805 
806     /**
807      * Unsets the message for the request.
808      */
809     public void unsetMessage()
810     {
811         this.message = null;
812     }
813 
814     /**
815      * Gets a FormMessages object where all the messages to the
816      * user should be stored.
817      *
818      * @return a FormMessages.
819      */
820     public FormMessages getMessages()
821     {
822         if (this.messages == null)
823         {
824             this.messages = new FormMessages();
825         }
826         return this.messages;
827     }
828 
829     /**
830      * Sets the FormMessages object for the request.
831      *
832      * @param msgs A FormMessages.
833      */
834     public void setMessages(FormMessages msgs)
835     {
836         this.messages = msgs;
837     }
838 
839     /**
840      * Gets the title of the page.
841      *
842      * @return a string.
843      */
844     public String getTitle()
845     {
846         return (this.title == null ? "" : this.title);
847     }
848 
849     /**
850      * Sets the title of the page.
851      *
852      * @param title a string.
853      */
854     public void setTitle(String title)
855     {
856         this.title = title;
857     }
858 
859     /**
860      * Checks if a user exists in this session.
861      *
862      * @return true if a user exists in this session.
863      */
864     public boolean userExists()
865     {
866         user = getUserFromSession();
867         return (user != null);
868     }
869 
870     /**
871      * Gets the user.
872      *
873      * @return a user.
874      */
875     public User getUser()
876     {
877         return this.user;
878     }
879 
880     /**
881      * Sets the user.
882      *
883      * @param user a user.
884      */
885     public void setUser(User user)
886     {
887         log.debug("user set: " + user.getName());
888         this.user = user;
889     }
890 
891     /**
892      * Attempts to get the user from the session. If it does
893      * not exist, it returns null.
894      *
895      * @return a user.
896      */
897     public User getUserFromSession()
898     {
899         return getUserFromSession(getSession());
900     }
901 
902     /**
903      * Allows one to invalidate the user in the default session.
904      *
905      * @return true if user was invalidated.
906      */
907     public boolean removeUserFromSession()
908     {
909         return removeUserFromSession(getSession());
910     }
911 
912     /**
913      * Checks to see if out is set.
914      *
915      * @return true if out is set.
916      * @deprecated no replacement planned, response writer will not be cached
917      */
918     @Deprecated
919     public boolean isOutSet()
920     {
921         return outSet;
922     }
923 
924     /**
925      * Gets the print writer. First time calling this
926      * will set the print writer via the response.
927      *
928      * @return a print writer.
929      * @throws IOException
930      * @deprecated no replacement planned, response writer will not be cached
931      */
932     @Deprecated
933     public PrintWriter getOut()
934             throws IOException
935     {
936         // Check to see if null first.
937         if (this.out == null)
938         {
939             setOut(res.getWriter());
940         }
941         pageSet = false;
942         outSet = true;
943         return this.out;
944     }
945 
946     /**
947      * Declares that output will be direct to the response stream,
948      * even though getOut() may never be called.  Useful for response
949      * mechanisms that may call res.getWriter() themselves
950      * (such as JSP.)
951      */
952     public void declareDirectResponse()
953     {
954         outSet = true;
955         pageSet = false;
956     }
957 
958     /**
959      * Gets the locale. If it has not already been defined with
960      * setLocale(), then  properties named "locale.default.lang"
961      * and "locale.default.country" are checked from the Resource
962      * Service and the corresponding locale is returned. If these
963      * properties are undefined, JVM's default locale is returned.
964      *
965      * @return the locale.
966      */
967     public Locale getLocale()
968     {
969         Locale locale = this.locale;
970         if (locale == null)
971         {
972             locale = getDefaultLocale();
973         }
974         return locale;
975     }
976 
977     /**
978      * Sets the locale.
979      *
980      * @param locale the new locale.
981      */
982     public void setLocale(Locale locale)
983     {
984         this.locale = locale;
985         
986         // propagate the locale to the parsers
987         if (this.parameters != null)
988         {
989             parameters.setLocale(locale);
990         }
991 
992         if (this.cookies != null)
993         {
994             cookies.setLocale(locale);
995         }
996     }
997 
998     /**
999      * Gets the charset. If it has not already been defined with
1000      * setCharSet(), then a property named "locale.default.charset"
1001      * is checked from the Resource Service and returned. If this
1002      * property is undefined, the default charset of the locale
1003      * is returned. If the locale is undefined, null is returned.
1004      *
1005      * @return the name of the charset or null.
1006      */
1007     public String getCharSet()
1008     {
1009         log.debug("getCharSet()");
1010 
1011         if (StringUtils.isEmpty(charSet))
1012         {
1013             log.debug("Charset was null!");
1014             return getDefaultCharSet();
1015         }
1016         else
1017         {
1018             return charSet;
1019         }
1020     }
1021 
1022     /**
1023      * Sets the charset.
1024      *
1025      * @param charSet the name of the new charset.
1026      */
1027     public void setCharSet(String charSet)
1028     {
1029         log.debug("setCharSet(" + charSet + ")");
1030         this.charSet = charSet;
1031     }
1032 
1033     /**
1034      * Gets the HTTP content type to return. If a charset
1035      * has been specified, it is included in the content type.
1036      * If the charset has not been specified and the main type
1037      * of the content type is "text", the default charset is
1038      * included. If the default charset is undefined, but the
1039      * default locale is defined and it is not the US locale,
1040      * a locale specific charset is included.
1041      *
1042      * @return the content type or an empty string.
1043      */
1044     public String getContentType()
1045     {
1046         if (StringUtils.isNotEmpty(contentType))
1047         {
1048             if (StringUtils.isEmpty(charSet))
1049             {
1050                 if (contentType.startsWith("text/"))
1051                 {
1052                     return contentType + "; charset=" + getDefaultCharSet();
1053                 }
1054                 
1055                 return contentType;
1056             }
1057             else
1058             {
1059                 return contentType + "; charset=" + charSet;
1060             }
1061         }
1062 
1063         return "";
1064     }
1065 
1066     /**
1067      * Sets the HTTP content type to return.
1068      *
1069      * @param contentType a string.
1070      */
1071     public void setContentType(String contentType)
1072     {
1073         this.contentType = contentType;
1074     }
1075 
1076     /**
1077      * Gets the redirect URI. If this is set, also make sure to set
1078      * the status code to 302.
1079      *
1080      * @return a string, "" if null.
1081      */
1082     public String getRedirectURI()
1083     {
1084         return (this.redirectURI == null ? "" : redirectURI);
1085     }
1086 
1087     /**
1088      * Sets the redirect uri. If this is set, also make sure to set
1089      * the status code to 302.
1090      *
1091      * @param ruri a string.
1092      */
1093     public void setRedirectURI(String ruri)
1094     {
1095         this.redirectURI = ruri;
1096     }
1097 
1098     /**
1099      * Gets the HTTP status code to return.
1100      *
1101      * @return the status.
1102      */
1103     public int getStatusCode()
1104     {
1105         return statusCode;
1106     }
1107 
1108     /**
1109      * Sets the HTTP status code to return.
1110      *
1111      * @param statusCode the status.
1112      */
1113     public void setStatusCode(int statusCode)
1114     {
1115         this.statusCode = statusCode;
1116     }
1117 
1118     /**
1119      * Gets an array of system errors.
1120      *
1121      * @return a SystemError[].
1122      */
1123     public SystemError[] getSystemErrors()
1124     {
1125         SystemError[] result = new SystemError[errors.size()];
1126         errors.toArray(result);
1127         return result;
1128     }
1129 
1130     /**
1131      * Adds a critical system error.
1132      *
1133      * @param err a system error.
1134      */
1135     public void setSystemError(SystemError err)
1136     {
1137         this.errors.add(err);
1138     }
1139 
1140     /**
1141      * Gets JNDI Contexts.
1142      *
1143      * @return a hashtable.
1144      */
1145     public Map<String, Context> getJNDIContexts()
1146     {
1147         if (jndiContexts == null)
1148             jndiContexts = new HashMap<String, Context>();
1149         return jndiContexts;
1150     }
1151 
1152     /**
1153      * Sets JNDI Contexts.
1154      *
1155      * @param contexts a hashtable.
1156      */
1157     public void setJNDIContexts(Map<String, Context> contexts)
1158     {
1159         this.jndiContexts = contexts;
1160     }
1161 
1162     /**
1163      * Gets the cached server scheme.
1164      *
1165      * @return a string.
1166      */
1167     public String getServerScheme()
1168     {
1169         return getServerData().getServerScheme();
1170     }
1171 
1172     /**
1173      * Gets the cached server name.
1174      *
1175      * @return a string.
1176      */
1177     public String getServerName()
1178     {
1179         return getServerData().getServerName();
1180     }
1181 
1182     /**
1183      * Gets the cached server port.
1184      *
1185      * @return an int.
1186      */
1187     public int getServerPort()
1188     {
1189         return getServerData().getServerPort();
1190     }
1191 
1192     /**
1193      * Gets the cached context path.
1194      *
1195      * @return a string.
1196      */
1197     public String getContextPath()
1198     {
1199         return getServerData().getContextPath();
1200     }
1201 
1202     /**
1203      * Gets the cached script name.
1204      *
1205      * @return a string.
1206      */
1207     public String getScriptName()
1208     {
1209         return getServerData().getScriptName();
1210     }
1211 
1212     /**
1213      * Gets the server data ofy the request.
1214      *
1215      * @return server data.
1216      */
1217     public ServerData getServerData()
1218     {
1219         return this.serverData;
1220     }
1221 
1222     /**
1223      * Gets the IP address of the client that sent the request.
1224      *
1225      * @return a string.
1226      */
1227     public String getRemoteAddr()
1228     {
1229         if (this.remoteAddr == null)
1230         {
1231             this.remoteAddr = this.getRequest().getRemoteAddr();
1232         }
1233 
1234         return this.remoteAddr;
1235     }
1236 
1237     /**
1238      * Gets the qualified name of the client that sent the request.
1239      *
1240      * @return a string.
1241      */
1242     public String getRemoteHost()
1243     {
1244         if (this.remoteHost == null)
1245         {
1246             this.remoteHost = this.getRequest().getRemoteHost();
1247         }
1248 
1249         return this.remoteHost;
1250     }
1251 
1252     /**
1253      * Get the user agent for the request. The semantics here
1254      * are muddled because RunData caches the value after the
1255      * first invocation. This is different e.g. from getCharSet().
1256      *
1257      * @return a string.
1258      */
1259     public String getUserAgent()
1260     {
1261         if (StringUtils.isEmpty(userAgent))
1262         {
1263             userAgent = this.getRequest().getHeader("User-Agent");
1264         }
1265 
1266         return userAgent;
1267     }
1268 
1269     /**
1270      * Pulls a user object from the session and increments the access
1271      * counter and sets the last access date for the object.
1272      */
1273     public void populate()
1274     {
1275         user = getUserFromSession();
1276 
1277         if (user != null)
1278         {
1279             user.setLastAccessDate();
1280             user.incrementAccessCounter();
1281             user.incrementAccessCounterForSession();
1282         }
1283     }
1284 
1285     /**
1286      * Saves a user object into the session.
1287      */
1288     public void save()
1289     {
1290         getSession().setAttribute(User.SESSION_KEY, user);
1291     }
1292 
1293     /**
1294      * Gets the stack trace if set.
1295      *
1296      * @return the stack trace.
1297      */
1298     public String getStackTrace()
1299     {
1300         return stackTrace;
1301     }
1302 
1303     /**
1304      * Gets the stack trace exception if set.
1305      *
1306      * @return the stack exception.
1307      */
1308     public Throwable getStackTraceException()
1309     {
1310         return stackTraceException;
1311     }
1312 
1313     /**
1314      * Sets the stack trace.
1315      *
1316      * @param trace the stack trace.
1317      * @param exp the exception.
1318      */
1319     public void setStackTrace(String trace, Throwable exp)
1320     {
1321         stackTrace = trace;
1322         stackTraceException = exp;
1323     }
1324 
1325     /**
1326      * Gets a Map of debug variables.
1327      *
1328      * @return a Map of debug variables.
1329      * @deprecated use {@link #getDebugVariables} instead
1330      */
1331     @Deprecated
1332     public Map<String, Object> getVarDebug()
1333     {
1334         return debugVariables;
1335     }
1336 
1337     /**
1338      * Sets a name/value pair in an internal Map that is accessible from the
1339      * Error screen.  This is a good way to get debugging information
1340      * when an exception is thrown.
1341      *
1342      * @param name name of the variable
1343      * @param value value of the variable.
1344      */
1345     public void setDebugVariable(String name, Object value)
1346     {
1347         this.debugVariables.put(name, value);
1348     }
1349 
1350     /**
1351      * Gets a Map of debug variables.
1352      *
1353      * @return a Map of debug variables.
1354      */
1355     public Map<String, Object> getDebugVariables()
1356     {
1357         return this.debugVariables;
1358     }
1359 
1360     // **********************************************
1361     // Implementation of the TurbineRunData interface
1362     // **********************************************
1363 
1364     /**
1365      * Gets the parameter parser without parsing the parameters.
1366      *
1367      * @return the parameter parser.
1368      * @todo Does this method make sense? Pulling the parameter out of
1369      *       the run data object before setting a request (which happens
1370      *       only in getParameters() leads to the Parameter parser having
1371      *       no object and thus the default or even an undefined encoding
1372      *       instead of the actual request character encoding).
1373      */
1374     public ParameterParser getParameterParser()
1375     {
1376         return parameters;
1377     }
1378 
1379     /**
1380      * Sets the parameter parser.
1381      *
1382      * @param parser a parameter parser.
1383      */
1384     public void setParameterParser(ParameterParser parser)
1385     {
1386         parameters = parser;
1387     }
1388 
1389     /**
1390      * Gets the cookie parser without parsing the cookies.
1391      *
1392      * @return the cookie parser.
1393      */
1394     public CookieParser getCookieParser()
1395     {
1396         return cookies;
1397     }
1398 
1399     /**
1400      * Sets the cookie parser.
1401      *
1402      * @param parser a cookie parser.
1403      */
1404     public void setCookieParser(CookieParser parser)
1405     {
1406         cookies = parser;
1407     }
1408 
1409     /**
1410      * Sets the servlet request.
1411      *
1412      * @param req a request.
1413      */
1414     public void setRequest(HttpServletRequest req)
1415     {
1416         this.req = req;
1417     }
1418 
1419     /**
1420      * Sets the servlet response.
1421      *
1422      * @param res a response.
1423      */
1424     public void setResponse(HttpServletResponse res)
1425     {
1426         this.res = res;
1427     }
1428 
1429     /**
1430      * Sets the servlet configuration used during servlet init.
1431      *
1432      * @param config a configuration.
1433      */
1434     public void setServletConfig(ServletConfig config)
1435     {
1436         this.config = config;
1437         if (config == null)
1438         {
1439             this.servletContext = null;
1440         }
1441         else
1442         {
1443             this.servletContext = config.getServletContext();
1444         }
1445     }
1446 
1447     /**
1448      * Sets the server data of the request.
1449      *
1450      * @param serverData server data.
1451      */
1452     public void setServerData(ServerData serverData)
1453     {
1454         this.serverData = serverData;
1455     }
1456 
1457     // ********************
1458     // Miscellanous setters
1459     // ********************
1460 
1461     /**
1462      * Sets the print writer.
1463      *
1464      * @param out a print writer.
1465      * @deprecated no replacement planned, response writer will not be cached
1466      */
1467     @Deprecated
1468     protected void setOut(PrintWriter out)
1469     {
1470         this.out = out;
1471     }
1472 
1473     /**
1474      * Sets the cached server scheme that is stored in the server data.
1475      *
1476      * @param serverScheme a string.
1477      */
1478     protected void setServerScheme(String serverScheme)
1479     {
1480         getServerData().setServerScheme(serverScheme);
1481     }
1482 
1483     /**
1484      * Sets the cached server same that is stored in the server data.
1485      *
1486      * @param serverName a string.
1487      */
1488     protected void setServerName(String serverName)
1489     {
1490         getServerData().setServerName(serverName);
1491     }
1492 
1493     /**
1494      * Sets the cached server port that is stored in the server data.
1495      *
1496      * @param port an int.
1497      */
1498     protected void setServerPort(int port)
1499     {
1500         getServerData().setServerPort(port);
1501     }
1502 
1503     /**
1504      * Sets the cached context path that is stored in the server data.
1505      *
1506      * @param contextPath a string.
1507      */
1508     protected void setContextPath(String contextPath)
1509     {
1510         getServerData().setContextPath(contextPath);
1511     }
1512 
1513     /**
1514      * Sets the cached script name that is stored in the server data.
1515      *
1516      * @param scriptName a string.
1517      */
1518     protected void setScriptName(String scriptName)
1519     {
1520         getServerData().setScriptName(scriptName);
1521     }
1522 
1523     /**
1524      * Checks whether the object is disposed.
1525      *
1526      * @return true, if the object is disposed.
1527      */
1528     public boolean isDisposed()
1529     {
1530         return disposed;
1531     }
1532 
1533 }