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.plugin.MojoFailureException;
40 import org.apache.maven.plugins.annotations.Component;
41 import org.apache.maven.plugins.annotations.Mojo;
42 import org.apache.maven.plugins.annotations.Parameter;
43 import org.apache.maven.settings.Mirror;
44 import org.apache.maven.settings.Settings;
45 import org.codehaus.plexus.archiver.UnArchiver;
46 import org.codehaus.plexus.components.io.fileselectors.FileSelector;
47 import org.eclipse.aether.RepositorySystem;
48 import org.eclipse.aether.artifact.Artifact;
49 import org.eclipse.aether.artifact.DefaultArtifact;
50 import org.eclipse.aether.resolution.ArtifactRequest;
51 import org.eclipse.aether.resolution.ArtifactResolutionException;
52 import org.eclipse.aether.resolution.ArtifactResult;
53
54 import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
55
56
57
58
59
60
61 @Mojo(name = "wrapper", aggregator = true, requiresProject = false)
62 public class WrapperMojo extends AbstractMojo {
63 private static final String MVNW_REPOURL = "MVNW_REPOURL";
64
65 protected 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 = "only-script", 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 @Component
152 private MavenSession session;
153
154
155
156 private static final String WRAPPER_DISTRIBUTION_GROUP_ID = "org.apache.maven.wrapper";
157
158 private static final String WRAPPER_DISTRIBUTION_ARTIFACT_ID = "maven-wrapper-distribution";
159
160 private static final String WRAPPER_DISTRIBUTION_EXTENSION = "zip";
161
162
163
164 @Inject
165 private RepositorySystem repositorySystem;
166
167 @Inject
168 private Map<String, UnArchiver> unarchivers;
169
170 @Override
171 public void execute() throws MojoExecutionException, MojoFailureException {
172 if (mvndVersion != null && mvndVersion.length() > 0 && !"only-script".equals(distributionType)) {
173 throw new MojoExecutionException("maven-wrapper with type=" + distributionType
174 + " cannot work with mvnd, please set type to 'only-script'.");
175 }
176
177 Path baseDir = Paths.get(session.getRequest().getBaseDirectory());
178 mavenVersion = getVersion(mavenVersion, Maven.class, "org.apache.maven/maven-core");
179 String wrapperVersion = getVersion(null, this.getClass(), "org.apache.maven.plugins/maven-wrapper-plugin");
180
181 final Artifact artifact = downloadWrapperDistribution(wrapperVersion);
182 final Path wrapperDir = createDirectories(baseDir.resolve(".mvn/wrapper"));
183
184 cleanup(wrapperDir);
185 unpack(artifact, baseDir);
186 replaceProperties(wrapperVersion, wrapperDir);
187 }
188
189 private Path createDirectories(Path dir) throws MojoExecutionException {
190 try {
191 return Files.createDirectories(dir);
192 } catch (IOException ioe) {
193 throw new MojoExecutionException(ioe.getMessage(), ioe);
194 }
195 }
196
197 private void cleanup(Path wrapperDir) throws MojoExecutionException {
198 try (DirectoryStream<Path> dsClass = Files.newDirectoryStream(wrapperDir, "*.class")) {
199 for (Path file : dsClass) {
200
201 Files.deleteIfExists(file);
202 }
203 Files.deleteIfExists(wrapperDir.resolve("MavenWrapperDownloader.java"));
204 Files.deleteIfExists(wrapperDir.resolve("maven-wrapper.jar"));
205 } catch (IOException ioe) {
206 throw new MojoExecutionException(ioe.getMessage(), ioe);
207 }
208 }
209
210 private Artifact downloadWrapperDistribution(String wrapperVersion) throws MojoExecutionException {
211
212 Artifact artifact = new DefaultArtifact(
213 WRAPPER_DISTRIBUTION_GROUP_ID,
214 WRAPPER_DISTRIBUTION_ARTIFACT_ID,
215 distributionType,
216 WRAPPER_DISTRIBUTION_EXTENSION,
217 wrapperVersion);
218
219 ArtifactRequest request = new ArtifactRequest();
220 request.setRepositories(session.getCurrentProject().getRemotePluginRepositories());
221 request.setArtifact(artifact);
222
223 try {
224 ArtifactResult artifactResult = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
225 return artifactResult.getArtifact();
226
227 } catch (ArtifactResolutionException e) {
228 throw new MojoExecutionException("artifact: " + artifact + " not resolved.", e);
229 }
230 }
231
232 private void unpack(Artifact artifact, Path targetFolder) {
233 UnArchiver unarchiver = unarchivers.get(WRAPPER_DISTRIBUTION_EXTENSION);
234 unarchiver.setDestDirectory(targetFolder.toFile());
235 unarchiver.setSourceFile(artifact.getFile());
236 if (!includeDebugScript) {
237 unarchiver.setFileSelectors(
238 new FileSelector[] {fileInfo -> !fileInfo.getName().contains("Debug")});
239 }
240 unarchiver.extract();
241 getLog().info("Unpacked " + buffer().strong(distributionType) + " type wrapper distribution " + artifact);
242 }
243
244
245
246
247
248
249
250
251
252 private void replaceProperties(String wrapperVersion, Path targetFolder) throws MojoExecutionException {
253 String repoUrl = getRepoUrl();
254
255 String distributionUrl = repoUrl + "/org/apache/maven/apache-maven/" + mavenVersion + "/apache-maven-"
256 + mavenVersion + "-bin.zip";
257 String wrapperUrl = repoUrl + "/org/apache/maven/wrapper/maven-wrapper/" + wrapperVersion + "/maven-wrapper-"
258 + wrapperVersion + ".jar";
259
260 if (mvndVersion != null && mvndVersion.length() > 0) {
261
262 distributionUrl = "https://archive.apache.org/dist/maven/mvnd/" + mvndVersion + "/maven-mvnd-" + mvndVersion
263 + "-bin.zip";
264 }
265
266 Path wrapperPropertiesFile = targetFolder.resolve("maven-wrapper.properties");
267
268 getLog().info("Configuring .mvn/wrapper/maven-wrapper.properties to use "
269 + buffer().strong("Maven " + mavenVersion) + " and download from " + repoUrl);
270
271 final String license = "# Licensed to the Apache Software Foundation (ASF) under one%n"
272 + "# or more contributor license agreements. See the NOTICE file%n"
273 + "# distributed with this work for additional information%n"
274 + "# regarding copyright ownership. The ASF licenses this file%n"
275 + "# to you under the Apache License, Version 2.0 (the%n"
276 + "# \"License\"); you may not use this file except in compliance%n"
277 + "# with the License. You may obtain a copy of the License at%n"
278 + "#%n"
279 + "# http://www.apache.org/licenses/LICENSE-2.0%n"
280 + "#%n"
281 + "# Unless required by applicable law or agreed to in writing,%n"
282 + "# software distributed under the License is distributed on an%n"
283 + "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY%n"
284 + "# KIND, either express or implied. See the License for the%n"
285 + "# specific language governing permissions and limitations%n"
286 + "# under the License.%n";
287
288 try (BufferedWriter out = Files.newBufferedWriter(wrapperPropertiesFile, StandardCharsets.UTF_8)) {
289 out.append(String.format(Locale.ROOT, license));
290 out.append("wrapperVersion=" + wrapperVersion + System.lineSeparator());
291 out.append("distributionUrl=" + distributionUrl + System.lineSeparator());
292 if (distributionSha256Sum != null) {
293 out.append("distributionSha256Sum=" + distributionSha256Sum + System.lineSeparator());
294 }
295 if (!distributionType.equals("only-script")) {
296 out.append("wrapperUrl=" + wrapperUrl + System.lineSeparator());
297 }
298 if (wrapperSha256Sum != null) {
299 out.append("wrapperSha256Sum=" + wrapperSha256Sum + System.lineSeparator());
300 }
301 if (alwaysDownload) {
302 out.append("alwaysDownload=" + Boolean.TRUE + System.lineSeparator());
303 }
304 if (alwaysUnpack) {
305 out.append("alwaysUnpack=" + Boolean.TRUE + System.lineSeparator());
306 }
307 } catch (IOException ioe) {
308 throw new MojoExecutionException("Can't create maven-wrapper.properties", ioe);
309 }
310 }
311
312 private String getVersion(String defaultVersion, Class<?> clazz, String path) {
313 String version = defaultVersion;
314 if (version == null || version.trim().length() == 0 || "true".equals(version)) {
315 Properties props = new Properties();
316 try (InputStream is = clazz.getResourceAsStream("/META-INF/maven/" + path + "/pom.properties")) {
317 if (is != null) {
318 props.load(is);
319 version = props.getProperty("version");
320 }
321 } catch (IOException e) {
322
323 }
324 }
325 return version;
326 }
327
328
329
330
331 private String getRepoUrl() {
332
333 String envRepoUrl = System.getenv(MVNW_REPOURL);
334 final String repoUrl = determineRepoUrl(envRepoUrl, session.getSettings());
335
336 getLog().debug("Determined repo URL to use as " + repoUrl);
337
338 return repoUrl;
339 }
340
341 protected String determineRepoUrl(String envRepoUrl, Settings settings) {
342
343 if (envRepoUrl != null && !envRepoUrl.trim().isEmpty() && envRepoUrl.length() > 4) {
344 String repoUrl = envRepoUrl.trim();
345
346 if (repoUrl.endsWith("/")) {
347 repoUrl = repoUrl.substring(0, repoUrl.length() - 1);
348 }
349
350 getLog().debug("Using repo URL from " + MVNW_REPOURL + " environment variable.");
351
352 return repoUrl;
353 }
354
355
356 if (settings.getMirrors() != null && !settings.getMirrors().isEmpty()) {
357 for (Mirror current : settings.getMirrors()) {
358 if ("*".equals(current.getMirrorOf())) {
359 getLog().debug("Using repo URL from * mirror in settings file.");
360 return current.getUrl();
361 }
362 }
363 }
364
365 return DEFAULT_REPOURL;
366 }
367 }