From fb8fb0e2f807aff5b56309acb692ed7d32b5ed31 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Fri, 29 Jul 2011 11:05:45 +1000 Subject: [PATCH] 353095 - maven-jetty-plugin: PermGen leak due to javax.el.BeanELResolver Also found that java beans can be leaked via java.beans.Introspector so fixed that too. --- .../servlet/listener/ELContextCleaner.java | 120 ++++++++++++++++++ .../servlet/listener/IntrospectorCleaner.java | 40 ++++++ .../src/main/config/etc/webdefault.xml | 16 +++ .../eclipse/jetty/webapp/WebAppContext.java | 1 + 4 files changed, 177 insertions(+) create mode 100644 jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java create mode 100644 jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/IntrospectorCleaner.java diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java new file mode 100644 index 00000000000..221f4127b47 --- /dev/null +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java @@ -0,0 +1,120 @@ +// ======================================================================== +// 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.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +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; + +/** + * 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 +{ + + 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.warn("Cannot purge classes from javax.el.BeanELResolver", e); + } + + } + + + 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 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()); + } + } +} diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/IntrospectorCleaner.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/IntrospectorCleaner.java new file mode 100644 index 00000000000..715152abe5e --- /dev/null +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/IntrospectorCleaner.java @@ -0,0 +1,40 @@ +// ======================================================================== +// 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 javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * IntrospectorCleaner + * + * Cleans a static cache of Methods held by java.beans.Introspector + * class when a context is undeployed. + * + * @see java.beans.Introspector + */ +public class IntrospectorCleaner implements ServletContextListener +{ + + public void contextInitialized(ServletContextEvent sce) + { + + } + + public void contextDestroyed(ServletContextEvent sce) + { + java.beans.Introspector.flushCaches(); + } + +} diff --git a/jetty-webapp/src/main/config/etc/webdefault.xml b/jetty-webapp/src/main/config/etc/webdefault.xml index c64fedba258..99ee5feda99 100644 --- a/jetty-webapp/src/main/config/etc/webdefault.xml +++ b/jetty-webapp/src/main/config/etc/webdefault.xml @@ -30,6 +30,22 @@ This file is applied to a Web application before it's own WEB_INF/web.xml file + + + + + + org.eclipse.jetty.servlet.listener.ELContextCleaner + + + + + + + + org.eclipse.jetty.servlet.listener.IntrospectorCleaner + + diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index 36de71b2f00..c9dbee06ab8 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -111,6 +111,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL "-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes "-org.eclipse.jetty.websocket.", // don't hide websocket extension "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet + "-org.eclipse.jetty.servlet.listener.", //don't hide useful listeners "org.eclipse.jetty." // hide other jetty classes } ;