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