Bug 549947 - Provide consistent ordering for calling
service/bundle/framework listeners

Change-Id: I8dffb2bf4b8d00d5778b778c6f4f54cbe9ec6667
Signed-off-by: Anjum Fatima <anjum.eclipse@gmail.com>
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java
index fc5a0a2..35b968f 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java
@@ -13,9 +13,24 @@
  *******************************************************************************/
 package org.eclipse.osgi.tests.bundles;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
 import org.eclipse.core.tests.harness.CoreTest;
+import org.eclipse.osgi.launch.Equinox;
 import org.eclipse.osgi.tests.OSGiTestsActivator;
-import org.osgi.framework.*;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
 
 public class AbstractBundleTests extends CoreTest {
 	public static int BUNDLE_LISTENER = 0x01;
@@ -28,6 +43,36 @@
 	public static EventListenerTestResults frameworkListenerResults;
 	public static BundleInstaller installer;
 
+	static class BundleBuilder {
+		static class BundleManifestBuilder {
+			private final Manifest manifest = new Manifest();
+
+			public Manifest build() {
+				manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+				return manifest;
+			}
+
+			public BundleManifestBuilder symbolicName(String value) {
+				manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, value);
+				return this;
+			}
+		}
+
+		private final BundleManifestBuilder manifestBuilder = new BundleManifestBuilder();
+
+		public InputStream build() throws IOException {
+			ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			JarOutputStream jos = new JarOutputStream(baos, manifestBuilder.build());
+			jos.close();
+			return new ByteArrayInputStream(baos.toByteArray());
+		}
+
+		public BundleBuilder symbolicName(String value) {
+			manifestBuilder.symbolicName(value);
+			return this;
+		}
+	}
+
 	protected void setUp() throws Exception {
 		installer = new BundleInstaller(BUNDLES_ROOT, OSGiTestsActivator.getContext());
 		installer.refreshPackages(null);
@@ -210,4 +255,27 @@
 		return result.toString();
 	}
 
+	protected Map<String, Object> createConfiguration() {
+		File file = OSGiTestsActivator.getContext().getDataFile(getName());
+		Map<String, Object> result = new HashMap<String, Object>();
+		result.put(Constants.FRAMEWORK_STORAGE, file.getAbsolutePath());
+		return result;
+	}
+
+	protected void initAndStart(Equinox equinox) throws BundleException {
+		equinox.init();
+		equinox.start();
+	}
+
+	protected void stopQuietly(Equinox equinox) {
+		if (equinox == null)
+			return;
+		try {
+			equinox.stop();
+			equinox.waitForStop(5000);
+		} catch (Exception e) {
+			// Ignore
+		}
+	}
+
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/BundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/BundleTests.java
index 5b04ee4..0b497d5 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/BundleTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/BundleTests.java
@@ -36,6 +36,7 @@
 		suite.addTest(ClassLoadingBundleTests.suite());
 		suite.addTest(NativeCodeBundleTests.suite());
 		suite.addTest(PlatformAdminBundleTests.suite());
+		suite.addTest(ListenerTests.suite());
 		return suite;
 	}
 }
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/DiscardBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/DiscardBundleTests.java
index 35aee2d..0908f92 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/DiscardBundleTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/DiscardBundleTests.java
@@ -13,15 +13,20 @@
  *******************************************************************************/
 package org.eclipse.osgi.tests.bundles;
 
-import java.io.*;
-import java.util.HashMap;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.Map;
-import java.util.jar.*;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 import org.eclipse.osgi.launch.Equinox;
 import org.eclipse.osgi.tests.OSGiTestsActivator;
