You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2009/11/24 02:24:45 UTC

svn commit: r883564 - in /myfaces/core/trunk/impl/src/main/java/org/apache/myfaces: config/ webapp/

Author: lu4242
Date: Tue Nov 24 01:24:37 2009
New Revision: 883564

URL: http://svn.apache.org/viewvc?rev=883564&view=rev
Log:
MYFACES-2396 @PreDestroy method of Bean in CustomScope not invoked (Thanks to Jakob Korherr for this patch)

Added:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanDestroyer.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java
Removed:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/AbstractMyFacesListener.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/MyFacesHttpSessionAttributeListener.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/MyFacesHttpSessionListener.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/MyFacesServletContextListener.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/MyFacesServletRequestListener.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/MyfacesServletRequestAttributeListener.java
Modified:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/StartupServletContextListener.java

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java?rev=883564&r1=883563&r2=883564&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java Tue Nov 24 01:24:37 2009
@@ -64,6 +64,8 @@
 import javax.faces.event.ActionListener;
 import javax.faces.event.PhaseListener;
 import javax.faces.event.PostConstructApplicationEvent;
+import javax.faces.event.PreDestroyCustomScopeEvent;
+import javax.faces.event.PreDestroyViewMapEvent;
 import javax.faces.event.SystemEvent;
 import javax.faces.lifecycle.Lifecycle;
 import javax.faces.lifecycle.LifecycleFactory;
@@ -1723,6 +1725,11 @@
         runtimeConfig.setVariableResolverChainHead(getApplicationObject(VariableResolver.class,
                                                                         dispenser.getVariableResolverIterator(),
                                                                         new VariableResolverImpl()));
