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.flow.cdi;
20  
21  import java.lang.annotation.Annotation;
22  import java.util.List;
23  import java.util.Map;
24  import javax.enterprise.context.ContextNotActiveException;
25  import javax.enterprise.context.spi.Context;
26  import javax.enterprise.context.spi.Contextual;
27  import javax.enterprise.context.spi.CreationalContext;
28  import javax.enterprise.inject.Typed;
29  import javax.enterprise.inject.spi.Bean;
30  import javax.enterprise.inject.spi.BeanManager;
31  import javax.enterprise.inject.spi.PassivationCapable;
32  import javax.faces.context.FacesContext;
33  import javax.faces.flow.Flow;
34  import javax.faces.flow.FlowHandler;
35  import javax.faces.flow.FlowScoped;
36  import org.apache.myfaces.cdi.util.BeanProvider;
37  import org.apache.myfaces.cdi.util.ContextualInstanceInfo;
38  import org.apache.myfaces.cdi.util.ContextualStorage;
39  import org.apache.myfaces.flow.FlowReference;
40  import org.apache.myfaces.flow.util.FlowUtils;
41  
42  /**
43   * Implementation of FlowScope.
44   * 
45   * @author Leonardo Uribe
46   */
47  @Typed()
48  public class FlowScopedContextImpl implements Context
49  {
50      /**
51       * Whether the Context is for a passivating scope.
52       */
53      private final boolean passivatingScope;
54  
55      /**
56       * needed for serialisation and passivationId
57       */
58      private BeanManager beanManager;
59      
60      private Map<Class, FlowReference> flowBeanReferences;
61  
62      //private FlowScopeBeanHolder flowScopeBeanHolder;
63      
64      public FlowScopedContextImpl(BeanManager beanManager, 
65          Map<Class, FlowReference> flowBeanReferences)
66      {
67          this.beanManager = beanManager;
68          this.flowBeanReferences = flowBeanReferences;
69          this.passivatingScope = beanManager.isPassivatingScope(getScope());
70      }
71      
72      /*
73      public void initFlowContext(FlowScopeBeanHolder flowScopeBeanHolder)
74      {
75          this.flowScopeBeanHolder = flowScopeBeanHolder;
76      }*/
77      
78      protected FlowScopeBeanHolder getFlowScopeBeanHolder()
79      {
80          return getFlowScopeBeanHolder(FacesContext.getCurrentInstance());
81      }
82      
83      protected FlowScopeBeanHolder getFlowScopeBeanHolder(FacesContext facesContext)
84      {
85          FlowScopeBeanHolder flowScopeBeanHolder = (FlowScopeBeanHolder) 
86              facesContext.getExternalContext().getApplicationMap().get(
87                  "oam.flow.FlowScopeBeanHolder");
88          if (flowScopeBeanHolder == null)
89          {
90              flowScopeBeanHolder = BeanProvider.getContextualReference(
91                  beanManager, FlowScopeBeanHolder.class, false);
92              facesContext.getExternalContext().getApplicationMap().put(
93                  "oam.flow.FlowScopeBeanHolder", flowScopeBeanHolder);
94          }
95          return flowScopeBeanHolder;
96      }
97      
98      public String getCurrentClientWindowFlowId(FacesContext facesContext)
99      {
100         String flowMapKey = null;
101         FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
102         Flow flow = flowHandler.getCurrentFlow(facesContext);
103         if (flow != null)
104         {
105             flowMapKey = FlowUtils.getFlowMapKey(facesContext, flow);
106         }
107         return flowMapKey;
108     }
109 
110     /**
111      * An implementation has to return the underlying storage which
112      * contains the items held in the Context.
113      * @param createIfNotExist whether a ContextualStorage shall get created if it doesn't yet exist.
114      * @return the underlying storage
115      */
116     protected ContextualStorage getContextualStorage(boolean createIfNotExist, String clientWindowFlowId)
117     {
118         //FacesContext facesContext = FacesContext.getCurrentInstance();
119         //String clientWindowFlowId = getCurrentClientWindowFlowId(facesContext);
120         if (clientWindowFlowId == null)
121         {
122             throw new ContextNotActiveException("FlowScopedContextImpl: no current active flow");
123         }
124 
125         if (createIfNotExist)
126         {
127             return getFlowScopeBeanHolder().getContextualStorage(beanManager, clientWindowFlowId);
128         }
129         else
130         {
131             return getFlowScopeBeanHolder().getContextualStorageNoCreate(beanManager, clientWindowFlowId);
132         }
133     }
134 
135     @Override
136     public Class<? extends Annotation> getScope()
137     {
138         return FlowScoped.class;
139     }
140 
141     @Override
142     public boolean isActive()
143     {
144         return isActive(FacesContext.getCurrentInstance());
145     }
146 
147     public boolean isActive(FacesContext facesContext)
148     {
149         if (facesContext == null)
150         {
151             return false;
152         }
153         Flow flow = facesContext.getApplication().
154             getFlowHandler().getCurrentFlow(facesContext);
155         
156         return flow != null;
157     }
158 
159     /**
160      * @return whether the served scope is a passivating scope
161      */
162     public boolean isPassivatingScope()
163     {
164         return passivatingScope;
165     }
166 
167     @Override
168     public <T> T get(Contextual<T> bean)
169     {
170         FacesContext facesContext = FacesContext.getCurrentInstance();
171 
172         checkActive(facesContext);
173 
174         
175         FlowReference reference = flowBeanReferences.get(((Bean)bean).getBeanClass());
176         if (reference != null)
177         {
178             String flowMapKey = FlowUtils.getFlowMapKey(facesContext, reference);
179             if (flowMapKey != null)
180             {
181                 ContextualStorage storage = getContextualStorage(false, flowMapKey);
182                 if (storage != null)
183                 {
184                     Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
185                     ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
186 
187                     if (contextualInstanceInfo != null)
188                     {
189                         return (T) contextualInstanceInfo.getContextualInstance();
190                     }
191                 }
192             }
193             else
194             {
195                 throw new IllegalStateException("Flow "+ reference.getId()+
196                     " cannot be found when resolving bean " +bean.toString());
197             }
198         }
199         
200         List<String> activeFlowMapKeys = getFlowScopeBeanHolder().getActiveFlowMapKeys(facesContext);
201         for (String flowMapKey : activeFlowMapKeys)
202         {
203             ContextualStorage storage = getContextualStorage(false, flowMapKey);
204             if (storage == null)
205             {
206                 //return null;
207                 continue;
208             }
209 
210             Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
211             ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
212             if (contextualInstanceInfo == null)
213             {
214                 //return null;
215                 continue;
216             }
217 
218             return (T) contextualInstanceInfo.getContextualInstance();
219         }
220         return null;
221     }
222 
223     @Override
224     public <T> T get(Contextual<T> bean, CreationalContext<T> creationalContext)
225     {
226         FacesContext facesContext = FacesContext.getCurrentInstance();
227         
228         checkActive(facesContext);
229 
230         if (passivatingScope && !(bean instanceof PassivationCapable))
231         {
232             throw new IllegalStateException(bean.toString() +
233                     " doesn't implement " + PassivationCapable.class.getName());
234         }
235         
236         FlowReference reference = flowBeanReferences.get(((Bean)bean).getBeanClass());
237         if (reference != null)
238         {
239             String flowMapKey = FlowUtils.getFlowMapKey(facesContext, reference);
240             if (flowMapKey != null)
241             {
242                 ContextualStorage storage = getContextualStorage(false, flowMapKey);
243                 if (storage != null)
244                 {
245                     Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
246                     ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
247 
248                     if (contextualInstanceInfo != null)
249                     {
250                         return (T) contextualInstanceInfo.getContextualInstance();
251                     }
252                 }
253             }
254             else
255             {
256                 throw new IllegalStateException("Flow "+ reference.getId()+
257                     " cannot be found when resolving bean " + bean.toString());
258             }
259             
260             FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
261             // Since it is possible to have only the flow id without documentId, the best
262             // is first get the flow using flowHandler.getFlow and then check if the flow is
263             // active or not, but using the documentId and id of the retrieved flow.
264             Flow flow = flowHandler.getFlow(facesContext, 
265                 reference.getDocumentId() == null ? "" : reference.getDocumentId(), reference.getId());
266             if (flow == null)
267             {
268                 throw new IllegalStateException(bean.toString() + "cannot be created because flow "+ reference.getId()+
269                     " is not registered");
270             }
271             if (!flowHandler.isActive(facesContext, flow.getDefiningDocumentId(), flow.getId())) 
272             {
273                 throw new IllegalStateException(bean.toString() + "cannot be created if flow "+ reference.getId()+
274                     " is not active");
275             }
276             
277             ContextualStorage storage = getContextualStorage(true, flowMapKey);
278             Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
279             ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
280 
281             if (contextualInstanceInfo != null)
282             {
283                 @SuppressWarnings("unchecked")
284                 final T instance =  (T) contextualInstanceInfo.getContextualInstance();
285 
286                 if (instance != null)
287                 {
288                     return instance;
289                 }
290             }
291 
292             return storage.createContextualInstance(bean, creationalContext);
293         }
294 
295         List<String> activeFlowMapKeys = getFlowScopeBeanHolder().getActiveFlowMapKeys(facesContext);
296         for (String flowMapKey : activeFlowMapKeys)
297         {
298             ContextualStorage storage = getContextualStorage(false, flowMapKey);
299 
300             Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
301             ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
302 
303             if (contextualInstanceInfo != null)
304             {
305                 @SuppressWarnings("unchecked")
306                 final T instance =  (T) contextualInstanceInfo.getContextualInstance();
307 
308                 if (instance != null)
309                 {
310                     return instance;
311                 }
312             }
313 
314         }
315         
316         ContextualStorage storage = getContextualStorage(true, getCurrentClientWindowFlowId(facesContext));
317         Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
318         ContextualInstanceInfo<?> contextualInstanceInfo = contextMap.get(storage.getBeanKey(bean));
319 
320         if (contextualInstanceInfo != null)
321         {
322             @SuppressWarnings("unchecked")
323             final T instance =  (T) contextualInstanceInfo.getContextualInstance();
324 
325             if (instance != null)
326             {
327                 return instance;
328             }
329         }
330             
331         return storage.createContextualInstance(bean, creationalContext);
332     }
333 
334     /**
335      * Destroy the Contextual Instance of the given Bean.
336      * @param bean dictates which bean shall get cleaned up
337      * @return <code>true</code> if the bean was destroyed, <code>false</code> if there was no such bean.
338      */
339     public boolean destroy(Contextual bean)
340     {
341         FacesContext facesContext = FacesContext.getCurrentInstance();
342         List<String> activeFlowMapKeys = getFlowScopeBeanHolder().getActiveFlowMapKeys(facesContext);
343         for (String flowMapKey : activeFlowMapKeys)
344         {
345             ContextualStorage storage = getContextualStorage(false, flowMapKey);
346             if (storage == null)
347             {
348                 //return false;
349                 continue;
350             }
351             ContextualInstanceInfo<?> contextualInstanceInfo = storage.getStorage().get(storage.getBeanKey(bean));
352 
353             if (contextualInstanceInfo == null)
354             {
355                 //return false;
356                 continue;
357             }
358 
359             bean.destroy(contextualInstanceInfo.getContextualInstance(), contextualInstanceInfo.getCreationalContext());
360             return true;
361         }
362         return false;
363     }
364 
365     /**
366      * destroys all the Contextual Instances in the Storage returned by
367      * {@link #getContextualStorage(boolean)}.
368      */
369     /*
370     public void destroyAllActive()
371     {
372         ContextualStorage storage = getContextualStorage(false);
373         if (storage == null)
374         {
375             return;
376         }
377 
378         destroyAllActive(storage);
379     }*/
380 
381     /**
382      * Destroys all the Contextual Instances in the specified ContextualStorage.
383      * This is a static method to allow various holder objects to cleanup
384      * properly in &#064;PreDestroy.
385      */
386     public static void destroyAllActive(ContextualStorage storage)
387     {
388         Map<Object, ContextualInstanceInfo<?>> contextMap = storage.getStorage();
389         for (Map.Entry<Object, ContextualInstanceInfo<?>> entry : contextMap.entrySet())
390         {
391             if (!FlowScopeBeanHolder.CURRENT_FLOW_SCOPE_MAP.equals(entry.getKey()))
392             {
393                 Contextual bean = storage.getBean(entry.getKey());
394 
395                 ContextualInstanceInfo<?> contextualInstanceInfo = entry.getValue();
396                 bean.destroy(contextualInstanceInfo.getContextualInstance(), 
397                     contextualInstanceInfo.getCreationalContext());
398             }
399         }
400     }
401 
402     /**
403      * Make sure that the Context is really active.
404      * @throws ContextNotActiveException if there is no active
405      *         Context for the current Thread.
406      */
407     protected void checkActive(FacesContext facesContext)
408     {
409         if (!isActive(facesContext))
410         {
411             throw new ContextNotActiveException("CDI context with scope annotation @"
412                 + getScope().getName() + " is not active with respect to the current thread");
413         }
414     }
415 
416 }