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