1 package org.apache.turbine.services.rundata;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29
30 import javax.naming.Context;
31 import javax.servlet.ServletConfig;
32 import javax.servlet.ServletContext;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35 import javax.servlet.http.HttpSession;
36
37 import org.apache.commons.lang.StringUtils;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.ecs.Document;
41 import org.apache.ecs.Element;
42 import org.apache.ecs.StringElement;
43 import org.apache.fulcrum.mimetype.MimeTypeService;
44 import org.apache.fulcrum.parser.CookieParser;
45 import org.apache.fulcrum.parser.ParameterParser;
46 import org.apache.fulcrum.pool.Recyclable;
47 import org.apache.fulcrum.security.acl.AccessControlList;
48 import org.apache.turbine.Turbine;
49 import org.apache.turbine.TurbineConstants;
50 import org.apache.turbine.om.security.User;
51 import org.apache.turbine.pipeline.DefaultPipelineData;
52 import org.apache.turbine.services.ServiceManager;
53 import org.apache.turbine.services.TurbineServices;
54 import org.apache.turbine.services.template.TurbineTemplate;
55 import org.apache.turbine.util.FormMessages;
56 import org.apache.turbine.util.ServerData;
57 import org.apache.turbine.util.SystemError;
58 import org.apache.turbine.util.template.TemplateInfo;
59
60 /**
61 * DefaultTurbineRunData is the default implementation of the
62 * TurbineRunData interface, which is distributed by the Turbine
63 * RunData service, if another implementation is not defined in
64 * the default or specified RunData configuration.
65 * TurbineRunData is an extension to RunData, which
66 * is an interface to run-rime information that is passed
67 * within Turbine. This provides the threading mechanism for the
68 * entire system because multiple requests can potentially come in
69 * at the same time. Thus, there is only one RunData implementation
70 * for each request that is being serviced.
71 *
72 * <p>DefaultTurbineRunData implements the Recyclable interface making
73 * it possible to pool its instances for recycling.
74 *
75 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
76 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
77 * @author <a href="mailto:bhoeneis@ee.ethz.ch">Bernie Hoeneisen</a>
78 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
79 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
80 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
81 * @version $Id: DefaultTurbineRunData.java 1706239 2015-10-01 13:18:35Z tv $
82 */
83 public class DefaultTurbineRunData
84 extends DefaultPipelineData
85 implements TurbineRunData, Recyclable
86 {
87 /**
88 * The disposed flag.
89 */
90 private boolean disposed;
91
92 /** The default locale. */
93 private static Locale defaultLocale = null;
94
95 /** The default charset. */
96 private static String defaultCharSet = null;
97
98 /** Determines if there is information in the document or not. */
99 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 }