Bug 486412 - [http whiteboard] Implement better equality (merged from squashed master impl)

Signed-off-by: Raymond Auge <raymond.auge@liferay.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 2513922..9624f30 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
@@ -8,6 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Raymond Augé <raymond.auge@liferay.com> - Bug 436698
+ *     Juan Gonzalez <juan.gonzalez@liferay.com> - Bug 486412
  *******************************************************************************/
 package org.eclipse.equinox.http.servlet.tests;
 
@@ -2794,7 +2795,56 @@
 			}
 		}
 	}
-	
+
+	public void test_Listener10() throws Exception {
+		BaseServletContextListener scl1 = new BaseServletContextListener();
+		BaseServletContextListener scl2 = new BaseServletContextListener();
+		BaseServletContextListener scl3 = new BaseServletContextListener();
+
+		Dictionary<String, String> listenerProps = new Hashtable<String, String>();
+		listenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		registrations.add(getBundleContext().registerService(ServletContextListener.class, scl1, listenerProps));
+
+		listenerProps = new Hashtable<String, String>();
+		listenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		registrations.add(getBundleContext().registerService(ServletContextListener.class, scl2, listenerProps));
+
+		Dictionary<String, String> contextProps = new Hashtable<String, String>();
+		contextProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "a");
+		contextProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/a");
+		registrations.add(getBundleContext().registerService(ServletContextHelper.class, new ServletContextHelper(){}, contextProps));
+
+		listenerProps = new Hashtable<String, String>();
+		listenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		listenerProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=a)");
+		registrations.add(getBundleContext().registerService(ServletContextListener.class, scl3, listenerProps));
+
+		ServletContext servletContext1 = scl1.servletContext;
+		ServletContext servletContext2 = scl2.servletContext;
+		ServletContext servletContext3 = scl3.servletContext;
+
+		Assert.assertNotNull(servletContext1);
+		Assert.assertNotNull(servletContext2);
+		Assert.assertNotNull(servletContext3);
+
+		Assert.assertTrue(servletContext1.equals(servletContext1));
+		Assert.assertTrue(servletContext2.equals(servletContext2));
+		Assert.assertTrue(servletContext3.equals(servletContext3));
+
+		Assert.assertTrue(servletContext1.equals(servletContext2));
+		Assert.assertFalse(servletContext1.equals(servletContext3));
+		Assert.assertFalse(servletContext2.equals(servletContext3));
+
+		// Asserts two invocations return the same value
+		Assert.assertEquals(servletContext1.hashCode(), servletContext1.hashCode());
+		Assert.assertEquals(servletContext2.hashCode(), servletContext2.hashCode());
+		Assert.assertEquals(servletContext3.hashCode(), servletContext3.hashCode());
+
+		Assert.assertEquals(servletContext1.hashCode(), servletContext2.hashCode());
+		Assert.assertNotEquals(servletContext1.hashCode(), servletContext3.hashCode());
+		Assert.assertNotEquals(servletContext2.hashCode(), servletContext3.hashCode());
+	}
+
 	public void test_Async1() throws Exception {
 
 		Servlet s1 = new BaseAsyncServlet("test_Listener8");
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/BaseServletContextListener.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/BaseServletContextListener.java
index f5bdfa8..d022513 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/BaseServletContextListener.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/BaseServletContextListener.java
@@ -1,28 +1,31 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2016 Raymond Augé and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *     Raymond Augé <raymond.auge@liferay.com> - Bug 436698
+ *     Raymond Augé <raymond.auge@liferay.com> - Bug 436698
+ *     Juan Gonzalez <juan.gonzalez@liferay.com> - Bug 486412
  ******************************************************************************/
 
 package org.eclipse.equinox.http.servlet.tests.util;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import javax.servlet.ServletContext;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 
 /**
- * @author Raymond Augé
+ * @author Raymond Augé
  */
 public class BaseServletContextListener implements ServletContextListener {
 
 	public AtomicBoolean initialized = new AtomicBoolean(false);
 	public AtomicBoolean destroyed = new AtomicBoolean(false);
+	public ServletContext servletContext;
 
 	@Override
 	public void contextDestroyed(ServletContextEvent servletContextEvent) {
@@ -32,6 +35,7 @@
 	@Override
 	public void contextInitialized(ServletContextEvent servletContextEvent) {
 		initialized.set(true);
+		servletContext = servletContextEvent.getServletContext();
 	}
 
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ServletContextAdaptor.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ServletContextAdaptor.java
index 2dc9697..66d2a49 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ServletContextAdaptor.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ServletContextAdaptor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2015 Cognos Incorporated, IBM Corporation and others.
+ * Copyright (c) 2005, 2016 Cognos Incorporated, IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *     Cognos Incorporated - initial API and implementation
  *     IBM Corporation - bug fixes and enhancements
  *     Raymond Augé <raymond.auge@liferay.com> - Bug 436698
+ *     Juan Gonzalez <juan.gonzalez@liferay.com> - Bug 486412
  *******************************************************************************/
 package org.eclipse.equinox.http.servlet.internal.servlet;
 
@@ -54,6 +55,19 @@
 			}
 		}
 
+		try {
+			Method equalsMethod = Object.class.getMethod("equals", Object.class);  //$NON-NLS-1$
+			Method equalsHandlerMethod = ServletContextAdaptor.class.getMethod("equals", Object.class); //$NON-NLS-1$
+			methods.put(equalsMethod, equalsHandlerMethod);
+
+			Method hashCodeMethod = Object.class.getMethod("hashCode", (Class<?>[])null);  //$NON-NLS-1$
+			Method hashCodeHandlerMethod = ServletContextAdaptor.class.getMethod("hashCode", (Class<?>[])null); //$NON-NLS-1$
+			methods.put(hashCodeMethod, hashCodeHandlerMethod);
+		}
+		catch (NoSuchMethodException e) {
+				// do nothing
+		}
+
 		return methods;
 	}
 
