You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2020/02/21 06:46:13 UTC

[sling-org-apache-sling-feature-io] branch master updated: SLING-9098 : Provide support for directly downloading artifacts through mvn

This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-io.git


The following commit(s) were added to refs/heads/master by this push:
     new 508836e  SLING-9098 : Provide support for directly downloading artifacts through mvn
508836e is described below

commit 508836e49a4f42d5e99594a5ffaf2da485717d95
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Feb 21 07:45:57 2020 +0100

    SLING-9098 : Provide support for directly downloading artifacts through mvn
---
 .../feature/io/artifacts/ArtifactHandler.java      |  19 +++-
 .../feature/io/artifacts/ArtifactManager.java      | 120 ++++++++++++++++++++-
 .../io/artifacts/ArtifactManagerConfig.java        |  63 +++++++++--
 3 files changed, 184 insertions(+), 18 deletions(-)

diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java
index 044a8ef..c4d87ce 100644
--- a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java
+++ b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactHandler.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sling.feature.io.artifacts;
 
+import java.io.File;
+import java.net.MalformedURLException;
 import java.net.URL;
 
 /**
@@ -29,7 +31,7 @@ public class ArtifactHandler {
 
     /**
      * Create a new handler.
-     * 
+     *
      * @param url  The url of the artifact
      * @param localURL The local URL for the artifact
      */
