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.webapp;
20  
21  import java.util.Enumeration;
22  import javax.faces.context.ExceptionHandler;
23  import javax.faces.context.ExternalContext;
24  import javax.faces.context.FacesContext;
25  
26  import javax.servlet.ServletContext;
27  import javax.servlet.ServletContextAttributeEvent;
28  import javax.servlet.ServletContextAttributeListener;
29  import javax.servlet.ServletContextEvent;
30  import javax.servlet.ServletContextListener;
31  import javax.servlet.ServletRequest;
32  import javax.servlet.ServletRequestAttributeEvent;
33  import javax.servlet.ServletRequestAttributeListener;
34  import javax.servlet.ServletRequestEvent;
35  import javax.servlet.ServletRequestListener;
36  import javax.servlet.http.HttpSessionAttributeListener;
37  import javax.servlet.http.HttpSessionBindingEvent;
38  import javax.servlet.http.HttpSessionEvent;
39  import javax.servlet.http.HttpSessionListener;
40  
41  import org.apache.myfaces.config.ManagedBeanDestroyer;
42  import org.apache.myfaces.context.ReleaseableExternalContext;
43  import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
44  import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
45  import org.apache.myfaces.shared.context.ExceptionHandlerImpl;
46  import org.apache.myfaces.spi.ViewScopeProvider;
47  
48  /**
49   * Listens to
50   *   - removing, replacing of attributes in context, session and request
51   *   - destroying of context, session and request
52   * for the ManagedBeanDestroyer to assure right destruction of managed beans in those scopes.
53   * 
54   * This listener is not registered in a tld or web.xml, but will be called by StartupServletContextListener.
55   * 
56   * @author Jakob Korherr (latest modification by $Author$)
57   * @version $Revision$ $Date$
58   * @since 2.0
59   */
60  public class ManagedBeanDestroyerListener implements 
61          HttpSessionAttributeListener, HttpSessionListener,
62          ServletContextListener, ServletContextAttributeListener,
63          ServletRequestListener, ServletRequestAttributeListener
64  {
65      /**
66       * The instance of the ManagedBeanDestroyerListener created by StartupServletContextListener
67       * is stored under this key in the ApplicationMap.
68       */
69      public static final String APPLICATION_MAP_KEY = ManagedBeanDestroyerListener.class.getName();
70  
71      private ManagedBeanDestroyer _destroyer = null;
72      private ViewScopeProvider _viewScopeHandler = null;
73      
74      @Override
75      public void contextInitialized(ServletContextEvent event)
76      {
77  
78      }
79      
80      @Override
81      public void contextDestroyed(ServletContextEvent event)
82      {
83          if (_destroyer != null)
84          {
85              ServletContext ctx = event.getServletContext();
86              Enumeration<String> attributes = ctx.getAttributeNames();
87              if (!attributes.hasMoreElements())
88              {
89                  // nothing to do
90                  return;
91              }
92  
93              while (attributes.hasMoreElements())
94              {
95                  String name = attributes.nextElement();
96                  Object value = ctx.getAttribute(name);
97                  _destroyer.destroy(name, value);
98              }
99          }
100     }
101 
102     /**
103      * Sets the ManagedBeanDestroyer instance to use.
104      *  
105      * @param destroyer
106      */
107     public void setManagedBeanDestroyer(ManagedBeanDestroyer destroyer)
108     {
109         _destroyer = destroyer;
110     }
111     
112     public void setViewScopeHandler(ViewScopeProvider listener)
113     {
114         _viewScopeHandler = listener;
115     }
116 
117     /* Session related methods ***********************************************/
118     @Override
119     public void attributeAdded(HttpSessionBindingEvent event)
120     {
121         // noop
122     }
123 
124     @Override
125     public void attributeRemoved(HttpSessionBindingEvent event)
126     {
127         if (_destroyer != null)
128         {
129             _destroyer.destroy(event.getName(), event.getValue());
130         }
131     }
132 
133     @Override
134     public void attributeReplaced(HttpSessionBindingEvent event)
135     {
136         if (_destroyer != null)
137         {
138             _destroyer.destroy(event.getName(), event.getValue());
139         }
140     }
141 
142     @Override
143     public void sessionCreated(HttpSessionEvent event)
144     {
145         // noop
146     }
147 
148     @Override
149     public void sessionDestroyed(HttpSessionEvent event)
150     {
151         // MYFACES-3040 @PreDestroy Has Called 2 times
152         // attributeRemoved receives the event too, so it does not 
153         // have sense to handle it here. Unfortunately, it is not possible to 
154         // handle it first and then on attributeRemoved, so the best bet is
155         // let the code in just one place.
156         /*
157         if (_destroyer != null)
158         {
159             HttpSession session = event.getSession();
160             Enumeration<String> attributes = session.getAttributeNames();
161             if (!attributes.hasMoreElements())
162             {
163                 // nothing to do
164                 return;
165             }
166 
167             while (attributes.hasMoreElements())
168             {
169                 String name = attributes.nextElement();
170                 Object value = session.getAttribute(name);
171                 _destroyer.destroy(name, value);
172             }
173         }*/
174         
175         // If we don't propagate this event, CDI will do for us but outside JSF control
176         // so when @PreDestroy methods are called there will not be an active FacesContext.
177         // The trick here is ensure clean the affected scopes to avoid duplicates.
178         // Remember cdi session scope is different from jsf session scope, because in
179         // jsf case the beans are stored under a session attribute, so it has the problem
180         // with attributeRemoved, but on cdi a wrapper is used instead, avoiding the problem.
181         if (_viewScopeHandler != null)
182         {
183             FacesContext facesContext = FacesContext.getCurrentInstance();
184             if (facesContext != null)
185             {
186                 _viewScopeHandler.onSessionDestroyed();
187             }
188             else
189             {
190                 // In case no FacesContext is available, we are on session invalidation
191                 // through timeout. In that case, create a dummy FacesContext for this one
192                 // like the one used in startup or shutdown and invoke the destroy method.
193                 try
194                 {
195                     ServletContext servletContext = event.getSession().getServletContext();
196                     ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, false);
197                     ((StartupServletExternalContextImpl)externalContext).setSession(event.getSession());
198                     ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
199                     facesContext = new StartupFacesContextImpl(externalContext, 
200                             (ReleaseableExternalContext) externalContext, exceptionHandler, false);
201                     _viewScopeHandler.onSessionDestroyed();
202                 }
203                 finally
204                 {
205                     facesContext.release();
206                 }
207             }
208         }
209     }
210     
211     /* Context related methods ***********************************************/
212     @Override
213     public void attributeAdded(ServletContextAttributeEvent event)
214     {
215         // noop
216     }
217 
218     @Override
219     public void attributeRemoved(ServletContextAttributeEvent event)
220     {
221         if (_destroyer != null)
222         {
223             _destroyer.destroy(event.getName(), event.getValue());
224         }
225     }
226 
227     @Override
228     public void attributeReplaced(ServletContextAttributeEvent event)
229     {
230         if (_destroyer != null)
231         {
232             _destroyer.destroy(event.getName(), event.getValue());
233         }
234     }
235     
236     /* Request related methods ***********************************************/
237     @Override
238     public void attributeAdded(ServletRequestAttributeEvent event)
239     {
240         // noop
241     }
242 
243     @Override
244     public void attributeRemoved(ServletRequestAttributeEvent event)
245     {
246         if (_destroyer != null)
247         {
248             _destroyer.destroy(event.getName(), event.getValue());
249         }
250     }
251 
252     @Override
253     public void attributeReplaced(ServletRequestAttributeEvent event)
254     {
255         if (_destroyer != null)
256         {
257             _destroyer.destroy(event.getName(), event.getValue());
258         }
259     }
260 
261     @Override
262     public void requestInitialized(ServletRequestEvent event)
263     {
264         // noop
265     }
266     
267     @Override
268     public void requestDestroyed(ServletRequestEvent event)
269     {
270         if (_destroyer != null)
271         {
272             ServletRequest request = event.getServletRequest();
273             Enumeration<String> attributes = request.getAttributeNames();
274             if (!attributes.hasMoreElements())
275             {
276                 // nothing to do
277                 return;
278             }
279             
280             while (attributes.hasMoreElements())
281             {
282                 String name = attributes.nextElement();
283                 Object value = request.getAttribute(name);
284                 _destroyer.destroy(name, value);
285             }
286         }
287     }
288 
289 }