-import org.osgi.framework.*;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
 
 /*
  * The framework must discard a persisted bundle when the 
@@ -155,13 +160,6 @@
 		return manifest;
 	}
 
-	private Map<String, Object> createConfiguration() {
-		File file = OSGiTestsActivator.getContext().getDataFile(getName());
-		Map<String, Object> result = new HashMap<String, Object>();
-		result.put(Constants.FRAMEWORK_STORAGE, file.getAbsolutePath());
-		return result;
-	}
-
 	private void doTest(Map<String, ?> configuration, boolean discard) throws Exception {
 		doTest(configuration, discard, getDirectoryLocation());
 		doTest(configuration, discard, getJarLocation());
@@ -206,11 +204,6 @@
 		return new File(root, BUNDLE_JAR);
 	}
 
-	private void initAndStart(Equinox equinox) throws BundleException {
-		equinox.init();
-		equinox.start();
-	}
-
 	private Equinox restart(Equinox equinox, Map<String, ?> configuration) throws BundleException, InterruptedException {
 		stop(equinox);
 		equinox = new Equinox(configuration);
@@ -224,17 +217,6 @@
 		assertEquals("The framework was not stopped", FrameworkEvent.STOPPED, event.getType());
 	}
 
-	private void stopQuietly(Equinox equinox) {
-		if (equinox == null)
-			return;
-		try {
-			equinox.stop();
-			equinox.waitForStop(5000);
-		} catch (Exception e) {
-			// Ignore
-		}
-	}
-
 	private void touchFile(File file) {
 		if (file.isDirectory())
 			file = new File(file, BUNDLE_MANIFEST);
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ListenerTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ListenerTests.java
new file mode 100644
index 0000000..7b693d9
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ListenerTests.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.tests.bundles;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.osgi.launch.Equinox;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+
+public class ListenerTests extends AbstractBundleTests {
+
+	private String methodName;
+	private List<Bundle> bundles;
+	private Equinox equinox;
+	private BundleContext bundleContext;
+
+	public static Test suite() {
+		return new TestSuite(ListenerTests.class);
+	}
+
+	public void setUp() throws Exception {
+		methodName = getName();
+		simpleResults = new TestResults();
+		bundles = new ArrayList<>();
+
+		Map<String, Object> configuration = createConfiguration();
+		equinox = new Equinox(configuration);
+		initAndStart(equinox);
+
+		bundleContext = equinox.getBundleContext();
+
+		for (int i = 0; i < 50; i++) {
+			Bundle b = installBundle(methodName + i);
+			bundles.add(b);
+			b.start();
+		}
+	}
+
+	public void tearDown() throws Exception {
+		simpleResults = null;
+		for (Bundle b : bundles) {
+			if (b != null) {
+				b.stop();
+				b = null;
+			}
+		}
+		stopQuietly(equinox);
+	}
+
+	public void testBundleListenersOrder() throws Exception {
+
+		BundleListener[] expectedBundleListeners = new BundleListener[200];
+
+		int i = 100;
+		for (Bundle b : bundles) {
+			BundleListener bundleListener = createBundleListener();
+			b.getBundleContext().addBundleListener(bundleListener);
+			expectedBundleListeners[i] = bundleListener;
+			i = i + 2;
+		}
+
+		int j = 101;
+		for (Bundle b : bundles) {
+			BundleListener bundleListener = createBundleListener();
+			b.getBundleContext().addBundleListener(bundleListener);
+			expectedBundleListeners[j] = bundleListener;
+			j = j + 2;
+		}
+
+		//synchronous listener will be called first
+		i = 0;
+		for (Bundle b : bundles) {
+			BundleListener bundleListener = createSynchronousBundleListener();
+			b.getBundleContext().addBundleListener(bundleListener);
+			expectedBundleListeners[i] = bundleListener;
+			i = i + 2;
+		}
+
+		j = 1;
+		for (Bundle b : bundles) {
+			BundleListener bundleListener = createSynchronousBundleListener();
+			b.getBundleContext().addBundleListener(bundleListener);
+			expectedBundleListeners[j] = bundleListener;
+			j = j + 2;
+		}
+
+		installBundle(methodName + "51");
+
+		Object[] actualBundleListeners = simpleResults.getResults(200);
+
+		compareResults(expectedBundleListeners, actualBundleListeners);
+
+	}
+
+	public void testFrameworkListenersOrder() throws Exception {
+		FrameworkListener[] expectedFrameworkListeners = new FrameworkListener[100];
+
+		int i = 0;
+		for (Bundle b : bundles) {
+			FrameworkListener frameworkListener = createFrameworkListener();
+			b.getBundleContext().addFrameworkListener(frameworkListener);
+			expectedFrameworkListeners[i] = frameworkListener;
+			i = i + 2;
+		}
+
+		int j = 1;
+		for (Bundle b : bundles) {
+			FrameworkListener frameworkListener = createFrameworkListener();
+			b.getBundleContext().addFrameworkListener(frameworkListener);
+			expectedFrameworkListeners[j] = frameworkListener;
+			j = j + 2;
+		}
+
+		equinox.adapt(FrameworkStartLevel.class).setStartLevel(5);
+
+		Object[] actualFrameworkListeners = simpleResults.getResults(100);
+
+		compareResults(expectedFrameworkListeners, actualFrameworkListeners);
+	}
+
+	public void testServiceListenersOrder() throws Exception {
+		ServiceListener[] expectedServiceListeners = new ServiceListener[100];
+
+		int i = 0;
+		for (Bundle b : bundles) {
+			ServiceListener serviceListener = createServiceListener();
+			b.getBundleContext().addServiceListener(serviceListener);
+			expectedServiceListeners[i] = serviceListener;
+			i = i + 2;
+		}
+
+		int j = 1;
+		for (Bundle b : bundles) {
+			ServiceListener serviceListener = createServiceListener();
+			b.getBundleContext().addServiceListener(serviceListener);
+			expectedServiceListeners[j] = serviceListener;
+			j = j + 2;
+		}
+
+		Bundle bundle = installBundle(methodName + "51");
+		bundle.start();
+		ServiceRegistration<Object> reg = bundle.getBundleContext().registerService(Object.class, new Object(), null);
+
+		Object[] actualServiceListeners = simpleResults.getResults(100);
+
+		compareResults(expectedServiceListeners, actualServiceListeners);
+
+		if (reg != null) {
+			reg.unregister();
+		}
+
+	}
+
+	private BundleListener createBundleListener() {
+
+		BundleListener bundleListener = new BundleListener() {
+			public void bundleChanged(BundleEvent event) {
+				simpleResults.addEvent(this);
+			}
+		};
+		return bundleListener;
+	}
+
+	private BundleListener createSynchronousBundleListener() {
+
+		SynchronousBundleListener bundleListener = new SynchronousBundleListener() {
+			public void bundleChanged(BundleEvent event) {
+				simpleResults.addEvent(this);
+			}
+		};
+		return bundleListener;
+	}
+
+	private FrameworkListener createFrameworkListener() {
+		FrameworkListener frameworkListener = new FrameworkListener() {
+			@Override
+			public void frameworkEvent(FrameworkEvent event) {
+				simpleResults.addEvent(this);
+			}
+		};
+		return frameworkListener;
+	}
+
+	private ServiceListener createServiceListener() {
+		ServiceListener serviceListener = new ServiceListener() {
+
+			@Override
+			public void serviceChanged(ServiceEvent event) {
+				simpleResults.addEvent(this);
+			}
+		};
+		return serviceListener;
+	}
+
+	private Bundle installBundle(String name) throws BundleException, IOException {
+		Bundle bundle = bundleContext.installBundle(name, new BundleBuilder().symbolicName(name).build());
+		assertNotNull(name + " bundle does not exist", bundleContext.getBundle(name));
+		return bundle;
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/PersistedBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/PersistedBundleTests.java
index 3d52259..eb40f94 100755
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/PersistedBundleTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/PersistedBundleTests.java
@@ -13,16 +13,10 @@
  *******************************************************************************/
 package org.eclipse.osgi.tests.bundles;
 