@@ -80,10 +94,29 @@
 		Class<?> clazz = getClass();
 		ClassLoader curClassLoader = clazz.getClassLoader();
 		Class<?>[] interfaces = new Class[] {ServletContext.class};
-		InvocationHandler invocationHandler = createInvocationHandler();
 
 		return (ServletContext)Proxy.newProxyInstance(
-			curClassLoader, interfaces, invocationHandler);
+			curClassLoader, interfaces, new AdaptorInvocationHandler());
+	}
+
+	public boolean equals (Object obj) {
+		if (!(obj instanceof ServletContext)) {
+			return false;
+		}
+
+		if (!(Proxy.isProxyClass(obj.getClass())))  {
+			return false;
+		}
+
+		InvocationHandler invocationHandler = Proxy.getInvocationHandler(obj);
+
+		if (!(invocationHandler instanceof AdaptorInvocationHandler)) {
+			return false;
+		}
+
+		AdaptorInvocationHandler adaptorInvocationHandler = (AdaptorInvocationHandler)invocationHandler;
+
+		return contextController.equals(adaptorInvocationHandler.getContextController());
 	}
 
 	public ClassLoader getClassLoader() {
@@ -243,6 +276,10 @@
 		return contextName;
 	}
 
+	public int hashCode() {
+		return contextController.hashCode();
+	}
+
 	public void removeAttribute(String attributeName) {
 		Dictionary<String, Object> attributes = getContextAttributes();
 		Object attributeValue = attributes.remove(attributeName);
@@ -361,24 +398,28 @@
 		}
 	}
 
-	private InvocationHandler createInvocationHandler() {
-		return new InvocationHandler() {
-			public Object invoke(Object proxy, Method method, Object[] args)
-				throws Throwable {
-
-				return ServletContextAdaptor.this.invoke(proxy, method, args);
-			}
-		};
-	}
-
 	private Dictionary<String, Object> getContextAttributes() {
 		return proxyContext.getContextAttributes(contextController);
 	}
 
+	private class AdaptorInvocationHandler implements InvocationHandler {
+		public AdaptorInvocationHandler() {}
+
+		public ContextController getContextController() {
+			return contextController;
+		}
+
+		public Object invoke(Object proxy, Method method, Object[] args)
+			throws Throwable {
+
+			return ServletContextAdaptor.this.invoke(proxy, method, args);
+		}
+	}
+
 	private final AccessControlContext acc;
 	private final Bundle bundle;
 	private final ClassLoader classLoader;
-	private final ContextController contextController;
+	final ContextController contextController;
 	private final String contextName;
 	private final EventListeners eventListeners;
 	private final ProxyContext proxyContext;