Bug 510753 [http servlet] Invoke session listeners when unregistering them. Fix ListenerRegistration.equals so it gets actually removed from available EventListeners

Signed-off-by: Juan Gonzalez <juangon@gmail.com>
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 b2c1a24..a0f8b48 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
@@ -39,6 +39,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.servlet.Filter;
@@ -49,6 +50,7 @@
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
@@ -3127,6 +3129,85 @@
 	}
 
 	@Test
+	public void test_Listener11() throws Exception {
+
+		final AtomicInteger listenerBalance = new AtomicInteger(0);
+		final AtomicReference<HttpSession> sessionReference = new AtomicReference<HttpSession>();
+
+		ServletContextListener scl = new ServletContextListener() {
+			
+			@Override
+			public void contextInitialized(ServletContextEvent arg0) {
+			}
+			
+			@Override
+			public void contextDestroyed(ServletContextEvent arg0) {
+				listenerBalance.decrementAndGet();
+			}
+		};
+
+		HttpSessionListener sl = new HttpSessionListener() {
+
+			@Override
+			public void sessionDestroyed(HttpSessionEvent se) {
+				listenerBalance.incrementAndGet();
+			}
+
+			@Override
+			public void sessionCreated(HttpSessionEvent se) {
+			}
+		};
+
+		Servlet sA = new HttpServlet() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void doGet(
+				HttpServletRequest req, HttpServletResponse resp)
+				throws ServletException, IOException {
+
+				HttpSession session = req.getSession();
+				sessionReference.set(session);
+
+				session.setAttribute("testAttribute", "testValue");
+				PrintWriter writer = resp.getWriter();
+				writer.write("S11 requested");
+			}
+		};
+
+		Collection<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>();
+		try {
+			Dictionary<String, String> scListenerProps = new Hashtable<String, String>();
+			scListenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+			registrations.add(getBundleContext().registerService(ServletContextListener.class, scl, scListenerProps));
+
+			Dictionary<String, String> sListenerProps = new Hashtable<String, String>();
+			sListenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+			registrations.add(getBundleContext().registerService(HttpSessionListener.class, sl, sListenerProps));
+
+			Dictionary<String, String> servletProps1 = new Hashtable<String, String>();
+			servletProps1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "S11");
+			servletProps1.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/s11");
+			registrations.add(getBundleContext().registerService(Servlet.class, sA, servletProps1));
+
+			String result = requestAdvisor.request("s11");
+			Assert.assertEquals("S11 requested", result);
+		}
+		finally {
+			for (ServiceRegistration<?> registration : registrations) {
+				registration.unregister();
+			}
+		}
+
+		//Emulate session expiration to check sessionListener
+		//is only called once (when unregister)
+		HttpSession session = sessionReference.get();
+
+		session.invalidate();
+
+		Assert.assertEquals(0, listenerBalance.get());
+	}
+
 	public void test_Async1() throws Exception {
 
 		Servlet s1 = new BaseAsyncServlet("test_Listener8");
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
index a2d4a72..019c6a3 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
@@ -15,9 +15,7 @@
 import java.net.URISyntaxException;
 import java.security.AccessController;
 import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 import javax.servlet.*;
@@ -786,6 +784,12 @@
 		}
 	}
 
+	public Map<String, HttpSessionAdaptor> getActiveSessions() {
+		checkShutdown();
+
+		return activeSessions;
+	}
+
 	public Set<EndpointRegistration<?>> getEndpointRegistrations() {
 		checkShutdown();
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ListenerRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ListenerRegistration.java
index ccbaf4d..99c9e66 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ListenerRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ListenerRegistration.java
@@ -12,11 +12,12 @@
 package org.eclipse.equinox.http.servlet.internal.registration;
 
 import java.lang.reflect.*;
-import java.util.EventListener;
-import java.util.List;
+import java.util.*;
 import javax.servlet.*;
+import javax.servlet.http.*;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController.ServiceHolder;
+import org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionAdaptor;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.http.runtime.dto.ListenerDTO;
 
@@ -65,6 +66,18 @@
 
 			super.destroy();
 
+			if (classes.contains(HttpSessionBindingListener.class) ||
+				classes.contains(HttpSessionAttributeListener.class) ||
+				classes.contains(HttpSessionListener.class)) {
+
+				Map<String, HttpSessionAdaptor> activeSessions =
+					contextController.getActiveSessions();
+
+				for (HttpSessionAdaptor adaptor : activeSessions.values()) {
+					adaptor.invokeSessionListeners(classes, super.getT());
+				}
+			}
+
 			if (classes.contains(ServletContextListener.class)) {
 				ServletContextListener servletContextListener =
 					(ServletContextListener)super.getT();
@@ -88,7 +101,7 @@
 
 		ListenerRegistration listenerRegistration = (ListenerRegistration)obj;
 
-		return super.getT().equals(listenerRegistration.getT());
+		return listenerRegistration.getT().equals(super.getT());
 	}
 
 	@Override
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java
index 6e6b27c..c53578b 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java
@@ -274,6 +274,38 @@
 		controller.removeActiveSession(session);
 	}
 
+	public void invokeSessionListeners (List<Class<? extends EventListener>> classes, EventListener listener) {
+		if (classes == null) {
+			return;
+		}
+
+		for (Class<? extends EventListener> clazz : classes) {
+			if (clazz.equals(HttpSessionListener.class)){
+				HttpSessionEvent sessionEvent = new HttpSessionEvent(this);
+				HttpSessionListener httpSessionListener = (HttpSessionListener) listener;
+				httpSessionListener.sessionDestroyed(sessionEvent);
+			}
+
+			if (clazz.equals(HttpSessionBindingListener.class) || clazz.equals(HttpSessionAttributeListener.class)) {
+				Enumeration<String> attributeNames = getAttributeNames();
+				while (attributeNames.hasMoreElements()) {
+					String attributeName = attributeNames.nextElement();
+					HttpSessionBindingEvent sessionBindingEvent = new HttpSessionBindingEvent(this, attributeName);
+
+					if (clazz.equals(HttpSessionBindingListener.class)) {
+						HttpSessionBindingListener httpSessionBindingListener = (HttpSessionBindingListener) listener;
+						httpSessionBindingListener.valueUnbound(sessionBindingEvent);
+					}
+
+					if (clazz.equals(HttpSessionAttributeListener.class)) {
+						HttpSessionAttributeListener httpSessionAttributeListener = (HttpSessionAttributeListener) listener;
+						httpSessionAttributeListener.attributeRemoved(sessionBindingEvent);
+					}
+				}
+			}
+		}
+	}
+
 	/**@deprecated*/
 	public void putValue(String arg0, Object arg1) {
 		setAttribute(arg0, arg1);