-import java.io.*;
-import java.util.HashMap;
 import java.util.Map;
-import java.util.jar.*;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 import org.eclipse.osgi.launch.Equinox;
-import org.eclipse.osgi.tests.OSGiTestsActivator;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
 
 /*
  * The framework must persist data according to the value of the 
@@ -35,35 +29,6 @@
  * 
  */
 public class PersistedBundleTests extends AbstractBundleTests {
-	static class BundleBuilder {
-		static class BundleManifestBuilder {
-			private final Manifest manifest = new Manifest();
-
-			public Manifest build() {
-				manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
-				return manifest;
-			}
-
-			public BundleManifestBuilder symbolicName(String value) {
-				manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, value);
-				return this;
-			}
-		}
-
-		private final BundleManifestBuilder manifestBuilder = new BundleManifestBuilder();
-
-		public InputStream build() throws IOException {
-			ByteArrayOutputStream baos = new ByteArrayOutputStream();
-			JarOutputStream jos = new JarOutputStream(baos, manifestBuilder.build());
-			jos.close();
-			return new ByteArrayInputStream(baos.toByteArray());
-		}
-
-		public BundleBuilder symbolicName(String value) {
-			manifestBuilder.symbolicName(value);
-			return this;
-		}
-	}
 
 	private static final String ECLIPSE_STATESAVEDELAYINTERVAL = "eclipse.stateSaveDelayInterval";
 
@@ -185,26 +150,4 @@
 		}
 	}
 
