View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.cdi.view;
20  
21  import javax.enterprise.context.ContextNotActiveException;
22  import javax.enterprise.inject.Typed;
23  import javax.enterprise.inject.spi.BeanManager;
24  
25  import java.lang.annotation.Annotation;
26  import java.util.Map;
27  import javax.enterprise.context.spi.Context;
28  import javax.enterprise.context.spi.Contextual;
29  import javax.enterprise.context.spi.CreationalContext;
30  import javax.enterprise.inject.spi.PassivationCapable;
31  import javax.faces.context.ExternalContext;
32  import javax.faces.context.FacesContext;
33  import javax.faces.view.ViewScoped;
34  
35  import org.apache.myfaces.cdi.util.BeanProvider;
36  import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
37  import org.apache.myfaces.config.ManagedBeanDestroyer;
38  import org.apache.myfaces.config.RuntimeConfig;
39  import org.apache.myfaces.config.annotation.LifecycleProvider;
40  import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
41  import org.apache.myfaces.view.ViewScopeProxyMap;
42  
43  /**
44   * CDI Context to handle @{@link ViewScoped} beans.
45   * 
46   * @author Leonardo Uribe
47   */
48  @Typed()
49  public class ViewScopeContextImpl implements Context
50  {
51  
52      /**
53       * needed for serialisation and passivationId
54       */
55      private BeanManager beanManager;
56  
57  
58      public ViewScopeContextImpl(BeanManager beanManager)
59      {
60          this.beanManager = beanManager;
61      }
62  
63      protected ViewScopeBeanHolder getViewScopeBeanHolder()
64      {
65          return getViewScopeBeanHolder(FacesContext.getCurrentInstance());
66      }
67      
68      protected ViewScopeBeanHolder getViewScopeBeanHolder(FacesContext facesContext)
69      {
70          ViewScopeBeanHolder viewScopeBeanHolder = (ViewScopeBeanHolder) 
71              facesContext.getExternalContext().getApplicationMap().get(
72                  "oam.view.ViewScopeBeanHolder");
73          if (viewScopeBeanHolder == null)
74          {
75              viewScopeBeanHolder = BeanProvider.getContextualReference(
76                  beanManager, ViewScopeBeanHolder.class, false);
77              facesContext.getExternalContext().getApplicationMap().put(
78                  "oam.view.ViewScopeBeanHolder", viewScopeBeanHolder);
79          }
80          return viewScopeBeanHolder;
81      }
82  
83      public String getCurrentViewScopeId(boolean create)
84      {
85          FacesContext facesContext = FacesContext.getCurrentInstance();
86          ViewScopeProxyMap map = (ViewScopeProxyMap) facesContext.getViewRoot().getViewMap(create);
87          if (map != null)
88          {
89              String id = map.getViewScopeId();
90              if (id == null && create)
91              {
92                  // Force create
93                  map.forceCreateWrappedMap(facesContext);
94                  id = map.getViewScopeId();
95              }
96              return id;
97          }
98          return null;
99      }
100 
101     protected ViewScopeContextualStorage getContextualStorage(boolean createIfNotExist)
102     {
103         String viewScopeId = getCurrentViewScopeId(createIfNotExist);
104         if (createIfNotExist && viewScopeId == null)
105         {
106             throw new ContextNotActiveException(
107                 "ViewScopeContextImpl: no viewScopeId set for the current view yet!");
108         }
109         if (viewScopeId != null)
110         {
111             return getViewScopeBeanHolder().getContextualStorage(beanManager, viewScopeId);
112         }
113         return null;
114     }
115 
116     public Class<? extends Annotation> getScope()
117     {
118         return ViewScoped.class;
119     }
120 
121     /**
122      * The WindowContext is active once a current windowId is set for the current Thread.
123      * @return
124      */
125     public boolean isActive()
126     {
127         FacesContext facesContext = FacesContext.getCurrentInstance();
128         return facesContext.getViewRoot() != null;
129     }
130 
131     public <T> T get(Contextual<T> bean)
132     {
133         checkActive();
134 
135         // force session creation if ViewScoped is used
136         FacesContext.getCurrentInstance().getExternalContext().getSession(true);
137 
138         ViewScopeContextualStorage storage = getContextualStorage(false);
139         if (storage == null)
140         {
141             return null;
142         }
143 
144         Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
145         ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
146         if (contextualInstanceInfo == null)
147         {
148             return null;
149         }
150 
151         return (T) contextualInstanceInfo.getContextualInstance();
152     }
153 
154     public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
155     {
156         checkActive();
157 
158         if (!(bean instanceof PassivationCapable))
159         {
160             throw new IllegalStateException(bean.toString() +
161                     " doesn't implement " + PassivationCapable.class.getName());
162         }
163 
164         // force session creation if ViewScoped is used
165         FacesContext.getCurrentInstance().getExternalContext().getSession(true);
166 
167         ViewScopeContextualStorage storage = getContextualStorage(true);
168 
169         Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
170         ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
171 
172         if (contextualInstanceInfo != null)
173         {
174             @SuppressWarnings("unchecked")
175             final T instance =  (T) contextualInstanceInfo.getContextualInstance();
176 
177             if (instance != null)
178             {
179                 return instance;
180             }
181         }
182 
183         return storage.createContextualInstance(bean, creationalContext);
184     }
185 
186     /**
187      * Destroy the Contextual Instance of the given Bean.
188      * @param bean dictates which bean shall get cleaned up
189      * @return <code>true</code> if the bean was destroyed, <code>false</code> if there was no such bean.
190      */
191     public boolean destroy(Contextual bean)
192     {
193         ViewScopeContextualStorage storage = getContextualStorage(false);
194         if (storage == null)
195         {
196             return false;
197         }
198         ContextualInstanceInfo<?> contextualInstanceInfo = 
199             storage.getStorage().get(storage.getBeanKey(bean));
200 
201         if (contextualInstanceInfo == null)
202         {
203             return false;
204         }
205 
206         bean.destroy(contextualInstanceInfo.getContextualInstance(), 
207             contextualInstanceInfo.getCreationalContext());
208 
209         return true;
210     }
211 
212     /**
213      * destroys all the Contextual Instances in the Storage returned by
214      * {@link #getContextualStorage(boolean)}.
215      */
216     public void destroyAllActive()
217     {
218         ViewScopeContextualStorage storage = getContextualStorage(false);
219         if (storage == null)
220         {
221             return;
222         }
223 
224         destroyAllActive(storage);
225     }
226 
227     public static void destroyAllActive(ViewScopeContextualStorage storage)
228     {
229         destroyAllActive(storage, FacesContext.getCurrentInstance());
230     }
231 
232     public static void destroyAllActive(ViewScopeContextualStorage storage, FacesContext facesContext)
233     {
234         Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
235         ManagedBeanDestroyer mbDestroyer = 
236             getManagedBeanDestroyer(facesContext.getExternalContext());
237         
238         for (Map.Entry<Object, ContextualInstanceInfo<?>> entry : contextMap.entrySet())
239         {
240             if (!(entry.getKey() instanceof _ContextualKey))
241             {            
242                 Contextual bean = storage.getBean(facesContext, entry.getKey());
243 
244                 ContextualInstanceInfo<?> contextualInstanceInfo = entry.getValue();
245                 bean.destroy(contextualInstanceInfo.getContextualInstance(), 
246                     contextualInstanceInfo.getCreationalContext());
247             }
248             else
249             {
250                 // Destroy the JSF managed view scoped bean.
251                 _ContextualKey key = (_ContextualKey) entry.getKey();
252                 mbDestroyer.destroy(key.getName(), entry.getValue().getContextualInstance());
253             }
254         }
255 
256         contextMap.clear();
257         
258         storage.deactivate();
259     }
260     
261     /**
262      * Make sure that the Context is really active.
263      * @throws ContextNotActiveException if there is no active
264      *         Context for the current Thread.
265      */
266     protected void checkActive()
267     {
268         if (!isActive())
269         {
270             throw new ContextNotActiveException("CDI context with scope annotation @"
271                 + getScope().getName() + " is not active with respect to the current thread");
272         }
273     }
274 
275     protected static ManagedBeanDestroyer getManagedBeanDestroyer(ExternalContext externalContext)
276     {
277         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
278         LifecycleProvider lifecycleProvider = LifecycleProviderFactory
279                 .getLifecycleProviderFactory(externalContext).getLifecycleProvider(externalContext);
280 
281         return new ManagedBeanDestroyer(lifecycleProvider, runtimeConfig);
282     }
283 }