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