+
+ if (!bundlesToDestroy.isEmpty()) {
+ bundlesToDestroy.sort((b1, b2) -> (int) (b2.getLastModified() - b1.getLastModified()));
+
+ LOG.debug("Selected bundles {} for destroy (no services in use)", bundlesToDestroy);
+ } else {
+ // There's either no more container bundles or they all have services being used. For
+ // the latter it means there's either circular service usage or a service is being used
+ // by a non-container bundle. But we need to make progress so we pick the bundle with a
+ // used service with the highest service ID. Each service is assigned a monotonically
+ // increasing ID as they are registered. By picking the bundle with the highest service
+ // ID, we're picking the bundle that was (likely) started after all the others and thus
+ // is likely the safest to destroy at this point.
+
+ Bundle bundle = findBundleWithHighestUsedServiceId(containerBundles);
+ if (bundle != null) {
+ bundlesToDestroy.add(bundle);
+ }
+
+ LOG.debug("Selected bundle {} for destroy (lowest ranking service or highest service ID)",
+ bundlesToDestroy);
+ }
+
+ return bundlesToDestroy;
+ }
+
+ private static @Nullable Bundle findBundleWithHighestUsedServiceId(final Collection<Bundle> containerBundles) {
+ ServiceReference<?> highestServiceRef = null;
+ for (Bundle bundle : containerBundles) {
+ ServiceReference<?>[] references = bundle.getRegisteredServices();
+ if (references == null) {
+ continue;
+ }
+
+ for (ServiceReference<?> reference : references) {
+ // We did check the service usage previously but it's possible the usage has changed since then.
+ if (getServiceUsage(reference) == 0) {
+ continue;
+ }
+
+ // Choose 'reference' if it has a lower service ranking or, if the rankings are equal
+ // which is usually the case, if it has a higher service ID. For the latter the < 0
+ // check looks backwards but that's how ServiceReference#compareTo is documented to work.
+ if (highestServiceRef == null || reference.compareTo(highestServiceRef) < 0) {
+ LOG.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference);
+ highestServiceRef = reference;
+ }
+ }
+ }
+
+ return highestServiceRef == null ? null : highestServiceRef.getBundle();
+ }
+
+ private static int getServiceUsage(final ServiceReference<?> ref) {
+ Bundle[] usingBundles = ref.getUsingBundles();
+ return usingBundles != null ? usingBundles.length : 0;