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}