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