Save This Page
Home » trinidad-1.2.8-src-all » org.apache.myfaces.trinidadinternal » application » [javadoc | source]
    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.trinidadinternal.application;
   20   
   21   import java.io.IOException;
   22   import java.io.Serializable;
   23   import java.util.ArrayList;
   24   import java.util.HashMap;
   25   import java.util.Iterator;
   26   import java.util.List;
   27   import java.util.Locale;
   28   import java.util.Map;
   29   
   30   import javax.faces.FactoryFinder;
   31   import javax.faces.application.StateManager;
   32   import javax.faces.application.StateManagerWrapper;
   33   import javax.faces.component.UIComponent;
   34   import javax.faces.component.UIViewRoot;
   35   import javax.faces.context.ExternalContext;
   36   import javax.faces.context.FacesContext;
   37   import javax.faces.render.RenderKit;
   38   import javax.faces.render.RenderKitFactory;
   39   import javax.faces.render.ResponseStateManager;
   40   
   41   import org.apache.myfaces.trinidad.component.UIXComponentBase;
   42   import org.apache.myfaces.trinidad.context.RequestContext;
   43   import org.apache.myfaces.trinidad.logging.TrinidadLogger;
   44   import org.apache.myfaces.trinidad.util.ExternalContextUtils;
   45   import org.apache.myfaces.trinidadinternal.util.LRUCache;
   46   import org.apache.myfaces.trinidadinternal.util.SubKeyMap;
   47   import org.apache.myfaces.trinidadinternal.util.TokenCache;
   48   
   49   import com.sun.facelets.FaceletViewHandler;
   50   
   51   /**
   52    * StateManager that handles a hybrid client/server strategy:  a
   53    * SerializedView is stored on the server, and only a small token
   54    * is stored on the client.
   55    * <p>
   56    * <h3>Application View cache</h3>
   57    * <p>
   58    * In addition, an optional Application view cache is supported.
   59    * This view cache will, when enabled, perform special caching
   60    * of all state for non-postback requests (that is, the initial
   61    * state of all pages).  For all pages, their SerializedView state
   62    * is stored in a Map at application scope, and reused across
   63    * all users.  This simultaneously eliminates the expense of saving
   64    * the state at all (except for the first request for any page),
   65    * and significantly reduces memory usage as long as users are
   66    * largely viewing initial pages only.
   67    * <p>
   68    * In addition, because the viewId is sufficient to identify the
   69    * page state out of the cache, the token can be completely
   70    * constant across requests and users.  This makes it possible
   71    * to cache the page content (which is not possible otherwise).
   72    * <p>
   73    * Since application scope objects do not support failover,
   74    * a mirror of the cache is saved at session scope.  The mirror
   75    * is an LRU map of the last 16 application-scoped entries, but
   76    * since it stores precisely the same SerializedView instances
   77    * as the application scope, the additional memory requirements
   78    * are minimal.
   79    * <p>
   80    * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/application/StateManagerImpl.java#2 $) $Date: 18-nov-2005.16:12:04 $
   81    */
   82   public class StateManagerImpl extends StateManagerWrapper
   83   {
   84     static public final String USE_APPLICATION_VIEW_CACHE_INIT_PARAM =
   85       "org.apache.myfaces.trinidad.USE_APPLICATION_VIEW_CACHE";
   86   
   87     static public final String CACHE_VIEW_ROOT_INIT_PARAM =
   88       "org.apache.myfaces.trinidad.CACHE_VIEW_ROOT";
   89   
   90   
   91     /**
   92      * Servlet context initialization parameter used by
   93      * StateManagerImpl to decide what sort of state should be saved
   94      * on the client.  Valid values are "token" and "all";  the
   95      * default is "token".
   96      */
   97     static public final String CLIENT_STATE_METHOD_PARAM_NAME =
   98       "org.apache.myfaces.trinidad.CLIENT_STATE_METHOD";
   99   
  100     /**
  101      * Servlet context initialization parameter used by
  102      * StateManagerImpl to decide how many tokens can be stored
  103      * per user.  The default is 15.
  104      */
  105     static public final String CLIENT_STATE_MAX_TOKENS_PARAM_NAME =
  106       "org.apache.myfaces.trinidad.CLIENT_STATE_MAX_TOKENS";
  107   
  108     /**
  109      * Value indicating that only a simple token will be stored
  110      * on the client.
  111      */
  112     static public final String CLIENT_STATE_METHOD_TOKEN = "token";
  113   
  114     /**
  115      * Value indicating that the entire component state will be stored
  116      * on the client.
  117      */
  118     static public final String CLIENT_STATE_METHOD_ALL = "all";
  119   
  120     public StateManagerImpl(
  121       StateManager delegate)
  122     {
  123       _delegate = delegate;
  124     }
  125     
  126     @Override
  127     protected StateManager getWrapped()
  128     {
  129       return _delegate;
  130     }
  131   
  132     @SuppressWarnings("deprecation")
  133     @Override
  134     public Object saveView(FacesContext context)
  135     {
  136       assert(context != null);
  137       
  138       if(isSavingStateInClient(context))
  139       {
  140         SerializedView view = _saveSerializedView(context);
  141         return new Object[]{view.getStructure(), view.getState()};
  142       }
  143       
  144       return super.saveView(context);
  145     }
  146   
  147     @Override @SuppressWarnings("deprecation")
  148     public SerializedView saveSerializedView(FacesContext context)
  149     {
  150       assert(context != null);
  151       
  152       if(isSavingStateInClient(context))
  153       {
  154         return _saveSerializedView(context);
  155       }
  156       
  157       return _delegate.saveSerializedView(context);
  158     }
  159   
  160     /**
  161      * Save a component tree as an Object.
  162      */
  163     static public Object saveComponentTree(
  164       FacesContext context,
  165       UIComponent  component)
  166     {
  167       // Don't remove transient components...
  168       Object structure = new Structure(component);
  169       Object state = component.processSaveState(context);
  170       return new PageState(context, structure, state, null);
  171     }
  172     
  173     /**
  174      * Take an object created by saveComponentTree()
  175      * and instantiate it as a UIComponent.
  176      */
  177     static public UIComponent restoreComponentTree(
  178       FacesContext context,
  179       Object       savedState) throws ClassNotFoundException,
  180                                       InstantiationException,
  181                                       IllegalAccessException
  182     {
  183       if (savedState == null)
  184         throw new NullPointerException();
  185   
  186       if (!(savedState instanceof PageState))
  187         throw new IllegalArgumentException(_LOG.getMessage(
  188           "INVALID_SAVED_STATE_OBJECT"));
  189   
  190       PageState viewState = (PageState) savedState;
  191   
  192       Object structure = viewState.getStructure();
  193       Object state = viewState.getState();
  194   
  195       UIComponent component =
  196         ((Structure) structure).createComponent();
  197   
  198       if (state != null)
  199         component.processRestoreState(context, state);
  200   
  201       return component;
  202     }
  203   
  204   
  205     /**
  206      * Save a view root.  Doesn't return a SerializedView because
  207      * SerializedView is a non-static inner class, and this needs
  208      * to be a static method.
  209      */
  210     static public Object saveViewRoot(
  211       FacesContext context,
  212       UIViewRoot   root)
  213     {
  214       _removeTransientComponents(root);
  215   
  216       Object structure = new Structure(root);
  217       Object state = root.processSaveState(context);
  218       return new PageState(context, structure, state, root);
  219     }
  220   
  221     static public UIViewRoot restoreViewRoot(
  222       FacesContext    context,
  223       Object          saved) throws ClassNotFoundException, InstantiationException,
  224                                     IllegalAccessException
  225   
  226     {
  227       if (saved == null)
  228         throw new NullPointerException();
  229   
  230       PageState viewState = (PageState) saved;
  231   
  232       UIViewRoot root = viewState.popRoot(context);
  233       if (root != null)
  234       {
  235         return root; // bug 4712492
  236       }
  237   
  238       Object structure = viewState.getStructure();
  239       Object state = viewState.getState();
  240   
  241       root = (UIViewRoot)
  242         ((Structure) structure).createComponent();
  243   
  244       if (state != null)
  245         root.processRestoreState(context, state);
  246   
  247       return root;
  248     }  
  249   
  250     @SuppressWarnings({"unchecked", "deprecation"})
  251     private SerializedView _saveSerializedView(FacesContext context)
  252     {
  253       SerializedView view = _getCachedSerializedView(context);
  254       if (view != null)
  255         return view;
  256   
  257       UIViewRoot root = context.getViewRoot();
  258       boolean dontSave = false;
  259   
  260       // See if we're going to use the application view cache for
  261       // this request
  262       Map<String, Object> applicationViewCache = null;
  263       Map<String, Object> perSessionApplicationViewCache = null;
  264       if (_useApplicationViewCache(context))
  265       {
  266         // OK, we are: so find the application cache and
  267         // the per-session mirror
  268         applicationViewCache = _getApplicationViewCache(context);
  269         perSessionApplicationViewCache =
  270           _getPerSessionApplicationViewCache(context);
  271   
  272         synchronized (applicationViewCache)
  273         {
  274           // If we've already got a copy of the state stored, then
  275           // we just need to make sure it's mirrored on the session
  276           Object applicationState = applicationViewCache.get(root.getViewId());
  277           if (applicationState != null)
  278           {
  279             // Note that we've got no work to do...
  280             dontSave = true;
  281             perSessionApplicationViewCache.put(root.getViewId(),
  282                                                applicationState);
  283           }
  284         }
  285       }
  286   
  287       _removeTransientComponents(root);
  288   
  289       Object structure = (dontSave || !_needStructure(context))
  290                            ? null
  291                            : new Structure(root);
  292       Object state = dontSave ? null : root.processSaveState(context);
  293   
  294       if (_saveAsToken(context))
  295       {
  296         String token;
  297         if (applicationViewCache == null)
  298         {
  299           assert(!dontSave);
  300           TokenCache cache = _getViewCache(context);
  301           assert(cache != null);
  302   
  303           // Store bits of the session as subkeys off of the session
  304           Map<String, Object> stateMap = new SubKeyMap(
  305                            context.getExternalContext().getSessionMap(),
  306                            _VIEW_CACHE_KEY + ".");
  307           // Sadly, we can't save just a SerializedView, because we should
  308           // save a serialized object, and SerializedView is a *non*-static
  309           // inner class of StateManager
  310           PageState pageState = new PageState(
  311               context,
  312               structure,
  313               state,
  314               // Save the view root into the page state as a transient
  315               // if this feature has not been disabled
  316               _useViewRootCache(context) ? root : null);
  317   
  318           String requestToken = _getRequestTokenForResponse(context);
  319           // If we have a cached token that we want to reuse,
  320           // and that token hasn't disappeared from the cache already
  321           // (unlikely, but not impossible), use the stateMap directly
  322           // without asking the cache for a new token
  323           if ((requestToken != null) && cache.isAvailable(requestToken))
  324           {
  325             // NOTE: under *really* high pressure, the cache might
  326             // have been emptied between the isAvailable() call and
  327             // this put().  This seems sufficiently implausible to
  328             // be worth punting on
  329             stateMap.put(requestToken, pageState);
  330             token = requestToken;
  331             // NOTE 2: we have not pinned this reused state to any old state
  332             // This is OK for current uses of pinning and state reuse,
  333             // as pinning stays constant within a window, and we're not
  334             // erasing pinning at all.
  335           }
  336           else
  337           {
  338             // See if we should pin this new state to any old state
  339             String pinnedToken = (String)
  340               context.getExternalContext().getRequestMap().get(_PINNED_STATE_TOKEN_KEY);
  341             token = cache.addNewEntry(pageState,
  342                                       stateMap,
  343                                       pinnedToken);
  344           }
  345         }
  346         // If we got the "applicationViewCache", we're using it.
  347         else
  348         {
  349           // use null viewRoot since this state is shared across users:
  350           Object applicationState = new PageState(context, structure, state, null);
  351           // If we need to, stash the state off in our cache
  352           if (!dontSave)
  353           {
  354             synchronized (applicationViewCache)
  355             {
  356               applicationViewCache.put(root.getViewId(),
  357                                        applicationState);
  358               perSessionApplicationViewCache.put(root.getViewId(),
  359                                                  applicationState);
  360             }
  361           }
  362   
  363           token = _APPLICATION_CACHE_TOKEN;
  364         }
  365   
  366         assert(token != null);
  367   
  368         // Create a "tokenView" which abuses SerializedView to store
  369         // our token only
  370         view = new SerializedView(token, null);
  371         
  372         // And store the token for this request
  373         context.getExternalContext().getRequestMap().put(_REQUEST_STATE_TOKEN_KEY,
  374                                                          token);
  375       }
  376       else
  377       {
  378         assert(!dontSave);
  379         view = new SerializedView(structure, state);
  380       }
  381   
  382       _saveCachedSerializedView(context, view);
  383   
  384       return view;
  385     }
  386   
  387     /**
  388      * Requests that an old state token be "pinned" to the state of
  389      * the current request.  This means that the view state corresponding
  390      * to the token will not be released before the state for this request
  391      * is released.
  392      */
  393     @SuppressWarnings("unchecked")
  394     static public void pinStateToRequest(FacesContext context, String stateToken)
  395     {
  396       context.getExternalContext().getRequestMap().put(
  397               _PINNED_STATE_TOKEN_KEY, stateToken);
  398       
  399     }
  400     
  401     /**
  402      * @return the state token for the current request
  403      */
  404     static public String getStateToken(FacesContext context)
  405     {
  406       return (String) context.getExternalContext().getRequestMap().get(
  407               _REQUEST_STATE_TOKEN_KEY);
  408     }
  409     
  410     
  411     /**
  412      * Mark the the incoming request token should be used for the response
  413      */
  414     @SuppressWarnings("unchecked")
  415     static public void reuseRequestTokenForResponse(ExternalContext ec)
  416     {
  417       ec.getRequestMap().put(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY, Boolean.TRUE);    
  418     }
  419     
  420     /**
  421      * Clears the flag indicating that the old request token should be used for the response.
  422      */
  423     @SuppressWarnings("unchecked")
  424     static public void clearReuseRequestTokenForResponse(ExternalContext ec)
  425     {
  426       ec.getRequestMap().remove(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY);    
  427     }
  428   
  429     /**
  430      * If we've been asked to reuse the request token for the response,
  431      * store it off.
  432      */
  433     @SuppressWarnings("unchecked")
  434     static private void _updateRequestTokenForResponse(
  435       FacesContext context, String token)
  436     {
  437       Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
  438       // Go from TRUE -> the saved token
  439       if (Boolean.TRUE.equals(
  440             requestMap.get(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY)))
  441       {
  442         requestMap.put(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY, token);
  443       }
  444     }
  445   
  446   
  447     /**
  448      * Get any cached token for the response.
  449      */
  450     @SuppressWarnings("unchecked")
  451     static private String _getRequestTokenForResponse(
  452       FacesContext context)
  453     {
  454       Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
  455       Object token = requestMap.get(_REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY);
  456       // We wanted to, but didn't have anything saved
  457       if (Boolean.TRUE.equals(token))
  458         return null;
  459   
  460       return (String) token;
  461     }
  462       
  463     
  464     @Override @SuppressWarnings("deprecation")
  465     public void writeState(FacesContext context,
  466                            SerializedView state) throws IOException
  467     {
  468       _delegate.writeState(context, state);
  469     }
  470   
  471     @SuppressWarnings({"unchecked", "deprecation"})
  472     @Override
  473     public UIViewRoot restoreView(FacesContext context, String viewId,
  474                                   String renderKitId)
  475     {
  476       if (!isSavingStateInClient(context))
  477         return _delegate.restoreView(context, viewId, renderKitId);
  478   
  479       final Object structure;
  480       final Object state;
  481       boolean recalculateLocale = false;
  482   
  483       ResponseStateManager rsm = _getResponseStateManager(context, renderKitId);
  484       if (_saveAsToken(context))
  485       {
  486         Object token = rsm.getTreeStructureToRestore(context, viewId);
  487         if (token == null)
  488         {
  489           _LOG.finest("No token in the request for view \"{0}\";  " +
  490                       "probably a first view.", viewId);
  491           return null;
  492         }
  493   
  494         assert(token instanceof String);
  495         _LOG.finer("Restoring saved view state for token {0}", token);
  496   
  497         PageState viewState;
  498   
  499         // Load from the application cache
  500         if (_APPLICATION_CACHE_TOKEN.equals(token))
  501         {
  502           Map<String, Object> cache = _getApplicationViewCache(context);
  503           Map<String, Object> perSessionCache =
  504             _getPerSessionApplicationViewCache(context);
  505   
  506           // Synchronize on the application-level cache.
  507           // =-=AEW This may produce excessive contention
  508           synchronized (cache)
  509           {
  510             // Look first in the per-session cache
  511             viewState = (PageState) perSessionCache.get(viewId);
  512             if (viewState == null)
  513             {
  514               // Nope, it's not there.  Look in the application cache
  515               viewState = (PageState) cache.get(viewId);
  516               // And if we find it there, then push it back into
  517               // the per-session cache (it may have expired)
  518               if (viewState != null)
  519                 perSessionCache.put(viewId, viewState);
  520             }
  521             
  522             // If the view was found in the application cache then we
  523             // know it would be unsafe to use its locale for this session.
  524             // Same conclusion, however, even if found in the per-session 
  525             // cache, since the latter is just a mirror of the former.
  526             recalculateLocale = true;
  527           }
  528         }
  529         else
  530         {
  531           Map<String, Object> stateMap = new SubKeyMap(
  532                            context.getExternalContext().getSessionMap(),
  533                            _VIEW_CACHE_KEY + ".");
  534           viewState = (PageState) stateMap.get(token);
  535   
  536           if (viewState != null)
  537             _updateRequestTokenForResponse(context, (String) token);
  538   
  539           // Make sure that if the view state is present, the cache still
  540           // has the token, and vice versa
  541   
  542           // NOTE: it's very important that we call through to the
  543           // token cache here, not just inside the assert.  If we don't,
  544           // then we don't actually access the token, so it doesn't
  545           // get bumped up to the front in the LRU Cache!
  546           boolean isAvailable =
  547             _getViewCache(context).isAvailable((String) token);
  548           assert ((viewState != null) == isAvailable);
  549         }
  550   
  551         if (viewState == null)
  552         {
  553           _LOG.severe("CANNOT_FIND_SAVED_VIEW_STATE", token);
  554           return null;
  555         }
  556   
  557         _LOG.fine("Successfully found view state for token {0}", token);
  558   
  559         UIViewRoot root = viewState.popRoot(context); // bug 4712492
  560         if (root != null)
  561         {
  562           _LOG.finer("UIViewRoot for token {0} already exists. Bypassing restoreState", token);
  563           return root;
  564         }
  565   
  566         structure = viewState.getStructure();
  567         state = viewState.getState();
  568       }
  569       else
  570       {
  571         structure = rsm.getTreeStructureToRestore(context, viewId);
  572         state = rsm.getComponentStateToRestore(context);
  573       }
  574   
  575       if (structure == null)
  576       {
  577   
  578         UIViewRoot root = context.getViewRoot();
  579         if (root == null && _needStructure(context))
  580         {
  581           _LOG.severe("NO_STRUCTURE_ROOT_AVAILABLE");
  582           return null;
  583         }
  584   
  585         if (state != null)
  586           root.processRestoreState(context, state);
  587   
  588         return root;
  589       }
  590       else
  591       {
  592         if (!(structure instanceof Structure))
  593         {
  594           _LOG.severe("NO_STRUCTURE_AVAILABLE");
  595           return null;
  596         }
  597   
  598         // OK, we've structure and state; let's see what we can do!
  599         try
  600         {
  601           UIViewRoot root = (UIViewRoot)
  602           ((Structure) structure).createComponent();
  603   
  604           if (state != null)
  605             root.processRestoreState(context, state);
  606           
  607           if (recalculateLocale)
  608           {
  609             // Ensure that locale gets re-calculated when next fetched.
  610             root.setLocale((Locale) null);
  611           }
  612   
  613           _LOG.finer("Restored state for view \"{0}\"", viewId);
  614           return root;
  615         }
  616         catch (ClassNotFoundException cnfe)
  617         {
  618           _LOG.severe(cnfe);
  619         }
  620         catch (InstantiationException ie)
  621         {
  622           _LOG.severe(ie);
  623         }
  624         catch (IllegalAccessException iae)
  625         {
  626           _LOG.severe(iae);
  627         }
  628       }
  629   
  630       return null;
  631     }
  632   
  633     @Override
  634     public boolean isSavingStateInClient(FacesContext context)
  635     {
  636       return _delegate.isSavingStateInClient(context);
  637     }
  638   
  639     //
  640     // Protected APIs: we don't want
  641     //
  642   
  643     @Override
  644     protected Object getTreeStructureToSave(FacesContext context)
  645     {
  646       throw new UnsupportedOperationException();
  647     }
  648   
  649     @Override
  650     protected Object getComponentStateToSave(FacesContext context)
  651     {
  652       throw new UnsupportedOperationException();
  653     }
  654   
  655     @Override
  656     protected UIViewRoot restoreTreeStructure
  657       (FacesContext context, String viewId, String renderKitId)
  658     {
  659       throw new UnsupportedOperationException();
  660     }
  661   
  662     @Override
  663     protected void restoreComponentState
  664       (FacesContext context, UIViewRoot viewRoot, String renderKitId)
  665     {
  666       throw new UnsupportedOperationException();
  667     }
  668   
  669   
  670     private TokenCache _getViewCache(FacesContext context)
  671     {
  672       return TokenCache.getTokenCacheFromSession(context,
  673                                                  _VIEW_CACHE_KEY,
  674                                                  true,
  675                                                  _getCacheSize(context));
  676     }
  677   
  678     /**
  679      * Tests whether to send a small string token, or the entire
  680      * serialized component state to the client-side.
  681      * @return true, if the small string token is to be sent to the client-side.
  682      */
  683     private boolean _saveAsToken(FacesContext context)
  684     {
  685       ExternalContext external = context.getExternalContext();
  686       Object clientMethod =
  687         external.getInitParameterMap().get(CLIENT_STATE_METHOD_PARAM_NAME);
  688       if ((clientMethod != null) &&
  689           CLIENT_STATE_METHOD_ALL.equalsIgnoreCase((String) clientMethod))
  690         return false;
  691   
  692       return true;
  693     }
  694   
  695     private int _getCacheSize(FacesContext context)
  696     {
  697       ExternalContext external = context.getExternalContext();
  698       Object maxTokens =
  699         external.getInitParameterMap().get(CLIENT_STATE_MAX_TOKENS_PARAM_NAME);
  700       if (maxTokens != null)
  701       {
  702         try
  703         {
  704           return Math.max(1, Integer.parseInt((String) maxTokens));
  705         }
  706         catch (NumberFormatException nfe)
  707         {
  708           _LOG.warning("Ignoring servlet init parameter:"+CLIENT_STATE_MAX_TOKENS_PARAM_NAME+
  709             "\n unable to parse:"+maxTokens, nfe);
  710           _LOG.warning(nfe);
  711         }
  712       }
  713   
  714       return _DEFAULT_CACHE_SIZE;
  715     }
  716   
  717     //
  718     // @todo Map is a bad structure
  719     // @todo a static size is bad
  720     //
  721     @SuppressWarnings("unchecked")
  722     static private Map<String, Object> _getApplicationViewCache(FacesContext context)
  723     {
  724       synchronized (_APPLICATION_VIEW_CACHE_LOCK)
  725       {
  726         Map<String, Object> appMap = context.getExternalContext().getApplicationMap();
  727         Map<String, Object> cache = (Map<String, Object>) appMap.get(_APPLICATION_VIEW_CACHE_KEY);
  728         if (cache == null)
  729         {
  730           cache = new HashMap<String, Object>(128);
  731           appMap.put(_APPLICATION_VIEW_CACHE_KEY, cache);
  732         }
  733   
  734         return cache;
  735       }
  736     }
  737   
  738     @SuppressWarnings("unchecked")
  739     static private Map<String, Object> _getPerSessionApplicationViewCache(FacesContext context)
  740     {
  741       ExternalContext external = context.getExternalContext();
  742       Object session = external.getSession(true);
  743       assert(session != null);
  744   
  745       Map<String, Object> cache;
  746       // Synchronize on the session object to ensure that
  747       // we don't ever create two different caches
  748       synchronized (session)
  749       {
  750         Map<String, Object> sessionMap = external.getSessionMap();
  751         cache = (Map<String, Object>) sessionMap.get(_APPLICATION_VIEW_CACHE_KEY);
  752         if (cache == null)
  753         {
  754           cache = _createPerSessionApplicationViewCache();
  755           sessionMap.put(_APPLICATION_VIEW_CACHE_KEY, cache);
  756         }
  757       }
  758   
  759       return cache;
  760     }
  761   
  762     //
  763     // For the per-session mirror of the application view cache,
  764     // use an LRU LinkedHashMap to store the latest 16 pages.
  765     //
  766     static private Map<String, Object> _createPerSessionApplicationViewCache()
  767     {
  768       return new LRUCache<String, Object>(_MAX_PER_SESSION_APPLICATION_SIZE);
  769     }
  770   
  771     static private final int _MAX_PER_SESSION_APPLICATION_SIZE = 16;
  772   
  773     //
  774     // Use the application view cache if and only if:
  775     // (1) We're saving state tokens on the client
  776     // (2) This is *not* a postback request
  777     // (3) The feature has been explicitly enabled
  778     //
  779     private boolean _useApplicationViewCache(FacesContext context)
  780     {
  781       if (_useApplicationViewCache == Boolean.FALSE)
  782         return false;
  783   
  784       if (_saveAsToken(context) &&
  785           // Note: do not use TrinidadPhaseListener, as that
  786           // will return "true" even after navigation has occured,
  787           // but the Application View Cache is still fine.
  788           //!TrinidadPhaseListener.isPostback(context)
  789           !RequestContext.getCurrentInstance().isPostback())
  790       {
  791         if (_useApplicationViewCache == null)
  792         {
  793           String s = context.getExternalContext().getInitParameter(
  794                                   USE_APPLICATION_VIEW_CACHE_INIT_PARAM);
  795           _useApplicationViewCache =
  796             "true".equalsIgnoreCase(s) ? Boolean.TRUE : Boolean.FALSE;
  797         }
  798   
  799         return _useApplicationViewCache.booleanValue();
  800       }
  801   
  802       return false;
  803     }
  804   
  805     private boolean _useViewRootCache(FacesContext context)
  806     {
  807       if (_useViewRootCache == null)
  808       {
  809         String s = context.getExternalContext().getInitParameter(
  810                           CACHE_VIEW_ROOT_INIT_PARAM);
  811         _useViewRootCache =
  812         (!"false".equalsIgnoreCase(s)) ? Boolean.TRUE : Boolean.FALSE;
  813       }
  814   
  815       return _useViewRootCache.booleanValue();
  816     }
  817   
  818   
  819   
  820     private boolean _needStructure(FacesContext context)
  821     {
  822       if (_structureGeneratedByTemplate == null)
  823       {
  824         ExternalContext external = context.getExternalContext();
  825         String restoreMode = external.getInitParameter(
  826           FaceletViewHandler.PARAM_BUILD_BEFORE_RESTORE);
  827         if ("true".equals(restoreMode))
  828           _structureGeneratedByTemplate = Boolean.TRUE;
  829         else
  830           _structureGeneratedByTemplate = Boolean.FALSE;
  831       }
  832   
  833       return !_structureGeneratedByTemplate.booleanValue();
  834     }
  835   
  836     static private ResponseStateManager _getResponseStateManager(
  837       FacesContext context,
  838       String       renderKitId)
  839     {
  840       RenderKitFactory factory = (RenderKitFactory)
  841         FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
  842       RenderKit kit = factory.getRenderKit(context, renderKitId);
  843       return kit.getResponseStateManager();
  844     }
  845   
  846     @SuppressWarnings("unchecked")
  847     static private void _removeTransientComponents(
  848       UIComponent root)
  849     {
  850       List<UIComponent> components = new ArrayList<UIComponent>();
  851       _gatherTransientComponents(root, components);
  852       Iterator<UIComponent> iter = components.iterator();
  853       while (iter.hasNext())
  854       {
  855         UIComponent kid = iter.next();
  856         UIComponent parent = kid.getParent();
  857         // First, see if its a child
  858         if (parent.getChildCount() > 0)
  859         {
  860           List<UIComponent> children = parent.getChildren();
  861           if (children.remove(kid))
  862           {
  863             continue;
  864           }
  865         }
  866   
  867         // Nope, guess it's a facet
  868         // 2006-08-02: -= Simon Lessard
  869         //             Not 1.5 structure and inefficient loop
  870         //             values() is more efficient as you don't have 
  871         //             to do a second lookup for the value.
  872         Map<String, UIComponent> facets = parent.getFacets();
  873         for(Iterator<UIComponent> facetIter = facets.values().iterator(); 
  874             facetIter.hasNext();)
  875         {
  876           if(facetIter.next() == kid)
  877           {
  878             facetIter.remove();
  879             // FIXME: -= Simon Lessard
  880             //        Is that continue need to labeled to go all the way up to 
  881             //        the first while? Currently it won't cause any problem, but 
  882             //        it's a performance loss.
  883             continue;
  884           }
  885         }
  886   
  887         // Couldn't find the component at all in its parent:  that's
  888         // not good.
  889         assert false;
  890       }
  891     }
  892   
  893     @SuppressWarnings("unchecked")
  894     static private void _gatherTransientComponents(
  895       UIComponent component, List<UIComponent> componentsToRemove)
  896     {
  897       Iterator<UIComponent> kids = component.getFacetsAndChildren();
  898       while (kids.hasNext())
  899       {
  900         UIComponent kid = kids.next();
  901         // UIXComponentBase doesn't mind transient components
  902         // in its saved state, so don't bother with this.
  903         if (!(component instanceof UIXComponentBase) &&
  904             kid.isTransient())
  905         {
  906           componentsToRemove.add(kid);
  907         }
  908         else
  909         {
  910           _gatherTransientComponents(kid, componentsToRemove);
  911         }
  912       }
  913     }
  914   
  915     @SuppressWarnings("deprecation")
  916     private SerializedView _getCachedSerializedView(
  917       FacesContext context)
  918     {
  919       return (SerializedView) context.getExternalContext().
  920                    getRequestMap().get(_CACHED_SERIALIZED_VIEW);
  921     }
  922   
  923     @SuppressWarnings({"unchecked","deprecation"})
  924     private void _saveCachedSerializedView(
  925       FacesContext context, SerializedView state)
  926     {
  927       context.getExternalContext().getRequestMap().put(_CACHED_SERIALIZED_VIEW,
  928                                                        state);
  929     }
  930   
  931     private static final class PageState implements Serializable
  932     {
  933       private static final long serialVersionUID = 1L;
  934   
  935       private final Object _structure, _state;
  936       // use transient since UIViewRoots are not Serializable.
  937       private transient UIViewRoot _root;
  938       // If the UIViewRoot is lost, then this state is useless, so mark it
  939       // transient as well:
  940       private transient Object _viewRootState;
  941   
  942       public PageState(FacesContext fc, Object structure, Object state, UIViewRoot root)
  943       {
  944         _structure = structure;
  945         _state = state;
  946         _root = root;
  947         // we need this state, as we are going to recreate the UIViewRoot later. see
  948         // the popRoot() method:
  949         _viewRootState = (root != null) ? root.saveState(fc) : null;
  950       }
  951   
  952       public Object getStructure()
  953       {
  954         return _structure;
  955       }
  956   
  957       public Object getState()
  958       {
  959         return _state;
  960       }
  961   
  962       @SuppressWarnings("unchecked")
  963       public UIViewRoot popRoot(FacesContext fc)
  964       {
  965         UIViewRoot root = null;
  966         Object viewRootState = null;
  967         // we need to synchronize because we are mutating _root
  968         // which is shared between simultaneous requests from the same user:
  969         synchronized(this)
  970         {
  971           if (_root != null)
  972           {
  973             root = _root;
  974             viewRootState = _viewRootState;
  975             // we must clear the cached viewRoot. This is because UIComponent trees
  976             // are mutable and if the back button
  977             // is used to come back to an old PageState, then it would be
  978             // really bad if we reused that component tree:
  979             _root = null;
  980             _viewRootState = null;
  981           }
  982         }
  983         
  984         if (root != null)
  985         {
  986           // If an error happens during updateModel, JSF 1.1 does not
  987           // clear FacesEvents (or FacesMessages, IIRC), so any pending
  988           // events will still be present, on the subsequent request.
  989           // so to clear the events, we create a new UIViewRoot.
  990           // must get the UIViewRoot from the application so that
  991           // we pick up any custom ViewRoot defined in faces-config.xml:
  992           UIViewRoot newRoot = (UIViewRoot) 
  993             fc.getApplication().createComponent(UIViewRoot.COMPONENT_TYPE);
  994           
  995           //This code handles automatic namespacing in a JSR-301 environment
  996           if(ExternalContextUtils.isPortlet(fc.getExternalContext())) 
  997           {
  998             //To avoid introducing a runtime dependency on the bridge,
  999             //this method should only be executed when we have a portlet
 1000             //request.  If we do have a portlet request then the bridge
 1001             //should be available anyway.
 1002             newRoot = PortletUtils.getPortletViewRoot(newRoot);
 1003           }
 1004   
 1005           
 1006           // must call restoreState so that we setup attributes, listeners,
 1007           // uniqueIds, etc ...
 1008           newRoot.restoreState(fc, viewRootState);
 1009   
 1010           // we need to use a temp list because as a side effect of
 1011           // adding a child to a UIComponent, that child is removed from
 1012           // the parent UIComponent. So the following will break:
 1013           // newRoot.getChildren().addAll(root.getChildren());
 1014           // because "root"'s child List is being mutated as the List
 1015           // is traversed.
 1016           List<UIComponent> temp = new ArrayList<UIComponent>(root.getChildCount());
 1017           temp.addAll(root.getChildren());
 1018           newRoot.getChildren().addAll(temp);
 1019           return newRoot;
 1020         }
 1021         
 1022         return null;
 1023       }    
 1024     }
 1025   
 1026     /* =-=AEW A utility function to dump out all the state that's getting
 1027        sent over.  To compile, you need to make most of TreeState public */
 1028     /*
 1029     static private void _dumpState(Object state, int depth)
 1030     {
 1031       String out = _SPACES.substring(0, depth * 2);
 1032       Object objectState = state;
 1033   
 1034       if (state instanceof org.apache.myfaces.trinidad.component.TreeState)
 1035         objectState = ((org.apache.myfaces.trinidad.component.TreeState) state)._state;
 1036   
 1037       if (objectState == null)
 1038         out += "null";
 1039       else if (objectState instanceof Object[])
 1040         out += "array";
 1041       else
 1042         out += objectState.toString() + "(" + objectState.getClass() + ")";
 1043   
 1044       if (objectState instanceof Object[])
 1045       {
 1046         Object[] array = (Object[]) objectState;
 1047         for (int i = 0; i < array.length; i++)
 1048           _dumpState(array[i], depth + 1);
 1049       }
 1050   
 1051   
 1052       if (state instanceof org.apache.myfaces.trinidad.component.TreeState)
 1053       {
 1054         Object[] array = ((org.apache.myfaces.trinidad.component.TreeState)state)._children;
 1055         if (array != null)
 1056         {
 1057           for (int i = 0; i < array.length; i++)
 1058             _dumpState(array[i], depth + 1);
 1059         }
 1060   
 1061         array = ((org.apache.myfaces.trinidad.component.TreeState)state)._facets;
 1062         if (array != null)
 1063         {
 1064           for (int i = 0; i < array.length; i++)
 1065             _dumpState(array[i], depth + 1);
 1066         }
 1067       }
 1068     }
 1069   
 1070     static private final String _SPACES = "                                                                            ";
 1071     */
 1072   
 1073     private final StateManager _delegate;
 1074     private       Boolean      _useViewRootCache;
 1075     private       Boolean      _useApplicationViewCache;
 1076     private       Boolean      _structureGeneratedByTemplate;
 1077   
 1078   
 1079     private static final int _DEFAULT_CACHE_SIZE = 15;
 1080   
 1081     private static final Object _APPLICATION_VIEW_CACHE_LOCK = new Object();
 1082     private static final String _VIEW_CACHE_KEY =
 1083       "org.apache.myfaces.trinidadinternal.application.VIEW_CACHE";
 1084   
 1085     private static final String _APPLICATION_VIEW_CACHE_KEY =
 1086       "org.apache.myfaces.trinidadinternal.application.APPLICATION_VIEW_CACHE";
 1087   
 1088     private static final String _CACHED_SERIALIZED_VIEW =
 1089       "org.apache.myfaces.trinidadinternal.application.CachedSerializedView";
 1090   
 1091     private static final String _REQUEST_STATE_TOKEN_KEY =
 1092       "org.apache.myfaces.trinidadinternal.application.REQUEST_STATE_TOKEN";
 1093   
 1094     private static final String _PINNED_STATE_TOKEN_KEY =
 1095       "org.apache.myfaces.trinidadinternal.application.PINNED_STATE_TOKEN";
 1096   
 1097     private static final String _REUSE_REQUEST_TOKEN_FOR_RESPONSE_KEY =
 1098       "org.apache.myfaces.trinidadinternal.application.REUSE_REQUEST_TOKEN_FOR_RESPONSE";
 1099   
 1100   
 1101     private static final String _APPLICATION_CACHE_TOKEN = "_a_";
 1102   
 1103     private static final long serialVersionUID = 1L;
 1104   
 1105     private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(StateManagerImpl.class);
 1106   }

Save This Page
Home » trinidad-1.2.8-src-all » org.apache.myfaces.trinidadinternal » application » [javadoc | source]