-	private Map<String, Object> createConfiguration() {
-		File file = OSGiTestsActivator.getContext().getDataFile(getName());
-		Map<String, Object> result = new HashMap<String, Object>();
-		result.put(Constants.FRAMEWORK_STORAGE, file.getAbsolutePath());
-		return result;
-	}
-
-	private void initAndStart(Equinox equinox) throws BundleException {
-		equinox.init();
-		equinox.start();
-	}
-
-	private void stopQuietly(Equinox equinox) {
-		if (equinox == null)
-			return;
-		try {
-			equinox.stop();
-			equinox.waitForStop(5000);
-		} catch (Exception e) {
-			// Ignore
-		}
-	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java
index 064983f..19daba4 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java
@@ -18,6 +18,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -65,13 +66,13 @@
 	 * installed in the Framework.
 	 */
 	// Map of BundleContexts for bundle's BundleListeners.
-	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> allBundleListeners = new HashMap<>();
+	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> allBundleListeners = new LinkedHashMap<>();
 
 	// Map of BundleContexts for bundle's SynchronousBundleListeners.
-	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> allSyncBundleListeners = new HashMap<>();
+	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> allSyncBundleListeners = new LinkedHashMap<>();
 
 	// Map of BundleContexts for bundle's FrameworkListeners.
-	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> allFrameworkListeners = new HashMap<>();
+	private final Map<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> allFrameworkListeners = new LinkedHashMap<>();
 
 	public EquinoxEventPublisher(EquinoxContainer container) {
 		this.container = container;
@@ -163,7 +164,7 @@
 		BundleContextImpl systemContext = null;
 		Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> systemBundleListenersSync = null;
 		synchronized (allSyncBundleListeners) {
-			listenersSync = new HashMap<>(allSyncBundleListeners.size());
+			listenersSync = new LinkedHashMap<>(allSyncBundleListeners.size());
 			for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> entry : allSyncBundleListeners.entrySet()) {
 				CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = entry.getValue();
 				if (!listeners.isEmpty()) {
@@ -183,7 +184,7 @@
 		Set<Map.Entry<BundleListener, BundleListener>> systemBundleListenersAsync = null;
 		if ((event.getType() & (BundleEvent.STARTING | BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) == 0) {
 			synchronized (allBundleListeners) {
-				listenersAsync = new HashMap<>(allBundleListeners.size());
+				listenersAsync = new LinkedHashMap<>(allBundleListeners.size());
 				for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> entry : allBundleListeners.entrySet()) {
 					CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = entry.getValue();
 					if (!listeners.isEmpty()) {
@@ -318,7 +319,7 @@
 		// Build the listener snapshot
 		Map<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> listenerSnapshot;
 		synchronized (allFrameworkListeners) {
-			listenerSnapshot = new HashMap<>(allFrameworkListeners.size());
+			listenerSnapshot = new LinkedHashMap<>(allFrameworkListeners.size());
 			for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> entry : allFrameworkListeners.entrySet()) {
 				CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = entry.getValue();
 				if (!listeners.isEmpty()) {
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java
index 5d4da71..d53f3b2 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/serviceregistry/ServiceRegistry.java
@@ -14,19 +14,49 @@
 
 package org.eclipse.osgi.internal.serviceregistry;
 
-import java.security.*;
-import java.util.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.eclipse.osgi.container.Module;
 import org.eclipse.osgi.container.ModuleRevision;
-import org.eclipse.osgi.framework.eventmgr.*;
+import org.eclipse.osgi.framework.eventmgr.CopyOnWriteIdentityMap;
+import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
+import org.eclipse.osgi.framework.eventmgr.ListenerQueue;
 import org.eclipse.osgi.internal.debug.Debug;
 import org.eclipse.osgi.internal.framework.BundleContextImpl;
 import org.eclipse.osgi.internal.framework.EquinoxContainer;
 import org.eclipse.osgi.internal.messages.Msg;
 import org.eclipse.osgi.storage.BundleInfo.Generation;
 import org.eclipse.osgi.util.NLS;
-import org.osgi.framework.*;
-import org.osgi.framework.hooks.service.*;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServicePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.service.EventHook;
+import org.osgi.framework.hooks.service.EventListenerHook;
+import org.osgi.framework.hooks.service.FindHook;
+import org.osgi.framework.hooks.service.ListenerHook;
 import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo;
 
 /**
@@ -97,7 +127,7 @@
 		publishedServicesByClass = new HashMap<>(initialCapacity);
 		publishedServicesByContext = new HashMap<>(initialCapacity);
 		allPublishedServices = new ArrayList<>(initialCapacity);
-		serviceEventListeners = new HashMap<>(initialCapacity);
+		serviceEventListeners = new LinkedHashMap<>(initialCapacity);
 		Module systemModule = container.getStorage().getModuleContainer().getModule(0);
 		systemBundleContext = (BundleContextImpl) systemModule.getBundle().getBundleContext();
 		systemBundleContext.provisionServicesInUseMap();
@@ -819,7 +849,7 @@
 		Set<Map.Entry<ServiceListener, FilteredServiceListener>> systemServiceListenersOrig = null;
 		BundleContextImpl systemContext = null;
 		synchronized (serviceEventListeners) {
-			listenerSnapshot = new HashMap<>(serviceEventListeners.size());
+			listenerSnapshot = new LinkedHashMap<>(serviceEventListeners.size());
 			for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<ServiceListener, FilteredServiceListener>> entry : serviceEventListeners.entrySet()) {
 				Map<ServiceListener, FilteredServiceListener> listeners = entry.getValue();
 				if (!listeners.isEmpty()) {