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.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.DirectoryStream;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
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.plugin.MojoFailureException;
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
61 @Mojo(name = "wrapper", aggregator = true, requiresDirectInvocation = true)
62 public class WrapperMojo extends AbstractMojo {
63 private static final String MVNW_REPOURL = "MVNW_REPOURL";
64
65 private static final String DEFAULT_REPOURL = "https://repo.maven.apache.org/maven2";
66
67
68
69
70
71
72
73
74
75 @Parameter(property = "maven")
76 private String mavenVersion;
77
78
79
80
81
82
83 @Parameter(property = "mvnd")
84 private String mvndVersion;
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 @Parameter(defaultValue = "bin", property = "type")
103 private String distributionType;
104
105
106
107
108
109
110 @Parameter(defaultValue = "false", property = "includeDebug")
111 private boolean includeDebugScript;
112
113
114
115
116
117
118
119 @Parameter(property = "wrapperSha256Sum")
120 private String wrapperSha256Sum;
121
122
123
124
125
126
127
128 @Parameter(property = "distributionSha256Sum")
129 private String distributionSha256Sum;
130
131
132
133
134
135
136
137 @Parameter(defaultValue = "false", property = "alwaysDownload")
138 private boolean alwaysDownload;
139
140
141
142
143
144
145
146 @Parameter(defaultValue = "false", property = "alwaysUnpack")
147 private boolean alwaysUnpack;
148
149
150
151 @Parameter(defaultValue = "${session}", readonly = true, required = true)
152 private MavenSession session;
153
154 @Parameter(defaultValue = "${settings}", readonly = true, required = true)
155 private Settings settings;
156
157
158 @Parameter(defaultValue = "${project.basedir}", readonly = true, required = true)
159 private File basedir;
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
170
171 @Inject
172 private RepositorySystem repositorySystem;
173
174 @Inject
175 private Map<String, UnArchiver> unarchivers;
176
177 @Override
178 public void execute() throws MojoExecutionException, MojoFailureException {
179 if (mvndVersion != null && mvndVersion.length() > 0 && !"only-script".equals(distributionType)) {
180 throw new MojoExecutionException("maven-wrapper with type=" + distributionType
181 + " cannot work with mvnd, please set type to 'only-script'.");
182 }
183
184 mavenVersion = getVersion(mavenVersion, Maven.class, "org.apache.maven/maven-core");
185 String wrapperVersion = getVersion(null, this.getClass(), "org.apache.maven.plugins/maven-wrapper-plugin");
186
187 final Artifact artifact = downloadWrapperDistribution(wrapperVersion);
188 final Path wrapperDir = createDirectories(basedir.toPath().resolve(".mvn/wrapper"));
189
190 cleanup(wrapperDir);
191 unpack(artifact, basedir.toPath());
192 replaceProperties(wrapperVersion, wrapperDir);
193 }
194
195 private Path createDirectories(Path dir) throws MojoExecutionException {
196 try {
197 return Files.createDirectories(dir);
198 } catch (IOException ioe) {
199 throw new MojoExecutionException(ioe.getMessage(), ioe);
200 }
201 }
202
203 private void cleanup(Path wrapperDir) throws MojoExecutionException {
204 try (DirectoryStream<Path> dsClass = Files.newDirectoryStream(wrapperDir, "*.class")) {
205 for (Path file : dsClass) {
206
207 Files.deleteIfExists(file);
208 }
209 Files.deleteIfExists(wrapperDir.resolve("MavenWrapperDownloader.java"));
210 Files.deleteIfExists(wrapperDir.resolve("maven-wrapper.jar"));
211 } catch (IOException ioe) {
212 throw new MojoExecutionException(ioe.getMessage(), ioe);
213 }
214 }
215
216 private Artifact downloadWrapperDistribution(String wrapperVersion) throws MojoExecutionException {
217
218 Artifact artifact = new DefaultArtifact(
219 WRAPPER_DISTRIBUTION_GROUP_ID,
220 WRAPPER_DISTRIBUTION_ARTIFACT_ID,
221 distributionType,
222 WRAPPER_DISTRIBUTION_EXTENSION,
223 wrapperVersion);
224
225 ArtifactRequest request = new ArtifactRequest();
226 request.setRepositories(session.getCurrentProject().getRemotePluginRepositories());
227 request.setArtifact(artifact);
228
229 try {
230 ArtifactResult artifactResult = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
231 return artifactResult.getArtifact();
232
233 } catch (ArtifactResolutionException e) {
234 throw new MojoExecutionException("artifact: " + artifact + " not resolved.", e);
235 }
236 }
237
238 private void unpack(Artifact artifact, Path targetFolder) {
239 UnArchiver unarchiver = unarchivers.get(WRAPPER_DISTRIBUTION_EXTENSION);
240 unarchiver.setDestDirectory(targetFolder.toFile());
241 unarchiver.setSourceFile(artifact.getFile());
242 if (!includeDebugScript) {
243 unarchiver.setFileSelectors(
244 new FileSelector[] {fileInfo -> !fileInfo.getName().contains("Debug")});
245 }
246 unarchiver.extract();
247 getLog().info("Unpacked " + buffer().strong(distributionType) + " type wrapper distribution " + artifact);
248 }
249
250
251
252
253
254
255
256
257
258 private void replaceProperties(String wrapperVersion, Path targetFolder) throws MojoExecutionException {
259 String repoUrl = getRepoUrl();
260
261 String distributionUrl = repoUrl + "/org/apache/maven/apache-maven/" + mavenVersion + "/apache-maven-"
262 + mavenVersion + "-bin.zip";
263 String wrapperUrl = repoUrl + "/org/apache/maven/wrapper/maven-wrapper/" + wrapperVersion + "/maven-wrapper-"
264 + wrapperVersion + ".jar";
265
266 if (mvndVersion != null && mvndVersion.length() > 0) {
267
268 distributionUrl = "https://archive.apache.org/dist/maven/mvnd/" + mvndVersion + "/maven-mvnd-" + mvndVersion
269 + "-bin.zip";
270 }
271
272 Path wrapperPropertiesFile = targetFolder.resolve("maven-wrapper.properties");
273
274 getLog().info("Configuring .mvn/wrapper/maven-wrapper.properties to use "
275 + buffer().strong("Maven " + mavenVersion) + " and download from " + repoUrl);
276
277 final String license = "# Licensed to the Apache Software Foundation (ASF) under one%n"
278 + "# or more contributor license agreements. See the NOTICE file%n"
279 + "# distributed with this work for additional information%n"
280 + "# regarding copyright ownership. The ASF licenses this file%n"
281 + "# to you under the Apache License, Version 2.0 (the%n"
282 + "# \"License\"); you may not use this file except in compliance%n"
283 + "# with the License. You may obtain a copy of the License at%n"
284 + "#%n"
285 + "# http://www.apache.org/licenses/LICENSE-2.0%n"
286 + "#%n"
287 + "# Unless required by applicable law or agreed to in writing,%n"
288 + "# software distributed under the License is distributed on an%n"
289 + "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY%n"
290 + "# KIND, either express or implied. See the License for the%n"
291 + "# specific language governing permissions and limitations%n"
292 + "# under the License.%n";
293
294 try (BufferedWriter out = Files.newBufferedWriter(wrapperPropertiesFile, StandardCharsets.UTF_8)) {
295 out.append(String.format(Locale.ROOT, license));
296 out.append("distributionUrl=" + distributionUrl + System.lineSeparator());
297 if (distributionSha256Sum != null) {
298 out.append("distributionSha256Sum=" + distributionSha256Sum + System.lineSeparator());
299 }
300 out.append("wrapperUrl=" + wrapperUrl + System.lineSeparator());
301 if (wrapperSha256Sum != null) {
302 out.append("wrapperSha256Sum=" + wrapperSha256Sum + System.lineSeparator());
303 }
304 if (alwaysDownload) {
305 out.append("alwaysDownload=" + Boolean.TRUE + System.lineSeparator());
306 }
307 if (alwaysUnpack) {
308 out.append("alwaysUnpack=" + Boolean.TRUE + System.lineSeparator());
309 }
310 } catch (IOException ioe) {
311 throw new MojoExecutionException("Can't create maven-wrapper.properties", ioe);
312 }
313 }
314
315 private String getVersion(String defaultVersion, Class<?> clazz, String path) {
316 String version = defaultVersion;
317 if (version == null || version.trim().length() == 0 || "true".equals(version)) {
318 Properties props = new Properties();
319 try (InputStream is = clazz.getResourceAsStream("/META-INF/maven/" + path + "/pom.properties")) {
320 if (is != null) {
321 props.load(is);
322 version = props.getProperty("version");
323 }
324 } catch (IOException e) {
325
326 }
327 }
328 return version;
329 }
330
331
332
333
334 private String getRepoUrl() {
335
336 String repoUrl = DEFAULT_REPOURL;
337
338
339 String mvnwRepoUrl = System.getenv(MVNW_REPOURL);
340 if (mvnwRepoUrl != null && !mvnwRepoUrl.isEmpty()) {
341 repoUrl = mvnwRepoUrl;
342 getLog().debug("Using repo URL from " + MVNW_REPOURL + " environment variable.");
343 }
344
345 else if (settings.getMirrors() != null && !settings.getMirrors().isEmpty()) {
346 for (Mirror current : settings.getMirrors()) {
347 if ("*".equals(current.getMirrorOf())) {
348 repoUrl = current.getUrl();
349 break;
350 }
351 }
352 getLog().debug("Using repo URL from * mirror in settings file.");
353 }
354
355 getLog().debug("Determined repo URL to use as " + repoUrl);
356
357 return repoUrl;
358 }
359 }