    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}