bug 383910: Track web app failures in the retry controller even they are for the same bundle. Extend the compare function that is used.
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java
index ae9fb33..5f18e79 100644
--- a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2010 VMware Inc.
+ * Copyright (c) 2009, 2012 VMware Inc.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -91,6 +91,18 @@
                 } else if (id1 > id2) {
                     return 1;
                 } else {
+                    return compareHashcode(wa1, wa2);
+                }
+            }
+
+            private int compareHashcode(StandardWebApplication wa1, StandardWebApplication wa2) {
+                long hashcode1 = wa1.hashCode();
+                long hashcode2 = wa2.hashCode();
+                if (hashcode1 < hashcode2) {
+                    return -1;
+                } else if (hashcode1 > hashcode2) {
+                    return 1;
+                } else {
                     return 0;
                 }
             }
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryControllerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryControllerTests.java
new file mode 100644
index 0000000..a622aeb
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryControllerTests.java
@@ -0,0 +1,276 @@
+/*******************************************************************************
+ * Copyright (c) 2012 SAP AG
+ *
+ * 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
+ * and 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.
+ *
+ * Contributors:
+ *   Violeta Georgieva - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.servlet.ServletContext;
+
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class WebApplicationStartFailureRetryControllerTests {
+
+    private static final String SYMBOLIC_NAME = "symbolic-name";
+
+    private static final String FIELD_NAME = "failures";
+
+    private static final String FILTER = "(objectClass=org.osgi.service.event.EventAdmin)";
+
+    private static final String CONTEXT_PATH_1 = "context-path-1";
+
+    private static final String CONTEXT_PATH_2 = "context-path-2";
+
+    private Bundle bundle1;
+
+    private Bundle bundle2;
+
+    private Bundle extender;
+
+    private WebApplicationHandle handle;
+
+    private ServletContainer container;
+
+    private BundleContext thisBundleContext;
+
+    private ServletContext servletContext;
+
+    private EventManager eventManager;
+
+    @Before
+    public void setUp() throws Exception {
+        this.bundle1 = createMock(Bundle.class);
+        this.bundle2 = createMock(Bundle.class);
+        this.extender = createMock(Bundle.class);
+        this.handle = createMock(WebApplicationHandle.class);
+        this.container = createMock(ServletContainer.class);
+        this.thisBundleContext = createMock(BundleContext.class);
+        this.servletContext = createMock(ServletContext.class);
+
+        expect(this.thisBundleContext.createFilter(FILTER)).andReturn(null);
+        expect(this.handle.getServletContext()).andReturn(this.servletContext).anyTimes();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        verify(this.bundle1, this.bundle2, this.extender, this.handle, this.container, this.thisBundleContext, this.servletContext);
+    }
+
+    @Test
+    public void testRecordFailureNoContextPath() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(null);
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        webApplicationStartFailureRetryController.recordFailure(createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController));
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+        assertTrue(((ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController)).size() == 0);
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testRecordFailureWithContextPath() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(CONTEXT_PATH_1).andReturn(CONTEXT_PATH_1).andReturn(CONTEXT_PATH_1).andReturn(
+            CONTEXT_PATH_2);
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        StandardWebApplication failedWebApplication = createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+        webApplicationStartFailureRetryController.recordFailure(createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController));
+        webApplicationStartFailureRetryController.recordFailure(createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController));
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+
+        ConcurrentMap<?, ?> failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 2);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_1)).size() == 2);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_2)).size() == 1);
+
+        webApplicationStartFailureRetryController.clear();
+        assertTrue(((ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController)).size() == 0);
+
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testRetryFailuresNoContextPath() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(null).anyTimes();
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        StandardWebApplication failedWebApplication = createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+
+        ConcurrentMap<?, ?> failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 0);
+
+        webApplicationStartFailureRetryController.retryFailures(failedWebApplication);
+
+        failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 0);
+
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testRetryFailuresTwoBundlesWithDifferentContextPaths() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(CONTEXT_PATH_1).andReturn(CONTEXT_PATH_2).andReturn(CONTEXT_PATH_2);
+        expect(this.bundle2.getBundleId()).andReturn(3L).anyTimes();
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        webApplicationStartFailureRetryController.recordFailure(createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController));
+        StandardWebApplication failedWebApplication = createStandardWebApplication(this.bundle2, webApplicationStartFailureRetryController);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+
+        ConcurrentMap<?, ?> failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 2);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_1)).size() == 1);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_2)).size() == 1);
+
+        webApplicationStartFailureRetryController.retryFailures(failedWebApplication);
+
+        failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 1);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_1)).size() == 1);
+
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testRetryFailuresTwoBundlesWithSameContextPaths() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(CONTEXT_PATH_1).anyTimes();
+        expect(this.bundle1.getBundleId()).andReturn(3L).anyTimes();
+        expect(this.bundle2.getBundleId()).andReturn(2L).anyTimes();
+        expect(this.bundle1.getSymbolicName()).andReturn(SYMBOLIC_NAME);
+        expect(this.bundle1.getHeaders()).andReturn(new Hashtable<String, String>());
+        BundleContext bundleContext = createMock(BundleContext.class);
+        expect(this.bundle1.getBundleContext()).andReturn(bundleContext);
+        this.container.startWebApplication(this.handle);
+        expectLastCall();
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        webApplicationStartFailureRetryController.recordFailure(createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController));
+        StandardWebApplication failedWebApplication = createStandardWebApplication(this.bundle2, webApplicationStartFailureRetryController);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+
+        ConcurrentMap<?, ?> failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 1);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_1)).size() == 2);
+
+        webApplicationStartFailureRetryController.retryFailures(failedWebApplication);
+
+        failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 0);
+
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testRetryFailuresOneBundleWithTwoFailures() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(CONTEXT_PATH_1).anyTimes();
+        expect(this.bundle1.getBundleId()).andReturn(3L).anyTimes();
+        expect(this.bundle1.getSymbolicName()).andReturn(SYMBOLIC_NAME);
+        expect(this.bundle1.getHeaders()).andReturn(new Hashtable<String, String>());
+        BundleContext bundleContext = createMock(BundleContext.class);
+        expect(this.bundle1.getBundleContext()).andReturn(bundleContext);
+        this.container.startWebApplication(this.handle);
+        expectLastCall();
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        webApplicationStartFailureRetryController.recordFailure(createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController));
+        StandardWebApplication failedWebApplication = createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+
+        ConcurrentMap<?, ?> failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 1);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_1)).size() == 2);
+
+        webApplicationStartFailureRetryController.retryFailures(failedWebApplication);
+
+        failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 0);
+
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testRetryFailuresOneBundleWithTwoEqualFailures() throws Exception {
+        expect(this.servletContext.getContextPath()).andReturn(CONTEXT_PATH_1).anyTimes();
+        expect(this.bundle1.getBundleId()).andReturn(3L).anyTimes();
+
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController = createWebApplicationStartFailureRetryController();
+        StandardWebApplication failedWebApplication = createStandardWebApplication(this.bundle1, webApplicationStartFailureRetryController);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+        webApplicationStartFailureRetryController.recordFailure(failedWebApplication);
+
+        Field field = webApplicationStartFailureRetryController.getClass().getDeclaredField(FIELD_NAME);
+        field.setAccessible(true);
+
+        ConcurrentMap<?, ?> failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 1);
+        assertTrue(((Set<?>) failures.get(CONTEXT_PATH_1)).size() == 1);
+
+        webApplicationStartFailureRetryController.retryFailures(failedWebApplication);
+
+        failures = (ConcurrentMap<?, ?>) field.get(webApplicationStartFailureRetryController);
+        assertTrue(failures.size() == 0);
+
+        field.setAccessible(false);
+    }
+
+    private WebApplicationStartFailureRetryController createWebApplicationStartFailureRetryController() {
+        replay(this.bundle1, this.bundle2, this.extender, this.handle, this.container, this.thisBundleContext, this.servletContext);
+        this.eventManager = new EventManager(this.thisBundleContext);
+        return new WebApplicationStartFailureRetryController();
+    }
+
+    private StandardWebApplication createStandardWebApplication(Bundle bundle,
+        WebApplicationStartFailureRetryController webApplicationStartFailureRetryController) {
+        return new StandardWebApplication(bundle, this.extender, this.handle, this.container, this.eventManager,
+            webApplicationStartFailureRetryController, this.thisBundleContext);
+    }
+
+}