@@ -39,8 +41,19 @@ public class ArtifactHandler {
     }
 
     /**
+     * Create a new handler.
+     *
+     * @param file The file for the artifact
+     * @throws MalformedURLException
+     * @since 1.1.0
+     */
+    public ArtifactHandler(final File file) throws MalformedURLException {
+        this(file.toURI().toString(), file.toURI().toURL());
+    }
+
+    /**
      * Get the url of the artifact
-     * 
+     *
      * @return The url.
      */
     public String getUrl() {
@@ -49,7 +62,7 @@ public class ArtifactHandler {
 
     /**
      * Get a local url for the artifact
-     * 
+     *
      * @return The file
      */
     public URL getLocalURL() {
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java
index 223ca78..d1515ac 100644
--- a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java
+++ b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManager.java
@@ -25,12 +25,19 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.Reader;
+import java.lang.ProcessBuilder.Redirect;
 import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Base64;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
 
@@ -86,6 +93,12 @@ public class ArtifactManager
         return new ArtifactManager(config, providers);
     }
 
+    /**
+     * Internal constructor for the manager
+     * @param config The configuration
+     * @param providers The provider map
+     * @throws IOException If the manager can't be initialized
+     */
     ArtifactManager(final ArtifactManagerConfig config, final Map<String, ArtifactProvider> providers)
     throws IOException {
         this.config = config;
@@ -126,6 +139,12 @@ public class ArtifactManager
         }
     }
 
+    /**
+     * Return a feature provider based on this artifact manager
+     *
+     * @return A feature provider
+     * @since 1.1.0
+     */
     public FeatureProvider toFeatureProvider() {
         return (id -> {
             try {
@@ -140,6 +159,7 @@ public class ArtifactManager
             }
         });
     }
+
     private final URL getArtifactFromProviders(final String url, final String relativeCachePath) throws IOException {
         final int pos = url.indexOf(":");
         final String scheme = url.substring(0, pos);
@@ -156,7 +176,7 @@ public class ArtifactManager
 
     /**
      * Get the full artifact url and file for an artifact.
-     * 
+     *
      * @param url Artifact url or relative path.
      * @return Absolute url and file in the form of a handler.
      * @throws IOException If something goes wrong or the artifact can't be found.
@@ -166,10 +186,16 @@ public class ArtifactManager
 
         final String path;
 
+        ArtifactId artifactId = null;
+
         if ( url.startsWith("mvn:") ) {
             // mvn url
-            path = ArtifactId.fromMvnUrl(url).toMvnPath();
-
+            try {
+                artifactId = ArtifactId.fromMvnUrl(url);
+                path = artifactId.toMvnPath();
+            } catch (final IllegalArgumentException iae) {
+                throw new IOException(iae.getMessage(), iae);
+            }
         } else if ( url.startsWith(":") ) {
             // repository path
             path = url.substring(1);
@@ -193,7 +219,7 @@ public class ArtifactManager
             if ( !f.exists()) {
                 throw new IOException("Artifact " + url + " not found.");
             }
-            return new ArtifactHandler(f.toURI().toString(), f.toURI().toURL());
+            return new ArtifactHandler(f);
         }
         logger.debug("Querying repositories for {}", path);
 
@@ -255,6 +281,14 @@ public class ArtifactManager
             }
         }
 
+        // if we have an artifact id and using mvn is enabled, we try this as a last
+        // resort
+        if (artifactId != null && this.config.isUseMvn()) {
+            final File file = getArtifactFromMvn(artifactId);
+            if (file != null) {
+                return new ArtifactHandler(file);
+            }
+        }
         throw new IOException("Artifact " + url + " not found in any repository.");
     }
 
@@ -293,6 +327,7 @@ public class ArtifactManager
         }
         return value;
     }
+
     public static String getLatestSnapshot(final String mavenMetadata) {
         final String timestamp = getValue(mavenMetadata, new String[] {"metadata", "versioning", "snapshot", "timestamp"});
         final String buildNumber = getValue(mavenMetadata, new String[] {"metadata", "versioning", "snapshot", "buildNumber"});
@@ -412,4 +447,81 @@ public class ArtifactManager
             return "DefaultArtifactHandler";
         }
     }
+
+    private File getArtifactFromMvn(final ArtifactId artifactId) {
+        final String filePath = this.config.getMvnHome()
+                .concat(artifactId.toMvnPath().replace('/', File.separatorChar));
+        logger.debug("Trying to fetch artifact {} from local mvn repository {}", artifactId.toMvnId(), filePath);
+        final File f = new File(filePath);
+        if (!f.exists() || !f.isFile() || !f.canRead()) {
+            logger.debug("Trying to download {}", artifactId.toMvnId());
+            try {
+                this.downloadArtifact(artifactId);
+            } catch (final IOException ioe) {
+                logger.debug("Error downloading file.", ioe);
+            }
+            if (!f.exists() || !f.isFile() || !f.canRead()) {
+                logger.info("Artifact not found {}", artifactId.toMvnId());
+
+                return null;
+            }
+        }
+        return f;
+    }
+
+    /**
+     * Download artifact from maven
+     *
+     * @throws IOException
+     */
+    private void downloadArtifact(final ArtifactId artifactId) throws IOException {
+        // create fake pom
+        final Path dir = Files.createTempDirectory(null);
+        try {
+            final List<String> lines = new ArrayList<String>();
+            lines.add(
+                    "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">");
+            lines.add("    <modelVersion>4.0.0</modelVersion>");
+            lines.add("    <groupId>org.apache.sling</groupId>");
+            lines.add("    <artifactId>temp-artifact</artifactId>");
+            lines.add("    <version>1-SNAPSHOT</version>");
+            lines.add("    <dependencies>");
+            lines.add("        <dependency>");
+            lines.add("            <groupId>".concat(artifactId.getGroupId()).concat("</groupId>"));
+            lines.add("            <artifactId>".concat(artifactId.getArtifactId()).concat("</artifactId>"));
+            lines.add("            <version>".concat(artifactId.getVersion()).concat("</version>"));
+            if (artifactId.getClassifier() != null) {
+                lines.add("            <classifier>".concat(artifactId.getClassifier()).concat("</classifier>"));
+            }
+            if (!"bundle".equals(artifactId.getType()) && !"jar".equals(artifactId.getType())) {
+                lines.add("            <type>".concat(artifactId.getType()).concat("</type>"));
+            }
+            lines.add("            <scope>provided</scope>");
+            lines.add("        </dependency>");
+            lines.add("    </dependencies>");
+            lines.add("</project>");
+            logger.debug("Writing pom to {}", dir);
+            Files.write(dir.resolve("pom.xml"), lines, Charset.forName("UTF-8"));
+
+            final File output = dir.resolve("output.txt").toFile();
+            final File error = dir.resolve("error.txt").toFile();
+
+            // invoke maven
+            logger.debug("Invoking mvn...");
+            final ProcessBuilder pb = new ProcessBuilder("mvn", "verify");
+            pb.directory(dir.toFile());
+            pb.redirectOutput(Redirect.to(output));
+            pb.redirectError(Redirect.to(error));
+
+            final Process p = pb.start();
+            try {
+                p.waitFor();
+            } catch (final InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+
+        } finally {
+            Files.walk(dir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
+        }
+    }
 }
diff --git a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java
index d03c7e3..82e6476 100644
--- a/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java
+++ b/src/main/java/org/apache/sling/feature/io/artifacts/ArtifactManagerConfig.java
@@ -19,6 +19,7 @@ package org.apache.sling.feature.io.artifacts;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.sling.feature.io.artifacts.spi.ArtifactProviderContext;
 
@@ -33,15 +34,25 @@ public class ArtifactManagerConfig implements ArtifactProviderContext {
     /** The cache directory. */
     private volatile File cacheDirectory;
 
-    private volatile long cachedArtifacts;
+    /** Metrics for artifacts used from the cache. */
+    private final AtomicLong cachedArtifacts = new AtomicLong();
 
-    private volatile long downloadedArtifacts;
+    /** Metrics for artifacts needed to be downloaded. */
+    private final AtomicLong downloadedArtifacts = new AtomicLong();
 
-    private volatile long localArtifacts;
+    /** Metrics for artifacts read locally. */
+    private final AtomicLong localArtifacts = new AtomicLong();
+
+    /** Whether locally mvn command can be used to download artifacts. */
+    private volatile boolean useMvn = false;
+
+    /**
+     * The .m2 directory.
+     */
+    private final String repoHome;
 
     /**
-     * Create a new configuration object.
-     * Set the default values
+     * Create a new configuration object. Set the default values
      */
     public ArtifactManagerConfig() {
         // set defaults
@@ -55,6 +66,7 @@ public class ArtifactManagerConfig implements ArtifactProviderContext {
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
+        this.repoHome = System.getProperty("user.home") + "/.m2/repository/";
     }
 
     /**
@@ -103,17 +115,17 @@ public class ArtifactManagerConfig implements ArtifactProviderContext {
 
     @Override
     public void incCachedArtifacts() {
-        this.cachedArtifacts++;
+        this.cachedArtifacts.incrementAndGet();
     }
 
     @Override
     public void incDownloadedArtifacts() {
-        this.downloadedArtifacts++;
+        this.downloadedArtifacts.incrementAndGet();
     }
 
     @Override
     public void incLocalArtifacts() {
-        this.localArtifacts++;
+        this.localArtifacts.incrementAndGet();
     }
 
     /**
@@ -121,7 +133,7 @@ public class ArtifactManagerConfig implements ArtifactProviderContext {
      * @return The number of cached artifacts
      */
     public long getCachedArtifacts() {
-        return this.cachedArtifacts;
+        return this.cachedArtifacts.get();
     }
 
     /**
@@ -129,7 +141,7 @@ public class ArtifactManagerConfig implements ArtifactProviderContext {
      * @return The number of downloaded artifacts
      */
     public long getDownloadedArtifacts() {
-        return this.downloadedArtifacts;
+        return this.downloadedArtifacts.get();
     }
 
     /**
@@ -137,6 +149,35 @@ public class ArtifactManagerConfig implements ArtifactProviderContext {
      * @return The number of local artifacts
      */
     public long getLocalArtifacts() {
-        return this.localArtifacts;
+        return this.localArtifacts.get();
+    }
+
+    /**
+     * Should mvn be used if an artifact can't be found in the repositories
+     *
+     * @return Whether mvn command should be used.
+     * @since 1.1.0
+     */
+    public boolean isUseMvn() {
+        return useMvn;
+    }
+
+    /**
+     * Set whether mvn should be used to get artifacts.
+     *
+     * @param useMvn flag for enabling mvn
+     * @since 1.1.0
+     */
+    public void setUseMvn(final boolean useMvn) {
+        this.useMvn = useMvn;
+    }
+
+    /**
+     * Return mvn home
+     * 
+     * @since 1.1.0
+     */
+    String getMvnHome() {
+        return this.repoHome;
     }
 }