Bug 541607 - fixes Eclipse Info center massive memory leak since Photon
Signed-off-by: Raymond Auge <raymond.auge@liferay.com>
Change-Id: I376dbb41fcd8973144aeb18f547ff049ba724ef5
diff --git a/bundles/org.eclipse.equinox.http.jetty/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.jetty/META-INF/MANIFEST.MF
index 8969336..71d2dfd 100644
--- a/bundles/org.eclipse.equinox.http.jetty/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.jetty/META-INF/MANIFEST.MF
@@ -4,7 +4,7 @@
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Bundle-SymbolicName: org.eclipse.equinox.http.jetty
-Bundle-Version: 3.6.200.qualifier
+Bundle-Version: 3.7.0.qualifier
Bundle-Activator: org.eclipse.equinox.http.jetty.internal.Activator
Import-Package: javax.servlet;version="[2.6.0,4.0.0)",
javax.servlet.http;version="[2.6.0,4.0.0)",
diff --git a/bundles/org.eclipse.equinox.http.jetty/pom.xml b/bundles/org.eclipse.equinox.http.jetty/pom.xml
index 55107c4..f240ce7 100644
--- a/bundles/org.eclipse.equinox.http.jetty/pom.xml
+++ b/bundles/org.eclipse.equinox.http.jetty/pom.xml
@@ -21,6 +21,6 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.http.jetty</artifactId>
- <version>3.6.200-SNAPSHOT</version>
+ <version>3.7.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
diff --git a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/JettyConstants.java b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/JettyConstants.java
index 2044bca..e8664d5 100644
--- a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/JettyConstants.java
+++ b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/JettyConstants.java
@@ -151,6 +151,11 @@
public static final String CONTEXT_SESSIONINACTIVEINTERVAL = "context.sessioninactiveinterval"; //$NON-NLS-1$
/**
+ * name="housekeeper.interval" type="Integer"
+ */
+ public static final String HOUSEKEEPER_INTERVAL = "housekeeper.interval"; //$NON-NLS-1$
+
+ /**
* name="customizer.class" type="String" <br />
* (full qualified name of the class that implements
* <code>org.eclipse.equinox.http.jetty.JettyCustomizer</code> and has a public no-arg constructor;
diff --git a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java
index 10a0f3d..f00174c 100644
--- a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java
+++ b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java
@@ -197,6 +197,16 @@
}
}
+ // House Keeper Interval
+ String houseKeeperInterval = Details.getStringProp(context, JettyConstants.HOUSEKEEPER_INTERVAL, null);
+ if (houseKeeperInterval != null) {
+ try {
+ defaultSettings.put(JettyConstants.HOUSEKEEPER_INTERVAL, Long.valueOf(houseKeeperInterval));
+ } catch (NumberFormatException e) {
+ //(log this) ignore
+ }
+ }
+
// Other Info
String otherInfo = Details.getStringProp(context, JettyConstants.OTHER_INFO, null);
if (otherInfo != null)
diff --git a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
index b4d65a4..ea16184 100644
--- a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
+++ b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/HttpServerManager.java
@@ -22,12 +22,12 @@
import java.lang.reflect.Method;
import java.util.*;
import javax.servlet.*;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionIdListener;
+import javax.servlet.http.*;
import org.eclipse.equinox.http.jetty.JettyConstants;
import org.eclipse.equinox.http.jetty.JettyCustomizer;
import org.eclipse.equinox.http.servlet.HttpServiceServlet;
import org.eclipse.jetty.server.*;
+import org.eclipse.jetty.server.session.HouseKeeper;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@@ -159,6 +159,9 @@
try {
server.start();
+
+ HouseKeeper houseKeeper = server.getSessionIdManager().getSessionHouseKeeper();
+ houseKeeper.setIntervalSec(Details.getLong(dictionary, JettyConstants.HOUSEKEEPER_INTERVAL, houseKeeper.getIntervalSec()));
} catch (Exception e) {
throw new ConfigurationException(pid, e.getMessage(), e);
}
@@ -250,24 +253,33 @@
}
}
- public static class InternalHttpServiceServlet implements HttpSessionIdListener, Servlet {
+ public static class InternalHttpServiceServlet implements HttpSessionListener, HttpSessionIdListener, Servlet {
// private static final long serialVersionUID = 7477982882399972088L;
- private Servlet httpServiceServlet = new HttpServiceServlet();
+ private final Servlet httpServiceServlet = new HttpServiceServlet();
private ClassLoader contextLoader;
- private Method method;
+ private final Method sessionDestroyed;
+ private final Method sessionIdChanged;
+
+ public InternalHttpServiceServlet() {
+ Class<?> clazz = httpServiceServlet.getClass();
+
+ try {
+ sessionDestroyed = clazz.getMethod("sessionDestroyed", new Class<?>[] {String.class}); //$NON-NLS-1$
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ try {
+ sessionIdChanged = clazz.getMethod("sessionIdChanged", new Class<?>[] {String.class}); //$NON-NLS-1$
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
@Override
public void init(ServletConfig config) throws ServletException {
ServletContext context = config.getServletContext();
contextLoader = (ClassLoader) context.getAttribute(INTERNAL_CONTEXT_CLASSLOADER);
- Class<?> clazz = httpServiceServlet.getClass();
- try {
- method = clazz.getMethod("sessionIdChanged", new Class<?>[] {String.class}); //$NON-NLS-1$
- } catch (Exception e) {
- throw new ServletException(e);
- }
-
Thread thread = Thread.currentThread();
ClassLoader current = thread.getContextClassLoader();
thread.setContextClassLoader(contextLoader);
@@ -314,12 +326,35 @@
}
@Override
+ public void sessionCreated(HttpSessionEvent event) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void sessionDestroyed(HttpSessionEvent event) {
+ Thread thread = Thread.currentThread();
+ ClassLoader current = thread.getContextClassLoader();
+ thread.setContextClassLoader(contextLoader);
+ try {
+ sessionDestroyed.invoke(httpServiceServlet, event.getSession().getId());
+ } catch (IllegalAccessException e) {
+ // not likely
+ } catch (IllegalArgumentException e) {
+ // not likely
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ } finally {
+ thread.setContextClassLoader(current);
+ }
+ }
+
+ @Override
public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {
Thread thread = Thread.currentThread();
ClassLoader current = thread.getContextClassLoader();
thread.setContextClassLoader(contextLoader);
try {
- method.invoke(httpServiceServlet, oldSessionId);
+ sessionIdChanged.invoke(httpServiceServlet, oldSessionId);
} catch (IllegalAccessException e) {
// not likely
} catch (IllegalArgumentException e) {
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
index 981c2c0..6304f11 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.equinox.http.servlet.tests
Bundle-SymbolicName: org.eclipse.equinox.http.servlet.tests
-Bundle-Version: 1.5.200.qualifier
+Bundle-Version: 1.5.300.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Eclipse-BundleShape: dir
Bundle-Activator: org.eclipse.equinox.http.servlet.tests.bundle.Activator
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml b/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml
index 5d1437d..a9b7e11 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/pom.xml
@@ -19,7 +19,7 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.http.servlet.tests</artifactId>
- <version>1.5.200-SNAPSHOT</version>
+ <version>1.5.300-SNAPSHOT</version>
<packaging>eclipse-test-plugin</packaging>
<build>
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
index 527492c..59dee05 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
@@ -69,6 +69,8 @@
System.setProperty("org.eclipse.jetty.servlet.LEVEL", "OFF");
System.setProperty("org.osgi.service.http.port", "0");
+ System.setProperty("org.eclipse.equinox.http.jetty.context.sessioninactiveinterval", "1");
+ System.setProperty("org.eclipse.equinox.http.jetty.housekeeper.interval", "10");
BundleContext bundleContext = getBundleContext();
installer = new BundleInstaller(TEST_BUNDLES_BINARY_DIRECTORY, bundleContext);
advisor = new BundleAdvisor(bundleContext);
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
index 74a8985..daac630 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
@@ -45,6 +45,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -1819,6 +1820,63 @@
}
@Test
+ public void test_Sessions05_Bug541607_MemoryLeak() throws Exception {
+ final List<String> sessionIds = new CopyOnWriteArrayList<>();
+ HttpSessionListener sessionListener = new HttpSessionListener() {
+
+ @Override
+ public void sessionDestroyed(HttpSessionEvent se) {
+ sessionIds.remove(se.getSession().getId());
+ }
+
+ @Override
+ public void sessionCreated(HttpSessionEvent se) {
+ sessionIds.add(se.getSession().getId());
+ }
+ };
+ HttpServlet sessionServlet = new HttpServlet() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
+ IOException {
+ HttpSession session = request.getSession();
+ response.getWriter().print("created " + session.getId());
+ }
+
+ };
+ ServiceRegistration<Servlet> servletReg = null;
+ ServiceRegistration<HttpSessionListener> sessionListenerReg = null;
+ Dictionary<String, Object> servletProps = new Hashtable<String, Object>();
+ servletProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/sessions");
+
+ try {
+ servletReg = getBundleContext().registerService(Servlet.class, sessionServlet, servletProps);
+ Dictionary<String, String> listenerProps = new Hashtable<String, String>();
+ listenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+ sessionListenerReg = getBundleContext().registerService(HttpSessionListener.class, sessionListener, listenerProps);
+
+ // call the servet 10 times, we should get 10 sessions
+ for (int i = 0; i < 10; i++) {
+ requestAdvisor.request("sessions");
+ }
+
+ assertEquals("Wrong result", 10, sessionIds.size());
+ Thread.sleep(12000); // 12 seconds
+ assertEquals("Wrong result", 0, sessionIds.size());
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ } finally {
+ if (servletReg != null) {
+ servletReg.unregister();
+ }
+ if (sessionListenerReg != null) {
+ sessionListenerReg.unregister();
+ }
+ }
+ }
+
+ @Test
public void test_Resource1() throws Exception {
String expected = "a";
String actual;
diff --git a/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
index 92348f3..d6285ac 100644
--- a/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %bundleName
Bundle-Vendor: %providerName
Bundle-SymbolicName: org.eclipse.equinox.http.servlet
-Bundle-Version: 1.5.200.qualifier
+Bundle-Version: 1.5.300.qualifier
Bundle-Activator: org.eclipse.equinox.http.servlet.internal.Activator
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/bundles/org.eclipse.equinox.http.servlet/pom.xml b/bundles/org.eclipse.equinox.http.servlet/pom.xml
index 9faae4f..a50ce57 100644
--- a/bundles/org.eclipse.equinox.http.servlet/pom.xml
+++ b/bundles/org.eclipse.equinox.http.servlet/pom.xml
@@ -20,6 +20,6 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.http.servlet</artifactId>
- <version>1.5.200-SNAPSHOT</version>
+ <version>1.5.300-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
index 44b55ee..1ad919b 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
@@ -1075,6 +1075,10 @@
}
}
+ public void sessionDestroyed(String sessionId) {
+ httpSessionTracker.invalidate(sessionId, false);
+ }
+
private Map<String, Object> attributes;
private final String targetFilter;
private final ServiceRegistration<ServletContextHelper> defaultContextReg;
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionTracker.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionTracker.java
index c4e7c8a..e783b52 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionTracker.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionTracker.java
@@ -69,7 +69,7 @@
List<HttpSessionAttributeListener> httpSessionAttributeListeners =
eventListeners.get(HttpSessionAttributeListener.class);
- if (!httpSessionListeners.isEmpty()) {
+ if (!httpSessionAttributeListeners.isEmpty()) {
Enumeration<String> enumeration =
httpSessionAdaptor.getAttributeNames();
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java
index a4d64bb..7f5b3c8 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ProxyServlet.java
@@ -53,6 +53,10 @@
this.httpServiceRuntimeImpl = httpServiceRuntimeImpl;
}
+ public void sessionDestroyed(String sessionId) {
+ httpServiceRuntimeImpl.sessionDestroyed(sessionId);
+ }
+
public void sessionIdChanged(String oldSessionId) {
httpServiceRuntimeImpl.fireSessionIdChanged(oldSessionId);
}