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