+        
+        // configure ManagedBeanDestroyer to listen to PreDestroyCustomScopeEvent and PreDestroyViewMapEvent
+        ManagedBeanDestroyer mbDestroyer = new ManagedBeanDestroyer();
+        application.subscribeToEvent(PreDestroyCustomScopeEvent.class, mbDestroyer);
+        application.subscribeToEvent(PreDestroyViewMapEvent.class, mbDestroyer);
     }
 
     /**

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanDestroyer.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanDestroyer.java?rev=883564&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanDestroyer.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanDestroyer.java Tue Nov 24 01:24:37 2009
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.config;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PreDestroyCustomScopeEvent;
+import javax.faces.event.PreDestroyViewMapEvent;
+import javax.faces.event.ScopeContext;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+
+import org.apache.myfaces.config.annotation.LifecycleProvider;
+import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
+
+/**
+ * Destroyes managed beans with the current LifecycleProvider.
+ * This guarantees the invocation of the @PreDestroy methods.
+ * @author Jakob Korherr (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * @since 2.0
+ */
+public class ManagedBeanDestroyer implements SystemEventListener
+{
+    
+    private static Logger log = Logger.getLogger(ManagedBeanDestroyer.class.getName());
+    
+    private RuntimeConfig runtimeConfig;
+    
+    public void setRuntimeConfig(RuntimeConfig runtimeConfig)
+    {
+        this.runtimeConfig = runtimeConfig;
+    }
+    
+    public RuntimeConfig getRuntimeConfig()
+    {
+        if (runtimeConfig == null)
+        {
+            ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
+            runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
+        }
+        return runtimeConfig;
+    }
+    
+    public boolean isListenerForSource(Object source)
+    {
+        // source of PreDestroyCustomScopeEvent is ScopeContext
+        // and source of PreDestroyViewMapEvent is UIViewRoot
+        return (source instanceof ScopeContext) || (source instanceof UIViewRoot);
+    }
+
+
+    /**
+     * Listens to PreDestroyCustomScopeEvent and PreDestroyViewMapEvent
+     * and invokes destroy() for every managed bean in the associated scope.
+     */
+    public void processEvent(SystemEvent event)
+    {
+        Map<String, Object> scope = null;
+        
+        if (event instanceof PreDestroyViewMapEvent)
+        {
+            UIViewRoot viewRoot = (UIViewRoot) ((PreDestroyViewMapEvent) event).getComponent();
+            scope = viewRoot.getViewMap(false);
+            if (scope == null)
+            {
+                // view map does not exist --> nothing to destroy
+                return;
+            }
+        }
+        else if (event instanceof PreDestroyCustomScopeEvent)
+        {
+            ScopeContext scopeContext = ((PreDestroyCustomScopeEvent) event).getContext();
+            scope = scopeContext.getScope();
+        }
+        else
+        {
+            // wrong event
+            return;
+        }
+        
+        LifecycleProvider provider = getCurrentLifecycleProvider();
+        for (String key : scope.keySet())
+        {
+            Object value = scope.get(key);
+            this.destroy(key, value, provider);
+        }
+    }
+    
+    /**
+     * Checks if the given managed bean exists in the RuntimeConfig.
+     * @param name
+     * @return
+     */
+    public boolean isManagedBean(String name)
+    {
+        return getRuntimeConfig().getManagedBean(name) != null;
+    }
+    
+    /**
+     * Destroys the given managed bean with the current LifecycleProvider.
+     * @param name
+     * @param instance
+     */
+    public void destroy(String name, Object instance)
+    {
+        destroy(name, instance, getCurrentLifecycleProvider());
+    }
+    
+    /**
+     * Destroys the given managed bean with the given LifecycleProvider
+     * @param name
+     * @param instance
+     * @param provider
+     */
+    public void destroy(String name, Object instance, LifecycleProvider provider)
+    {
+        if (isManagedBean(name)) {
+            if (provider == null)
+            {
+                provider = getCurrentLifecycleProvider();
+            }
+            try
+            {
+                provider.destroyInstance(instance);
+            } 
+            catch (IllegalAccessException e)
+            {
+                log.log(Level.SEVERE, "Could not access @PreDestroy method of managed bean " + name, e);
+            } 
+            catch (InvocationTargetException e)
+            {
+                log.log(Level.SEVERE, "An Exception occured while invoking " +
+                        "@PreDestroy method of managed bean " + name, e);
+            }
+        }
+    }
+    
+    /**
+     * Retrieves the current LifecycleProvider from the LifecycleProviderFactory
+     * @return
+     */
+    public LifecycleProvider getCurrentLifecycleProvider()
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        if (facesContext == null)
+        {
+            return LifecycleProviderFactory.getLifecycleProviderFactory().getLifecycleProvider(null);
+        }
+        ExternalContext externalContext = facesContext.getExternalContext();
+        return LifecycleProviderFactory.getLifecycleProviderFactory().getLifecycleProvider(externalContext);
+    }
+    
+}

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java?rev=883564&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java (added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/ManagedBeanDestroyerListener.java Tue Nov 24 01:24:37 2009
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.webapp;
+
+import java.util.Enumeration;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.myfaces.config.ManagedBeanDestroyer;
+import org.apache.myfaces.config.RuntimeConfig;
+import org.apache.myfaces.config.annotation.LifecycleProvider;
+
+/**
+ * Listens to
+ *   - removing, replacing of attributes in context, session and request
+ *   - destroying of context, session and request
+ * for the ManagedBeanDestroyer to assure right destruction of managed beans in those scopes.
+ * 
+ * This listener is not registered in a tld or web.xml, but will be called by StartupServletContextListener.
+ * 
+ * @author Jakob Korherr (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ * @since 2.0
+ */
+public class ManagedBeanDestroyerListener implements 
+        HttpSessionAttributeListener, HttpSessionListener,
+        ServletContextListener, ServletContextAttributeListener,
+        ServletRequestListener, ServletRequestAttributeListener
+{
+    
+    private ManagedBeanDestroyer destroyer = new ManagedBeanDestroyer();
+
+    /* Session related methods */
+    
+    public void attributeAdded(HttpSessionBindingEvent event)
+    {
+        // noop
+    }
+
+    public void attributeRemoved(HttpSessionBindingEvent event)
+    {
+        destroyer.destroy(event.getName(), event.getValue());
+    }
+
+    public void attributeReplaced(HttpSessionBindingEvent event)
+    {
+        destroyer.destroy(event.getName(), event.getValue());
+    }
+
+    public void sessionCreated(HttpSessionEvent event)
+    {
+        // noop
+    }
+
+    @SuppressWarnings("unchecked")
+    public void sessionDestroyed(HttpSessionEvent event)
+    {
+        HttpSession session = event.getSession();
+        Enumeration<String> attributes = session.getAttributeNames();
+        if (!attributes.hasMoreElements())
+        {
+            // nothing to do
+            return;
+        }
+        // optimization: provide the LifecycleProvider, because there could be a lot of elements
+        LifecycleProvider provider = destroyer.getCurrentLifecycleProvider();
+        
+        while (attributes.hasMoreElements())
+        {
+            String name = attributes.nextElement();
+            Object value = session.getAttribute(name);
+            destroyer.destroy(name, value, provider);
+        }
+    }
+    
+    /* Context related methods */
+    
+    public void attributeAdded(ServletContextAttributeEvent event)
+    {
+        // noop
+    }
+
+    public void attributeRemoved(ServletContextAttributeEvent event)
+    {
+        destroyer.destroy(event.getName(), event.getValue());
+    }
+
+    public void attributeReplaced(ServletContextAttributeEvent event)
+    {
+        destroyer.destroy(event.getName(), event.getValue());
+    }
+
+    public void contextInitialized(ServletContextEvent event)
+    {
+        // set the RuntimeConfig of ManagedBeanDestroyer, because
+        // in requestDestroyed, contextDestroyed FacesContext.getCurrentInstance() returns
+        // null and so we wouln't get the RuntimeConfig.
+        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
+        RuntimeConfig config = RuntimeConfig.getCurrentInstance(externalContext);
+        destroyer.setRuntimeConfig(config);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public void contextDestroyed(ServletContextEvent event)
+    {
+        ServletContext ctx = event.getServletContext();
+        Enumeration<String> attributes = ctx.getAttributeNames();
+        if (!attributes.hasMoreElements())
+        {
+            // nothing to do
+            return;
+        }
+        // optimization: provide the LifecycleProvider, because there could be a lot of elements
+        LifecycleProvider provider = destroyer.getCurrentLifecycleProvider();
+        
+        while (attributes.hasMoreElements())
+        {
+            String name = attributes.nextElement();
+            Object value = ctx.getAttribute(name);
+            destroyer.destroy(name, value, provider);
+        }
+
+    }
+    
+    /* Request related methods */
+    
+    public void attributeAdded(ServletRequestAttributeEvent event)
+    {
+        // noop
+    }
+
+    public void attributeRemoved(ServletRequestAttributeEvent event)
+    {
+        destroyer.destroy(event.getName(), event.getValue());
+    }
+
+    public void attributeReplaced(ServletRequestAttributeEvent event)
+    {
+        destroyer.destroy(event.getName(), event.getValue());
+    }
+
+    public void requestInitialized(ServletRequestEvent event)
+    {
+        // noop
+    }
+    
+    @SuppressWarnings("unchecked")
+    public void requestDestroyed(ServletRequestEvent event)
+    {        
+        ServletRequest request = event.getServletRequest();
+        Enumeration<String> attributes = request.getAttributeNames();
+        if (!attributes.hasMoreElements())
+        {
+            // nothing to do
+            return;
+        }
+        // optimization: provide the LifecycleProvider, because there could be a lot of elements
+        LifecycleProvider provider = destroyer.getCurrentLifecycleProvider();
+        
+        while (attributes.hasMoreElements())
+        {
+            String name = attributes.nextElement();
+            Object value = request.getAttribute(name);
+            destroyer.destroy(name, value, provider);
+        }
+    }
+
+}

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/StartupServletContextListener.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/StartupServletContextListener.java?rev=883564&r1=883563&r2=883564&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/StartupServletContextListener.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/webapp/StartupServletContextListener.java Tue Nov 24 01:24:37 2009
@@ -18,16 +18,24 @@
  */
 package org.apache.myfaces.webapp;
 
-import java.util.Enumeration;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.faces.FactoryFinder;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
 
-import org.apache.myfaces.config.ManagedBeanBuilder;
 import org.apache.myfaces.shared_impl.util.ClassUtils;
 import org.apache.myfaces.util.ContainerUtils;
 
@@ -43,10 +51,16 @@
  * generated. Very old versions of MyFaces Core do not register the listener in the .tld file, so those also need a
  * manual entry in web.xml. However all versions since at least 1.1.2 have this entry in the tld.
  * 
+ * This listener also delegates all session, request and context events to ManagedBeanDestroyer. 
+ * Because of that we only need to register one listener in the tld.
+ * 
  * @author Manfred Geiler (latest modification by $Author$)
  * @version $Revision$ $Date$
  */
-public class StartupServletContextListener extends AbstractMyFacesListener implements ServletContextListener
+public class StartupServletContextListener implements ServletContextListener,
+        HttpSessionAttributeListener, HttpSessionListener,
+        ServletRequestListener, ServletRequestAttributeListener,
+        ServletContextAttributeListener
 {
     static final String FACES_INIT_DONE = StartupServletContextListener.class.getName() + ".FACES_INIT_DONE";
     /*comma delimited list of plugin classes which can be hooked into myfaces*/
@@ -62,6 +76,7 @@
 
     private FacesInitializer _facesInitializer;
     private ServletContext _servletContext;
+    private ManagedBeanDestroyerListener _detroyerListener = new ManagedBeanDestroyerListener();
 
 
     /**
@@ -88,7 +103,7 @@
                 //for now the initializers have to be stateless to
                 //so that we do not have to enforce that the initializer
                 //must be serializable
-                Class pluginClass = ClassUtils.getContextClassLoader().loadClass(plugin);
+                Class<?> pluginClass = ClassUtils.getContextClassLoader().loadClass(plugin);
                 StartupListener initializer = (StartupListener) pluginClass.newInstance();
                 
                 switch(operation) {
@@ -140,6 +155,9 @@
         {
             log.info("MyFaces already initialized");
         }
+        
+        // call contextInitialized on ManagedBeanDestroyerListener
+        _detroyerListener.contextInitialized(event);
     }
 
     protected FacesInitializer getFacesInitializer()
@@ -179,7 +197,9 @@
 
     public void contextDestroyed(ServletContextEvent event)
     {
-        doPredestroy(event);
+        dispatchInitializationEvent(event, FACES_INIT_PHASE_PREDESTROY);
+        // call contextDestroyed on ManagedBeanDestroyerListener to destroy the attributes
+        _detroyerListener.contextDestroyed(event);
 
         if (_facesInitializer != null && _servletContext != null)
         {
@@ -190,20 +210,77 @@
 
         _servletContext = null;
     }
+    
+    /* the following methods are needed to serve ManagedBeanDestroyerListener */
+    /* Session related methods */
+    
+    public void attributeAdded(HttpSessionBindingEvent event)
+    {
+        _detroyerListener.attributeAdded(event);
+    }
 
-    @SuppressWarnings("unchecked")
-    private void doPredestroy(ServletContextEvent event)
+    public void attributeRemoved(HttpSessionBindingEvent event)
     {
-        ServletContext ctx = event.getServletContext();
-        dispatchInitializationEvent(event, FACES_INIT_PHASE_PREDESTROY);
+        _detroyerListener.attributeRemoved(event);
+    }
 
-        Enumeration<String> attributes = ctx.getAttributeNames();
+    public void attributeReplaced(HttpSessionBindingEvent event)
+    {
+        _detroyerListener.attributeReplaced(event);
+    }
 
-        while (attributes.hasMoreElements())
-        {
-            String name = attributes.nextElement();
-            Object value = ctx.getAttribute(name);
-            doPreDestroy(value, name, ManagedBeanBuilder.APPLICATION);
-        }
+    public void sessionCreated(HttpSessionEvent event)
+    {
+        _detroyerListener.sessionCreated(event);
+    }
+
+    public void sessionDestroyed(HttpSessionEvent event)
+    {
+        _detroyerListener.sessionDestroyed(event);
     }
+    
+    /* Context related methods */
+    
+    public void attributeAdded(ServletContextAttributeEvent event)
+    {
+        _detroyerListener.attributeAdded(event);
+    }
+
+    public void attributeRemoved(ServletContextAttributeEvent event)
+    {
+        _detroyerListener.attributeRemoved(event);
+    }
+
+    public void attributeReplaced(ServletContextAttributeEvent event)
+    {
+        _detroyerListener.attributeReplaced(event);
+    }
+    
+    /* Request related methods */
+    
+    public void attributeAdded(ServletRequestAttributeEvent event)
+    {
+        _detroyerListener.attributeAdded(event);
+    }
+
+    public void attributeRemoved(ServletRequestAttributeEvent event)
+    {
+        _detroyerListener.attributeRemoved(event);
+    }
+
+    public void attributeReplaced(ServletRequestAttributeEvent event)
+    {
+        _detroyerListener.attributeReplaced(event);
+    }
+
+    public void requestInitialized(ServletRequestEvent event)
+    {
+        _detroyerListener.requestInitialized(event);
+    }
+    
+    public void requestDestroyed(ServletRequestEvent event)
+    {        
+        _detroyerListener.requestDestroyed(event);
+    }
+
 }