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