// ======================================================================== // Copyright (c) 2011 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.servlet.listener; import java.lang.reflect.Field; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * ELContextCleaner * * Clean up BeanELResolver when the context is going out * of service: * * See http://java.net/jira/browse/GLASSFISH-1649 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=353095 */ public class ELContextCleaner implements ServletContextListener { private static final Logger LOG = Log.getLogger(ELContextCleaner.class); public void contextInitialized(ServletContextEvent sce) { } public void contextDestroyed(ServletContextEvent sce) { try { //Check that the BeanELResolver class is on the classpath Class beanELResolver = Loader.loadClass(this.getClass(), "javax.el.BeanELResolver"); //Get a reference via reflection to the properties field which is holding class references Field field = getField(beanELResolver); //Get rid of references purgeEntries(field); LOG.info("javax.el.BeanELResolver purged"); } catch (ClassNotFoundException e) { //BeanELResolver not on classpath, ignore } catch (SecurityException e) { LOG.warn("Cannot purge classes from javax.el.BeanELResolver", e); } catch (IllegalArgumentException e) { LOG.warn("Cannot purge classes from javax.el.BeanELResolver", e); } catch (IllegalAccessException e) { LOG.warn("Cannot purge classes from javax.el.BeanELResolver", e); } catch (NoSuchFieldException e) { LOG.info("Not cleaning cached beans: no such field javax.el.BeanELResolver.properties"); } } protected Field getField (Class beanELResolver) throws SecurityException, NoSuchFieldException { if (beanELResolver == null) return null; return beanELResolver.getDeclaredField("properties"); } protected void purgeEntries (Field properties) throws IllegalArgumentException, IllegalAccessException { if (properties == null) return; if (!properties.isAccessible()) properties.setAccessible(true); ConcurrentHashMap map = (ConcurrentHashMap) properties.get(null); if (map == null) return; Iterator<Class> itor = map.keySet().iterator(); while (itor.hasNext()) { Class clazz = itor.next(); LOG.info("Clazz: "+clazz+" loaded by "+clazz.getClassLoader()); if (Thread.currentThread().getContextClassLoader().equals(clazz.getClassLoader())) { itor.remove(); LOG.info("removed"); } else LOG.info("not removed: "+"contextclassloader="+Thread.currentThread().getContextClassLoader()+"clazz's classloader="+clazz.getClassLoader()); } } }