View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.context;
20  
21  import java.awt.Color;
22  
23  import java.io.IOException;
24  
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.TimeZone;
33  import java.util.concurrent.ConcurrentHashMap;
34  import java.util.concurrent.ConcurrentMap;
35  
36  import javax.faces.component.UIComponent;
37  import javax.faces.component.UIViewRoot;
38  import javax.faces.component.visit.VisitContext;
39  import javax.faces.component.visit.VisitHint;
40  import javax.faces.context.ExternalContext;
41  import javax.faces.context.FacesContext;
42  import javax.faces.event.PhaseId;
43  
44  import org.apache.myfaces.trinidad.change.ChangeManager;
45  import org.apache.myfaces.trinidad.config.RegionManager;
46  import org.apache.myfaces.trinidad.event.WindowLifecycleListener;
47  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
48  import org.apache.myfaces.trinidad.util.ArrayMap;
49  import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
50  import org.apache.myfaces.trinidad.webapp.UploadedFileProcessor;
51  
52  
53  /**
54   * Context class for all per-request and per-webapp information
55   * required by Trinidad.  A <code>RequestContext</code> object can be
56   * retrieved with the static {@link #getCurrentInstance} method.
57   * There is one and only one <code>RequestContext</code> object
58   * active in any one thread.
59   * <p>
60   * This class does not extend <code>FacesContext</code>;  this is intentional,
61   * as extending <code>FacesContext</code> requires taking over the
62   * <code>FacesContextFactory</code>.
63   * <p>
64   */
65  // TODO Refactor this class after everything gets added to it.
66  // TODO There's some values in here that seem to affect only output (e.g.,
67  //  right-to-left); that's not great, since ideally that detail would be
68  //  buried in something more renderer-specific.
69  abstract public class RequestContext
70  {
71    /**
72     * Name of the EL implicit variable ("requestContext") that is used to
73     * expose this context object.
74     */
75    static public final String VARIABLE_NAME =
76      "requestContext";
77  
78    // Omitted APIs:
79    //
80    // LocaleContext
81    // =============
82    // Locale getTranslationLocale
83    //
84    // DateFormatContext:
85    // =================
86    // int getTwoDigitYearStart
87    // boolean isLenient (very lame API, definitely gone)
88  
89  
90    /**
91     * Retrieves the RequestContext active for the current thread.
92     */
93    static public RequestContext getCurrentInstance()
94    {
95      return _CURRENT_CONTEXT.get();
96    }
97  
98  
99  
100   /**
101    * Creates an RequestContext.  RequestContext is abstract
102    * and may not be instantiated directly.
103    * @see RequestContextFactory
104    */
105   protected RequestContext()
106   {
107   }
108 
109   //
110   // State related APIs
111   //
112 
113   /**
114    * Returns a Map of objects at "pageFlow" scope.
115    */
116   public abstract Map<String, Object> getPageFlowScope();
117 
118   /**
119    * @deprecated use getPageFlowScope()
120    */
121   @Deprecated
122   final public Map<String, Object> getProcessScope()
123   {
124     return getPageFlowScope();
125   }
126 
127   /**
128    * Method to obtain a Map stored on the view.
129    * <p>This calls {@link #getViewMap(boolean)} with a true value for create.</p>
130    * <p>This is a pre-cursor implementation to the JSF 2.0 UIViewRoot.getViewMap() function.
131    * The implementation is taken from the initial UIViewRoot implementation in JSF but without
132    * the event notification support.</p>
133    */
134   public abstract Map<String, Object> getViewMap();
135 
136   /**
137    * Method to obtain a Map stored on the view.
138    * <p>This is a pre-cursor implementation to the JSF 2.0 UIViewRoot.getViewMap() function.
139    * The implementation is taken from the initial UIViewRoot implementation in JSF but without
140    * the event notification support.</p>
141    *
142    * @param create if the map should be created if it already does not exist.
143    */
144   public abstract Map<String, Object> getViewMap(boolean create);
145 
146   /**
147    * Returns a Map of objects associated with the current window if any.  If there is no
148    * current window, the Session Map is returned.
149    * @return Map for storing objects associated with the current window.
150    * @see org.apache.myfaces.trinidad.context.Window#getWindowMap
151    */
152   public Map<String, Object> getWindowMap()
153   {
154     WindowManager wm = getWindowManager();
155 
156     ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
157 
158     Window window = wm.getCurrentWindow(extContext);
159 
160     if (window != null)
161     {
162 
163       return window.getWindowMap();
164     }
165     else
166     {
167       return extContext.getSessionMap();
168     }
169   }
170 
171   //
172   // Dialog APIs
173   //
174 
175   /**
176    * Returns from a dialog raised by a
177    * {@link org.apache.myfaces.trinidad.component.UIXCommand UIXCommand} component,
178    * or any component implementing
179    * {@link org.apache.myfaces.trinidad.component.DialogSource DialogSource},
180    * or any direct calls to {@link #launchDialog launchDialog()}.
181    * <p>
182    * @see org.apache.myfaces.trinidad.event.ReturnEvent
183    * @param returnValue the value to be delivered in the the ReturnEvent
184    */
185   // TODO Do we need an explicit "cancelled" concept, or
186   // is a null returnValue good enough?
187   public abstract void returnFromDialog(
188     Object returnValue,
189     Map<Object, Object> returnParameters);
190 
191 
192   /**
193    * Returns an DialogService, which exposes a number
194    * of APIs needed by component and framework developers.  This
195    * will only rarely be needed by page authors.
196    */
197   public abstract DialogService getDialogService();
198 
199   /**
200    * Launch a dialog, optionally raising it in a new dialog window.
201    * <p>
202    * The dialog will receive a new <code>pageFlowScope</code> map,
203    * which includes all the values of the currently available pageFlowScope
204    * as well as a set of properties passed to this function in
205    * the <code>dialogParameters</code> map.  Changes to this newly
206    * created scope will not be visible once the dialog returns.
207    * <p>
208    * @param dialogRoot the UIViewRoot for the page being launched
209    * @param dialogParameters a set of parameters to populate the
210    *   newly created pageFlowScope
211    * @param source the UIComponent that launched the dialog and
212    *   should receive the {@link org.apache.myfaces.trinidad.event.ReturnEvent}
213    *   when the dialog is complete.
214    * @param useWindow if true, use a popup window for the dialog
215    *    if available on the current user agent device
216    * @param windowProperties the set of UI parameters used to
217    *   modify the window, if one is used.  The set of properties that\
218    *   are supported will depend on the <code>RenderKit</code>, but
219    *   common examples include "width", "height", "top" and "left".
220    */
221   public abstract void launchDialog(
222     UIViewRoot  dialogRoot,
223     Map<String, Object> dialogParameters,
224     UIComponent source,
225     boolean     useWindow,
226     Map<String, Object> windowProperties);
227 
228   //
229   // General Apache Trinidad
230   //
231 
232   /**
233    * Returns true if JSF is currently processing a postback request.
234    * <code>isPostback()</code> will return false if this is a request
235    * for an initial render of a page (that is, if Apply Request Values
236    * never executes), or if during the request the user is navigated
237    * to a different page (because of a navigation rule, etc).  For
238    * example, during a request that results in a navigation to a new
239    * page, <code>isPostback()</code> will return true from Apply
240    * Request Values to Invoke Application, then false afterwards;
241    * whereas if there was no navigation, it would return true
242    * <p>
243    * The value of this method is undefined during (or before)
244    * the Restore View phase, but can be used in the afterPhase()
245    * method of a PhaseListener for Restore View.
246    * </p>
247    */
248   public abstract boolean isPostback();
249 
250   /**
251    * Method to indicate if this current HTTP request is a
252    * partial page rendering request.
253    *
254    * @param context the <code>FacesContext</code> object for
255    * the request we are processing
256    * @return is this request a PPR request?
257    */
258   public abstract boolean isPartialRequest(FacesContext context);
259   /**
260    * Returns true if output should contain debugging information.
261    */
262   public abstract boolean isDebugOutput();
263 
264   /**
265    * Returns true if client-side validation should be disabled.
266    */
267   public abstract boolean isClientValidationDisabled();
268 
269   /**
270    * Returns the "output mode" - printable, etc.
271    */
272   public abstract String getOutputMode();
273 
274   /**
275    * Returns the OutputMode enumeration
276    * @return OutputMode
277    */
278   public OutputMode getOutputModeEnum()
279   {
280     return OutputMode.fromId(getOutputMode());
281   }
282 
283   /**
284    * Returns the name of the preferred skin family.
285    */
286   public abstract String getSkinFamily();
287 
288   /**
289    * Returns the name of the skin version that goes with the skin-family.
290    */
291   public String getSkinVersion()
292   {
293     return null;
294   }
295 
296   /**
297    * Determines whether the current View Root is an internal view
298    * @param context Faces context
299    * @return true if the current View Root is an internal view, false otherwise
300    */
301   public abstract boolean isInternalViewRequest(FacesContext context);
302 
303   /**
304    * Enumeration representing OutputModes
305    */
306   public enum OutputMode
307   {
308     DEFAULT("default"), PORTLET("portlet"), PRINTABLE("printable"), EMAIL("email"),
309     ATTACHMENT("attachment"), WEB_CRAWLER("webcrawler");
310 
311     private OutputMode(String id)
312     {
313       if (id == null) throw new NullPointerException();
314 
315       _id = id;
316     }
317 
318     /**
319      * @return the id of this OutputMode.
320      */
321     public String id()
322     {
323       return _id;
324     }
325 
326     @Override
327     public String toString()
328     {
329       return _id;
330     }
331 
332     /**
333      * Returns the OutputMode isntance of <code>null</code> if no id matches.
334      * @param id of OutputMode to return
335      * @return The OutputMode with the specified id
336      * @throws NullPointerException if <code>id</code> is null.
337      * @throws IllegalArgumentException if there is no enum with the specified name.
338      */
339     public static OutputMode fromId(String id)
340     {
341       if (id == null)
342         throw new NullPointerException();
343 
344       OutputMode outputMode = ID_TO_OUTPUT_MODE.get(id);
345 
346       if (outputMode == null)
347         throw new IllegalArgumentException();
348 
349       return outputMode;
350     }
351 
352     private static final Map<String, OutputMode> ID_TO_OUTPUT_MODE = new HashMap<String, OutputMode>();
353 
354     static
355     {
356       OutputMode[] instances = OutputMode.class.getEnumConstants();
357 
358       for (int i = 0; i < instances.length; i++)
359       {
360         ID_TO_OUTPUT_MODE.put(instances[i].toString(), instances[i]);
361       }
362     }
363 
364     private final String _id;
365   }
366 
367   public enum Accessibility
368   {
369     /**
370      * Output supports accessibility features
371      */
372     DEFAULT("default"),
373 
374     /**
375      * Accessibility-specific constructs are stripped out to optimize output size
376      */
377     INACCESSIBLE("inaccessible"),
378 
379     /**
380      * Accessibility-specific constructs are added to improve behavior under a screen reader
381      * (but may affect other users negatively)
382      */
383     SCREEN_READER("screenReader");
384 
385     /**
386      * Creates an Accessibility constant.
387      * @param displayName a user-friendly display name for the constant.
388      */
389     Accessibility(String displayName)
390     {
391       _displayName = displayName;
392     }
393 
394     @Override
395     public String toString()
396     {
397       return displayName();
398     }
399 
400     /**
401      * Returns the display name for this enum constant.
402      */
403     public String displayName()
404     {
405       return _displayName;
406     }
407 
408     /**
409      * Performs a reverse lookup of an Accessibilty constant based on
410      * its display name.
411      * 
412      * @param displayName the display name of the Accessibility constant to return.
413      * @return the non-null Accessibility constant associated with the display name.
414      * @throws IllegalArgumentException if displayName does not correspond
415      *   to some Accessibility constant.
416      * @throws NullPointerException if displayName is null. 
417      */
418     public static Accessibility valueOfDisplayName(String displayName)
419     {
420       if (displayName == null)
421       {
422         throw new NullPointerException();
423       }
424       
425       Accessibility accessibility = _displayNameMap.get(displayName);
426       
427       if (accessibility == null)
428       {
429         String message = _LOG.getMessage("ILLEGAL_ENUM_VALUE", 
430                            new Object[] { Accessibility.class.getName(), displayName });
431         throw new IllegalArgumentException(message);
432       }
433       
434       return accessibility;
435     }
436 
437     private final String _displayName;
438     
439     private static final Map<String, Accessibility> _displayNameMap;
440     
441     static
442     {
443       Map<String, Accessibility> displayNameMap = new ArrayMap<String, Accessibility>(3);
444       for (Accessibility accessibility : Accessibility.values())
445       {
446         displayNameMap.put(accessibility.displayName(), accessibility);
447       }
448       
449       _displayNameMap = Collections.unmodifiableMap(displayNameMap);
450     }
451   };
452 
453 
454   public enum ClientValidation
455   {
456     ALERT("alert"),
457     INLINE("inline"),
458     DISABLED("disabled");
459 
460     ClientValidation(String name)
461     {
462       _name = name;
463     }
464 
465     @Override
466     public String toString()
467     {
468       return _name;
469     }
470 
471     private final String _name;
472   };
473 
474   /**
475    * Returns the name of the current accessibility mode.
476    */
477   public abstract Accessibility getAccessibilityMode();
478 
479   /**
480    * Returns the accessibility profile for the current request.
481    */
482   public abstract AccessibilityProfile getAccessibilityProfile();
483 
484   /**
485    * Returns the name of the current client validation mode.
486    */
487   public abstract ClientValidation getClientValidation();
488 
489   /**
490    * Returns the system wide setting to turn animation on/off.
491    */
492   public abstract boolean isAnimationEnabled();
493 
494   //
495   //  General localization
496   //
497 
498   /**
499    * Returns true if the user should be shown output in right-to-left.
500    */
501   public abstract boolean isRightToLeft();
502 
503   /**
504    * Returns the formatting locale.  Converters without an explicit locale
505    * should use this to format values.  If not set, converters should
506    * default to the value of FacesContext.getViewRoot().getLocale().
507    * This will, by default, simply return null.
508    */
509   public abstract Locale getFormattingLocale();
510 
511   //
512   //  Number formatting
513   //
514 
515   /**
516    * Return the separator used for groups of numbers.  If NUL (zero),
517    * use the default separator for the current language.
518    */
519   public abstract char getNumberGroupingSeparator();
520 
521   /**
522    * Return the separator used as the decimal point.  If NUL (zero),
523    * use the default separator for the current language.
524    */
525   public abstract char getDecimalSeparator();
526 
527   /**
528    * Return the ISO 4217 currency code used by default for formatting
529    * currency fields when those fields do not specify an explicit
530    * currency field via their converter.  If this returns null, the default
531    * code for the current locale will be used.
532    */
533   // TODO do we need to provide getCurrencySymbol() as well?
534   public abstract String getCurrencyCode();
535 
536   //
537   // DateFormating API
538   //
539   /**
540    * Returns the year offset for parsing years with only two digits.
541    * If not set this is defaulted to <code>1950</code>
542    * This is used by @link{org.apache.myfaces.trinidad.faces.view.converter.DateTimeConverter}
543    * while converting strings to Date object.
544    */
545   public abstract int getTwoDigitYearStart();
546 
547   //
548   // Help APIs
549   //
550 
551   /**
552    * Return the URL to an Oracle Help for the Web servlet.
553    */
554   // TODO Add support for non-OHW help systems
555   public abstract String getOracleHelpServletUrl();
556 
557   /**
558    * Returns a Map that will accept topic names as keys, and return
559    * an URL as a result.
560    */
561   public abstract Map<String, Object> getHelpTopic();
562 
563   /**
564    * Returns a Map that will accept help system properties as keys, and return
565    * an URL as a result.
566    */
567   public abstract Map<String, Object> getHelpSystem();
568 
569   //
570   // Date formatting
571   //
572 
573   /**
574    * Returns the default TimeZone used for interpreting and formatting
575    * date values.
576    */
577   public abstract TimeZone getTimeZone();
578 
579   /**
580    * Gets the ChangeManager for the current application.
581    */
582   public abstract ChangeManager getChangeManager();
583 
584   /**
585    * Gets a per application concurrent map. There is no synchronization
586    * with ServletContext attributes.
587    */
588    public ConcurrentMap<String, Object> getApplicationScopedConcurrentMap()
589    {
590      ClassLoader cl = _getClassLoader();
591 
592      ConcurrentMap<String, Object> classMap = _APPLICATION_MAPS.get(cl);
593 
594      if (classMap == null)
595      {
596        ConcurrentMap<String, Object> newClassMap = new ConcurrentHashMap<String, Object>();
597        ConcurrentMap<String, Object> oldClassMap = _APPLICATION_MAPS.putIfAbsent(cl, newClassMap);
598 
599        classMap = ((oldClassMap != null)? oldClassMap : newClassMap);
600 
601        assert(classMap != null);
602      }
603 
604      return classMap;
605    }
606 
607   /**
608     * Gets the PageFlowScopeProvider for the current application.
609     */
610   public abstract PageFlowScopeProvider getPageFlowScopeProvider();
611 
612   /**
613     * Gets the PageResolver for the current application.
614     */
615   public abstract PageResolver getPageResolver();
616 
617   /**
618    * Gets the RegionManager for the current application.
619    */
620   public abstract RegionManager getRegionManager();
621 
622   //
623   // Partial Page Rendering support
624   //
625   /**
626    * Add a component as a partial target. In response to a partial event, only
627    * components registered as partial targets are re-rendered.  For
628    * a component to be successfully re-rendered when it is manually
629    * added with this API, it should have an explictly set "id".  If
630    * not, partial re-rendering may or may not work depending on the
631    * component.
632    */
633   public abstract void addPartialTarget(UIComponent newTarget);
634 
635   /**
636    * Add components relative to the given component as partial targets.
637    * <p>
638    * See {@link #addPartialTarget(UIComponent)} for more information.
639    * </p>
640    * @param from the component to use as a relative reference for any
641    * relative IDs in the list of targets
642    * @param targets array of targets relative to the from component that
643    * should be added as targets.
644    * @see ComponentUtils#findRelativeComponent(UIComponent, String)
645    */
646   public abstract void addPartialTargets(UIComponent from, String... targets);
647 
648   /**
649    * Returns the set of partial targets related to a given UIComponent.
650    */
651   public abstract Set<UIComponent> getPartialTargets(UIComponent newTarget);
652 
653   /**
654    * Adds a listener on a set of particular triggering components. If one of
655    * the named components gets updated in response to a partial event, then
656    * this listener component will be rerendered during the render phase (i.e.
657    * it will be added as a partialTarget). The list should consist of names
658    * suitable for use with the findComponent method on UIComponent.
659    */
660   public abstract void addPartialTriggerListeners(UIComponent listener,
661                                                   String[] trigger);
662 
663   /**
664    * Called when any component gets updated. Any partial target components
665    * listening on this component will be added to the partialTargets list in
666    * the render phase.
667    */
668   public abstract void partialUpdateNotify(UIComponent updated);
669 
670   //
671   // Miscellaneous functionality
672   //
673 
674   /**
675    * <p>Creates a VisitContext instance for use with
676    * {@link UIComponent#visitTree}.</p>
677    *
678    * @param context the FacesContext for the current request
679    * @param ids the client ids of the components to visit.  If null,
680    *   all components will be visited.
681    * @param hints the VisitHints to apply to the visit
682    * @param phaseId.  ignored.
683    * @return a VisitContext instance that is initialized with the
684    *   specified ids and hints.
685    * @deprecated use {@link VisitContext#createVisitContext(FacesContext, Collection<String>, Set<VisitHint>)} instead
686    */
687   public final VisitContext createVisitContext(
688     FacesContext context,
689     Collection<String> ids,
690     Set<VisitHint> hints,
691     PhaseId phaseId)
692   {
693     return VisitContext.createVisitContext(context, ids, hints);
694   }
695 
696   public abstract UploadedFileProcessor getUploadedFileProcessor();
697 
698   public abstract Long getUploadedFileMaxMemory();
699 
700   public abstract Long getUploadedFileMaxDiskSpace();
701 
702   public abstract String getUploadedFileTempDir();
703 
704   /**
705    * Returns a Map that takes color palette names as keys, and returns
706    * the color palette as a result.
707    */
708   public abstract Map<String, List<Color>> getColorPalette();
709 
710   /**
711    * Returns a Map that performs message formatting with a recursive Map
712    * structure.  The first key must be the message formatting mask, and the
713    * second the first parameter into the message. (The formatter Map supports
714    * only a single parameter at this time.)
715    */
716   public abstract Map<Object, Map<Object,String>> getFormatter();
717 
718   /**
719    * Returns the Agent information for the current context
720    */
721   public abstract Agent getAgent();
722 
723   /**
724    * Saves the state of a UIComponent tree into an Object.  The
725    * Object will be serializable, unless a UIComponent
726    * in this tree contains a non-serializable property.  This
727    * method does not check that condition.
728    * @param component the component
729    * @return an Object that can be passed to restoreComponent()
730    *  to reinstantiate the state
731    */
732   public abstract Object saveComponent(UIComponent component);
733 
734   /**
735    * Restores the state of a component.
736    * @param state an Object created by a prior call to saveComponent().
737    * @return the component
738    */
739   public abstract UIComponent restoreComponent(Object state)
740                             throws ClassNotFoundException,
741                                    InstantiationException,
742                                    IllegalAccessException;
743 
744   /**
745    * <p>
746    * Returns the WindowManager for this request.  A non-null WindowManager
747    * will always be returned.
748    * </p><p>
749    * The default implementation uses the first WindowManagerFactory specified
750    * implementation class in a file named
751    * <code>org.apache.myfaces.trinidad.context.WindowManagerFactory</code>
752    * in the <code>META-INF/services</code> directory and uses the WindowManagerFactory
753    * to create the WindowManager for this Session.  If no WindowManagerFactory is
754    * found, a default WindowManager that never returns any Windows is used.
755    * </p>
756    * @return the WindowManager used for this Session.
757    */
758   public WindowManager getWindowManager()
759   {
760     // implement getWindowManager() in RequestContext for backwards compatibility
761 
762     // check if we have cached it for the request
763     WindowManager windowManager = _windowManager;
764 
765     // get instance using the WindowManagerFactory
766     if (windowManager == null)
767     {
768       FacesContext context = FacesContext.getCurrentInstance();
769 
770       // just in case we're called before the real JSF lifecycle starts
771       if (context != null)
772       {
773         // check if we have cached it per session
774         ExternalContext extContext = context.getExternalContext();
775 
776         // create a new instance using the WindowManagerFactory
777         ConcurrentMap<String, Object> concurrentAppMap = getApplicationScopedConcurrentMap();
778 
779         WindowManagerFactory windowManagerFactory = (WindowManagerFactory)concurrentAppMap.get(
780                                                               _WINDOW_MANAGER_FACTORY_CLASS_NAME);
781 
782         if (windowManagerFactory == null)
783         {
784           // we haven't registered a WindowManagerFactory yet, so use the services api to see
785           // if a factory has been registered
786           List<WindowManagerFactory> windowManagerFactories =
787                                   ClassLoaderUtils.getServices(_WINDOW_MANAGER_FACTORY_CLASS_NAME);
788 
789           if (windowManagerFactories.isEmpty())
790           {
791             // no factory registered so use the factory that returns dummy stub WindowManagers
792             windowManagerFactory = _STUB_WINDOW_MANAGER_FACTORY;
793           }
794           else
795           {
796             // only one WindowManager is allowed, use it
797             windowManagerFactory = windowManagerFactories.get(0);
798           }
799 
800           // save the WindowManagerFactory to the application if it hasn't already been saved
801           // if it has been saved, use the previously registered WindowManagerFactory
802           WindowManagerFactory oldWindowManagerFactory = (WindowManagerFactory)
803                               concurrentAppMap.putIfAbsent(_WINDOW_MANAGER_FACTORY_CLASS_NAME,
804                                                            windowManagerFactory);
805 
806           if (oldWindowManagerFactory != null)
807             windowManagerFactory = oldWindowManagerFactory;
808         } // create WindowManagerFactory
809 
810         // get the WindowManager from the factory.  The factory will create a new instance
811         // for this session if necessary
812         windowManager = windowManagerFactory.getWindowManager(extContext);
813 
814         // remember for the next call on this thread
815         _windowManager = windowManager;
816       }
817     }
818 
819     return windowManager;
820   }
821 
822   /**
823    * Get the component context manager.
824    * @return The manager of component context changes to be used to suspend and resume
825    * component specific context changes.
826    */
827    public ComponentContextManager getComponentContextManager()
828    {
829      if (_componentContextManager == null)
830      {
831        _componentContextManager = new ComponentContextManagerImpl();
832      }
833 
834      return _componentContextManager;
835    }
836 
837   /**
838    * Releases the RequestContext object.  This method must only
839    * be called by the code that created the RequestContext.
840    * @exception IllegalStateException if no RequestContext is attached
841    * to the thread, or the attached context is not this object
842    */
843   public void release()
844   {
845     if (_LOG.isFinest())
846     {
847       _LOG.finest("RequestContext released.",
848                   new RuntimeException("This is not an error. This trace is for debugging."));
849     }
850 
851     Object o = _CURRENT_CONTEXT.get();
852     if (o == null)
853       throw new IllegalStateException(
854               _addHelp("RequestContext was already released or " +
855                        "had never been attached."));
856     if (o != this)
857       throw new IllegalStateException(_LOG.getMessage(
858         "RELEASE_DIFFERENT_REQUESTCONTEXT_THAN_CURRENT_ONE"));
859 
860     _CURRENT_CONTEXT.remove();
861   }
862 
863   /**
864    * Attaches a RequestContext to the current thread.  This method
865    * should only be called by a RequestContext object itself.
866    * @exception IllegalStateException if an RequestContext is already
867    * attached to the thread
868    */
869   public void attach()
870   {
871     if (_LOG.isFinest())
872     {
873       _LOG.finest("RequestContext attached.",
874                   new RuntimeException(_LOG.getMessage(
875                     "DEBUGGING_TRACE_NOT_ERROR")));
876     }
877 
878     Object o = _CURRENT_CONTEXT.get();
879     if (o != null)
880     {
881       throw new IllegalStateException(
882               _addHelp("Trying to attach RequestContext to a " +
883                        "thread that already had one."));
884     }
885     _CURRENT_CONTEXT.set(this);
886   }
887 
888   private static String _addHelp(String error)
889   {
890     if (!_LOG.isFinest())
891     {
892       error += " To enable stack traces of each RequestContext attach/release call," +
893         " enable Level.FINEST logging for the "+RequestContext.class;
894     }
895     return error;
896   }
897 
898   //
899   // Pick a ClassLoader
900   //
901   private ClassLoader _getClassLoader()
902   {
903     return Thread.currentThread().getContextClassLoader();
904   }
905 
906   /**
907    * Default WindowManagerFactory implementation that returns the StubWindowManager
908    */
909   private static final class StubWindowManagerFactory extends WindowManagerFactory
910   {
911     public WindowManager getWindowManager(ExternalContext extContext)
912     {
913       return _STUB_WINDOW_MANAGER;
914     }
915 
916     private static final WindowManager _STUB_WINDOW_MANAGER = new StubWindowManager();
917   }
918 
919   /**
920    * Default WindowManager implementation that returns no Windows
921    */
922   private static final class StubWindowManager extends WindowManager
923   {
924     @Override
925     public Window getCurrentWindow(ExternalContext extContext)
926     {
927       return null;
928     }
929 
930     @Override
931     public Map<String, Window> getWindows(ExternalContext extContext)
932     {
933       return Collections.emptyMap();
934     }
935 
936     @Override
937     public void addWindowLifecycleListener(
938       ExternalContext extContext,
939       WindowLifecycleListener windowListener)
940     {
941       // do nothing
942     }
943 
944     @Override
945     public void removeWindowLifecycleListener(
946       ExternalContext extContext,
947       WindowLifecycleListener windowListener)
948     {
949       // do nothing
950     }
951 
952     @Override
953     public void writeState(FacesContext context) throws IOException
954     {
955       // do nothing
956     }
957   }
958 
959   /* singleton for WindowManagerFactory that returns WindowManagers that don't do anything */
960   private static final WindowManagerFactory _STUB_WINDOW_MANAGER_FACTORY =
961                                                                     new StubWindowManagerFactory();
962 
963   private static final String _WINDOW_MANAGER_FACTORY_CLASS_NAME =
964                                                               WindowManagerFactory.class.getName();
965 
966   @SuppressWarnings({"CollectionWithoutInitialCapacity"})
967   private static final ConcurrentMap<ClassLoader, ConcurrentMap<String, Object>> _APPLICATION_MAPS =
968        new ConcurrentHashMap<ClassLoader, ConcurrentMap<String, Object>>();
969   static private final ThreadLocal<RequestContext> _CURRENT_CONTEXT =
970     new ThreadLocal<RequestContext>();
971   static private final TrinidadLogger _LOG =
972     TrinidadLogger.createTrinidadLogger(RequestContext.class);
973 
974   private ComponentContextManager _componentContextManager;
975 
976   // window manager for this request
977   private WindowManager _windowManager;
978 }