Coverage Report - org.apache.myfaces.shared.context.flash.FlashImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
FlashImpl
77%
265/340
56%
91/160
2.72
FlashImpl$MessageEntry
100%
5/5
N/A
2.72
 
 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.shared.context.flash;
 20  
 
 21  
 import org.apache.myfaces.shared.util.SubKeyMap;
 22  
 import org.apache.myfaces.shared.util.ExternalContextUtils;
 23  
 
 24  
 import javax.faces.application.FacesMessage;
 25  
 import javax.faces.context.ExternalContext;
 26  
 import javax.faces.context.FacesContext;
 27  
 import javax.faces.context.Flash;
 28  
 import javax.faces.event.PhaseId;
 29  
 import javax.servlet.http.Cookie;
 30  
 import javax.servlet.http.HttpServletResponse;
 31  
 import java.io.Serializable;
 32  
 import java.math.BigInteger;
 33  
 import java.security.NoSuchAlgorithmException;
 34  
 import java.security.SecureRandom;
 35  
 import java.util.ArrayList;
 36  
 import java.util.Collection;
 37  
 import java.util.Collections;
 38  
 import java.util.Iterator;
 39  
 import java.util.List;
 40  
 import java.util.Map;
 41  
 import java.util.Set;
 42  
 import java.util.concurrent.atomic.AtomicLong;
 43  
 import java.util.logging.Logger;
 44  
 import javax.faces.event.PostKeepFlashValueEvent;
 45  
 import javax.faces.event.PostPutFlashValueEvent;
 46  
 import javax.faces.event.PreClearFlashEvent;
 47  
 import javax.faces.event.PreRemoveFlashValueEvent;
 48  
 import javax.faces.lifecycle.ClientWindow;
 49  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 50  
 import org.apache.myfaces.shared.util.ServletSpecifications;
 51  
 
 52  
 /**
 53  
  * Implementation of Flash object
 54  
  */
 55  0
 public class FlashImpl extends Flash implements ReleasableFlash
 56  
 {
 57  
     
 58  
     // ~ static fields --------------------------------------------------------
 59  
     
 60  1
     private static final Logger log = Logger.getLogger(FlashImpl.class.getName());
 61  
     
 62  
     /**
 63  
      * Use this prefix instead of the whole class name, because
 64  
      * this makes the Cookies and the SubKeyMap operations (actually
 65  
      * every String based operation where this is used as a key) faster.
 66  
      */
 67  
     private static final String FLASH_PREFIX = "oam.Flash";
 68  
     
 69  
     /**
 70  
      * Key on application map to keep current instance
 71  
      */
 72  
     static final String FLASH_INSTANCE = FLASH_PREFIX + ".INSTANCE";
 73  
 
 74  
     /**
 75  
      * Key to store if this setRedirect(true) was called on this request,
 76  
      * and to store the redirect Cookie.
 77  
      */
 78  
     static final String FLASH_REDIRECT = FLASH_PREFIX + ".REDIRECT";
 79  
     
 80  
     /**
 81  
      * Key to store the value of the redirect cookie
 82  
      */
 83  
     static final String FLASH_PREVIOUS_REQUEST_REDIRECT 
 84  
             = FLASH_PREFIX + ".REDIRECT.PREVIOUSREQUEST";
 85  
     
 86  
     /**
 87  
      * Key used to check if this request should keep messages
 88  
      */
 89  
     static final String FLASH_KEEP_MESSAGES = FLASH_PREFIX + ".KEEP_MESSAGES";
 90  
 
 91  
     /**
 92  
      * Key used to store the messages list in the render FlashMap.
 93  
      */
 94  
     static final String FLASH_KEEP_MESSAGES_LIST = "KEEPMESSAGESLIST";
 95  
 
 96  
     /**
 97  
      * Session map prefix to flash maps
 98  
      */
 99  
     static final String FLASH_SESSION_MAP_SUBKEY_PREFIX = FLASH_PREFIX + ".SCOPE";
 100  
     
 101  
     /**
 102  
      * Key for the cached render FlashMap instance on the request map.
 103  
      */
 104  
     static final String FLASH_RENDER_MAP = FLASH_PREFIX + ".RENDERMAP";
 105  
     
 106  
     /**
 107  
      * Key for the current render FlashMap token.
 108  
      */
 109  
     static final String FLASH_RENDER_MAP_TOKEN = FLASH_PREFIX + ".RENDERMAP.TOKEN";
 110  
     
 111  
     /**
 112  
      * Key for the cached execute FlashMap instance on the request map.
 113  
      */
 114  
     static final String FLASH_EXECUTE_MAP = FLASH_PREFIX + ".EXECUTEMAP";
 115  
 
 116  
     /**
 117  
      * Key for the current execute FlashMap token.
 118  
      */
 119  
     static final String FLASH_EXECUTE_MAP_TOKEN = FLASH_PREFIX + ".EXECUTEMAP.TOKEN";
 120  
     
 121  
     static final String FLASH_CW_LRU_MAP = FLASH_PREFIX + ".CW.LRUMAP";
 122  
     
 123  
     /**
 124  
      * Token separator.
 125  
      */
 126  
     static final char SEPARATOR_CHAR = '.';
 127  
     
 128  
      // ~ static methods  -----------------------------------------------------
 129  
     
 130  
     /**
 131  
      * Return a Flash instance from the application map
 132  
      * 
 133  
      * @param context
 134  
      * @return
 135  
      */
 136  
     public static Flash getCurrentInstance(ExternalContext context)
 137  
     {
 138  13
         return getCurrentInstance(context, true);
 139  
     }
 140  
     
 141  
     public static Flash getCurrentInstance(ExternalContext context, boolean create)
 142  
     {
 143  13
         Map<String, Object> applicationMap = context.getApplicationMap();
 144  
         
 145  13
         Flash flash = (Flash) applicationMap.get(FLASH_INSTANCE);
 146  13
         if (flash == null && create)
 147  
         {
 148  
             // synchronize the ApplicationMap to ensure that only
 149  
             // once instance of FlashImpl is created and stored in it.
 150  13
             synchronized (applicationMap)
 151  
             {
 152  
                 // check again, because first try was un-synchronized
 153  13
                 flash = (Flash) applicationMap.get(FLASH_INSTANCE);
 154  13
                 if (flash == null)
 155  
                 {
 156  13
                     flash = new FlashImpl(context);
 157  13
                     applicationMap.put(FLASH_INSTANCE, flash);
 158  
                 }
 159  13
             }
 160  
         }
 161  
 
 162  13
         return flash;
 163  
     }
 164  
 
 165  
     /**
 166  
      * Returns a cryptographically secure random number to use as the _count seed
 167  
      */
 168  
     private static long _getSeed()
 169  
     {
 170  
         SecureRandom rng;
 171  
         try
 172  
         {
 173  
             // try SHA1 first
 174  13
             rng = SecureRandom.getInstance("SHA1PRNG");
 175  
         }
 176  0
         catch (NoSuchAlgorithmException e)
 177  
         {
 178  
             // SHA1 not present, so try the default (which could potentially not be
 179  
             // cryptographically secure)
 180  0
             rng = new SecureRandom();
 181  13
         }
 182  
 
 183  
         // use 48 bits for strength and fill them in
 184  13
         byte[] randomBytes = new byte[6];
 185  13
         rng.nextBytes(randomBytes);
 186  
 
 187  
         // convert to a long
 188  13
         return new BigInteger(randomBytes).longValue();
 189  
     }
 190  
     
 191  
     // ~ private fields and constructor ---------------------------------------
 192  
     
 193  
     // the current token value
 194  
     private final AtomicLong _count;
 195  
     private boolean _flashScopeDisabled;
 196  
     
 197  
     public FlashImpl(ExternalContext externalContext)
 198  13
     {
 199  13
         _count = new AtomicLong(_getSeed());
 200  
 
 201  
         // Read whether flash scope is disabled.
 202  13
         _flashScopeDisabled = MyfacesConfig.getCurrentInstance(externalContext).isFlashScopeDisabled();
 203  13
     }
 204  
     
 205  
     // ~ methods from javax.faces.context.Flash -------------------------------
 206  
 
 207  
     /**
 208  
      * Used to restore the redirect value and the FacesMessages of the previous 
 209  
      * request and to manage the flashMap tokens for this request before phase
 210  
      * restore view starts.
 211  
      */
 212  
     @Override
 213  
     public void doPrePhaseActions(FacesContext facesContext)
 214  
     {
 215  29
         if (!_flashScopeDisabled)
 216  
         {
 217  29
             final PhaseId currentPhaseId = facesContext.getCurrentPhaseId();
 218  
         
 219  29
             if (PhaseId.RESTORE_VIEW.equals(currentPhaseId))
 220  
             {
 221  
                 // restore the redirect value
 222  
                 // note that the result of this method is used in many places, 
 223  
                 // thus it has to be the first thing to do
 224  29
                 _restoreRedirectValue(facesContext);
 225  
             
 226  
                 // restore the FlashMap token from the previous request
 227  
                 // and create a new token for this request
 228  29
                 _manageFlashMapTokens(facesContext);
 229  
             
 230  
                 // try to restore any saved messages
 231  29
                 _restoreMessages(facesContext);
 232  
             }
 233  
         }
 234  29
     }
 235  
     
 236  
     /**
 237  
      * Used to destroy the executeMap and to save all FacesMessages for the
 238  
      * next request, but only if this is the last invocation of this method
 239  
      * in the current lifecycle (if redirect phase 5, otherwise phase 6).
 240  
      */
 241  
     @Override
 242  
     public void doPostPhaseActions(FacesContext facesContext)
 243  
     {
 244  28
         if (!_flashScopeDisabled)
 245  
         {
 246  
             // do the actions only if this is the last time
 247  
             // doPostPhaseActions() is called on this request
 248  28
             if (_isLastPhaseInRequest(facesContext))
 249  
             {
 250  27
                 if (_isRedirectTrueOnThisRequest(facesContext))
 251  
                 {
 252  
                     // copy entries from executeMap to renderMap, if they do not exist
 253  2
                     Map<String, Object> renderMap = _getRenderFlashMap(facesContext);
 254  
 
 255  
                     for (Map.Entry<String, Object> entry 
 256  2
                         : _getExecuteFlashMap(facesContext).entrySet())
 257  
                     {
 258  0
                         if (!renderMap.containsKey(entry.getKey()))
 259  
                         {
 260  0
                             renderMap.put(entry.getKey(), entry.getValue());
 261  
                         }
 262  0
                     }
 263  
                 }
 264  
             
 265  
                 // remove execute Map entries from session (--> "destroy" executeMap)
 266  27
                 _clearExecuteFlashMap(facesContext);
 267  
             
 268  
                 // save the current FacesMessages in the renderMap, if wanted
 269  
                 // Note that this also works on a redirect even though the redirect
 270  
                 // was already performed and the response has already been committed,
 271  
                 // because the renderMap is stored in the session.
 272  27
                 _saveMessages(facesContext);
 273  
                 
 274  27
                 _clearRenderFlashTokenIfMapEmpty(facesContext);
 275  
             }
 276  
         }
 277  28
     }
 278  
     
 279  
     /**
 280  
      * Return the value of this property for the flash for this session.
 281  
      * 
 282  
      * This must be false unless:
 283  
      *   - setRedirect(boolean) was called for the current lifecycle traversal
 284  
      *     with true as the argument.
 285  
      *   - The current lifecycle traversal for this session is in the "execute"
 286  
      *     phase and the previous traversal had setRedirect(boolean) called with
 287  
      *     true as the argument.
 288  
      */
 289  
     @Override
 290  
     public boolean isRedirect()
 291  
     {
 292  10
         FacesContext facesContext = FacesContext.getCurrentInstance();
 293  10
         boolean thisRedirect = _isRedirectTrueOnThisRequest(facesContext);
 294  10
         boolean prevRedirect = _isRedirectTrueOnPreviousRequest(facesContext);
 295  10
         boolean executePhase = !PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId());
 296  
         
 297  10
         return thisRedirect || (executePhase && prevRedirect);
 298  
     }
 299  
     
 300  
     @Override
 301  
     public void setRedirect(boolean redirect)
 302  
     {
 303  
         // FIXME this method has a design flaw, because the only valid value
 304  
         // is true and it should only be called by the NavigationHandler
 305  
         // in a redirect case RIGHT BEFORE ExternalContext.redirect().
 306  
         // Maybe a PreRedirectEvent issued by the ExternalContext would be a good
 307  
         // choice for JSF 2.1.
 308  
         
 309  4
         FacesContext facesContext = FacesContext.getCurrentInstance();
 310  4
         ExternalContext externalContext = facesContext.getExternalContext();
 311  4
         Map<String, Object> requestMap = externalContext.getRequestMap();
 312  
         
 313  
         // save the value on the requestMap for this request
 314  4
         Boolean alreadySet = (Boolean) requestMap.get(FLASH_REDIRECT);
 315  4
         alreadySet = (alreadySet == null ? Boolean.FALSE : Boolean.TRUE);
 316  
         
 317  
         // if true and not already set, store it for the following request
 318  4
         if (!alreadySet && redirect)
 319  
         {
 320  4
             requestMap.put(FLASH_REDIRECT, Boolean.TRUE);
 321  
             
 322  
             // save redirect=true for the next request
 323  4
             _saveRedirectValue(facesContext);
 324  
         }
 325  
         else
 326  
         {
 327  0
             if (alreadySet)
 328  
             {
 329  0
                 log.warning("Multiple call to setRedirect() ignored.");
 330  
             }
 331  
             else // redirect = false
 332  
             {
 333  0
                 log.warning("Ignored call to setRedirect(false), because this "
 334  
                         + "should only be set to true by the NavigationHandler. "
 335  
                         + "No one else should change it.");
 336  
             }
 337  
         }
 338  4
     }
 339  
     
 340  
     /**
 341  
      * Take a value from the requestMap, or if it does not exist from the
 342  
      * execute FlashMap, and put it on the render FlashMap, so it is visible on
 343  
      * the next request.
 344  
      */
 345  
     @Override
 346  
     public void keep(String key)
 347  
     {
 348  4
         _checkFlashScopeDisabled();
 349  4
         FacesContext facesContext = FacesContext.getCurrentInstance();
 350  4
         Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
 351  4
         Object value = requestMap.get(key);
 352  
         
 353  
         // if the key does not exist in the requestMap,
 354  
         // try to get it from the execute FlashMap
 355  4
         if (value == null)
 356  
         {
 357  1
             Map<String, Object> executeMap = _getExecuteFlashMap(facesContext);
 358  
             // Null-check, because in the GET request of a POST-REDIRECT-GET 
 359  
             // pattern there is no execute map
 360  1
             if (executeMap != null)
 361  
             {
 362  1
                 value = executeMap.get(key);
 363  
                 // Store it on request map so we can get it later. For example, 
 364  
                 // this is used by org.apache.myfaces.el.FlashELResolver to return
 365  
                 // the value that has been promoted.
 366  1
                 requestMap.put(key, value);
 367  
             }
 368  
         }
 369  
         
 370  
         // put it in the render FlashMap
 371  4
         _getRenderFlashMap(facesContext).put(key, value);
 372  
         
 373  4
         facesContext.getApplication().publishEvent(facesContext,
 374  
                 PostKeepFlashValueEvent.class, key);
 375  4
     }
 376  
 
 377  
     /**
 378  
      * This is just an alias for the request scope map.
 379  
      */
 380  
     @Override
 381  
     public void putNow(String key, Object value)
 382  
     {
 383  5
         _checkFlashScopeDisabled();
 384  5
         FacesContext.getCurrentInstance().getExternalContext()
 385  
                 .getRequestMap().put(key, value);
 386  5
     }
 387  
 
 388  
     /**
 389  
      * Returns the value of a previous call to setKeepMessages() from this
 390  
      * request. If there was no call yet, false is returned.
 391  
      */
 392  
     @Override
 393  
     public boolean isKeepMessages()
 394  
     {
 395  38
         FacesContext facesContext = FacesContext.getCurrentInstance();
 396  38
         Boolean keepMessages = null;
 397  38
         if (facesContext != null)
 398  
         {
 399  38
             ExternalContext externalContext = facesContext.getExternalContext();
 400  38
             Map<String, Object> requestMap = externalContext.getRequestMap();
 401  38
             keepMessages = (Boolean) requestMap.get(FLASH_KEEP_MESSAGES);
 402  
         }
 403  
         
 404  38
         return (keepMessages == null ? Boolean.FALSE : keepMessages);
 405  
     }
 406  
     
 407  
     /**
 408  
      * If this property is true, the messages should be kept for the next 
 409  
      * request, no matter if it is a normal postback case or a POST-
 410  
      * REDIRECT-GET case.
 411  
      * 
 412  
      * Note that we don't have to store this value for the next request
 413  
      * (like setRedirect()), because we will know if it was true on the 
 414  
      * next request, if we can find any stored messages in the FlashMap.
 415  
      * (also see _saveMessages() and _restoreMessages()).
 416  
      */
 417  
     @Override
 418  
     public void setKeepMessages(boolean keepMessages)
 419  
     {
 420  5
         FacesContext facesContext = FacesContext.getCurrentInstance();
 421  5
         ExternalContext externalContext = facesContext.getExternalContext();
 422  5
         Map<String, Object> requestMap = externalContext.getRequestMap();
 423  5
         requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
 424  5
     }
 425  
     
 426  
     // ~ Methods from Map interface -------------------------------------------
 427  
     
 428  
     // NOTE that all these methods do not necessarily delegate to the same Map,
 429  
     // because we differentiate between reading and writing operations.
 430  
     
 431  
     public void clear()
 432  
     {
 433  1
         _checkFlashScopeDisabled();
 434  1
         _getFlashMapForWriting().clear();
 435  1
     }
 436  
 
 437  
     public boolean containsKey(Object key)
 438  
     {
 439  7
         _checkFlashScopeDisabled();
 440  7
         return _getFlashMapForReading().containsKey(key);
 441  
     }
 442  
 
 443  
     public boolean containsValue(Object value)
 444  
     {
 445  7
         _checkFlashScopeDisabled();
 446  7
         return _getFlashMapForReading().containsValue(value);
 447  
     }
 448  
 
 449  
     public Set<java.util.Map.Entry<String, Object>> entrySet()
 450  
     {
 451  0
         _checkFlashScopeDisabled();
 452  0
         return _getFlashMapForReading().entrySet();
 453  
     }
 454  
 
 455  
     public Object get(Object key)
 456  
     {
 457  19
         _checkFlashScopeDisabled();
 458  19
         if (key == null)
 459  
         {
 460  0
             return null;
 461  
         }
 462  
         
 463  19
         if ("keepMessages".equals(key))
 464  
         {
 465  2
             return isKeepMessages();
 466  
         }
 467  17
         else if ("redirect".equals(key))
 468  
         {
 469  2
             return isRedirect();
 470  
         }
 471  
         
 472  15
         return _getFlashMapForReading().get(key);
 473  
     }
 474  
     
 475  
     public boolean isEmpty()
 476  
     {
 477  7
         _checkFlashScopeDisabled();
 478  7
         return _getFlashMapForReading().isEmpty();
 479  
     }
 480  
 
 481  
     public Set<String> keySet()
 482  
     {
 483  8
         _checkFlashScopeDisabled();
 484  8
         return _getFlashMapForReading().keySet();
 485  
     }
 486  
 
 487  
     public Object put(String key, Object value)
 488  
     {
 489  6
         _checkFlashScopeDisabled();
 490  6
         if (key == null)
 491  
         {
 492  0
             return null;
 493  
         }
 494  
         
 495  6
         if ("keepMessages".equals(key))
 496  
         {
 497  1
             Boolean booleanValue = _convertToBoolean(value);
 498  1
             this.setKeepMessages(booleanValue);
 499  1
             return booleanValue;
 500  
         }
 501  5
         else if ("redirect".equals(key))
 502  
         {
 503  1
             Boolean booleanValue = _convertToBoolean(value);
 504  1
             this.setRedirect(booleanValue);
 505  1
             return booleanValue;
 506  
         }
 507  
         else
 508  
         {
 509  4
             Object resp = _getFlashMapForWriting().put(key, value); 
 510  
             
 511  4
             FacesContext facesContext = FacesContext.getCurrentInstance();
 512  4
             facesContext.getApplication().publishEvent(facesContext,
 513  
                 PostPutFlashValueEvent.class, key);
 514  
 
 515  4
             return resp;
 516  
         }
 517  
     }
 518  
 
 519  
     public void putAll(Map<? extends String, ? extends Object> m)
 520  
     {
 521  1
         _checkFlashScopeDisabled();
 522  1
         _getFlashMapForWriting().putAll(m);
 523  1
     }
 524  
 
 525  
     public Object remove(Object key)
 526  
     {
 527  1
         _checkFlashScopeDisabled();
 528  
         
 529  1
         FacesContext facesContext = FacesContext.getCurrentInstance();
 530  1
         facesContext.getApplication().publishEvent(facesContext,
 531  
             PreRemoveFlashValueEvent.class, key);
 532  
         
 533  1
         return _getFlashMapForWriting().remove(key);
 534  
     }
 535  
 
 536  
     public int size()
 537  
     {
 538  7
         _checkFlashScopeDisabled();
 539  7
         return _getFlashMapForReading().size();
 540  
     }
 541  
 
 542  
     public Collection<Object> values()
 543  
     {
 544  7
         _checkFlashScopeDisabled();
 545  7
         return _getFlashMapForReading().values();
 546  
     }
 547  
     
 548  
     // ~ Implementation methods ----------------------------------------------- 
 549  
     
 550  
     /**
 551  
      * Returns true if the current phase is the last phase in the request
 552  
      * and thus if doPostPhaseActions() is called for the last time.
 553  
      * 
 554  
      * This will be true if either we are in phase 6 (render response)
 555  
      * or if setRedirect(true) was called on this request and we are
 556  
      * in phase 5 (invoke application).
 557  
      */
 558  
     private boolean _isLastPhaseInRequest(FacesContext facesContext)
 559  
     {
 560  28
         final PhaseId currentPhaseId = facesContext.getCurrentPhaseId();
 561  
         
 562  28
         boolean lastPhaseNormalRequest = PhaseId.RENDER_RESPONSE.equals(currentPhaseId);
 563  
         // According to the spec, if there is a redirect, responseComplete() 
 564  
         // has been called, and Flash.setRedirect() has been called too,
 565  
         // so we just need to check both are present.
 566  28
         boolean lastPhaseIfRedirect = facesContext.getResponseComplete()
 567  
                 && _isRedirectTrueOnThisRequest(facesContext);
 568  
         
 569  28
         return lastPhaseNormalRequest || lastPhaseIfRedirect;
 570  
     }
 571  
     
 572  
     /**
 573  
      * Return true if setRedirect(true) was called on this request.
 574  
      * @param facesContext
 575  
      * @return
 576  
      */
 577  
     private boolean _isRedirectTrueOnThisRequest(FacesContext facesContext)
 578  
     {
 579  40
         ExternalContext externalContext = facesContext.getExternalContext();
 580  40
         Map<String, Object> requestMap = externalContext.getRequestMap();
 581  40
         Boolean redirect = (Boolean) requestMap.get(FLASH_REDIRECT);
 582  
         
 583  40
         return Boolean.TRUE.equals(redirect);
 584  
     }
 585  
     
 586  
     /**
 587  
      * Return true if setRedirect(true) was called on the previous request.
 588  
      * Precondition: doPrePhaseActions() must have been called on restore view phase.
 589  
      * @param facesContext
 590  
      * @return
 591  
      */
 592  
     private boolean _isRedirectTrueOnPreviousRequest(FacesContext facesContext)
 593  
     {
 594  10
         ExternalContext externalContext = facesContext.getExternalContext();
 595  10
         Map<String, Object> requestMap = externalContext.getRequestMap();
 596  10
         Boolean redirect = (Boolean) requestMap.get(FLASH_PREVIOUS_REQUEST_REDIRECT);
 597  
         
 598  10
         return Boolean.TRUE.equals(redirect);
 599  
     }
 600  
     
 601  
     /**
 602  
      * Saves the value of setRedirect() for the next request, if it was true
 603  
      */
 604  
     private void _saveRedirectValue(FacesContext facesContext)
 605  
     {
 606  4
         ExternalContext externalContext = facesContext.getExternalContext();
 607  
         
 608  
         // This request contains a redirect. This condition is in general 
 609  
         // triggered by a NavigationHandler. After a redirect all request scope
 610  
         // values get lost, so in order to preserve this value we need to
 611  
         // pass it between request. One strategy is use a cookie that is never sent
 612  
         // to the client. Other alternative is use the session map.
 613  
         // See _restoreRedirectValue() for restoring this value.
 614  4
         HttpServletResponse httpResponse = ExternalContextUtils
 615  
                 .getHttpServletResponse(externalContext);
 616  4
         if (httpResponse != null)
 617  
         {
 618  4
             Cookie cookie = _createFlashCookie(FLASH_REDIRECT, "true", externalContext);
 619  4
             httpResponse.addCookie(cookie);
 620  4
         }
 621  
         else
 622  
         {
 623  0
             externalContext.getSessionMap().put(FLASH_REDIRECT, true);
 624  
         }
 625  4
     }
 626  
 
 627  
     /**
 628  
      * Restores the redirect value of the previous request and saves
 629  
      * it in the RequestMap under the key FLASH_PREVIOUS_REQUEST_REDIRECT.
 630  
      * Must not be called more than once per request.
 631  
      * After this method was invoked, the requestMap will contain Boolean.TRUE
 632  
      * if setRedirect(true) was called on the previous request or Boolean.FALSE
 633  
      * or null otherwise.
 634  
      */
 635  
     private void _restoreRedirectValue(FacesContext facesContext)
 636  
     {
 637  29
         ExternalContext externalContext = facesContext.getExternalContext();
 638  29
         HttpServletResponse httpResponse = ExternalContextUtils
 639  
                 .getHttpServletResponse(externalContext);
 640  29
         if (httpResponse != null)
 641  
         {
 642  
             // Request values are lost after a redirect. We can create a 
 643  
             // temporal cookie to pass the params between redirect calls.
 644  
             // It is better than use HttpSession object, because this cookie
 645  
             // is never sent by the server.
 646  29
             Cookie cookie = (Cookie) externalContext
 647  
                     .getRequestCookieMap().get(FLASH_REDIRECT);
 648  29
             if (cookie != null)
 649  
             {
 650  
                 // the cookie exists means there was a redirect, regardless of the value
 651  3
                 externalContext.getRequestMap().put(
 652  
                         FLASH_PREVIOUS_REQUEST_REDIRECT, Boolean.TRUE);
 653  
                 
 654  
                 // A redirect happened, so it is safe to remove the cookie, setting
 655  
                 // the maxAge to 0 seconds. The effect is we passed FLASH_REDIRECT param 
 656  
                 // to this request object
 657  3
                 cookie.setMaxAge(0);
 658  3
                 cookie.setPath(_getCookiePath(externalContext));
 659  
                 //MYFACES-3354 jetty 6.1.5 does not allow this,
 660  
                 //call setMaxAge(0) is enough
 661  
                 //cookie.setValue(null);
 662  3
                 httpResponse.addCookie(cookie);
 663  
             }
 664  29
         }
 665  
         else
 666  
         {
 667  
             // Note that on portlet world we can't create cookies,
 668  
             // so we are forced to use the session map. Anyway, 
 669  
             // according to the Bridge implementation(for example see 
 670  
             // org.apache.myfaces.portlet.faces.bridge.BridgeImpl)
 671  
             // session object is created at start faces request
 672  0
             Map<String, Object> sessionMap = externalContext.getSessionMap();
 673  
             
 674  
             // remove the value from the sessionMap
 675  0
             Boolean redirect = (Boolean) sessionMap.remove(FLASH_REDIRECT);
 676  
             
 677  
             // put the value into the requestMap
 678  0
             externalContext.getRequestMap().put(
 679  
                     FLASH_PREVIOUS_REQUEST_REDIRECT, redirect);
 680  
         }
 681  29
     }
 682  
     
 683  
     /**
 684  
      * Saves the current FacesMessages as a List on the render FlashMap for the
 685  
      * next request if isKeepMessages() is true. Otherwise it removes any
 686  
      * existing FacesMessages-List from the renderFlashMap. 
 687  
      * @param facesContext
 688  
      */
 689  
     private void _saveMessages(FacesContext facesContext)
 690  
     {
 691  27
         if (isKeepMessages()) 
 692  
         {
 693  
             // get all messages from the FacesContext and store
 694  
             // them on the renderMap
 695  4
             List<MessageEntry> messageList = null;
 696  
                 
 697  4
             Iterator<String> iterClientIds = facesContext.getClientIdsWithMessages();
 698  12
             while (iterClientIds.hasNext())
 699  
             {
 700  8
                 String clientId = (String) iterClientIds.next();
 701  8
                 Iterator<FacesMessage> iterMessages = facesContext.getMessages(clientId);
 702  
                 
 703  16
                 while (iterMessages.hasNext())
 704  
                 {
 705  8
                     FacesMessage message = iterMessages.next();
 706  
     
 707  8
                     if (messageList == null)
 708  
                     {
 709  4
                         messageList = new ArrayList<MessageEntry>();
 710  
                     }
 711  8
                     messageList.add(new MessageEntry(clientId, message));
 712  8
                 }
 713  8
             }
 714  
     
 715  4
             _getRenderFlashMap(facesContext).put(FLASH_KEEP_MESSAGES_LIST, messageList);
 716  4
         }
 717  
         else
 718  
         {
 719  
             // do not keep messages --> remove messagesList from renderMap
 720  23
             _getRenderFlashMap(facesContext).remove(FLASH_KEEP_MESSAGES_LIST);
 721  
         }
 722  27
     }
 723  
 
 724  
     /**
 725  
      * Restore any saved FacesMessages from the previous request.
 726  
      * Note that we don't need to save the keepMessages value for this request,
 727  
      * because we just have to check if the value for FLASH_KEEP_MESSAGES_LIST exists.
 728  
      * @param facesContext
 729  
      */
 730  
     @SuppressWarnings("unchecked")
 731  
     private void _restoreMessages(FacesContext facesContext)
 732  
     {
 733  29
         List<MessageEntry> messageList = (List<MessageEntry>) 
 734  
                 _getExecuteFlashMap(facesContext).get(FLASH_KEEP_MESSAGES_LIST);
 735  
 
 736  29
         if (messageList != null)
 737  
         {
 738  4
             Iterator<MessageEntry> iterMessages = messageList.iterator();
 739  
 
 740  12
             while (iterMessages.hasNext())
 741  
             {
 742  8
                 MessageEntry entry = iterMessages.next();
 743  8
                 facesContext.addMessage(entry.clientId, entry.message);
 744  8
             }
 745  
 
 746  
             // we can now remove the messagesList from the flashMap
 747  4
             _getExecuteFlashMap(facesContext).remove(FLASH_KEEP_MESSAGES_LIST);
 748  
         }
 749  29
     }
 750  
     
 751  
     /**
 752  
      * Take the render map key and store it as a key for the next request.
 753  
      * 
 754  
      * On the next request we can get it with _getRenderFlashMapTokenFromPreviousRequest().
 755  
      * @param facesContext
 756  
      */
 757  
     private void _saveRenderFlashMapTokenForNextRequest(FacesContext facesContext)
 758  
     {
 759  29
         ExternalContext externalContext = facesContext.getExternalContext();
 760  29
         String tokenValue = (String) externalContext.getRequestMap().get(FLASH_RENDER_MAP_TOKEN);
 761  29
         ClientWindow clientWindow = externalContext.getClientWindow();
 762  29
         if (clientWindow != null)
 763  
         {
 764  0
             if (facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext))
 765  
             {
 766  
                 //Use HttpSession or PortletSession object
 767  0
                 Map<String, Object> sessionMap = externalContext.getSessionMap();
 768  0
                 sessionMap.put(FLASH_RENDER_MAP_TOKEN+SEPARATOR_CHAR+clientWindow.getId(), tokenValue);
 769  0
             }
 770  
             else
 771  
             {
 772  0
                 FlashClientWindowTokenCollection lruMap = getFlashClientWindowTokenCollection(externalContext, true);
 773  0
                 lruMap.put(clientWindow.getId(), tokenValue);
 774  0
             }
 775  
         }
 776  
         else
 777  
         {
 778  29
             HttpServletResponse httpResponse = ExternalContextUtils.getHttpServletResponse(externalContext);
 779  29
             if (httpResponse != null)
 780  
             {
 781  29
                 Cookie cookie = _createFlashCookie(FLASH_RENDER_MAP_TOKEN, tokenValue, externalContext);
 782  29
                 httpResponse.addCookie(cookie);
 783  29
             }
 784  
             else
 785  
             {
 786  
                 //Use HttpSession or PortletSession object
 787  0
                 Map<String, Object> sessionMap = externalContext.getSessionMap();
 788  0
                 sessionMap.put(FLASH_RENDER_MAP_TOKEN, tokenValue);
 789  
             }
 790  
         }
 791  29
     }
 792  
     
 793  
     /**
 794  
      * Retrieve the map token of the render map from the previous request.
 795  
      * 
 796  
      * Returns the value of _saveRenderFlashMapTokenForNextRequest() from
 797  
      * the previous request.
 798  
      * @param facesContext
 799  
      * @return
 800  
      */
 801  
     private String _getRenderFlashMapTokenFromPreviousRequest(FacesContext facesContext)
 802  
     {
 803  29
         ExternalContext externalContext = facesContext.getExternalContext();
 804  29
         String tokenValue = null;
 805  29
         ClientWindow clientWindow = externalContext.getClientWindow();
 806  29
         if (clientWindow != null)
 807  
         {
 808  0
             if (facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext))
 809  
             {
 810  0
                 Map<String, Object> sessionMap = externalContext.getSessionMap();
 811  0
                 tokenValue = (String) sessionMap.get(FLASH_RENDER_MAP_TOKEN+
 812  
                         SEPARATOR_CHAR+clientWindow.getId());                
 813  0
             }
 814  
             else
 815  
             {
 816  0
                 FlashClientWindowTokenCollection lruMap = getFlashClientWindowTokenCollection(externalContext, false);
 817  0
                 if (lruMap != null)
 818  
                 {
 819  0
                     tokenValue = (String) lruMap.get(clientWindow.getId());
 820  
                 }
 821  0
             }
 822  
         }
 823  
         else
 824  
         {
 825  29
             HttpServletResponse httpResponse = ExternalContextUtils.getHttpServletResponse(externalContext);
 826  29
             if (httpResponse != null)
 827  
             {
 828  
                 //Use a cookie
 829  29
                 Cookie cookie = (Cookie) externalContext.getRequestCookieMap().get(FLASH_RENDER_MAP_TOKEN);
 830  29
                 if (cookie != null)
 831  
                 {
 832  20
                     tokenValue = cookie.getValue();
 833  
                 }
 834  29
             }
 835  
             else
 836  
             {
 837  
                 //Use HttpSession or PortletSession object
 838  0
                 Map<String, Object> sessionMap = externalContext.getSessionMap();
 839  0
                 tokenValue = (String) sessionMap.get(FLASH_RENDER_MAP_TOKEN);
 840  
             }
 841  
         }
 842  29
         return tokenValue;
 843  
     }
 844  
     
 845  
     /**
 846  
      * Restores the render FlashMap token from the previous request.
 847  
      * This is the token of the executeMap for this request.
 848  
      * Furthermore it also creates a new token for this request's renderMap
 849  
      * (and thus implicitly a new renderMap).
 850  
      * @param facesContext
 851  
      */
 852  
     private void _manageFlashMapTokens(FacesContext facesContext)
 853  
     {
 854  29
         ExternalContext externalContext = facesContext.getExternalContext();
 855  29
         Map<String, Object> requestMap = externalContext.getRequestMap();
 856  
 
 857  29
         final String previousRenderToken 
 858  
                 = _getRenderFlashMapTokenFromPreviousRequest(facesContext);
 859  29
         if (previousRenderToken != null)
 860  
         {
 861  
             // "restore" the renderMap from the previous request
 862  
             // and put it as the executeMap for this request
 863  20
             requestMap.put(FLASH_EXECUTE_MAP_TOKEN, previousRenderToken);
 864  
         }
 865  
         else
 866  
         {
 867  9
             if (facesContext.isPostback())
 868  
             {
 869  0
                 if (facesContext.getExternalContext().getClientWindow() == null)
 870  
                 {
 871  
                     // on a postback, we should always have a previousToken
 872  0
                     log.warning("Identifier for execute FlashMap was lost on " +
 873  
                             "the postback, thus FlashScope information is gone.");
 874  
                 }
 875  
                 else
 876  
                 {
 877  
                     // Next token was not preserved in session, which means flash map
 878  
                     // is empty. Create a new token and store it as execute map, which
 879  
                     // will be empty.
 880  0
                     requestMap.put(FLASH_EXECUTE_MAP_TOKEN, _getNextToken());
 881  
                 }
 882  
             }
 883  
             
 884  
             // create a new token (and thus a new Map) for this request's 
 885  
             // executeMap so that we have an executeMap in any possible case.
 886  9
             final String newExecuteToken = _getNextToken();
 887  9
             requestMap.put(FLASH_EXECUTE_MAP_TOKEN, newExecuteToken);
 888  
         }
 889  
         
 890  
         // create a new token (and thus a new Map) for this request's renderMap
 891  29
         final String newRenderToken = _getNextToken();
 892  29
         requestMap.put(FLASH_RENDER_MAP_TOKEN, newRenderToken);
 893  
         
 894  
         // we now have the final render token for this request, thus we can
 895  
         // already save it for the next request, because it won't change
 896  29
         _saveRenderFlashMapTokenForNextRequest(facesContext);
 897  29
     }
 898  
     
 899  
     /**
 900  
      * Get the next token to be assigned to this request
 901  
      * 
 902  
      * @return
 903  
      */
 904  
     private String _getNextToken()
 905  
     {
 906  
         // atomically increment the value
 907  38
         long nextToken = _count.incrementAndGet();
 908  
 
 909  
         // convert using base 36 because it is a fast efficient subset of base-64
 910  38
         return Long.toString(nextToken, 36);
 911  
     }
 912  
 
 913  
     /**
 914  
      * Create a new subkey-wrapper of the session map with the given prefix.
 915  
      * This wrapper is used to implement the maps for the flash scope.
 916  
      * For more information see the SubKeyMap doc.
 917  
      */
 918  
     private Map<String, Object> _createSubKeyMap(FacesContext context, String prefix)
 919  
     {
 920  60
         ExternalContext external = context.getExternalContext();
 921  60
         Map<String, Object> sessionMap = external.getSessionMap();
 922  
 
 923  60
         return new SubKeyMap<Object>(sessionMap, prefix);
 924  
     }
 925  
 
 926  
     /**
 927  
      * Return the flash map created on this traversal.
 928  
      * 
 929  
      * This FlashMap will be the execute FlashMap of the next traversal.
 930  
      * 
 931  
      * Note that it is supposed that FLASH_RENDER_MAP_TOKEN is initialized
 932  
      * before restore view phase (see doPrePhaseActions() for details).
 933  
      * 
 934  
      * @param context
 935  
      * @return
 936  
      */
 937  
     @SuppressWarnings("unchecked")
 938  
     private Map<String, Object> _getRenderFlashMap(FacesContext context)
 939  
     {
 940  
         // Note that we don't have to synchronize here, because it is no problem
 941  
         // if we create more SubKeyMaps with the same subkey, because they are
 942  
         // totally equal and point to the same entries in the SessionMap.
 943  
         
 944  39
         Map<String, Object> requestMap = context.getExternalContext().getRequestMap();
 945  39
         Map<String, Object> map = (Map<String, Object>) requestMap.get(FLASH_RENDER_MAP);
 946  39
         if (map == null)
 947  
         {
 948  30
             String token = (String) requestMap.get(FLASH_RENDER_MAP_TOKEN);
 949  30
             String fullToken = FLASH_SESSION_MAP_SUBKEY_PREFIX + SEPARATOR_CHAR + token + SEPARATOR_CHAR;
 950  30
             map =  _createSubKeyMap(context, fullToken);
 951  30
             requestMap.put(FLASH_RENDER_MAP, map);
 952  
         }
 953  39
         return map;
 954  
     }
 955  
     
 956  
     /**
 957  
      * Return the execute Flash Map.
 958  
      * 
 959  
      * This FlashMap was the render FlashMap of the previous traversal. 
 960  
      * 
 961  
      * @param context
 962  
      * @return
 963  
      */
 964  
     @SuppressWarnings("unchecked")
 965  
     private Map<String, Object> _getExecuteFlashMap(FacesContext context)
 966  
     {
 967  
         // Note that we don't have to synchronize here, because it is no problem
 968  
         // if we create more SubKeyMaps with the same subkey, because they are
 969  
         // totally equal and point to the same entries in the SessionMap.
 970  
         
 971  122
         Map<String, Object> requestMap = context != null && context.getExternalContext() != null ?
 972  
                 context.getExternalContext().getRequestMap() : null;
 973  122
         Map<String, Object> map = requestMap != null ? (Map<String, Object>) requestMap.get(FLASH_EXECUTE_MAP) : null;
 974  122
         if (map == null)
 975  
         {
 976  30
             if (requestMap != null)
 977  
             {
 978  30
                 String token = (String) requestMap.get(FLASH_EXECUTE_MAP_TOKEN);
 979  30
                 String fullToken = FLASH_SESSION_MAP_SUBKEY_PREFIX + SEPARATOR_CHAR + token + SEPARATOR_CHAR;
 980  30
                 map = _createSubKeyMap(context, fullToken);
 981  30
                 requestMap.put(FLASH_EXECUTE_MAP, map);
 982  30
             }
 983  
             else
 984  
             {
 985  0
                 map = Collections.emptyMap();
 986  
             }
 987  
         }
 988  122
         return map;
 989  
     }
 990  
     
 991  
     /**
 992  
      * Get the proper map according to the current phase:
 993  
      * 
 994  
      * Normal case:
 995  
      * 
 996  
      * - First request, restore view phase (create a new one): render map n
 997  
      * - First request, execute phase: Skipped
 998  
      * - First request, render  phase: render map n
 999  
      * 
 1000  
      *   Render map n saved and put as execute map n
 1001  
      * 
 1002  
      * - Second request, execute phase: execute map n
 1003  
      * - Second request, render  phase: render map n+1
 1004  
      * 
 1005  
      * Post Redirect Get case: Redirect is triggered by a call to setRedirect(true) from NavigationHandler
 1006  
      * or earlier using c:set tag.
 1007  
      * 
 1008  
      * - First request, restore view phase (create a new one): render map n
 1009  
      * - First request, execute phase: Skipped
 1010  
      * - First request, render  phase: render map n
 1011  
      * 
 1012  
      *   Render map n saved and put as execute map n
 1013  
      * 
 1014  
      * POST
 1015  
      * 
 1016  
      * - Second request, execute phase: execute map n
 1017  
      *   Note that render map n+1 is also created here to perform keep().
 1018  
      * 
 1019  
      * REDIRECT
 1020  
      * 
 1021  
      * - NavigationHandler do the redirect, requestMap data lost, called Flash.setRedirect(true)
 1022  
      * 
 1023  
      *   Render map n+1 saved and put as render map n+1 on GET request.
 1024  
      * 
 1025  
      * GET
 1026  
      * 
 1027  
      * - Third  request, restore view phase (create a new one): render map n+1 (restorred)
 1028  
      *   (isRedirect() should return true as javadoc says)
 1029  
      * - Third  request, execute phase: skipped
 1030  
      * - Third  request, render  phase: render map n+1
 1031  
      * 
 1032  
      * In this way proper behavior is preserved even in the case of redirect, since the GET part is handled as
 1033  
      * the "render" part of the current traversal, keeping the semantic of flash object.
 1034  
      * 
 1035  
      * @return
 1036  
      */
 1037  
     private Map<String, Object> _getActiveFlashMap()
 1038  
     {
 1039  7
         FacesContext facesContext = FacesContext.getCurrentInstance();
 1040  7
         if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId()) 
 1041  
                 || !facesContext.isPostback())
 1042  
         {
 1043  6
             return _getRenderFlashMap(facesContext);
 1044  
         }
 1045  
         else
 1046  
         {
 1047  1
             return _getExecuteFlashMap(facesContext);
 1048  
         }
 1049  
     }
 1050  
     
 1051  
     /**
 1052  
      * Returns the FlashMap used in the reading methods of java.util.Map
 1053  
      * like e.g. get() or values().
 1054  
      * @return
 1055  
      */
 1056  
     private Map<String, Object> _getFlashMapForReading()
 1057  
     {
 1058  58
         return _getExecuteFlashMap(FacesContext.getCurrentInstance());
 1059  
     }
 1060  
     
 1061  
     /**
 1062  
      * Returns the FlashMap used in the writing methods of java.util.Map
 1063  
      * like e.g. put() or clear().
 1064  
      * @return
 1065  
      */
 1066  
     private Map<String, Object> _getFlashMapForWriting()
 1067  
     {
 1068  7
         return _getActiveFlashMap();
 1069  
     }
 1070  
 
 1071  
     /**
 1072  
      * Destroy the execute FlashMap, because it is not needed anymore.
 1073  
      * @param facesContext
 1074  
      */
 1075  
     private void _clearExecuteFlashMap(FacesContext facesContext)
 1076  
     {
 1077  27
         Map<String, Object> map = _getExecuteFlashMap(facesContext);
 1078  
 
 1079  27
         if (!map.isEmpty())
 1080  
         {
 1081  
             //JSF 2.2 invoke PreClearFlashEvent
 1082  3
             facesContext.getApplication().publishEvent(facesContext, 
 1083  
                 PreClearFlashEvent.class, map);
 1084  
 
 1085  
             // Clear everything - note that because of naming conventions,
 1086  
             // this will in fact automatically recurse through all children
 1087  
             // grandchildren etc. - which is kind of a design flaw of SubKeyMap,
 1088  
             // but one we're relying on
 1089  
 
 1090  
             // NOTE that we do not need a null check here, because there will
 1091  
             // always be an execute Map, however sometimes an empty one!
 1092  3
             map.clear();
 1093  
         }
 1094  27
     }
 1095  
     
 1096  
     private void _clearRenderFlashTokenIfMapEmpty(FacesContext facesContext)
 1097  
     {
 1098  
         // Keep in mind that we cannot remove a cookie once the response has been sent,
 1099  
         // but we can remove an attribute from session anytime, so the idea is check
 1100  
         // if the map is empty or not and if that so, do not save the token. The effect
 1101  
         // is we can reduce the session size a bit.
 1102  27
         ExternalContext externalContext = facesContext.getExternalContext();
 1103  27
         Object session = facesContext.getExternalContext().getSession(false);
 1104  27
         ClientWindow clientWindow = externalContext.getClientWindow();
 1105  27
         if (session != null && clientWindow != null)
 1106  
         {
 1107  0
             Map<String, Object> map = _getRenderFlashMap(facesContext);
 1108  0
             if (map.isEmpty())
 1109  
             {
 1110  0
                 if (facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext))
 1111  
                 {
 1112  0
                     Map<String, Object> sessionMap = externalContext.getSessionMap();
 1113  0
                     sessionMap.remove(FLASH_RENDER_MAP_TOKEN+SEPARATOR_CHAR+clientWindow.getId());                
 1114  0
                 }
 1115  
                 else
 1116  
                 {
 1117  
                     // Remove token, because it is not necessary
 1118  0
                     FlashClientWindowTokenCollection lruMap = getFlashClientWindowTokenCollection(
 1119  
                             externalContext, false);
 1120  0
                     if (lruMap != null)
 1121  
                     {
 1122  0
                         lruMap.remove(clientWindow.getId());
 1123  0
                         Map<String, Object> sessionMap = externalContext.getSessionMap();
 1124  0
                         if (lruMap.isEmpty())
 1125  
                         {
 1126  0
                             sessionMap.remove(FLASH_CW_LRU_MAP);
 1127  
                         }
 1128  
                         else
 1129  
                         {
 1130  
                             //refresh remove
 1131  0
                             sessionMap.put(FLASH_CW_LRU_MAP, lruMap);
 1132  
                         }
 1133  
                     }
 1134  
                 }
 1135  
             }
 1136  
         }
 1137  27
     }
 1138  
     
 1139  
     protected FlashClientWindowTokenCollection getFlashClientWindowTokenCollection(
 1140  
             ExternalContext externalContext, boolean create)
 1141  
     {
 1142  0
         Object session = externalContext.getSession(false);
 1143  0
         if (session == null && !create)
 1144  
         {
 1145  0
             return null;
 1146  
         }
 1147  0
         Map<String, Object> sessionMap = externalContext.getSessionMap();
 1148  0
         FlashClientWindowTokenCollection lruMap = (FlashClientWindowTokenCollection)
 1149  
                 sessionMap.get(FLASH_CW_LRU_MAP);
 1150  0
         if (lruMap == null)
 1151  
         {
 1152  0
             Integer ft = MyfacesConfig.getCurrentInstance(externalContext).getNumberOfFlashTokensInSession();
 1153  0
             lruMap = new FlashClientWindowTokenCollection(new ClientWindowFlashTokenLRUMap(ft));
 1154  
         }    
 1155  
         
 1156  0
         if (create)
 1157  
         {
 1158  0
             sessionMap.put(FLASH_CW_LRU_MAP, lruMap);
 1159  
         }
 1160  0
         return lruMap;
 1161  
     }
 1162  
 
 1163  
     public void clearFlashMap(FacesContext facesContext, String clientWindowId, String token)
 1164  
     {
 1165  0
         if ((!_flashScopeDisabled) && 
 1166  
                 (!facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext)))
 1167  
         {
 1168  0
             ExternalContext externalContext = facesContext.getExternalContext();
 1169  0
             ClientWindow clientWindow = externalContext.getClientWindow();
 1170  0
             if (clientWindow != null)
 1171  
             {
 1172  0
                 if (token != null)
 1173  
                 {
 1174  0
                     String fullToken = FLASH_SESSION_MAP_SUBKEY_PREFIX + SEPARATOR_CHAR + token + SEPARATOR_CHAR;
 1175  0
                     Map<String, Object> map =  _createSubKeyMap(facesContext, fullToken);
 1176  0
                     map.clear();
 1177  
                 }
 1178  
             }
 1179  
         }
 1180  0
     }
 1181  
 
 1182  
     /**
 1183  
      * Creates a Cookie with the given name and value.
 1184  
      * In addition, it will be configured with maxAge=-1, the current request path and secure value.
 1185  
      *
 1186  
      * @param name
 1187  
      * @param value
 1188  
      * @param externalContext
 1189  
      * @return
 1190  
      */
 1191  
     private Cookie _createFlashCookie(String name, String value, ExternalContext externalContext)
 1192  
     {
 1193  33
         Cookie cookie = new Cookie(name, value);
 1194  
 
 1195  33
         cookie.setMaxAge(-1);
 1196  33
         cookie.setPath(_getCookiePath(externalContext));
 1197  33
         cookie.setSecure(externalContext.isSecure());
 1198  
         //cookie.setHttpOnly(true);
 1199  33
         if (ServletSpecifications.isServlet30Available())
 1200  
         {
 1201  33
             _Servlet30Utils.setCookieHttpOnly(cookie, true);
 1202  
         }
 1203  33
         return cookie;
 1204  
     }
 1205  
 
 1206  
     /**
 1207  
      * Returns the path for the Flash-Cookies.
 1208  
      * @param externalContext
 1209  
      * @return
 1210  
      */
 1211  
     private String _getCookiePath(ExternalContext externalContext)
 1212  
     {
 1213  36
         String contextPath = externalContext.getRequestContextPath();
 1214  
 
 1215  36
         if (contextPath == null || "".equals(contextPath))
 1216  
         {
 1217  36
             contextPath = "/";
 1218  
         }
 1219  
 
 1220  36
         return contextPath;
 1221  
     }
 1222  
     
 1223  
     /**
 1224  
      * Convert the Object to a Boolean.
 1225  
      * @param value
 1226  
      * @return
 1227  
      */
 1228  
     private Boolean _convertToBoolean(Object value)
 1229  
     {
 1230  
         Boolean booleanValue;
 1231  2
         if (value instanceof Boolean)
 1232  
         {
 1233  2
             booleanValue = (Boolean) value;
 1234  
         }
 1235  
         else
 1236  
         {
 1237  0
             booleanValue = Boolean.parseBoolean(value.toString());
 1238  
         }
 1239  2
         return booleanValue;
 1240  
     }
 1241  
     
 1242  
     /**
 1243  
      * Checks whether flash scope is disabled.
 1244  
      * @throws FlashScopeDisabledException if flash scope is disabled
 1245  
      */
 1246  
     private void _checkFlashScopeDisabled()
 1247  
     {
 1248  80
         if (_flashScopeDisabled)
 1249  
         {
 1250  0
             throw new FlashScopeDisabledException("Flash scope was disabled by context param " 
 1251  
                 + MyfacesConfig.INIT_PARAM_FLASH_SCOPE_DISABLED + " but erroneously accessed");
 1252  
         }
 1253  80
     }
 1254  
     
 1255  
     // ~ Inner classes --------------------------------------------------------
 1256  
     
 1257  
     /**
 1258  
      * Class used to store a FacesMessage with its clientId.
 1259  
      */
 1260  16
     private static class MessageEntry implements Serializable
 1261  
     {
 1262  
         private static final long serialVersionUID = -690264660230199234L;
 1263  
         private final String clientId;
 1264  
         private final FacesMessage message;
 1265  
 
 1266  
         public MessageEntry(String clientId, FacesMessage message)
 1267  8
         {
 1268  8
             this.clientId = clientId;
 1269  8
             this.message = message;
 1270  8
         }
 1271  
     }
 1272  
     
 1273  
 }