Prefer local features 41/79241/3
authorStephen Kitt <skitt@redhat.com>
Fri, 4 Jan 2019 16:51:15 +0000 (17:51 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 7 Jan 2019 10:28:24 +0000 (11:28 +0100)
PopulateLocalRepoMojo should prefer features from the local repository
(the Karaf installation repository, typically) to those in the system
repositories: this allows features to be patched, and the patches
taken into account when resolving dependencies.

(Adding the local repository to Aether’s repositories doesn’t have the
intended effect, because the feature dependencies have generally
already been resolved, and are resolved again from the cache when
PopulateLocalRepoMojo runs.)

JIRA: ODLPARENT-189
Change-Id: I232115e0fcdef1ae17c22a17028021f825573e31
Signed-off-by: Stephen Kitt <skitt@redhat.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
karaf-plugin/src/main/java/org/opendaylight/odlparent/AetherUtil.java
karaf-plugin/src/main/java/org/opendaylight/odlparent/FeatureUtil.java
karaf-plugin/src/main/java/org/opendaylight/odlparent/PopulateLocalRepoMojo.java

index 3d6c35486f8660d77c15c5b8712be84076e97c15..bba2ef550b64cf778cb6c5f25edebe7f1583cb3f 100644 (file)
@@ -72,7 +72,7 @@ public class AetherUtil {
      */
     public Set<Artifact> resolveDependencies(List<Dependency> dependencies, DependencyFilter filter)
             throws DependencyResolutionException {
-        Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
+        Set<Artifact> artifacts = new LinkedHashSet<>();
         CollectRequest collectRequest = new CollectRequest();
         collectRequest.setDependencies(dependencies);
         collectRequest.setRepositories(remoteRepos);
@@ -122,7 +122,7 @@ public class AetherUtil {
      * @return The resolved artifacts. Unresolvable coordinates are skipped without error.
      */
     public Set<Artifact> resolveArtifacts(Set<String> coords) {
-        Set<Artifact> result = new LinkedHashSet<Artifact>();
+        Set<Artifact> result = new LinkedHashSet<>();
         for (String coord : coords) {
             Artifact artifact = resolveArtifact(coord);
             if (artifact != null) {
@@ -151,7 +151,7 @@ public class AetherUtil {
      * @return The corresponding dependencies.
      */
     public List<Dependency> toDependencies(List<String> coords) {
-        List<Dependency> result = new ArrayList<Dependency>();
+        List<Dependency> result = new ArrayList<>();
         for (String coord : coords) {
             result.add(toDependency(coord));
         }
index 797efc5db8a91a3f06bf93f7e371c7e4fcd7081c..01bcecb661efd45c82786f08e4c15ecdf5cfa41b 100644 (file)
@@ -13,6 +13,7 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -28,7 +29,6 @@ import org.apache.karaf.features.internal.model.Features;
 import org.apache.karaf.features.internal.model.JaxbUtil;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.eclipse.aether.artifact.Artifact;
-import org.eclipse.aether.resolution.ArtifactResolutionException;
 import org.ops4j.pax.url.mvn.internal.Parser;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,8 +43,12 @@ public final class FeatureUtil {
     private static final String VERSION_STRIP_PATTERN_STR = "\\$.*$";
     private static final Pattern VERSION_STRIP_PATTERN = Pattern.compile(VERSION_STRIP_PATTERN_STR);
 
-    private FeatureUtil() {
-        throw new UnsupportedOperationException();
+    private final AetherUtil aetherUtil;
+    private final File localRepo;
+
+    public FeatureUtil(AetherUtil aetherUtil, File localRepo) {
+        this.aetherUtil = aetherUtil;
+        this.localRepo = localRepo;
     }
 
     /**
@@ -257,7 +261,7 @@ public final class FeatureUtil {
      * @return The features.
      * @throws FileNotFoundException if a file is missing.
      */
-    public static Set<Features> readFeatures(final Set<Artifact> featureArtifacts) throws FileNotFoundException {
+    public Set<Features> readFeatures(final Set<Artifact> featureArtifacts) throws FileNotFoundException {
         Set<Features> result = new LinkedHashSet<>();
         for (Artifact artifact : featureArtifacts) {
             result.add(readFeature(artifact));
@@ -273,7 +277,7 @@ public final class FeatureUtil {
      * @return The features.
      * @throws FileNotFoundException if a file is missing.
      */
-    public static Features readFeature(final Artifact artifact) throws FileNotFoundException {
+    public Features readFeature(final Artifact artifact) throws FileNotFoundException {
         return readFeature(artifact.getFile());
     }
 
@@ -284,8 +288,9 @@ public final class FeatureUtil {
      * @return The features.
      * @throws FileNotFoundException if a file is missing.
      */
-    public static Features readFeature(final File file) throws FileNotFoundException {
-        FileInputStream stream = new FileInputStream(file);
+    public Features readFeature(final File file) throws FileNotFoundException {
+        File localFile = getFileInLocalRepo(file);
+        FileInputStream stream = new FileInputStream(localFile != null ? localFile : file);
         Features result = JaxbUtil.unmarshal(file.toURI().toString(), stream, false);
         LOG.trace("readFeature({}) returns {} without resolving first", file, result.getName());
         return result;
@@ -294,14 +299,11 @@ public final class FeatureUtil {
     /**
      * Unmarshal the features matching the given artifact coordinates.
      *
-     * @param aetherUtil The Aether resolver.
      * @param coords The artifact coordinates.
      * @return The features.
-     * @throws ArtifactResolutionException if the coordinates can't be resolved.
      * @throws FileNotFoundException if a file is missing.
      */
-    public static Features readFeature(final AetherUtil aetherUtil, final String coords)
-            throws ArtifactResolutionException, FileNotFoundException {
+    public Features readFeature(final String coords) throws FileNotFoundException {
         Artifact artifact = aetherUtil.resolveArtifact(coords);
         Features result = readFeature(artifact);
         LOG.trace("readFeature({}) returns {} after resolving first", coords, result.getName());
@@ -311,17 +313,14 @@ public final class FeatureUtil {
     /**
      * Unmarshals all the features starting from the given feature.
      *
-     * @param aetherUtil The Aether resolver.
      * @param features The starting features.
      * @param existingCoords The artifact coordinates which have already been unmarshalled.
      * @return The features.
      * @throws MalformedURLException if a URL is malformed.
      * @throws FileNotFoundException if a file is missing.
-     * @throws ArtifactResolutionException if artifact coordinates can't be resolved.
      */
-    public static Set<Features> findAllFeaturesRecursively(
-            final AetherUtil aetherUtil, final Features features, final Set<String> existingCoords)
-            throws MalformedURLException, FileNotFoundException, ArtifactResolutionException {
+    public Set<Features> findAllFeaturesRecursively(final Features features, final Set<String> existingCoords)
+            throws MalformedURLException, FileNotFoundException {
         LOG.debug("findAllFeaturesRecursively({}) starts", features.getName());
         LOG.trace("findAllFeaturesRecursively knows about these coords: {}", existingCoords);
         Set<Features> result = new LinkedHashSet<>();
@@ -330,11 +329,10 @@ public final class FeatureUtil {
             if (!existingCoords.contains(coord)) {
                 LOG.trace("findAllFeaturesRecursively() going to add {}", coord);
                 existingCoords.add(coord);
-                Features feature = FeatureUtil.readFeature(aetherUtil, coord);
+                Features feature = readFeature(coord);
                 result.add(feature);
                 LOG.debug("findAllFeaturesRecursively() added {}", coord);
-                result.addAll(findAllFeaturesRecursively(aetherUtil, FeatureUtil.readFeature(aetherUtil, coord),
-                        existingCoords));
+                result.addAll(findAllFeaturesRecursively(readFeature(coord), existingCoords));
             } else {
                 LOG.trace("findAllFeaturesRecursively() skips known {}", coord);
             }
@@ -345,20 +343,17 @@ public final class FeatureUtil {
     /**
      * Unmarshals all the features starting from the given features.
      *
-     * @param aetherUtil The Aether resolver.
      * @param features The starting features.
      * @param existingCoords The artifact coordinates which have already been unmarshalled.
      * @return The features.
      * @throws MalformedURLException if a URL is malformed.
      * @throws FileNotFoundException if a file is missing.
-     * @throws ArtifactResolutionException if artifact coordinates can't be resolved.
      */
-    public static Set<Features> findAllFeaturesRecursively(
-            final AetherUtil aetherUtil, final Set<Features> features, final Set<String> existingCoords)
-            throws MalformedURLException, FileNotFoundException, ArtifactResolutionException {
+    public Set<Features> findAllFeaturesRecursively(final Set<Features> features, final Set<String> existingCoords)
+            throws MalformedURLException, FileNotFoundException {
         Set<Features> result = new LinkedHashSet<>();
         for (Features feature : features) {
-            result.addAll(findAllFeaturesRecursively(aetherUtil, feature, existingCoords));
+            result.addAll(findAllFeaturesRecursively(feature, existingCoords));
         }
         return result;
     }
@@ -366,16 +361,26 @@ public final class FeatureUtil {
     /**
      * Unmarshals all the features (including known ones) starting from the given features.
      *
-     * @param aetherUtil The Aether resolver.
      * @param features The starting features.
      * @return The features.
      * @throws MalformedURLException if a URL is malformed.
      * @throws FileNotFoundException if a file is missing.
-     * @throws ArtifactResolutionException if artifact coordinates can't be resolved.
      */
-    public static Set<Features> findAllFeaturesRecursively(final AetherUtil aetherUtil, final Set<Features> features)
-            throws MalformedURLException, FileNotFoundException, ArtifactResolutionException {
-        return findAllFeaturesRecursively(aetherUtil, features, new LinkedHashSet<String>());
+    public Set<Features> findAllFeaturesRecursively(final Set<Features> features)
+            throws MalformedURLException, FileNotFoundException {
+        return findAllFeaturesRecursively(features, new LinkedHashSet<>());
     }
 
+    private File getFileInLocalRepo(File file) {
+        Path filePath = file.toPath();
+        Path parent = filePath.getParent();
+        while (parent != null) {
+            File candidate = new File(localRepo, parent.relativize(filePath).toString());
+            if (candidate.exists()) {
+                return candidate;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
 }
index 025d8f15e36d99769d1c43c316afe0b3ad04be85..8d208ef3018fc6b7bcef9abd99594ad4234bde13 100644 (file)
@@ -54,8 +54,7 @@ import org.slf4j.LoggerFactory;
 @Mojo(name = "populate-local-repo", defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
 // URL.setURLStreamHandlerFactory throws an Error directly, so we can’t do any better than this...
 @SuppressWarnings("checkstyle:IllegalCatch")
-public class PopulateLocalRepoMojo
-    extends AbstractMojo {
+public class PopulateLocalRepoMojo extends AbstractMojo {
     private static final Logger LOG = LoggerFactory.getLogger(PopulateLocalRepoMojo.class);
 
     static {
@@ -99,26 +98,25 @@ public class PopulateLocalRepoMojo
     @Parameter
     private File localRepo;
 
-    private AetherUtil aetherUtil;
-
     @Override
     @SuppressFBWarnings("REC_CATCH_EXCEPTION")
     public void execute() throws MojoExecutionException {
 
-        aetherUtil = new AetherUtil(repoSystem, repoSession, remoteRepos, localRepo);
+        AetherUtil aetherUtil = new AetherUtil(repoSystem, repoSession, remoteRepos, localRepo);
+        FeatureUtil featureUtil = new FeatureUtil(aetherUtil, localRepo);
         try {
-            Set<Artifact> startupArtifacts = readStartupProperties();
+            Set<Artifact> startupArtifacts = readStartupProperties(aetherUtil);
             aetherUtil.installArtifacts(startupArtifacts);
             Set<Artifact> featureArtifacts = new LinkedHashSet<>();
             Set<Features> features = new LinkedHashSet<>();
-            readFeatureCfg(featureArtifacts, features);
+            readFeatureCfg(aetherUtil, featureUtil, featureArtifacts, features);
             featureArtifacts.addAll(
                 aetherUtil.resolveDependencies(MvnToAetherMapper.toAether(project.getDependencies()),
                     new KarafFeaturesDependencyFilter()));
-            features.addAll(FeatureUtil.readFeatures(featureArtifacts));
+            features.addAll(featureUtil.readFeatures(featureArtifacts));
             // Do not provide FeatureUtil.featuresRepositoryToCoords(features)) as existingCoords
             // to findAllFeaturesRecursively, as those coords are not resolved yet, and it would lead to Bug 6187.
-            features.addAll(FeatureUtil.findAllFeaturesRecursively(aetherUtil, features));
+            features.addAll(featureUtil.findAllFeaturesRecursively(features));
             for (Features feature : features) {
                 LOG.info("Feature repository discovered recursively: {}", feature.getName());
             }
@@ -142,7 +140,8 @@ public class PopulateLocalRepoMojo
         }
     }
 
-    private void readFeatureCfg(Set<Artifact> artifacts, Set<Features> features) {
+    private void readFeatureCfg(AetherUtil aetherUtil, FeatureUtil featureUtil, Set<Artifact> artifacts,
+            Set<Features> features) {
         String karafHome = localRepo.getParent();
         File file = new File(karafHome + "/etc/org.apache.karaf.features.cfg");
         Properties prop = new Properties();
@@ -154,7 +153,7 @@ public class PopulateLocalRepoMojo
                 if (fixedUrl.startsWith("file:")) {
                     try {
                         // Local feature file
-                        features.add(FeatureUtil.readFeature(new File(new URI(fixedUrl))));
+                        features.add(featureUtil.readFeature(new File(new URI(fixedUrl))));
                     } catch (URISyntaxException e) {
                         LOG.info("Could not resolve URI: {}", fixedUrl, e);
                     }
@@ -169,7 +168,7 @@ public class PopulateLocalRepoMojo
         }
     }
 
-    private Set<Artifact> readStartupProperties() {
+    private Set<Artifact> readStartupProperties(AetherUtil aetherUtil) {
         Set<Artifact> artifacts = new LinkedHashSet<>();
         File file = new File(localRepo.getParentFile().toString() + "/etc/startup.properties");
         Properties prop = new Properties();