1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package de.softwareforge.testing.maven;
16
17 import static java.lang.String.format;
18 import static java.util.Objects.requireNonNull;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.SortedSet;
28 import java.util.TreeSet;
29
30 import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
31 import org.apache.maven.settings.Profile;
32 import org.apache.maven.settings.Repository;
33 import org.apache.maven.settings.Settings;
34 import org.apache.maven.settings.building.DefaultSettingsBuilder;
35 import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
36 import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
37 import org.apache.maven.settings.building.SettingsBuildingException;
38 import org.apache.maven.settings.building.SettingsBuildingRequest;
39 import org.apache.maven.settings.building.SettingsBuildingResult;
40 import org.eclipse.aether.DefaultRepositorySystemSession;
41 import org.eclipse.aether.RepositoryException;
42 import org.eclipse.aether.RepositorySystem;
43 import org.eclipse.aether.RepositorySystemSession;
44 import org.eclipse.aether.artifact.Artifact;
45 import org.eclipse.aether.artifact.DefaultArtifact;
46 import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
47 import org.eclipse.aether.impl.DefaultServiceLocator;
48 import org.eclipse.aether.repository.LocalRepository;
49 import org.eclipse.aether.repository.RemoteRepository;
50 import org.eclipse.aether.resolution.ArtifactRequest;
51 import org.eclipse.aether.resolution.ArtifactResult;
52 import org.eclipse.aether.resolution.VersionRangeRequest;
53 import org.eclipse.aether.resolution.VersionRangeResolutionException;
54 import org.eclipse.aether.resolution.VersionRangeResult;
55 import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
56 import org.eclipse.aether.spi.connector.transport.TransporterFactory;
57 import org.eclipse.aether.spi.locator.ServiceLocator;
58 import org.eclipse.aether.transport.file.FileTransporterFactory;
59 import org.eclipse.aether.transport.http.HttpTransporterFactory;
60 import org.eclipse.aether.version.Version;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70 public final class MavenArtifactLoader {
71
72 private static final Logger LOG = LoggerFactory.getLogger(MavenArtifactLoader.class);
73
74 static final RemoteRepository CENTRAL_REPO = new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/").build();
75
76 private static final String USER_HOME = System.getProperty("user.home");
77 private static final File USER_MAVEN_HOME = new File(USER_HOME, ".m2");
78 private static final String ENV_M2_HOME = System.getenv("M2_HOME");
79
80 private static final File DEFAULT_USER_SETTINGS_FILE = new File(USER_MAVEN_HOME, "settings.xml");
81 private static final File DEFAULT_USER_REPOSITORY = new File(USER_MAVEN_HOME, "repository");
82 private static final File DEFAULT_GLOBAL_SETTINGS_FILE =
83 new File(System.getProperty("maven.home", Objects.requireNonNullElse(ENV_M2_HOME, "")), "conf/settings.xml");
84
85 private final RepositorySystem repositorySystem;
86 private final RepositorySystemSession mavenSession;
87 private final List<RemoteRepository> remoteRepositories;
88
89 private final String extension;
90
91
92
93
94 public MavenArtifactLoader() {
95 this("jar");
96 }
97
98
99
100
101
102
103 public MavenArtifactLoader(String extension) {
104 this(extension, null);
105 }
106
107 MavenArtifactLoader(String extension, List<RemoteRepository> remoteRepositoriesForTesting) {
108 this.extension = requireNonNull(extension, "extension is null");
109
110 @SuppressWarnings("deprecation")
111 ServiceLocator serviceLocator = createServiceLocator();
112 this.repositorySystem = serviceLocator.getService(RepositorySystem.class);
113
114 try {
115 Settings settings = createSettings();
116 File localRepositoryLocation = settings.getLocalRepository() != null ? new File(settings.getLocalRepository()) : DEFAULT_USER_REPOSITORY;
117 LocalRepository localRepository = new LocalRepository(localRepositoryLocation);
118
119 if (remoteRepositoriesForTesting != null) {
120 this.remoteRepositories = remoteRepositoriesForTesting;
121 } else {
122 this.remoteRepositories = extractRemoteRepositories(settings);
123 }
124
125 DefaultRepositorySystemSession mavenSession = MavenRepositorySystemUtils.newSession();
126
127 this.mavenSession = mavenSession.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(mavenSession, localRepository));
128
129 } catch (SettingsBuildingException e) {
130 throw new IllegalStateException("Could not load maven settings:", e);
131 }
132 }
133
134
135
136
137
138
139
140
141 public MavenVersionMatchBuilder builder(String groupId, String artifactId) {
142 requireNonNull(groupId, "groupId is null");
143 requireNonNull(artifactId, "artifactId is null");
144
145 return new MavenVersionMatchBuilder(this, groupId, artifactId);
146 }
147
148
149
150
151
152
153
154
155
156
157 public File getArtifactFile(String groupId, String artifactId, String version) throws IOException {
158 requireNonNull(groupId, "groupId is null");
159 requireNonNull(artifactId, "artifactId is null");
160 requireNonNull(version, "version is null");
161
162 ArtifactRequest artifactRequest = new ArtifactRequest();
163 artifactRequest.setArtifact(new DefaultArtifact(groupId, artifactId, extension, version));
164 artifactRequest.setRepositories(this.remoteRepositories);
165 try {
166 ArtifactResult artifactResult = this.repositorySystem.resolveArtifact(mavenSession, artifactRequest);
167 Artifact artifact = artifactResult.getArtifact();
168 return artifact.getFile();
169 } catch (RepositoryException e) {
170 throw new IOException(e);
171 }
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 public String findLatestVersion(String groupId, String artifactId, String version) throws IOException {
198 requireNonNull(groupId, "groupId is null");
199 requireNonNull(artifactId, "artifactId is null");
200 requireNonNull(version, "version is null");
201 return builder(groupId, artifactId)
202 .partialMatch(version)
203 .extension(extension)
204 .includeSnapshots(true)
205 .findBestMatch()
206 .orElseThrow(() -> new IOException(format("No suitable candidate for %s:%s:%s found!", groupId, artifactId, version)));
207 }
208
209 SortedSet<Version> findAllVersions(MavenVersionMatchBuilder builder) throws IOException {
210
211 Artifact artifact = new DefaultArtifact(builder.groupId(), builder.artifactId(), builder.extension(), "[0,)");
212
213 VersionRangeRequest rangeRequest = new VersionRangeRequest();
214 rangeRequest.setArtifact(artifact);
215 rangeRequest.setRepositories(this.remoteRepositories);
216
217 try {
218 VersionRangeResult rangeResult = this.repositorySystem.resolveVersionRange(mavenSession, rangeRequest);
219 SortedSet<Version> resultBuilder = new TreeSet<>();
220 List<Version> artifactVersions = rangeResult.getVersions();
221 VersionStrategy versionStrategy = builder.versionStrategy();
222 if (artifactVersions != null) {
223 for (Version artifactVersion : artifactVersions) {
224 boolean isSnapshot = artifactVersion.toString().endsWith("-SNAPSHOT");
225 boolean match = versionStrategy.matchVersion(artifactVersion);
226
227
228 if (isSnapshot) {
229 match &= builder.includeSnapshots();
230 }
231
232 if (match) {
233 resultBuilder.add(artifactVersion);
234 }
235 }
236 }
237 return Collections.unmodifiableSortedSet(resultBuilder);
238 } catch (VersionRangeResolutionException e) {
239 throw new IOException(format("Could not resolve version range: %s", rangeRequest), e);
240 }
241 }
242
243
244 @SuppressWarnings("deprecation")
245 private static ServiceLocator createServiceLocator() {
246 DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
247
248 locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
249 locator.addService(TransporterFactory.class, FileTransporterFactory.class);
250 locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
251
252 locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() {
253 @Override
254 public void serviceCreationFailed(Class<?> type, Class<?> impl, Throwable e) {
255 LOG.error(format("Could not create instance of %s (implementation %s): ", type.getSimpleName(), impl.getSimpleName()), e);
256 }
257 });
258
259 return locator;
260 }
261
262 private static Settings createSettings() throws SettingsBuildingException {
263 SettingsBuildingRequest settingsBuildingRequest = new DefaultSettingsBuildingRequest()
264 .setSystemProperties(System.getProperties())
265 .setUserSettingsFile(DEFAULT_USER_SETTINGS_FILE)
266 .setGlobalSettingsFile(DEFAULT_GLOBAL_SETTINGS_FILE);
267
268 DefaultSettingsBuilderFactory settingBuilderFactory = new DefaultSettingsBuilderFactory();
269 DefaultSettingsBuilder settingsBuilder = settingBuilderFactory.newInstance();
270 SettingsBuildingResult settingsBuildingResult = settingsBuilder.build(settingsBuildingRequest);
271
272 return settingsBuildingResult.getEffectiveSettings();
273 }
274
275 private static List<RemoteRepository> extractRemoteRepositories(Settings settings) {
276 Map<String, Profile> profiles = settings.getProfilesAsMap();
277 List<RemoteRepository> builder = new ArrayList<>();
278
279 boolean foundRepository = false;
280 for (String profileName : settings.getActiveProfiles()) {
281 Profile profile = profiles.get(profileName);
282 if (profile != null) {
283 List<Repository> repositories = profile.getRepositories();
284 if (repositories != null) {
285 for (Repository repo : repositories) {
286 builder.add(new RemoteRepository.Builder(repo.getId(), "default", repo.getUrl()).build());
287 foundRepository = true;
288 }
289 }
290 }
291 }
292
293 if (!foundRepository && !settings.isOffline()) {
294 builder.add(CENTRAL_REPO);
295 }
296
297 return Collections.unmodifiableList(builder);
298 }
299 }