452642:  Blueprint extender shutdown ordering affected by external bundles

Signed-off-by: Olaf Otto <olaf@x100.de>
diff --git a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/shutdown/ShutdownSorter.java b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/shutdown/ShutdownSorter.java
index 2b84337..6b3507f 100644
--- a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/shutdown/ShutdownSorter.java
+++ b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/shutdown/ShutdownSorter.java
@@ -14,20 +14,18 @@
 

 package org.eclipse.gemini.blueprint.extender.internal.dependencies.shutdown;

 

-import java.util.ArrayList;

-import java.util.Arrays;

-import java.util.Collection;

-import java.util.Collections;

-import java.util.Comparator;

-import java.util.List;

-

 import org.apache.commons.logging.Log;

 import org.apache.commons.logging.LogFactory;

-import org.osgi.framework.Bundle;

-import org.osgi.framework.ServiceReference;

 import org.eclipse.gemini.blueprint.util.OsgiServiceReferenceUtils;

 import org.eclipse.gemini.blueprint.util.OsgiStringUtils;

-import org.springframework.util.ObjectUtils;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.ServiceReference;

+

+import java.util.*;

+

+import static java.util.Arrays.stream;

+import static java.util.stream.Collectors.toList;

+import static org.springframework.util.ObjectUtils.isEmpty;

 

 /**

  * Utility for sorting out bundles during shutdown based on the OSGi 4.2 shutdown algorithm. Please see section 121.3.11

@@ -68,19 +66,19 @@
 		}

 	}

 

-	private static List<Bundle> unusedBundles(Collection<Bundle> unsorted) {

+	private static List<Bundle> unusedBundles(Collection<Bundle> unsortedManagedBundles) {

 		List<Bundle> unused = new ArrayList<Bundle>();

 

 		boolean trace = log.isTraceEnabled();

 

-		for (Bundle bundle : unsorted) {

+		for (Bundle bundle : unsortedManagedBundles) {

 		    try {

                 String bundleToString = null;

                 if (trace) {

                     bundleToString = OsgiStringUtils.nullSafeSymbolicName(bundle);

                 }

                 ServiceReference[] services = bundle.getRegisteredServices();

-                if (ObjectUtils.isEmpty(services)) {

+                if (isEmpty(services)) {

                     if (trace) {

                         log.trace("Bundle " + bundleToString + " has no registered services; added for shutdown");

                     }

@@ -89,7 +87,12 @@
                     boolean unusedBundle = true;

                     for (ServiceReference serviceReference : services) {

                         Bundle[] usingBundles = serviceReference.getUsingBundles();

-                        if (!ObjectUtils.isEmpty(usingBundles)) {

+

+						if (!isEmpty(usingBundles)) {

+							usingBundles = stream(usingBundles).filter(unsortedManagedBundles::contains).collect(toList()).toArray(new Bundle[]{});

+						}

+

+                        if (!isEmpty(usingBundles)) {

                             if (trace)

                                 log.trace("Bundle " + bundleToString + " has registered services in use; postponing shutdown. The using bundles are "

                                     + Arrays.toString(usingBundles));

@@ -106,12 +109,12 @@
                     }

                 }

             }

-		    catch (IllegalStateException _) {

+		    catch (IllegalStateException ignored) {

 		        unused.add(bundle);

 		    }

 		}

 

-		Collections.sort(unused, ReverseBundleIdSorter.INSTANCE);

+		unused.sort(ReverseBundleIdSorter.INSTANCE);

 

 		return unused;

 	}

@@ -191,10 +194,10 @@
 	private static int getRegisteredServiceInUseLowestRanking(Bundle bundle) {

 		ServiceReference[] services = bundle.getRegisteredServices();

 		int min = Integer.MAX_VALUE;

-		if (!ObjectUtils.isEmpty(services)) {

+		if (!isEmpty(services)) {

 			for (ServiceReference ref : services) {

 				// make sure somebody is using the service

-				if (!ObjectUtils.isEmpty(ref.getUsingBundles())) {

+				if (!isEmpty(ref.getUsingBundles())) {

 					int localRank = OsgiServiceReferenceUtils.getServiceRanking(ref);

 					if (localRank < min) {

 						min = localRank;

@@ -208,7 +211,7 @@
 	private static long getHighestServiceId(Bundle bundle) {

 		ServiceReference[] services = bundle.getRegisteredServices();

 		long max = Long.MIN_VALUE;

-		if (!ObjectUtils.isEmpty(services)) {

+		if (!isEmpty(services)) {

 			for (ServiceReference ref : services) {

 				long id = OsgiServiceReferenceUtils.getServiceId(ref);

 				if (id > max) {

@@ -226,7 +229,7 @@
 		public int compare(Bundle o1, Bundle o2) {

 		    try {

 		        return (int) (o2.getBundleId() - o1.getBundleId());

-		    } catch (IllegalStateException _) {

+		    } catch (IllegalStateException ignored) {

 		        return o1 == o2 ? 0 : 1; // cannot tell which is larger, but must provide a total ordering

 		    }

 		}

diff --git a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/BlueprintShutdownSorterTest.java b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/BlueprintShutdownSorterTest.java
index 22d05da..0b5380a 100644
--- a/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/BlueprintShutdownSorterTest.java
+++ b/extender/src/test/java/org/eclipse/gemini/blueprint/extender/internal/dependencies/BlueprintShutdownSorterTest.java
@@ -64,6 +64,30 @@
 		assertOrder(new Bundle[] { c, a, b, d, e }, order);

 	}

 

+	/**

+	 * If the service of a managed bundle are consumed by an unmanaged bundle,

+	 * that dependency should not affect the shutdown ordering as gemini blueprint is only responsible for

+	 * orderly shutting down the bundles it is managing.

+	 */

+	public void testUnmanagedBundlesAreIgnoredForShutdownOrdering() throws Exception {

+		DependencyMockBundle a = new DependencyMockBundle("A");

+		DependencyMockBundle b = new DependencyMockBundle("B");

+		DependencyMockBundle c = new DependencyMockBundle("C");

+		DependencyMockBundle d = new DependencyMockBundle("D");

+		DependencyMockBundle e = new DependencyMockBundle("E");

+		DependencyMockBundle unmanaged = new DependencyMockBundle("F");

+

+		b.setDependentOn(c);

+		d.setDependentOn(e, -13, 12);

+		e.setDependentOn(d, 0, 14);

+		a.setDependentOn(unmanaged);

+

+		List<Bundle> order = getOrder(a, b, c, d, e);

+		System.out.println("Shutdown order is " + order);

+		assertOrder(new Bundle[] { c, a, b, d, e }, order);

+	}

+

+

 	private void assertOrder(Bundle[] expected, List<Bundle> ordered) {

 		assertTrue("shutdown order is incorrect", Arrays.equals(expected, ordered.toArray()));

 	}

diff --git a/pom.xml b/pom.xml
index 5cda637..2060faf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -540,11 +540,10 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.6.1</version>
                 <configuration>
-                    <source>1.7</source>
-                    <target>1.7</target>
-                    <debug>true</debug>
-                    <fork>true</fork>
+                    <source>1.8</source>
+                    <target>1.8</target>
                 </configuration>
             </plugin>
             <plugin>