├── .gitignore ├── src └── main │ ├── resources │ └── META-INF │ │ └── plexus │ │ └── components.xml │ └── java │ └── de │ └── qaware │ └── maven │ └── plugin │ └── offline │ ├── RepositoryType.java │ ├── ArtifactWithRepoType.java │ ├── ReactorArtifact.java │ ├── AbstractGoOfflineMojo.java │ ├── ResolveDependenciesMojo.java │ ├── DynamicDependency.java │ └── DependencyDownloader.java ├── Changelog.md ├── Readme.md ├── License.txt └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | *.iml 4 | .java-version 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plexus/components.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | de.qaware.maven.plugin.offline.DependencyDownloader 6 | default 7 | de.qaware.maven.plugin.offline.DependencyDownloader 8 | 9 | false 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/RepositoryType.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | /** 4 | * Enum for the types of remote repositories used in maven. 5 | * 6 | * @author Andreas Janning andreas.janning@qaware.de 7 | */ 8 | public enum RepositoryType { 9 | 10 | /** 11 | * RemoteRepository used for downloading project dependencies 12 | */ 13 | MAIN("project"), 14 | 15 | /** 16 | * RemoteRepository used for downloading plugins and their dependencies. 17 | */ 18 | PLUGIN("plugin"); 19 | 20 | private final String requestContext; 21 | 22 | /** 23 | * Constructor for RepositoryType 24 | * 25 | * @param requestContext the context for the request 26 | */ 27 | RepositoryType(String requestContext) { 28 | this.requestContext = requestContext; 29 | } 30 | 31 | /** 32 | * Returns the remote repository request context. 33 | * 34 | * @return the remote repository request context. 35 | */ 36 | public String getRequestContext() { 37 | return requestContext; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/ArtifactWithRepoType.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | import org.eclipse.aether.artifact.Artifact; 4 | 5 | import java.util.Objects; 6 | 7 | /** 8 | * Artifact with associated repository type for artifact resolution. 9 | * 10 | * @author Andreas Janning 11 | */ 12 | public class ArtifactWithRepoType { 13 | 14 | private final Artifact artifact; 15 | private final RepositoryType repositoryType; 16 | 17 | /** 18 | * Create a new ArtifactWithRepoType. 19 | * 20 | * @param artifact - The artifact to add a repository type to 21 | * @param repositoryType - The repository type to associate with the artifact 22 | */ 23 | public ArtifactWithRepoType(Artifact artifact, RepositoryType repositoryType) { 24 | this.artifact = artifact; 25 | this.repositoryType = repositoryType; 26 | } 27 | 28 | /** 29 | * Get the artifact associated with this ArtifactWithRepoType 30 | * @return the artifact associated with this ArtifactWithRepoType 31 | */ 32 | public Artifact getArtifact() { 33 | return artifact; 34 | } 35 | 36 | /** 37 | * Get the repository type associated with this ArtifactWithRepoType 38 | * @return the repository type associated with this ArtifactWithRepoType 39 | */ 40 | public RepositoryType getRepositoryType() { 41 | return repositoryType; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object o) { 46 | if (this == o) return true; 47 | if (o == null || getClass() != o.getClass()) return false; 48 | ArtifactWithRepoType that = (ArtifactWithRepoType) o; 49 | return artifact.equals(that.artifact) && 50 | repositoryType == that.repositoryType; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Objects.hash(artifact, repositoryType); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "ArtifactWithRepoType{" + 61 | "artifact=" + artifact + 62 | ", repositoryType=" + repositoryType + 63 | '}'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.2.8 4 | 5 | - [#23](https://github.com/qaware/go-offline-maven-plugin/issues/23) Fixed a bug where only one version of a plugin 6 | would be fetched if multiple versions of the same plugin where part of the build 7 | 8 | ## 1.2.7 9 | 10 | - [#22](https://github.com/qaware/go-offline-maven-plugin/issues/22) Added \ attribute to DynamicDependency 11 | 12 | ## 1.2.6 13 | 14 | - [#20](https://github.com/qaware/go-offline-maven-plugin/issues/20) Validate the user provided DynamicDependency 15 | configuration and print an error to help the user fix the configuration if it is incorrect. 16 | 17 | ## 1.2.6 18 | 19 | - fixed: Errors when downloading dependencies completely skipped the download of plugins. 20 | 21 | ## 1.2.4 22 | 23 | - [#16](https://github.com/qaware/go-offline-maven-plugin/issues/16) fixed attachedArtifacts with different types/extensions than the main artifact not recognized as part of the reactor build. 24 | - [#16](https://github.com/qaware/go-offline-maven-plugin/issues/16) fixed: 'IgnoreArtifactDescriptorRepositories' flag was always set to true instead of using maven provided value. This could lead to unresolvable artifacts if a dependency declared an additional repository. 25 | 26 | ## 1.2.3 27 | 28 | - [#14](https://github.com/qaware/go-offline-maven-plugin/issues/14) fixed Dependencies not resolving correctly when depending on a reactor artifact and changing one of the transitive dependencies of that artifact via dependency management 29 | 30 | ## 1.2.2 31 | 32 | - [#15](https://github.com/qaware/go-offline-maven-plugin/issues/15) added support for classifier to DynamicDependency configuration 33 | 34 | ## 1.2.1 35 | - [#10](https://github.com/qaware/go-offline-maven-plugin/issues/10) Fixed regression in 1.2.0: Only transitive dependencies of plugins and dynamicDependencies where downloaded, 36 | not the artifact itself. 37 | 38 | ## 1.2.0 39 | 40 | - added ability to fail on errors 41 | - rewrote how dependencies are downloaded. Should fix concurrency issues with broken downloads. 42 | 43 | ##1.1.0 44 | 45 | - [#4](https://github.com/qaware/go-offline-maven-plugin/issues/4) Added downloadJavadoc feature 46 | - [#3](https://github.com/qaware/go-offline-maven-plugin/issues/3) Fixed typos and improve readablility of Readme (thanks [vlow](https://github.com/vlow)) 47 | 48 | ## 1.0 49 | 50 | - initial version -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/ReactorArtifact.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | 4 | import java.util.Objects; 5 | 6 | /** 7 | * Describes an artifact in the current build reactor. Used to compare artifacts in a set. 8 | *

9 | * To determine if an artifact to download is part of the current reactor (and thus should not be downloaded from the internet) 10 | * we have to test if an artifact with the same groupId, artifactId and version is part of the build reactor. 11 | *

12 | * Since a maven project can output multiple artifacts with different types and classifiers, we explicitly do not test 13 | * for those properties. This is not 100% correct, since it theoretically possible to output an artifact with the same 14 | * groupId:artifactId:version identifier and different type/classifier from different projects. But the information on 15 | * which additional artifacts are produced by a project are not available to the go-offline-maven plugin, since they are 16 | * added dynamically at build time. So we have to live with this fuzziness. 17 | * 18 | * @author andreas.janning 19 | */ 20 | public class ReactorArtifact { 21 | 22 | private final String groupId; 23 | private final String artifactId; 24 | private final String version; 25 | 26 | /** 27 | * Convert a maven artifact to a ReactorArtifact 28 | * 29 | * @param mavenArtifact the artifact to create a ReactorArtifact for. 30 | */ 31 | public ReactorArtifact(org.apache.maven.artifact.Artifact mavenArtifact) { 32 | this(mavenArtifact.getGroupId(), mavenArtifact.getArtifactId(), mavenArtifact.getBaseVersion()); 33 | } 34 | 35 | /** 36 | * Convert a aether artifact to a ReactorArtifact 37 | * 38 | * @param aetherArtifact the artifact to create a ReactorArtifact for. 39 | */ 40 | public ReactorArtifact(org.eclipse.aether.artifact.Artifact aetherArtifact) { 41 | this(aetherArtifact.getGroupId(), aetherArtifact.getArtifactId(), aetherArtifact.getBaseVersion()); 42 | } 43 | 44 | private ReactorArtifact(String groupId, String artifactId, String version) { 45 | this.groupId = groupId; 46 | this.artifactId = artifactId; 47 | this.version = version; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) return true; 53 | if (o == null || getClass() != o.getClass()) return false; 54 | ReactorArtifact that = (ReactorArtifact) o; 55 | return groupId.equals(that.groupId) && 56 | artifactId.equals(that.artifactId) && 57 | version.equals(that.version); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return Objects.hash(groupId, artifactId, version); 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | final StringBuilder sb = new StringBuilder("ReactorArtifact{"); 68 | sb.append("groupId='").append(groupId).append('\''); 69 | sb.append(", artifactId='").append(artifactId).append('\''); 70 | sb.append(", version='").append(version).append('\''); 71 | sb.append('}'); 72 | return sb.toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/AbstractGoOfflineMojo.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | import org.apache.maven.artifact.repository.ArtifactRepository; 4 | import org.apache.maven.execution.MavenSession; 5 | import org.apache.maven.plugin.AbstractMojo; 6 | import org.apache.maven.plugins.annotations.Parameter; 7 | import org.apache.maven.project.DefaultProjectBuildingRequest; 8 | import org.apache.maven.project.MavenProject; 9 | import org.apache.maven.project.ProjectBuildingRequest; 10 | 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | /** 15 | * Base class for mojos in the in the go-offline maven plugin. 16 | *

17 | * Provides access to parameters and injected configuration needed by the plugin. 18 | *

19 | * Also provides functionality to schedule tasks for asynchronous completion. 20 | * 21 | * @author Andreas Janning andreas.janning@qaware.de 22 | */ 23 | public abstract class AbstractGoOfflineMojo extends AbstractMojo { 24 | 25 | /** 26 | * Remote repositories used to download dependencies. 27 | */ 28 | @Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true) 29 | private List remoteRepositories; 30 | 31 | /** 32 | * Remote repositories used to download plugins. 33 | */ 34 | @Parameter(defaultValue = "${project.pluginArtifactRepositories}", readonly = true, required = true) 35 | private List remotePluginRepositories; 36 | 37 | /** 38 | * Contains the full list of projects in the reactor. 39 | */ 40 | @Parameter(defaultValue = "${reactorProjects}", readonly = true) 41 | private List reactorProjects; 42 | 43 | /** 44 | * The Maven session. 45 | */ 46 | @Parameter(defaultValue = "${session}", readonly = true, required = true) 47 | private MavenSession session; 48 | 49 | private ProjectBuildingRequest buildingRequest; 50 | 51 | AbstractGoOfflineMojo() { 52 | // Noop 53 | } 54 | 55 | /** 56 | * Returns a building request initialized with the data of the current maven session 57 | * 58 | * @return a building request initialized with the data of the current maven session 59 | */ 60 | protected ProjectBuildingRequest getBuildingRequest() { 61 | if (buildingRequest == null) { 62 | buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); 63 | buildingRequest.setRemoteRepositories(remoteRepositories); 64 | buildingRequest.setPluginArtifactRepositories(remotePluginRepositories); 65 | buildingRequest.setRepositoryMerging(ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT); 66 | buildingRequest.setResolveDependencies(true); 67 | } 68 | return buildingRequest; 69 | } 70 | 71 | /** 72 | * Returns the full list of projects in the current build-reactor 73 | * 74 | * @return the full list of projects in the current build-reactor 75 | */ 76 | protected List getReactorProjects() { 77 | return Collections.unmodifiableList(reactorProjects); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/ResolveDependenciesMojo.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | import org.apache.maven.model.Plugin; 4 | import org.apache.maven.plugin.MojoExecutionException; 5 | import org.apache.maven.plugins.annotations.Component; 6 | import org.apache.maven.plugins.annotations.Mojo; 7 | import org.apache.maven.plugins.annotations.Parameter; 8 | import org.apache.maven.project.MavenProject; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashSet; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | /** 16 | * Mojo used to download all dependencies of a project or reactor to the local repository. 17 | *

18 | * This includes: 19 | *

25 | * 26 | * @author Andreas Janning andreas.janning@qaware.de 27 | */ 28 | @Mojo(name = "resolve-dependencies", threadSafe = true, requiresOnline = true, aggregator = true) 29 | public class ResolveDependenciesMojo extends AbstractGoOfflineMojo { 30 | 31 | @Component 32 | private DependencyDownloader dependencyDownloader; 33 | 34 | @Parameter 35 | private List dynamicDependencies; 36 | 37 | @Parameter(defaultValue = "false", property = "downloadSources") 38 | private boolean downloadSources; 39 | 40 | @Parameter(defaultValue = "false", property = "downloadJavadoc") 41 | private boolean downloadJavadoc; 42 | 43 | @Parameter(defaultValue = "false", property = "failOnErrors") 44 | private boolean failOnErrors; 45 | 46 | ResolveDependenciesMojo() { 47 | // Noop 48 | } 49 | 50 | public void execute() throws MojoExecutionException { 51 | validateConfiguration(); 52 | dependencyDownloader.init(getBuildingRequest(), getReactorProjects(), getLog()); 53 | if (downloadSources) { 54 | dependencyDownloader.enableDownloadSources(); 55 | } 56 | if (downloadJavadoc) { 57 | dependencyDownloader.enableDownloadJavadoc(); 58 | } 59 | 60 | List allPlugins = new ArrayList<>(); 61 | for (MavenProject mavenProject : getReactorProjects()) { 62 | List buildPlugins = mavenProject.getBuildPlugins(); 63 | allPlugins.addAll(buildPlugins); 64 | } 65 | 66 | Set artifactsToDownload = new HashSet<>(); 67 | 68 | 69 | for (Plugin plugin : allPlugins) { 70 | artifactsToDownload.addAll(dependencyDownloader.resolvePlugin(plugin)); 71 | } 72 | for (MavenProject project : getReactorProjects()) { 73 | artifactsToDownload.addAll(dependencyDownloader.resolveDependencies(project)); 74 | } 75 | if (dynamicDependencies != null) { 76 | for (DynamicDependency dep : dynamicDependencies) { 77 | artifactsToDownload.addAll(dependencyDownloader.resolveDynamicDependency(dep)); 78 | } 79 | } 80 | 81 | dependencyDownloader.downloadArtifacts(artifactsToDownload); 82 | 83 | 84 | List errors = dependencyDownloader.getErrors(); 85 | for (Exception error : errors) { 86 | getLog().warn(error.getMessage()); 87 | } 88 | 89 | if (failOnErrors && !errors.isEmpty()) { 90 | throw new MojoExecutionException("Unable to download dependencies, consult the errors and warnings printed above."); 91 | } 92 | } 93 | 94 | private void validateConfiguration() throws MojoExecutionException { 95 | if (dynamicDependencies != null) { 96 | for (DynamicDependency dynamicDependency : dynamicDependencies) { 97 | dynamicDependency.validate(); 98 | } 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/DynamicDependency.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | import org.apache.maven.plugin.MojoExecutionException; 4 | 5 | /** 6 | * Configuration used to declare extra dependencies for the {@link ResolveDependenciesMojo}. 7 | *

8 | * Users can declare extra dependencies that do not appear in any dependency tree of the project but are still needed for the build to work. 9 | *

10 | * The most common examples are artifacts that are dynamically loaded by plugins during build runtime 11 | * 12 | * @author Andreas Janning andreas.janning@qaware.de 13 | */ 14 | public class DynamicDependency { 15 | 16 | /** The config error message */ 17 | public static final String CONFIGURATION_ERROR_MESSAGE = "DynamicDependency configuration error"; 18 | 19 | private String artifactId; 20 | private String groupId; 21 | private String version; 22 | private String classifier; 23 | private String type; 24 | private RepositoryType repositoryType; 25 | 26 | DynamicDependency() { 27 | // Noop 28 | } 29 | 30 | /** 31 | * Validate that all required parameters are set. 32 | * 33 | * @throws MojoExecutionException if any required parameter is not set 34 | */ 35 | public void validate() throws MojoExecutionException { 36 | if (artifactId == null || artifactId.isEmpty()) { 37 | throw new MojoExecutionException(this, CONFIGURATION_ERROR_MESSAGE, "Invalid " + this + ": The artifactId must not empty"); 38 | } 39 | if (groupId == null || groupId.isEmpty()) { 40 | throw new MojoExecutionException(this, CONFIGURATION_ERROR_MESSAGE, "Invalid " + this + ": The groupId must not empty"); 41 | } 42 | if (version == null || version.isEmpty()) { 43 | throw new MojoExecutionException(this, CONFIGURATION_ERROR_MESSAGE, "Invalid " + this + ": The version must not empty"); 44 | } 45 | if (repositoryType == null) { 46 | throw new MojoExecutionException(this, CONFIGURATION_ERROR_MESSAGE, "Invalid " + this + ": The repositoryType must be defined"); 47 | } 48 | } 49 | 50 | /** 51 | * Returns the artifactId of the {@link DynamicDependency} 52 | * 53 | * @return The artifactId of the {@link DynamicDependency} 54 | */ 55 | public String getArtifactId() { 56 | return artifactId; 57 | } 58 | 59 | /** 60 | * Set the artifactId 61 | * 62 | * @param artifactId - Id to set 63 | */ 64 | public void setArtifactId(String artifactId) { 65 | this.artifactId = artifactId; 66 | } 67 | 68 | /** 69 | * Returns the groupId of the {@link DynamicDependency} 70 | * 71 | * @return The groupId of the {@link DynamicDependency} 72 | */ 73 | public String getGroupId() { 74 | return groupId; 75 | } 76 | 77 | /** 78 | * Set the groupId 79 | * 80 | * @param groupId - Id to set 81 | */ 82 | public void setGroupId(String groupId) { 83 | this.groupId = groupId; 84 | } 85 | 86 | /** 87 | * Returns the version of the {@link DynamicDependency} 88 | * 89 | * @return The version of the {@link DynamicDependency} 90 | */ 91 | public String getVersion() { 92 | return version; 93 | } 94 | 95 | /** 96 | * Set the version 97 | * 98 | * @param version - Version to set 99 | */ 100 | public void setVersion(String version) { 101 | this.version = version; 102 | } 103 | 104 | /** 105 | * Either returns the user entered value or "jar" as a default if the user provided no value. 106 | * 107 | * @return The type of the {@link DynamicDependency}. 108 | */ 109 | public String getType() { 110 | if (type == null || type.isEmpty()) { 111 | return "jar"; 112 | } 113 | return type; 114 | } 115 | 116 | /** 117 | * Set the type 118 | * 119 | * @param type - Type to set 120 | */ 121 | public void setType(String type) { 122 | this.type = type; 123 | } 124 | 125 | /** 126 | * Returns the classifier of the {@link DynamicDependency}. May be null. 127 | * 128 | * @return The classifier of the {@link DynamicDependency}. May be null. 129 | */ 130 | public String getClassifier() { 131 | return classifier; 132 | } 133 | 134 | /** 135 | * Set the classifier 136 | * 137 | * @param classifier - classifier to set 138 | */ 139 | public void setClassifier(String classifier) { 140 | this.classifier = classifier; 141 | } 142 | 143 | /** 144 | * Returns from which type of remoteRepository this dependency must be downloaded 145 | * 146 | * @return from which type of remoteRepository this dependency must be downloaded 147 | */ 148 | public RepositoryType getRepositoryType() { 149 | return repositoryType; 150 | } 151 | 152 | /** 153 | * Set the type of remoteRepository from which the dependency should be downloaded 154 | * 155 | * @param repositoryType - The repository type to set 156 | */ 157 | public void setRepositoryType(RepositoryType repositoryType) { 158 | this.repositoryType = repositoryType; 159 | } 160 | 161 | @Override 162 | public String toString() { 163 | return "DynamicDependency{" + 164 | "artifactId='" + artifactId + '\'' + 165 | ", groupId='" + groupId + '\'' + 166 | ", version='" + version + '\'' + 167 | ", classifier='" + classifier + '\'' + 168 | ", repositoryType=" + repositoryType + 169 | '}'; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Go Offline Maven Plugin 2 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.qaware.maven/go-offline-maven-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.qaware.maven/go-offline-maven-plugin) 4 | 5 | Maven Plugin used to download all Dependencies and Plugins required in a Maven build, 6 | so the build can be run without an internet connection afterwards. 7 | 8 | This is especially relevant for modern CI-Systems like Gitlab and Circle-CI which 9 | need a consistent local Maven repository in their cache to build efficiently. 10 | 11 | The plugin can also be used to download all source files of all transitive dependencies 12 | of a project. 13 | 14 | Maven already has an official way to do all this: the maven-dependency-plugin go-offline goal; 15 | Unfortunately, the go-offline goal suffers from several drawbacks: 16 | 17 | - Multi-Module builds are not supported since the plugin tries to download reactor-dependencies from the remote repository 18 | - Most parameters simply do not work 19 | - There is no option to download dynamic dependencies 20 | 21 | The Go Offline Maven Plugin fixes these drawbacks. 22 | 23 | ## Requirements 24 | - Java 1.7 or higher 25 | - Maven 3.1.x or higher 26 | 27 | **Warning:** Maven 3.5.2 has a Bug that causes the Plugin to hang. If you experience hangups during downloading dependencies, please upgrade to Maven 3.5.3 or higher (See [MNG-6323](https://issues.apache.org/jira/browse/MNG-6323) ) 28 | 29 | **Warning:** Maven versions 3.2.x and 3.3.x have a bug that can cause the plugin to fail if maven is started in parallel build mode (-T option). Please call the go-offline-maven-plugin 30 | without the -T option or upgrade Maven to a newer version. (See [MNG-6170](https://issues.apache.org/jira/browse/MNG-6170)) 31 | 32 | ## Goals 33 | The Go Offline Maven Plugin only has one goal: "resolve-dependencies". This goal downloads 34 | all external dependencies and plugins needed for your build to your local repository. 35 | Dependencies that are built inside the reactor build of your project are excluded. For downloading, 36 | the repositories specified in your pom.xml are used. 37 | 38 | ## Usage 39 | Simply add the plugin to the pom.xml of your project. Use the root reactor pom in case of a multi module project. 40 | Make sure to configure any dynamic dependency your project has (see below). 41 | 42 | 43 | de.qaware.maven 44 | go-offline-maven-plugin 45 | 1.2.8 46 | 47 | 48 | 49 | org.apache.maven.surefire 50 | surefire-junit4 51 | 2.20.1 52 | PLUGIN 53 | 54 | 55 | com.querydsl 56 | querydsl-apt 57 | 4.2.1 58 | jpa 59 | MAIN 60 | 61 | 62 | org.flywaydb 63 | flyway-commandline 64 | 4.0.3 65 | zip 66 | MAIN 67 | 68 | 69 | 70 | 71 | 72 | To download all dependencies to your local repository, use 73 | 74 | mvn de.qaware.maven:go-offline-maven-plugin:resolve-dependencies 75 | 76 | Make sure to activate any profiles etc. so that all relevant modules of your project are included 77 | in the Maven run. 78 | 79 | ### Dynamic Dependencies 80 | Unfortunately some Maven Plugins dynamically load additional dependencies when they are run. Since those 81 | dependencies are not necessarily specified anywhere in the plugins pom.xml, the Go Offline Maven Plugin 82 | cannot know that it has to download those dependencies. Most prominently, the surefire-maven-plugin dynamically 83 | loads test-providers based on the tests it finds in the project. 84 | 85 | You must tell the Go Offline Maven Plugin of those dynamic depenencies to ensure all dependencies are downloaded. 86 | For each dependency, add a DynamicDependency block to the plugin's configuration as seen in the [Usage](#usage) section. 87 | Each dynamic dependency block consists of the following parameters: 88 | 89 | - *groupId* The GroupId of the dynamic dependency to download 90 | - *artifactId* The ArtifactId of the dynamic dependency to download 91 | - *version* The version of the dynamic dependency to download 92 | - *classifier* (optional) The classifier of the dynamic dependency to download 93 | - *type* (optional) The type of the dynamic dependency to download 94 | - *repositoryType* Either 'MAIN' or 'PLUGIN' to control from which repository the dependency is downloaded 95 | 96 | Note that Plugins are not consistent about where they pull their dynamic dependencies from. Some use the Plugin-Repository 97 | , some the Main-Repository. If one doesn't work, try the other. 98 | 99 | ### Downloading Sources and Javadoc 100 | The plugin can also download the source files and/or javadoc of the project's transitive dependencies. This behaviour can either be activated via the pom.xml 101 | or a command line parameter. 102 | 103 | 104 | de.qaware.maven 105 | go-offline-maven-plugin 106 | 1.2.8 107 | 108 | true 109 | true 110 | 111 | 112 | 113 | or 114 | 115 | mvn de.qaware.maven:go-offline-maven-plugin:resolve-dependencies -DdownloadSources -DdownloadJavadoc 116 | 117 | ### Usage in CI environments 118 | The Go Offline Maven Plugin can be used to build a clean repository for build server environments. The resulting repository includes exactly the dependencies and 119 | plugins needed for building the project. 120 | 121 | #### Gitlab 122 | 123 | For gitlab, add the following build step to the front of your pipeline: 124 | 125 | download-dependencies: 126 | image: maven:3-jdk-8 127 | stage: prepare 128 | script: 129 | - 'mvn de.qaware.maven:go-offline-maven-plugin:1.2.8:resolve-dependencies -Dmaven.repo.local=.m2/repository' 130 | cache: 131 | key: M2_REPO 132 | paths: 133 | - .m2/repository 134 | 135 | This will fill the cache "M2_REPO" with all needed artifacts, reusing the previous "M2_REPO" cache to avoid downloading all artifacts on each run. 136 | 137 | Build steps using the repository may then be configured like this: 138 | 139 | .build 140 | image: maven:3-jdk-8 141 | stage: build 142 | script: 143 | - 'mvn install -Dmaven.repo.local=.m2/repository' 144 | cache: 145 | key: M2_REPO 146 | paths: 147 | - .m2/repository 148 | policy: pull 149 | 150 | This will pull the previously filled cache into the build image and use it as the local maven repository. 151 | Policy: pull ensures that artifacts that are generated as part of the build are not written back to the cache 152 | . 153 | 154 | ## License 155 | 156 | Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) 157 | 158 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | de.qaware.maven 8 | go-offline-maven-plugin 9 | 1.2.8 10 | maven-plugin 11 | Go Offline Maven Plugin 12 | Maven Plugin used to download all Dependencies and Plugins required in a Maven build, 13 | so the build can be run without an internet connection afterwards. 14 | 15 | https://github.com/qaware/go-offline-maven-plugin 16 | 17 | 2018 18 | 19 | 20 | Apache License, Version 2.0 21 | https://www.apache.org/licenses/LICENSE-2.0.txt 22 | 23 | 24 | 25 | QAware GmbH 26 | https://www.qaware.de/ 27 | 28 | 29 | 30 | ajanning 31 | Andreas Janning 32 | andreas.janning@qaware.de 33 | QAware GmbH 34 | https://www.qaware.de/ 35 | 36 | 37 | 38 | 39 | scm:git:https://github.com/qaware/go-offline-maven-plugin.git 40 | scm:git:https://github.com/qaware/go-offline-maven-plugin.git 41 | scm:git:https://github.com/qaware/go-offline-maven-plugin/tree/master 42 | 43 | 44 | 45 | GitHub 46 | https://github.com/qaware/go-offline-maven-plugin/issues 47 | 48 | 49 | 50 | 3.8.6 51 | UTF-8 52 | 1.8 53 | 1.8 54 | 3.6.4 55 | 56 | 57 | 58 | ${mavenVersion} 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven 65 | maven-core 66 | ${mavenVersion} 67 | true 68 | provided 69 | 70 | 71 | 72 | org.apache.maven 73 | maven-model 74 | ${mavenVersion} 75 | true 76 | provided 77 | 78 | 79 | 80 | org.apache.maven 81 | maven-settings 82 | ${mavenVersion} 83 | true 84 | provided 85 | 86 | 87 | 88 | org.apache.maven 89 | maven-settings-builder 90 | ${mavenVersion} 91 | true 92 | provided 93 | 94 | 95 | 96 | org.apache.maven 97 | maven-builder-support 98 | ${mavenVersion} 99 | true 100 | provided 101 | 102 | 103 | 104 | org.apache.maven 105 | maven-repository-metadata 106 | ${mavenVersion} 107 | true 108 | provided 109 | 110 | 111 | 112 | org.apache.maven 113 | maven-artifact 114 | ${mavenVersion} 115 | true 116 | provided 117 | 118 | 119 | 120 | org.apache.maven 121 | maven-plugin-api 122 | ${mavenVersion} 123 | true 124 | provided 125 | 126 | 127 | 128 | org.apache.maven 129 | maven-model-builder 130 | ${mavenVersion} 131 | true 132 | provided 133 | 134 | 135 | 136 | org.apache.maven 137 | maven-resolver-provider 138 | ${mavenVersion} 139 | true 140 | provided 141 | 142 | 143 | 144 | 145 | org.apache.maven.plugin-tools 146 | maven-plugin-annotations 147 | ${maven.plugin.tools.version} 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-enforcer-plugin 157 | true 158 | 159 | 160 | enforce-versions 161 | 162 | enforce 163 | 164 | 165 | 166 | 167 | ${mavenVersion} 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | org.apache.maven.plugins 177 | maven-source-plugin 178 | 179 | 180 | attach-sources 181 | verify 182 | 183 | jar-no-fork 184 | 185 | 186 | 187 | 188 | 189 | org.apache.maven.plugins 190 | maven-javadoc-plugin 191 | 192 | 193 | attach-javadoc 194 | verify 195 | 196 | jar 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | org.apache.maven.plugins 206 | maven-clean-plugin 207 | 3.2.0 208 | 209 | 210 | org.apache.maven.plugins 211 | maven-compiler-plugin 212 | 3.10.1 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-deploy-plugin 217 | 3.0.0 218 | 219 | 220 | org.apache.maven.plugins 221 | maven-enforcer-plugin 222 | 3.1.0 223 | 224 | 225 | org.apache.maven.plugins 226 | maven-install-plugin 227 | 3.0.1 228 | 229 | 230 | org.apache.maven.plugins 231 | maven-jar-plugin 232 | 3.3.0 233 | 234 | 235 | org.apache.maven.plugins 236 | maven-javadoc-plugin 237 | 3.4.1 238 | 239 | 240 | org.apache.maven.plugins 241 | maven-plugin-plugin 242 | ${maven.plugin.tools.version} 243 | 244 | 245 | org.apache.maven.plugins 246 | maven-project-info-reports-plugin 247 | 3.4.1 248 | 249 | 250 | org.apache.maven.plugins 251 | maven-resources-plugin 252 | 3.3.0 253 | 254 | 255 | org.apache.maven.plugins 256 | maven-site-plugin 257 | 4.0.0-M3 258 | 259 | 260 | org.apache.maven.plugins 261 | maven-source-plugin 262 | 3.2.1 263 | 264 | 265 | org.apache.maven.plugins 266 | maven-surefire-plugin 267 | 3.0.0-M7 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | org.apache.maven.plugins 277 | maven-project-info-reports-plugin 278 | 3.4.1 279 | 280 | 281 | 282 | 283 | 284 | 285 | bintray-qaware-oss 286 | https://api.bintray.com/maven/qaware-oss/maven/go-offline-maven-plugin/;publish=1 287 | 288 | 289 | -------------------------------------------------------------------------------- /src/main/java/de/qaware/maven/plugin/offline/DependencyDownloader.java: -------------------------------------------------------------------------------- 1 | package de.qaware.maven.plugin.offline; 2 | 3 | import org.apache.maven.RepositoryUtils; 4 | import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; 5 | import org.apache.maven.model.DependencyManagement; 6 | import org.apache.maven.model.Plugin; 7 | import org.apache.maven.plugin.logging.Log; 8 | import org.apache.maven.project.MavenProject; 9 | import org.apache.maven.project.ProjectBuildingRequest; 10 | import org.codehaus.plexus.component.annotations.Component; 11 | import org.codehaus.plexus.component.annotations.Requirement; 12 | import org.eclipse.aether.DefaultRepositoryCache; 13 | import org.eclipse.aether.DefaultRepositorySystemSession; 14 | import org.eclipse.aether.RepositoryException; 15 | import org.eclipse.aether.RepositorySystem; 16 | import org.eclipse.aether.RepositorySystemSession; 17 | import org.eclipse.aether.artifact.Artifact; 18 | import org.eclipse.aether.artifact.ArtifactType; 19 | import org.eclipse.aether.artifact.ArtifactTypeRegistry; 20 | import org.eclipse.aether.artifact.DefaultArtifact; 21 | import org.eclipse.aether.collection.CollectRequest; 22 | import org.eclipse.aether.collection.CollectResult; 23 | import org.eclipse.aether.collection.DependencyCollectionException; 24 | import org.eclipse.aether.collection.DependencySelector; 25 | import org.eclipse.aether.graph.Dependency; 26 | import org.eclipse.aether.graph.DependencyNode; 27 | import org.eclipse.aether.graph.DependencyVisitor; 28 | import org.eclipse.aether.repository.RemoteRepository; 29 | import org.eclipse.aether.resolution.ArtifactRequest; 30 | import org.eclipse.aether.resolution.ArtifactResolutionException; 31 | import org.eclipse.aether.util.graph.selector.AndDependencySelector; 32 | import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector; 33 | import org.eclipse.aether.util.graph.selector.OptionalDependencySelector; 34 | import org.eclipse.aether.util.graph.selector.ScopeDependencySelector; 35 | 36 | import java.lang.reflect.Constructor; 37 | import java.util.ArrayList; 38 | import java.util.Collection; 39 | import java.util.Collections; 40 | import java.util.HashSet; 41 | import java.util.List; 42 | import java.util.Set; 43 | 44 | /** 45 | * Downloads artifacts for project dependencies and plugins. This class maintains two separate sessions with separate caches 46 | * for project and plugin dependencies so all artifacts are resolved for all remote repository contexts. 47 | *

48 | * The downloader must be initialized by calling {@link #init(ProjectBuildingRequest, List, Log)} before any of its other methods my be used. 49 | *

50 | * This class only works for maven versions >=3.1 51 | * 52 | * @author Andreas Janning andreas.janning@qaware.de 53 | */ 54 | @Component(role = DependencyDownloader.class, hint = "default") 55 | public class DependencyDownloader { 56 | 57 | /** 58 | * Artifact type for maven plugins. 59 | */ 60 | private static final String MAVEN_PLUGIN_ARTIFACT_TYPE = "maven-plugin"; 61 | 62 | /** 63 | * Aether repository system 64 | */ 65 | @Requirement 66 | private RepositorySystem repositorySystem; 67 | 68 | /** 69 | * Maven artifact handler manager 70 | */ 71 | @Requirement 72 | private ArtifactHandlerManager artifactHandlerManager; 73 | 74 | private DefaultRepositorySystemSession remoteSession; 75 | private DefaultRepositorySystemSession pluginSession; 76 | private List remoteRepositories; 77 | private List pluginRepositories; 78 | private ArtifactTypeRegistry typeRegistry; 79 | private Log log; 80 | private List errors; 81 | 82 | private boolean downloadSources = false; 83 | private boolean downloadJavadoc = false; 84 | private Set reactorArtifacts; 85 | 86 | DependencyDownloader() { 87 | // Noop 88 | } 89 | 90 | /** 91 | * Initialize the DependencyDownloader 92 | * 93 | * @param buildingRequest a buildingRequest containing the maven session and Repositories to be used to download artifacts 94 | * @param reactorProjects the reactorProjects of the current build used to exclude reactor artifacts from the dependency download. 95 | * @param logger used to log infos and warnings. 96 | */ 97 | public void init(ProjectBuildingRequest buildingRequest, List reactorProjects, Log logger) { 98 | this.log = logger; 99 | typeRegistry = RepositoryUtils.newArtifactTypeRegistry(artifactHandlerManager); 100 | remoteRepositories = RepositoryUtils.toRepos(buildingRequest.getRemoteRepositories()); 101 | pluginRepositories = RepositoryUtils.toRepos(buildingRequest.getPluginArtifactRepositories()); 102 | remoteSession = new DefaultRepositorySystemSession(buildingRequest.getRepositorySession()); 103 | 104 | DependencySelector wagonExcluder = null; 105 | try { 106 | Class wagonExcluderClass = Class.forName("org.apache.maven.plugin.internal.WagonExcluder"); 107 | Constructor wagonExcluderConstructor = wagonExcluderClass.getDeclaredConstructor(); 108 | wagonExcluderConstructor.setAccessible(true); 109 | wagonExcluder = (DependencySelector) wagonExcluderConstructor.newInstance(); 110 | } catch (ReflectiveOperationException e) { 111 | log.warn("Could not initialize wagonExcluder, might not be able to download plugin dependencies correctly", e); 112 | } 113 | 114 | reactorArtifacts = computeReactorArtifacts(reactorProjects); 115 | DependencySelector selector = new AndDependencySelector(new ScopeDependencySelector("system", "test", "provided"), new OptionalDependencySelector(), new ExclusionDependencySelector()); 116 | remoteSession.setDependencySelector(selector); 117 | 118 | pluginSession = new DefaultRepositorySystemSession(remoteSession); 119 | remoteSession.setCache(new DefaultRepositoryCache()); 120 | pluginSession.setCache(new DefaultRepositoryCache()); 121 | if (wagonExcluder != null) { 122 | pluginSession.setDependencySelector(new AndDependencySelector(new ScopeDependencySelector("system", "test", "provided"), new OptionalDependencySelector(), wagonExcluder, new ExclusionDependencySelector())); 123 | } 124 | this.errors = new ArrayList<>(); 125 | } 126 | 127 | /** 128 | * Enable the download of the source artifacts. 129 | */ 130 | public void enableDownloadSources() { 131 | this.downloadSources = true; 132 | } 133 | 134 | /** 135 | * Enable the download of the Javadoc artifacts. 136 | */ 137 | public void enableDownloadJavadoc() { 138 | this.downloadJavadoc = true; 139 | } 140 | 141 | /** 142 | * Download the collection of artifacts 143 | * 144 | * @param artifacts the collection of ReactorArtifacts that wants to be downloaded. 145 | */ 146 | public void downloadArtifacts(Collection artifacts) { 147 | List mainRequests = new ArrayList<>(artifacts.size()); 148 | List pluginRequests = new ArrayList<>(artifacts.size()); 149 | for (ArtifactWithRepoType artifactWithRepoType : artifacts) { 150 | Artifact artifact = artifactWithRepoType.getArtifact(); 151 | RepositoryType context = artifactWithRepoType.getRepositoryType(); 152 | 153 | ArtifactRequest artifactRequest = new ArtifactRequest(); 154 | artifactRequest.setArtifact(artifact); 155 | artifactRequest.setRepositories(context == RepositoryType.MAIN ? remoteRepositories : pluginRepositories); 156 | if (context == RepositoryType.MAIN) { 157 | artifactRequest.setRequestContext(context.getRequestContext()); 158 | mainRequests.add(artifactRequest); 159 | 160 | } else { 161 | artifactRequest.setRequestContext(context.getRequestContext()); 162 | pluginRequests.add(artifactRequest); 163 | } 164 | if (context == RepositoryType.MAIN && "jar".equals(artifact.getExtension())) { 165 | if (downloadSources) { 166 | Artifact sourceArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), "sources", artifact.getExtension(), artifact.getVersion()); 167 | mainRequests.add(new ArtifactRequest(sourceArtifact, remoteRepositories, context.getRequestContext())); 168 | } 169 | if (downloadJavadoc) { 170 | Artifact javadocArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), "javadoc", artifact.getExtension(), artifact.getVersion()); 171 | mainRequests.add(new ArtifactRequest(javadocArtifact, remoteRepositories, context.getRequestContext())); 172 | } 173 | } 174 | } 175 | try { 176 | repositorySystem.resolveArtifacts(remoteSession, mainRequests); 177 | } catch (ArtifactResolutionException | RuntimeException e) { 178 | log.error("Error downloading dependencies for project"); 179 | handleRepositoryException(e); 180 | } 181 | try { 182 | repositorySystem.resolveArtifacts(pluginSession, pluginRequests); 183 | } catch (ArtifactResolutionException | RuntimeException e) { 184 | log.error("Error downloading plugin dependencies for project"); 185 | handleRepositoryException(e); 186 | } 187 | } 188 | 189 | /** 190 | * Download all dependencies of a maven project including transitive dependencies. 191 | * Dependencies that refer to an artifact in the current reactor build are ignored. 192 | * Transitive dependencies that are marked as optional are ignored 193 | * Transitive dependencies with the scopes "test", "system" and "provided" are ignored. 194 | * 195 | * @param project the project to download the dependencies for. 196 | * @return The set of resolved ArtifactRepositoryType pairs 197 | */ 198 | public Set resolveDependencies(MavenProject project) { 199 | Artifact projectArtifact = RepositoryUtils.toArtifact(project.getArtifact()); 200 | CollectRequest collectRequest = new CollectRequest(); 201 | collectRequest.setRepositories(remoteRepositories); 202 | collectRequest.setRootArtifact(projectArtifact); 203 | collectRequest.setRequestContext(RepositoryType.MAIN.getRequestContext()); 204 | 205 | List aetherDependencies = new ArrayList<>(); 206 | for (org.apache.maven.model.Dependency d : project.getDependencies()) { 207 | Dependency dependency = RepositoryUtils.toDependency(d, typeRegistry); 208 | aetherDependencies.add(dependency); 209 | } 210 | 211 | collectRequest.setDependencies(aetherDependencies); 212 | 213 | List aetherDepManagement = new ArrayList<>(); 214 | DependencyManagement dependencyManagement = project.getDependencyManagement(); 215 | if (dependencyManagement != null) { 216 | for (org.apache.maven.model.Dependency d : dependencyManagement.getDependencies()) { 217 | Dependency dependency = RepositoryUtils.toDependency(d, typeRegistry); 218 | aetherDepManagement.add(dependency); 219 | } 220 | } 221 | collectRequest.setManagedDependencies(aetherDepManagement); 222 | 223 | try { 224 | CollectResult collectResult = repositorySystem.collectDependencies(remoteSession, collectRequest); 225 | return getArtifactsFromCollectResult(collectResult, RepositoryType.MAIN); 226 | } catch (RepositoryException | RuntimeException e) { 227 | log.error("Error resolving dependencies for project " + project.getGroupId() + ":" + project.getArtifactId()); 228 | handleRepositoryException(e); 229 | } 230 | return Collections.emptySet(); 231 | } 232 | 233 | private Set getArtifactsFromCollectResult(CollectResult collectResult, RepositoryType context) { 234 | CollectAllDependenciesVisitor visitor = new CollectAllDependenciesVisitor(); 235 | collectResult.getRoot().accept(visitor); 236 | Set visitorArtifacts = visitor.getArtifacts(); 237 | Set artifacts = new HashSet<>(); 238 | for (Artifact visitorArtifact : visitorArtifacts) { 239 | if (!isReactorArtifact(visitorArtifact)) { 240 | artifacts.add(new ArtifactWithRepoType(visitorArtifact, context)); 241 | } 242 | } 243 | Artifact rootArtifact = collectResult.getRoot().getArtifact(); 244 | if (!isReactorArtifact(rootArtifact)) { 245 | artifacts.add(new ArtifactWithRepoType(rootArtifact, context)); 246 | } 247 | return artifacts; 248 | } 249 | 250 | /** 251 | * Download a plugin, all of its transitive dependencies and dependencies declared on the plugin declaration. 252 | *

253 | * Dependencies and plugin artifacts that refer to an artifact in the current reactor build are ignored. 254 | * Transitive dependencies that are marked as optional are ignored 255 | * Transitive dependencies with the scopes "test", "system" and "provided" are ignored. 256 | * 257 | * @param plugin the plugin to download 258 | * @return The set of resolved ArtifactRepositoryType pairs 259 | */ 260 | public Set resolvePlugin(Plugin plugin) { 261 | Artifact pluginArtifact = toArtifact(plugin); 262 | Dependency pluginDependency = new Dependency(pluginArtifact, null); 263 | CollectRequest collectRequest = new CollectRequest(pluginDependency, pluginRepositories); 264 | collectRequest.setRequestContext(RepositoryType.PLUGIN.getRequestContext()); 265 | 266 | List pluginDependencies = new ArrayList<>(); 267 | for (org.apache.maven.model.Dependency d : plugin.getDependencies()) { 268 | Dependency dependency = RepositoryUtils.toDependency(d, typeRegistry); 269 | pluginDependencies.add(dependency); 270 | } 271 | collectRequest.setDependencies(pluginDependencies); 272 | 273 | try { 274 | CollectResult collectResult = repositorySystem.collectDependencies(pluginSession, collectRequest); 275 | return getArtifactsFromCollectResult(collectResult, RepositoryType.PLUGIN); 276 | } catch (DependencyCollectionException | RuntimeException e) { 277 | log.error("Error resolving plugin " + plugin.getGroupId() + ":" + plugin.getArtifactId()); 278 | handleRepositoryException(e); 279 | } 280 | return Collections.emptySet(); 281 | } 282 | 283 | /** 284 | * Download a single dependency and all of its transitive dependencies that is needed by the build without appearing in any dependency tree 285 | *

286 | * Dependencies and plugin artifacts that refer to an artifact in the current reactor build are ignored. 287 | * Transitive dependencies that are marked as optional are ignored 288 | * Transitive dependencies with the scopes "test", "system" and "provided" are ignored. 289 | * 290 | * @param dynamicDependency the dependency to download 291 | * @return The set of resolved ArtifactRepositoryType pairs 292 | */ 293 | public Set resolveDynamicDependency(DynamicDependency dynamicDependency) { 294 | ArtifactType artifactType = typeRegistry.get(dynamicDependency.getType()); 295 | DefaultArtifact artifact; 296 | if (artifactType == null) { 297 | artifact = new DefaultArtifact(dynamicDependency.getGroupId(), dynamicDependency.getArtifactId(), dynamicDependency.getClassifier(), dynamicDependency.getType(), dynamicDependency.getVersion()); 298 | } else { 299 | artifact = new DefaultArtifact(dynamicDependency.getGroupId(), dynamicDependency.getArtifactId(), dynamicDependency.getClassifier(), artifactType.getExtension(), dynamicDependency.getVersion(), artifactType); 300 | } 301 | CollectRequest collectRequest = new CollectRequest(); 302 | collectRequest.setRoot(new Dependency(artifact, null)); 303 | RepositoryType repositoryType = dynamicDependency.getRepositoryType(); 304 | RepositorySystemSession session; 305 | switch (repositoryType) { 306 | case MAIN: 307 | session = remoteSession; 308 | collectRequest.setRepositories(remoteRepositories); 309 | collectRequest.setRequestContext(repositoryType.getRequestContext()); 310 | break; 311 | case PLUGIN: 312 | session = pluginSession; 313 | collectRequest.setRepositories(pluginRepositories); 314 | collectRequest.setRequestContext(repositoryType.getRequestContext()); 315 | break; 316 | default: 317 | throw new IllegalStateException("Unknown enum val " + repositoryType); 318 | 319 | } 320 | try { 321 | CollectResult collectResult = repositorySystem.collectDependencies(session, collectRequest); 322 | return getArtifactsFromCollectResult(collectResult, repositoryType); 323 | } catch (DependencyCollectionException | RuntimeException e) { 324 | log.error("Error resolving dynamic dependency" + dynamicDependency.getGroupId() + ":" + dynamicDependency.getArtifactId()); 325 | handleRepositoryException(e); 326 | } 327 | return Collections.emptySet(); 328 | } 329 | 330 | /** 331 | * Returns a List of errors encountered during the downloading of artifacts since this class has been initialized. 332 | * 333 | * @return a List of errors encountered during the downloading of artifacts since this class has been initialized. 334 | */ 335 | public List getErrors() { 336 | return Collections.unmodifiableList(errors); 337 | } 338 | 339 | private Set computeReactorArtifacts(List reactorProjects) { 340 | Set artifacts = new HashSet<>(reactorProjects.size()); 341 | for (MavenProject p : reactorProjects) { 342 | artifacts.add(new ReactorArtifact(p.getArtifact())); 343 | } 344 | return artifacts; 345 | } 346 | 347 | private boolean isReactorArtifact(Artifact artifact) { 348 | return reactorArtifacts.contains(new ReactorArtifact(artifact)); 349 | } 350 | 351 | private void handleRepositoryException(Exception e) { 352 | log.error(e.getMessage()); 353 | log.debug(e); 354 | addToErrorList(e); 355 | } 356 | 357 | private synchronized void addToErrorList(Exception e) { 358 | errors.add(e); 359 | } 360 | 361 | private Artifact toArtifact(Plugin plugin) { 362 | ArtifactType artifactType = typeRegistry.get(MAVEN_PLUGIN_ARTIFACT_TYPE); 363 | return new DefaultArtifact(plugin.getGroupId(), plugin.getArtifactId(), artifactType.getClassifier(), artifactType.getExtension(), plugin.getVersion(), 364 | artifactType); 365 | } 366 | 367 | private static class CollectAllDependenciesVisitor implements DependencyVisitor { 368 | 369 | private boolean root = true; 370 | private Set artifacts = new HashSet<>(); 371 | 372 | @Override 373 | public boolean visitEnter(DependencyNode node) { 374 | if (root) { 375 | root = false; 376 | return true; 377 | } 378 | return artifacts.add(node.getArtifact()); 379 | } 380 | 381 | @Override 382 | public boolean visitLeave(DependencyNode node) { 383 | return true; 384 | } 385 | 386 | public Set getArtifacts() { 387 | return artifacts; 388 | } 389 | } 390 | } 391 | --------------------------------------------------------------------------------