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