Coverage Report - org.apache.myfaces.cdi.view.ViewScopeBeanHolder
 
Classes in this File Line Coverage Branch Coverage Complexity
ViewScopeBeanHolder
0%
0/60
0%
0/24
2.333
 
 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.cdi.view;
 20  
 
 21  
 import java.io.Serializable;
 22  
 import java.util.Map;
 23  
 import java.util.Random;
 24  
 import java.util.concurrent.ConcurrentHashMap;
 25  
 import javax.annotation.PostConstruct;
 26  
 import javax.annotation.PreDestroy;
 27  
 import javax.enterprise.context.SessionScoped;
 28  
 import javax.enterprise.inject.spi.BeanManager;
 29  
 import javax.faces.context.ExceptionHandler;
 30  
 import javax.faces.context.ExternalContext;
 31  
 import javax.faces.context.FacesContext;
 32  
 import javax.inject.Inject;
 33  
 import javax.servlet.ServletContext;
 34  
 import org.apache.myfaces.context.ReleaseableExternalContext;
 35  
 import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
 36  
 import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
 37  
 import org.apache.myfaces.shared.context.ExceptionHandlerImpl;
 38  
 
 39  
 /**
 40  
  *
 41  
  * @author Leonardo Uribe
 42  
  */
 43  
 @SessionScoped
 44  
 public class ViewScopeBeanHolder implements Serializable
 45  
 {
 46  
     /**
 47  
      * key: the windowId for the browser tab or window
 48  
      * value: the {@link ViewScopeContextualStorage} which holds all the
 49  
      * {@link javax.enterprise.inject.spi.Bean}s.
 50  
      */
 51  
     private Map<String, ViewScopeContextualStorage> storageMap;
 52  
     
 53  0
     private static final Random RANDOM_GENERATOR = new Random();
 54  
     
 55  
     private static final String VIEW_SCOPE_PREFIX = "oam.view.SCOPE";
 56  
     
 57  
     public static final String VIEW_SCOPE_PREFIX_KEY = VIEW_SCOPE_PREFIX+".KEY";
 58  
     
 59  
     @Inject
 60  
     ApplicationContextBean applicationContextBean;
 61  
     
 62  
     public ViewScopeBeanHolder()
 63  0
     {
 64  0
     }
 65  
     
 66  
     @PostConstruct
 67  
     public void init()
 68  
     {
 69  0
         storageMap = new ConcurrentHashMap<String, ViewScopeContextualStorage>();
 70  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 71  0
         facesContext.getExternalContext().getSessionMap().put(VIEW_SCOPE_PREFIX_KEY,
 72  
             1);
 73  0
     }
 74  
     
 75  
     /**
 76  
      * This method will return the ViewScopeContextualStorage or create a new one
 77  
      * if no one is yet assigned to the current windowId.
 78  
      * @param beanManager we need the CDI {@link BeanManager} for serialisation.
 79  
      * @param windowId the windowId for the current browser tab or window.
 80  
      */
 81  
     public ViewScopeContextualStorage getContextualStorage(
 82  
         BeanManager beanManager, String viewScopeId)
 83  
     {
 84  0
         ViewScopeContextualStorage contextualStorage = storageMap.get(viewScopeId);
 85  0
         if (contextualStorage == null)
 86  
         {
 87  0
             synchronized (this)
 88  
             {            
 89  0
                 contextualStorage = storageMap.get(viewScopeId);
 90  0
                 if (contextualStorage == null)
 91  
                 {            
 92  0
                     contextualStorage = new ViewScopeContextualStorage(beanManager);
 93  0
                     storageMap.put(viewScopeId, contextualStorage);
 94  
                 }
 95  0
             }
 96  
         }
 97  0
         return contextualStorage;
 98  
     }
 99  
 
 100  
     public Map<String, ViewScopeContextualStorage> getStorageMap()
 101  
     {
 102  0
         return storageMap;
 103  
     }
 104  
 
 105  
     /**
 106  
      *
 107  
      * This method will replace the storageMap and with
 108  
      * a new empty one.
 109  
      * This method can be used to properly destroy the WindowBeanHolder beans
 110  
      * without having to sync heavily. Any
 111  
      * {@link javax.enterprise.inject.spi.Bean#destroy(Object, javax.enterprise.context.spi.CreationalContext)}
 112  
      * should be performed on the returned old storage map.
 113  
      * @return the old storageMap.
 114  
      */
 115  
     public Map<String, ViewScopeContextualStorage> forceNewStorage()
 116  
     {
 117  0
         Map<String, ViewScopeContextualStorage> oldStorageMap = storageMap;
 118  0
         storageMap = new ConcurrentHashMap<String, ViewScopeContextualStorage>();
 119  0
         return oldStorageMap;
 120  
     }
 121  
 
 122  
     /**
 123  
      * This method properly destroys all current &#064;WindowScoped beans
 124  
      * of the active session and also prepares the storage for new beans.
 125  
      * It will automatically get called when the session context closes
 126  
      * but can also get invoked manually, e.g. if a user likes to get rid
 127  
      * of all it's &#064;ViewScoped beans.
 128  
      */
 129  
     //@PreDestroy
 130  
     public void destroyBeans()
 131  
     {
 132  
         // we replace the old windowBeanHolder beans with a new storage Map
 133  
         // an afterwards destroy the old Beans without having to care about any syncs.
 134  
         // This behavior also helps as a check to avoid destroy the same beans twice.
 135  0
         Map<String, ViewScopeContextualStorage> oldWindowContextStorages = forceNewStorage();
 136  
 
 137  0
         for (ViewScopeContextualStorage contextualStorage : oldWindowContextStorages.values())
 138  
         {
 139  0
             ViewScopeContextImpl.destroyAllActive(contextualStorage);
 140  0
         }
 141  0
     }
 142  
     
 143  
     public void destroyBeans(String viewScopeId)
 144  
     {
 145  0
         ViewScopeContextualStorage contextualStorage = storageMap.get(viewScopeId);
 146  0
         if (contextualStorage != null)
 147  
         {
 148  
             try
 149  
             {
 150  0
                 FacesContext facesContext = FacesContext.getCurrentInstance();
 151  0
                 if (facesContext == null &&
 152  
                     applicationContextBean.getServletContext() != null)
 153  
                 {
 154  
                     try
 155  
                     {
 156  0
                         ServletContext servletContext = applicationContextBean.getServletContext();
 157  0
                         ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
 158  0
                         ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
 159  0
                         facesContext = new StartupFacesContextImpl(externalContext, 
 160  
                                 (ReleaseableExternalContext) externalContext, exceptionHandler, false);
 161  0
                         ViewScopeContextImpl.destroyAllActive(contextualStorage, facesContext);
 162  
                     }
 163  
                     finally
 164  
                     {
 165  0
                         facesContext.release();
 166  0
                     }
 167  
                 }
 168  
                 else
 169  
                 {
 170  0
                     ViewScopeContextImpl.destroyAllActive(contextualStorage, facesContext);
 171  
                 }
 172  
             }
 173  
             finally
 174  
             {
 175  
                 //remove the viewScopeId to prevent memory leak
 176  0
                 storageMap.remove(viewScopeId);
 177  0
             }
 178  
         }
 179  0
     }
 180  
     
 181  
     @PreDestroy
 182  
     public void destroyBeansOnPreDestroy()
 183  
     {
 184  
         // After some testing done two things are clear:
 185  
         // 1. jetty +  weld call @PreDestroy at the end of the request
 186  
         // 2. use a HttpServletListener in tomcat + owb does not work, because
 187  
         //    CDI listener is executed first.
 188  
         // So we need a mixed approach using both a listener and @PreDestroy annotations.
 189  
         // When the first one in being called replace the storages with a new map 
 190  
         // and call PreDestroy, when the second one is called, it founds an empty map
 191  
         // and the process stops. A hack to get ServletContext from CDI is required to
 192  
         // provide a valid FacesContext instance.
 193  0
         Map<String, ViewScopeContextualStorage> oldWindowContextStorages = forceNewStorage();
 194  0
         if (!oldWindowContextStorages.isEmpty())
 195  
         {
 196  0
             FacesContext facesContext = FacesContext.getCurrentInstance();
 197  0
             if (facesContext == null &&
 198  
                 applicationContextBean.getServletContext() != null)
 199  
             {
 200  
                 try
 201  
                 {
 202  0
                     ServletContext servletContext = applicationContextBean.getServletContext();
 203  0
                     ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
 204  0
                     ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
 205  0
                     facesContext = new StartupFacesContextImpl(externalContext, 
 206  
                             (ReleaseableExternalContext) externalContext, exceptionHandler, false);
 207  0
                     for (ViewScopeContextualStorage contextualStorage : oldWindowContextStorages.values())
 208  
                     {
 209  0
                         ViewScopeContextImpl.destroyAllActive(contextualStorage, facesContext);
 210  0
                     }
 211  
                 }
 212  
                 finally
 213  
                 {
 214  0
                     facesContext.release();
 215  0
                 }
 216  
             }
 217  
             else
 218  
             {
 219  0
                 for (ViewScopeContextualStorage contextualStorage : oldWindowContextStorages.values())
 220  
                 {
 221  0
                     ViewScopeContextImpl.destroyAllActive(contextualStorage);
 222  0
                 }
 223  
             }
 224  
         }
 225  0
     }
 226  
     
 227  
     public String generateUniqueViewScopeId()
 228  
     {
 229  
         // To ensure uniqueness we just use a random generator and we check
 230  
         // if the key is already used.
 231  
         String key;
 232  
         do 
 233  
         {
 234  0
             key = Integer.toString(RANDOM_GENERATOR.nextInt());
 235  0
         } while (storageMap.containsKey(key));
 236  0
         return key;
 237  
     }
 238  
 
 239  
 }