Coverage Report - org.apache.myfaces.flow.cdi.FlowScopeBeanHolder
 
Classes in this File Line Coverage Branch Coverage Complexity
FlowScopeBeanHolder
0%
0/115
0%
0/52
3
 
 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.flow.cdi;
 20  
 
 21  
 import java.io.Serializable;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Collections;
 24  
 import java.util.HashMap;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.concurrent.ConcurrentHashMap;
 28  
 import java.util.logging.Level;
 29  
 import java.util.logging.Logger;
 30  
 import javax.annotation.PostConstruct;
 31  
 import javax.annotation.PreDestroy;
 32  
 import javax.enterprise.context.SessionScoped;
 33  
 import javax.enterprise.inject.spi.BeanManager;
 34  
 import javax.faces.context.ExceptionHandler;
 35  
 import javax.faces.context.ExternalContext;
 36  
 import javax.faces.context.FacesContext;
 37  
 import javax.faces.flow.Flow;
 38  
 import javax.faces.flow.FlowHandler;
 39  
 import javax.faces.lifecycle.ClientWindow;
 40  
 import javax.inject.Inject;
 41  
 import javax.servlet.ServletContext;
 42  
 import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
 43  
 import org.apache.myfaces.cdi.util.ContextualStorage;
 44  
 import org.apache.myfaces.cdi.view.ApplicationContextBean;
 45  
 import org.apache.myfaces.context.ReleaseableExternalContext;
 46  
 import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
 47  
 import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
 48  
 import org.apache.myfaces.flow.util.FlowUtils;
 49  
 import org.apache.myfaces.shared.config.MyfacesConfig;
 50  
 import org.apache.myfaces.shared.context.ExceptionHandlerImpl;
 51  
 
 52  
 
 53  
 /**
 54  
  *
 55  
  * This holder will store the flow scope active ids and it's beans for the current
 56  
  * HTTP Session. We use standard SessionScoped bean to not need
 57  
  * to treat async-supported and similar headache.
 58  
  * 
 59  
  * @author lu4242
 60  
  */
 61  
 @SessionScoped
 62  
 public class FlowScopeBeanHolder implements Serializable
 63  
 {
 64  
     /**
 65  
      * key: client window id + flow id
 66  
      * value: the {@link ContextualStorage} which holds all the
 67  
      * {@link javax.enterprise.inject.spi.Bean}s.
 68  
      */
 69  
     private Map<String, ContextualStorage> storageMap;
 70  
     
 71  
     private Map<String, List<String>> activeFlowMapKeys;
 72  
     
 73  
     private FacesFlowClientWindowCollection windowCollection;
 74  
     
 75  
     public static final String CURRENT_FLOW_SCOPE_MAP = "oam.CURRENT_FLOW_SCOPE_MAP";
 76  
     
 77  
     private static final String FLOW_SCOPE_PREFIX = "oam.flow.SCOPE";
 78  
     
 79  
     public static final String FLOW_SCOPE_PREFIX_KEY = FLOW_SCOPE_PREFIX+".KEY";
 80  
     
 81  
     @Inject
 82  
     ApplicationContextBean applicationContextBean;
 83  
 
 84  
     public FlowScopeBeanHolder()
 85  0
     {
 86  0
     }
 87  
     
 88  
     @PostConstruct
 89  
     public void init()
 90  
     {
 91  0
         storageMap = new ConcurrentHashMap<String, ContextualStorage>();
 92  0
         activeFlowMapKeys = new ConcurrentHashMap<String, List<String>>();
 93  0
         windowCollection = null;
 94  
         
 95  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 96  0
         this.refreshClientWindow(facesContext);
 97  0
         facesContext.getExternalContext().getSessionMap().put(FLOW_SCOPE_PREFIX_KEY,
 98  
             1);
 99  0
     }
 100  
     
 101  
     /**
 102  
      * This method will return the ContextualStorage or create a new one
 103  
      * if no one is yet assigned to the current flowClientWindowId.
 104  
      * @param beanManager we need the CDI {@link BeanManager} for serialisation.
 105  
      * @param flowClientWindowId the flowClientWindowId for the current flow.
 106  
      */
 107  
     public ContextualStorage getContextualStorage(BeanManager beanManager, String flowClientWindowId)
 108  
     {
 109  0
         ContextualStorage contextualStorage = storageMap.get(flowClientWindowId);
 110  0
         if (contextualStorage == null)
 111  
         {
 112  0
             synchronized (this)
 113  
             {
 114  0
                 contextualStorage = storageMap.get(flowClientWindowId);
 115  0
                 if (contextualStorage == null)
 116  
                 {
 117  0
                     contextualStorage = new ContextualStorage(beanManager, true, true);
 118  0
                     storageMap.put(flowClientWindowId, contextualStorage);
 119  
                 }
 120  0
             }
 121  
         }
 122  
 
 123  0
         return contextualStorage;
 124  
     }
 125  
     
 126  
     public ContextualStorage getContextualStorageNoCreate(BeanManager beanManager, String flowClientWindowId)
 127  
     {
 128  0
         return storageMap.get(flowClientWindowId);
 129  
     }
 130  
 
 131  
     public Map<String, ContextualStorage> getStorageMap()
 132  
     {
 133  0
         return storageMap;
 134  
     }
 135  
     
 136  
     public Map<Object, Object> getFlowScopeMap(
 137  
         BeanManager beanManager, String flowClientWindowId, boolean create)
 138  
     {
 139  0
         Map<Object, Object> map = null;
 140  0
         if (create)
 141  
         {
 142  0
             ContextualStorage contextualStorage = getContextualStorage(
 143  
                 beanManager, flowClientWindowId);
 144  0
             ContextualInstanceInfo info = contextualStorage.getStorage().get(CURRENT_FLOW_SCOPE_MAP);
 145  0
             if (info == null)
 146  
             {
 147  0
                 info = new ContextualInstanceInfo<Object>();
 148  0
                 contextualStorage.getStorage().put(CURRENT_FLOW_SCOPE_MAP, info);
 149  
             }
 150  0
             map = (Map<Object, Object>) info.getContextualInstance();
 151  0
             if (map == null)
 152  
             {
 153  0
                 map = new HashMap<Object,Object>();
 154  0
                 info.setContextualInstance(map);
 155  
             }
 156  0
         }
 157  
         else
 158  
         {
 159  0
             ContextualStorage contextualStorage = getContextualStorageNoCreate(
 160  
                 beanManager, flowClientWindowId);
 161  0
             if (contextualStorage != null)
 162  
             {
 163  0
                 ContextualInstanceInfo info = contextualStorage.getStorage().get(CURRENT_FLOW_SCOPE_MAP);
 164  0
                 if (info != null)
 165  
                 {
 166  0
                     map = (Map<Object, Object>) info.getContextualInstance();
 167  
                 }
 168  
             }
 169  
         }
 170  0
         return map;
 171  
     }
 172  
 
 173  
     /**
 174  
      *
 175  
      * This method will replace the storageMap and with
 176  
      * a new empty one.
 177  
      * This method can be used to properly destroy the WindowBeanHolder beans
 178  
      * without having to sync heavily. Any
 179  
      * {@link javax.enterprise.inject.spi.Bean#destroy(Object, javax.enterprise.context.spi.CreationalContext)}
 180  
      * should be performed on the returned old storage map.
 181  
      * @return the old storageMap.
 182  
      */
 183  
     public Map<String, ContextualStorage> forceNewStorage()
 184  
     {
 185  0
         Map<String, ContextualStorage> oldStorageMap = storageMap;
 186  0
         storageMap = new ConcurrentHashMap<String, ContextualStorage>();
 187  0
         return oldStorageMap;
 188  
     }
 189  
 
 190  
     /**
 191  
      * This method properly destroys all current &#064;WindowScoped beans
 192  
      * of the active session and also prepares the storage for new beans.
 193  
      * It will automatically get called when the session context closes
 194  
      * but can also get invoked manually, e.g. if a user likes to get rid
 195  
      * of all it's &#064;WindowScoped beans.
 196  
      */
 197  
     //@PreDestroy
 198  
     public void destroyBeans()
 199  
     {
 200  
         // we replace the old windowBeanHolder beans with a new storage Map
 201  
         // an afterwards destroy the old Beans without having to care about any syncs.
 202  0
         Map<String, ContextualStorage> oldWindowContextStorages = forceNewStorage();
 203  
 
 204  0
         for (ContextualStorage contextualStorage : oldWindowContextStorages.values())
 205  
         {
 206  0
             FlowScopedContextImpl.destroyAllActive(contextualStorage);
 207  0
         }
 208  0
     }
 209  
     
 210  
     /**
 211  
      * See description on ViewScopeBeanHolder for details about how this works
 212  
      */
 213  
     @PreDestroy
 214  
     public void destroyBeansOnPreDestroy()
 215  
     {
 216  0
         Map<String, ContextualStorage> oldWindowContextStorages = forceNewStorage();
 217  0
         if (!oldWindowContextStorages.isEmpty())
 218  
         {
 219  0
             FacesContext facesContext = FacesContext.getCurrentInstance();
 220  0
             ServletContext servletContext = null;
 221  0
             if (facesContext == null)
 222  
             {
 223  
                 try
 224  
                 {
 225  0
                     servletContext = applicationContextBean.getServletContext();
 226  
                 }
 227  0
                 catch (Throwable e)
 228  
                 {
 229  0
                     Logger.getLogger(FlowScopeBeanHolder.class.getName()).log(Level.WARNING,
 230  
                         "Cannot locate servletContext to create FacesContext on @PreDestroy flow scope beans. "
 231  
                                 + "The beans will be destroyed without active FacesContext instance.");
 232  0
                     servletContext = null;
 233  0
                 }
 234  
             }
 235  0
             if (facesContext == null &&
 236  
                 servletContext != null)
 237  
             {
 238  
                 try
 239  
                 {
 240  0
                     ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
 241  0
                     ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
 242  0
                     facesContext = new StartupFacesContextImpl(externalContext, 
 243  
                             (ReleaseableExternalContext) externalContext, exceptionHandler, false);
 244  0
                     for (ContextualStorage contextualStorage : oldWindowContextStorages.values())
 245  
                     {
 246  0
                         FlowScopedContextImpl.destroyAllActive(contextualStorage);
 247  0
                     }
 248  
                 }
 249  
                 finally
 250  
                 {
 251  0
                     facesContext.release();
 252  0
                 }
 253  
             }
 254  
             else
 255  
             {
 256  0
                 for (ContextualStorage contextualStorage : oldWindowContextStorages.values())
 257  
                 {
 258  0
                     FlowScopedContextImpl.destroyAllActive(contextualStorage);
 259  0
                 }
 260  
             }
 261  
         }
 262  0
     }
 263  
     
 264  
     public void refreshClientWindow(FacesContext facesContext)
 265  
     {
 266  0
         if (windowCollection == null)
 267  
         {
 268  0
             Integer ft = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext()).
 269  
                     getNumberOfFacesFlowClientWindowIdsInSession();
 270  0
             windowCollection = new FacesFlowClientWindowCollection(new ClientWindowFacesFlowLRUMap(ft));
 271  
         }
 272  0
         ClientWindow cw = facesContext.getExternalContext().getClientWindow();
 273  0
         if (cw != null && cw.getId() != null)
 274  
         {
 275  0
             windowCollection.setFlowScopeBeanHolder(this);
 276  0
             windowCollection.put(cw.getId(), "");
 277  
         }
 278  0
     }
 279  
     
 280  
     public void clearFlowMap(String clientWindowId)
 281  
     {
 282  0
         List<String> activeFlowKeys = activeFlowMapKeys.remove(clientWindowId);
 283  0
         if (activeFlowKeys != null && !activeFlowKeys.isEmpty())
 284  
         {
 285  0
             for (String flowMapKey : activeFlowKeys)
 286  
             {
 287  0
                 ContextualStorage contextualStorage = storageMap.remove(flowMapKey);
 288  0
                 if (contextualStorage != null)
 289  
                 {
 290  0
                     FlowScopedContextImpl.destroyAllActive(contextualStorage);
 291  
                 }
 292  0
             }
 293  
         }
 294  0
     }
 295  
     
 296  
     public List<String> getActiveFlowMapKeys(FacesContext facesContext)
 297  
     {
 298  0
         ClientWindow cw = facesContext.getExternalContext().getClientWindow();
 299  0
         String baseKey = cw.getId();
 300  0
         List<String> activeFlowKeys = activeFlowMapKeys.get(baseKey);
 301  0
         if (activeFlowKeys == null)
 302  
         {
 303  0
             return Collections.emptyList();
 304  
         }
 305  
         else
 306  
         {
 307  0
             return activeFlowKeys;
 308  
         }
 309  
     }
 310  
     
 311  
     public void createCurrentFlowScope(FacesContext facesContext)
 312  
     {
 313  0
         ClientWindow cw = facesContext.getExternalContext().getClientWindow();
 314  0
         String baseKey = cw.getId();
 315  
         
 316  0
         FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
 317  0
         Flow flow = flowHandler.getCurrentFlow(facesContext);
 318  0
         String flowMapKey = FlowUtils.getFlowMapKey(facesContext, flow);
 319  
 
 320  0
         List<String> activeFlowKeys = activeFlowMapKeys.get(baseKey);
 321  0
         if (activeFlowKeys == null)
 322  
         {
 323  0
             activeFlowKeys = new ArrayList<String>();
 324  
             
 325  
         }
 326  0
         activeFlowKeys.add(0, flowMapKey);
 327  0
         activeFlowMapKeys.put(baseKey, activeFlowKeys);
 328  0
         refreshClientWindow(facesContext);
 329  0
     }
 330  
     
 331  
     public void destroyCurrentFlowScope(FacesContext facesContext)
 332  
     {
 333  0
         ClientWindow cw = facesContext.getExternalContext().getClientWindow();
 334  0
         String baseKey = cw.getId();
 335  
         
 336  0
         FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
 337  0
         Flow flow = flowHandler.getCurrentFlow(facesContext);
 338  0
         String flowMapKey = FlowUtils.getFlowMapKey(facesContext, flow);
 339  
 
 340  0
         ContextualStorage contextualStorage = storageMap.remove(flowMapKey);
 341  0
         if (contextualStorage != null)
 342  
         {
 343  0
             FlowScopedContextImpl.destroyAllActive(contextualStorage);
 344  
         }
 345  
         
 346  0
         List<String> activeFlowKeys = activeFlowMapKeys.get(baseKey);
 347  0
         if (activeFlowKeys != null && !activeFlowKeys.isEmpty())
 348  
         {
 349  0
             activeFlowKeys.remove(flowMapKey);
 350  
         }
 351  0
     }
 352  
 }