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