1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.wrapper;
20
21 import javax.inject.Inject;
22
23 import java.io.BufferedWriter;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.DirectoryStream;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.util.Map;
32 import java.util.Properties;
33
34 import org.apache.maven.Maven;
35 import org.apache.maven.execution.MavenSession;
36 import org.apache.maven.plugin.AbstractMojo;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugins.annotations.Mojo;
39 import org.apache.maven.plugins.annotations.Parameter;
40 import org.apache.maven.settings.Mirror;
41 import org.apache.maven.settings.Settings;
42 import org.codehaus.plexus.archiver.UnArchiver;
43 import org.codehaus.plexus.components.io.fileselectors.FileSelector;
44 import org.eclipse.aether.RepositorySystem;
45 import org.eclipse.aether.artifact.Artifact;
46 import org.eclipse.aether.artifact.DefaultArtifact;
47 import org.eclipse.aether.resolution.ArtifactRequest;
48 import org.eclipse.aether.resolution.ArtifactResolutionException;
49 import org.eclipse.aether.resolution.ArtifactResult;
50
51 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
52
53
54
55
56
57
58 @Mojo(name = "wrapper", aggregator = true, requiresProject = false)
59 public class WrapperMojo extends AbstractMojo {
60 private static final String MVNW_REPOURL = "MVNW_REPOURL";
61
62 protected static final String DEFAULT_REPOURL = "https://repo.maven.apache.org/maven2";
63
64
65
66
67
68
69
70
71
72 @Parameter(property = "maven")
73 private String mavenVersion;
74
75
76
77
78
79
80 @Parameter(property = "mvnd")
81 private String mvndVersion;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 @Parameter(property = "type")
108 private String distributionType;
109
110
111
112
113
114
115 @Parameter(defaultValue = "false", property = "includeDebug")
116 private boolean includeDebugScript;
117
118
119
120
121
122
123
124 @Parameter(property = "wrapperSha256Sum")
125 private String wrapperSha256Sum;
126
127
128
129
130
131
132
133 @Parameter(property = "distributionSha256Sum")
134 private String distributionSha256Sum;
135
136
137
138
139
140
141
142 @Parameter(defaultValue = "false", property = "alwaysDownload")
143 private boolean alwaysDownload;
144
145
146
147
148
149
150
151 @Parameter(defaultValue = "false", property = "alwaysUnpack")
152 private boolean alwaysUnpack;
153
154
155
156
157
158
159
160
161 @Parameter(property = "distributionUrl")
162 private String distributionUrl;
163
164
165
166 @Inject
167 private MavenSession session;
168
169
170
171 private static final String WRAPPER_DISTRIBUTION_GROUP_ID = "org.apache.maven.wrapper";
172
173 private static final String WRAPPER_DISTRIBUTION_ARTIFACT_ID = "maven-wrapper-distribution";
174
175 private static final String WRAPPER_DISTRIBUTION_EXTENSION = "zip";
176
177 private static final String WRAPPER_DIR = ".mvn/wrapper";
178
179 private static final String WRAPPER_PROPERTIES_FILENAME = "maven-wrapper.properties";
180
181 private static final String DISTRIBUTION_TYPE_PROPERTY_NAME = "distributionType";
182
183 private static final String TYPE_ONLY_SCRIPT = "only-script";
184
185 private static final String DEFAULT_DISTRIBUTION_TYPE = TYPE_ONLY_SCRIPT;
186
187
188
189 @Inject
190 private RepositorySystem repositorySystem;
191
192 @Inject
193 private Map<String, UnArchiver> unarchivers;
194
195 @Override
196 public void execute() throws MojoExecutionException {
197 final Path baseDir = Paths.get(session.getRequest().getBaseDirectory());
198 final Path wrapperDir = baseDir.resolve(WRAPPER_DIR);
199
200 if (distributionType == null) {
201 distributionType = determineDistributionType(wrapperDir);
202 }
203
204 if (mvndVersion != null && mvndVersion.length() > 0 && !TYPE_ONLY_SCRIPT.equals(distributionType)) {
205 throw new MojoExecutionException("maven-wrapper with type=" + distributionType
206 + " cannot work with mvnd, please set type to '" + TYPE_ONLY_SCRIPT + "'.");
207 }
208
209 mavenVersion = getVersion(mavenVersion, Maven.class, "org.apache.maven/maven-core");
210 String wrapperVersion = getVersion(null, this.getClass(), "org.apache.maven.plugins/maven-wrapper-plugin");
211
212 final Artifact artifact = downloadWrapperDistribution(wrapperVersion);
213
214 createDirectories(wrapperDir);
215 cleanup(wrapperDir);
216 unpack(artifact, baseDir);
217 replaceProperties(wrapperVersion, wrapperDir);
218 }
219
220 private String determineDistributionType(final Path wrapperDir) {
221 final String typeFromMavenWrapperProperties = distributionTypeFromExistingMavenWrapperProperties(wrapperDir);
222 if (typeFromMavenWrapperProperties != null) {
223 return typeFromMavenWrapperProperties;
224 }
225
226 return DEFAULT_DISTRIBUTION_TYPE;
227 }
228
229 private String distributionTypeFromExistingMavenWrapperProperties(final Path wrapperDir) {
230 final Path mavenWrapperProperties = wrapperDir.resolve(WRAPPER_PROPERTIES_FILENAME);
231 try (InputStream inputStream = Files.newInputStream(mavenWrapperProperties)) {
232 Properties properties = new Properties();
233 properties.load(inputStream);
234 return properties.getProperty(DISTRIBUTION_TYPE_PROPERTY_NAME);
235 } catch (IOException e) {
236 return null;
237 }
238 }
239
240 private void createDirectories(Path dir) throws MojoExecutionException {
241 try {
242 Files.createDirectories(dir);
243 } catch (IOException ioe) {
244 throw new MojoExecutionException(ioe.getMessage(), ioe);
245 }
246 }
247
248 private void cleanup(Path wrapperDir) throws MojoExecutionException {
249 try (DirectoryStream<Path> dsClass = Files.newDirectoryStream(wrapperDir, "*.class")) {
250 for (Path file : dsClass) {
251
252 Files.deleteIfExists(file);
253 }
254 Files.deleteIfExists(wrapperDir.resolve("MavenWrapperDownloader.java"));
255 Files.deleteIfExists(wrapperDir.resolve("maven-wrapper.jar"));
256 } catch (IOException ioe) {
257 throw new MojoExecutionException(ioe.getMessage(), ioe);
258 }
259 }
260
261 private Artifact downloadWrapperDistribution(String wrapperVersion) throws MojoExecutionException {
262
263 Artifact artifact = new DefaultArtifact(
264 WRAPPER_DISTRIBUTION_GROUP_ID,
265 WRAPPER_DISTRIBUTION_ARTIFACT_ID,
266 distributionType,
267 WRAPPER_DISTRIBUTION_EXTENSION,
268 wrapperVersion);
269
270 ArtifactRequest request = new ArtifactRequest();
271 request.setRepositories(session.getCurrentProject().getRemotePluginRepositories());
272 request.setArtifact(artifact);
273
274 try {
275 ArtifactResult artifactResult = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
276 return artifactResult.getArtifact();
277
278 } catch (ArtifactResolutionException e) {
279 throw new MojoExecutionException("artifact: " + artifact + " not resolved.", e);
280 }
281 }
282
283 private void unpack(Artifact artifact, Path targetFolder) {
284 UnArchiver unarchiver = unarchivers.get(WRAPPER_DISTRIBUTION_EXTENSION);
285 unarchiver.setDestDirectory(targetFolder.toFile());
286 unarchiver.setSourceFile(artifact.getFile());
287 if (!includeDebugScript) {
288 unarchiver.setFileSelectors(
289 new FileSelector[] {fileInfo -> !fileInfo.getName().contains("Debug")});
290 }
291 unarchiver.extract();
292 getLog().info("Unpacked " + buffer().strong(distributionType) + " type wrapper distribution " + artifact);
293 }
294
295
296
297
298
299
300
301
302
303 private void replaceProperties(String wrapperVersion, Path targetFolder) throws MojoExecutionException {
304 String repoUrl = getRepoUrl();
305
306 String finalDistributionUrl;
307 if (distributionUrl != null && !distributionUrl.trim().isEmpty()) {
308
309 finalDistributionUrl = distributionUrl.trim();
310 } else if (mvndVersion != null && mvndVersion.length() > 0) {
311
312 finalDistributionUrl = "https://archive.apache.org/dist/maven/mvnd/" + mvndVersion + "/maven-mvnd-"
313 + mvndVersion + "-bin.zip";
314 } else {
315
316 finalDistributionUrl = repoUrl + "/org/apache/maven/apache-maven/" + mavenVersion + "/apache-maven-"
317 + mavenVersion + "-bin.zip";
318 }
319
320 String wrapperUrl = repoUrl + "/org/apache/maven/wrapper/maven-wrapper/" + wrapperVersion + "/maven-wrapper-"
321 + wrapperVersion + ".jar";
322
323 Path wrapperPropertiesFile = targetFolder.resolve("maven-wrapper.properties");
324
325 getLog().info("Configuring .mvn/wrapper/maven-wrapper.properties to use "
326 + buffer().strong("Maven " + mavenVersion) + " and download from " + repoUrl);
327
328 try (BufferedWriter out = Files.newBufferedWriter(wrapperPropertiesFile, StandardCharsets.UTF_8)) {
329 out.append(DISTRIBUTION_TYPE_PROPERTY_NAME + "=" + distributionType + System.lineSeparator());
330 out.append("distributionUrl=" + finalDistributionUrl + System.lineSeparator());
331 if (distributionSha256Sum != null) {
332 out.append("distributionSha256Sum=" + distributionSha256Sum + System.lineSeparator());
333 }
334 if (!distributionType.equals(TYPE_ONLY_SCRIPT)) {
335 out.append("wrapperUrl=" + wrapperUrl + System.lineSeparator());
336 out.append("wrapperVersion=" + wrapperVersion + System.lineSeparator());
337 }
338 if (wrapperSha256Sum != null) {
339 out.append("wrapperSha256Sum=" + wrapperSha256Sum + System.lineSeparator());
340 }
341 if (alwaysDownload) {
342 out.append("alwaysDownload=" + Boolean.TRUE + System.lineSeparator());
343 }
344 if (alwaysUnpack) {
345 out.append("alwaysUnpack=" + Boolean.TRUE + System.lineSeparator());
346 }
347 } catch (IOException ioe) {
348 throw new MojoExecutionException("Can't create maven-wrapper.properties", ioe);
349 }
350 }
351
352 private String getVersion(String defaultVersion, Class<?> clazz, String path) {
353 String version = defaultVersion;
354 if (version == null || version.trim().length() == 0 || "true".equals(version)) {
355 Properties props = new Properties();
356 try (InputStream is = clazz.getResourceAsStream("/META-INF/maven/" + path + "/pom.properties")) {
357 if (is != null) {
358 props.load(is);
359 version = props.getProperty("version");
360 }
361 } catch (IOException e) {
362
363 }
364 }
365 return version;
366 }
367
368
369
370
371 private String getRepoUrl() {
372
373 String envRepoUrl = System.getenv(MVNW_REPOURL);
374 final String repoUrl = determineRepoUrl(envRepoUrl, session.getSettings());
375
376 getLog().debug("Determined repo URL to use as " + repoUrl);
377
378 return repoUrl;
379 }
380
381 protected String determineRepoUrl(String envRepoUrl, Settings settings) {
382
383 if (envRepoUrl != null && !envRepoUrl.trim().isEmpty() && envRepoUrl.length() > 4) {
384 String repoUrl = envRepoUrl.trim();
385
386 if (repoUrl.endsWith("/")) {
387 repoUrl = repoUrl.substring(0, repoUrl.length() - 1);
388 }
389
390 getLog().debug("Using repo URL from " + MVNW_REPOURL + " environment variable.");
391
392 return repoUrl;
393 }
394
395
396 if (settings.getMirrors() != null && !settings.getMirrors().isEmpty()) {
397 for (Mirror current : settings.getMirrors()) {
398 if ("*".equals(current.getMirrorOf())) {
399 getLog().debug("Using repo URL from * mirror in settings file.");
400 return current.getUrl();
401 }
402 }
403 }
404
405 return DEFAULT_REPOURL;
406 }
407 }