001package org.apache.turbine.services.rundata;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.io.PrintWriter;
024import java.nio.charset.Charset;
025import java.util.ArrayList;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Locale;
029import java.util.Map;
030
031import javax.naming.Context;
032import javax.servlet.ServletConfig;
033import javax.servlet.ServletContext;
034import javax.servlet.http.HttpServletRequest;
035import javax.servlet.http.HttpServletResponse;
036import javax.servlet.http.HttpSession;
037
038import org.apache.commons.lang3.StringUtils;
039import org.apache.fulcrum.parser.CookieParser;
040import org.apache.fulcrum.parser.ParameterParser;
041import org.apache.fulcrum.security.acl.AccessControlList;
042import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
043import org.apache.logging.log4j.LogManager;
044import org.apache.logging.log4j.Logger;
045import org.apache.turbine.Turbine;
046import org.apache.turbine.TurbineConstants;
047import org.apache.turbine.om.security.User;
048import org.apache.turbine.pipeline.DefaultPipelineData;
049import org.apache.turbine.services.TurbineServices;
050import org.apache.turbine.services.template.TemplateService;
051import org.apache.turbine.util.FormMessages;
052import org.apache.turbine.util.LocaleUtils;
053import org.apache.turbine.util.ServerData;
054import org.apache.turbine.util.SystemError;
055import org.apache.turbine.util.template.TemplateInfo;
056
057/**
058 * DefaultTurbineRunData is the default implementation of the
059 * TurbineRunData interface, which is distributed by the Turbine
060 * RunData service, if another implementation is not defined in
061 * the default or specified RunData configuration.
062 * TurbineRunData is an extension to RunData, which
063 * is an interface to run-time information that is passed
064 * within Turbine. This provides the threading mechanism for the
065 * entire system because multiple requests can potentially come in
066 * at the same time.  Thus, there is only one RunData instance
067 * for each request that is being serviced.
068 *
069 * <p>DefaultTurbineRunData implements the Recyclable interface making
070 * it possible to pool its instances for recycling.
071 *
072 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
073 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
074 * @author <a href="mailto:bhoeneis@ee.ethz.ch">Bernie Hoeneisen</a>
075 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
076 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
077 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
078 * @version $Id$
079 */
080public class DefaultTurbineRunData
081        extends DefaultPipelineData
082        implements TurbineRunData
083{
084    /**
085     * The disposed flag.
086     */
087    private boolean disposed;
088
089    /** Cached action name to execute for this request. */
090    private String action;
091
092    /** This is the layout that the page will use to render the screen. */
093    private String layout;
094
095    /** Cached screen name to execute for this request. */
096    private String screen;
097
098    /** The character encoding of template files. */
099    private String templateEncoding;
100
101    /** This is what will build the <title></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}