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