/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.karaf.features.internal.region; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import org.apache.karaf.features.internal.resolver.ResolverUtil; import org.osgi.framework.Version; import org.osgi.framework.namespace.BundleNamespace; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.wiring.BundleCapability; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; public class CandidateComparator implements Comparator<Capability> { private final Set<Resource> mandatory; public CandidateComparator(Set<Resource> mandatory) { this.mandatory = mandatory; } public int compare(Capability cap1, Capability cap2) { int c = 0; // Always prefer system bundle if (cap1 instanceof BundleCapability && !(cap2 instanceof BundleCapability)) { c = -1; } else if (!(cap1 instanceof BundleCapability) && cap2 instanceof BundleCapability) { c = 1; } // Always prefer mandatory resources if (c == 0) { if (mandatory.contains(cap1.getResource()) && !mandatory.contains(cap2.getResource())) { c = -1; } else if (!mandatory.contains(cap1.getResource()) && mandatory.contains(cap2.getResource())) { c = 1; } } // Compare revision capabilities. if ((c == 0) && cap1.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE)) { c = compareNames(cap1, cap2, BundleNamespace.BUNDLE_NAMESPACE); if (c == 0) { c = compareVersions(cap1, cap2, BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); } // Compare package capabilities. } else if ((c == 0) && cap1.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE)) { c = compareNames(cap1, cap2, PackageNamespace.PACKAGE_NAMESPACE); if (c == 0) { c = compareVersions(cap1, cap2, PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE); // if same version, rather compare on the bundle version if (c == 0) { c = compareVersions(cap1, cap2, BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE); } } // Compare feature capabilities } else if ((c == 0) && cap1.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE)) { c = compareNames(cap1, cap2, IdentityNamespace.IDENTITY_NAMESPACE); if (c == 0) { c = compareVersions(cap1, cap2, IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE); } } if (c == 0) { // We just want to have a deterministic heuristic final Resource resource1 = cap1.getResource(); final Resource resource2 = cap2.getResource(); String n1 = ResolverUtil.getSymbolicName(resource1); String n2 = ResolverUtil.getSymbolicName(resource2); c = n1.compareTo(n2); // Resources looks like identical, but it required by different features/subsystems/regions // so use this difference for deterministic heuristic if (c == 0) { final List<Requirement> reqs1 = resource1.getRequirements(IdentityNamespace.IDENTITY_NAMESPACE); final List<Requirement> reqs2 = resource2.getRequirements(IdentityNamespace.IDENTITY_NAMESPACE); if (!reqs1.isEmpty() && !reqs2.isEmpty()) { Requirement identityReq1 = reqs1.get(0); Requirement identityReq2 = reqs2.get(0); Object identity1 = identityReq1.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE); Object identity2 = identityReq2.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE); c = String.valueOf(identity1).compareTo(String.valueOf(identity2)); } } } return c; } private int compareNames(Capability cap1, Capability cap2, String attribute) { Object o1 = cap1.getAttributes().get(attribute); Object o2 = cap2.getAttributes().get(attribute); if (o1 instanceof List || o2 instanceof List) { List<String> l1 = o1 instanceof List ? (List) o1 : Collections.singletonList((String) o1); List<String> l2 = o2 instanceof List ? (List) o2 : Collections.singletonList((String) o2); for (String s : l1) { if (l2.contains(s)) { return 0; } } return l1.get(0).compareTo(l2.get(0)); } else { return((String) o1).compareTo((String) o2); } } private int compareVersions(Capability cap1, Capability cap2, String attribute) { Version v1 = (!cap1.getAttributes().containsKey(attribute)) ? Version.emptyVersion : (Version) cap1.getAttributes().get(attribute); Version v2 = (!cap2.getAttributes().containsKey(attribute)) ? Version.emptyVersion : (Version) cap2.getAttributes().get(attribute); // Compare these in reverse order, since we want // highest version to have priority. return v2.compareTo(v1); } }