1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.install;
20
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.StandardCopyOption;
29 import java.util.Objects;
30 import java.util.function.Predicate;
31 import java.util.jar.JarEntry;
32 import java.util.jar.JarFile;
33 import java.util.regex.Pattern;
34
35 import org.apache.maven.execution.MavenSession;
36 import org.apache.maven.model.Model;
37 import org.apache.maven.model.Parent;
38 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
39 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
40 import org.apache.maven.plugin.AbstractMojo;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugin.MojoFailureException;
43 import org.apache.maven.plugins.annotations.Component;
44 import org.apache.maven.plugins.annotations.Mojo;
45 import org.apache.maven.plugins.annotations.Parameter;
46 import org.codehaus.plexus.util.FileUtils;
47 import org.codehaus.plexus.util.StringUtils;
48 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
49 import org.eclipse.aether.DefaultRepositoryCache;
50 import org.eclipse.aether.DefaultRepositorySystemSession;
51 import org.eclipse.aether.RepositorySystem;
52 import org.eclipse.aether.RepositorySystemSession;
53 import org.eclipse.aether.artifact.Artifact;
54 import org.eclipse.aether.artifact.ArtifactType;
55 import org.eclipse.aether.artifact.DefaultArtifact;
56 import org.eclipse.aether.installation.InstallRequest;
57 import org.eclipse.aether.installation.InstallationException;
58 import org.eclipse.aether.repository.LocalRepository;
59 import org.eclipse.aether.repository.LocalRepositoryManager;
60 import org.eclipse.aether.util.artifact.SubArtifact;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 import static java.util.Objects.isNull;
65
66
67
68
69
70
71 @Mojo(name = "install-file", requiresProject = false, aggregator = true, threadSafe = true)
72 public class InstallFileMojo extends AbstractMojo {
73 private static final String LS = System.lineSeparator();
74 private final Logger log = LoggerFactory.getLogger(getClass());
75
76 @Component
77 private RepositorySystem repositorySystem;
78
79 @Parameter(defaultValue = "${session}", required = true, readonly = true)
80 private MavenSession session;
81
82
83
84
85
86 @Parameter(property = "groupId")
87 private String groupId;
88
89
90
91
92
93 @Parameter(property = "artifactId")
94 private String artifactId;
95
96
97
98
99
100 @Parameter(property = "version")
101 private String version;
102
103
104
105
106
107 @Parameter(property = "packaging")
108 private String packaging;
109
110
111
112
113
114
115
116 @Parameter(property = "classifier")
117 private String classifier;
118
119
120
121
122
123
124
125 @Parameter(property = "extension")
126 private String extension;
127
128
129
130
131 @Parameter(property = "file", required = true)
132 private File file;
133
134
135
136
137
138
139 @Parameter(property = "javadoc")
140 private File javadoc;
141
142
143
144
145
146
147 @Parameter(property = "sources")
148 private File sources;
149
150
151
152
153
154
155
156 @Parameter(property = "pomFile")
157 private File pomFile;
158
159
160
161
162
163
164
165 @Parameter(property = "generatePom")
166 private Boolean generatePom;
167
168
169
170
171
172
173
174 @Parameter(property = "localRepositoryPath")
175 private File localRepositoryPath;
176
177 private static final Predicate<String> IS_EMPTY = s -> isNull(s) || s.isEmpty();
178
179 private static final Predicate<String> IS_POM_PACKAGING = "pom"::equals;
180
181 @Override
182 public void execute() throws MojoExecutionException, MojoFailureException {
183 if (!file.exists()) {
184 String message = "The specified file '" + file.getPath() + "' does not exist";
185 log.error(message);
186 throw new MojoFailureException(message);
187 }
188
189 RepositorySystemSession repositorySystemSession = session.getRepositorySession();
190 if (localRepositoryPath != null) {
191
192 DefaultRepositorySystemSession newSession =
193 new DefaultRepositorySystemSession(session.getRepositorySession());
194
195 newSession.setCache(new DefaultRepositoryCache());
196
197 String contentType = newSession.getLocalRepository().getContentType();
198 if ("enhanced".equals(contentType)) {
199 contentType = "default";
200 }
201 LocalRepositoryManager localRepositoryManager = repositorySystem.newLocalRepositoryManager(
202 newSession, new LocalRepository(localRepositoryPath, contentType));
203 newSession.setLocalRepositoryManager(localRepositoryManager);
204 repositorySystemSession = newSession;
205 log.debug(
206 "localRepoPath: {}", localRepositoryManager.getRepository().getBasedir());
207 }
208
209 File temporaryPom = null;
210
211 if (pomFile == null) {
212 temporaryPom = readingPomFromJarFile();
213 if (!Boolean.TRUE.equals(generatePom)) {
214 pomFile = temporaryPom;
215 log.debug("Using JAR embedded POM as pomFile");
216 }
217 } else {
218 processModel(readModel(pomFile));
219 }
220
221 if (isNull(groupId) || isNull(artifactId) || isNull(version) || isNull(packaging)) {
222 throw new MojoExecutionException("The artifact information is incomplete: 'groupId', 'artifactId', "
223 + "'version' and 'packaging' are required.");
224 }
225
226 if (!isValidId(groupId) || !isValidId(artifactId) || !isValidVersion(version)) {
227 throw new MojoExecutionException("The artifact information is not valid: uses invalid characters.");
228 }
229
230 InstallRequest installRequest = new InstallRequest();
231
232 String mainArtifactExtension;
233 if (classifier == null && "pom".equals(packaging)) {
234 mainArtifactExtension = "pom";
235 } else {
236 ArtifactType artifactType =
237 session.getRepositorySession().getArtifactTypeRegistry().get(packaging);
238 if (artifactType != null) {
239 if (StringUtils.isEmpty(classifier) && !StringUtils.isEmpty(artifactType.getClassifier())) {
240 classifier = artifactType.getClassifier();
241 }
242 mainArtifactExtension = artifactType.getExtension();
243 } else {
244 mainArtifactExtension = packaging;
245 }
246 }
247 if (extension != null && !Objects.equals(extension, mainArtifactExtension)) {
248 log.warn(
249 "Main artifact extension should be '{}' but was overridden to '{}'",
250 mainArtifactExtension,
251 extension);
252 }
253 Artifact mainArtifact = new DefaultArtifact(
254 groupId, artifactId, classifier, extension != null ? extension : mainArtifactExtension, version)
255 .setFile(file);
256 installRequest.addArtifact(mainArtifact);
257
258 File artifactLocalFile = getLocalRepositoryFile(repositorySystemSession, mainArtifact);
259 File pomLocalFile = getPomLocalRepositoryFile(repositorySystemSession, mainArtifact);
260
261 if (file.equals(artifactLocalFile)) {
262 throw new MojoFailureException("Cannot install artifact. " + "Artifact is already in the local repository."
263 + LS + LS + "File in question is: " + file + LS);
264 }
265
266 if (!IS_POM_PACKAGING.test(packaging)) {
267 if (isNull(pomFile)) {
268 if (Boolean.TRUE.equals(generatePom) || (generatePom == null && !pomLocalFile.exists())) {
269 temporaryPom = generatePomFile();
270 log.debug("Installing generated POM");
271 installRequest.addArtifact(new SubArtifact(mainArtifact, "", "pom", temporaryPom));
272 } else if (generatePom == null) {
273 log.debug("Skipping installation of generated POM, already present in local repository");
274 }
275 } else {
276 installRequest.addArtifact(new SubArtifact(mainArtifact, "", "pom", pomFile));
277 }
278 }
279
280 if (sources != null) {
281 installRequest.addArtifact(new SubArtifact(mainArtifact, "sources", "jar", sources));
282 }
283
284 if (javadoc != null) {
285 installRequest.addArtifact(new SubArtifact(mainArtifact, "javadoc", "jar", javadoc));
286 }
287
288 try {
289 repositorySystem.install(repositorySystemSession, installRequest);
290 } catch (InstallationException e) {
291 throw new MojoExecutionException(e.getMessage(), e);
292 } finally {
293 if (temporaryPom != null) {
294
295 temporaryPom.delete();
296 }
297 }
298 }
299
300 private static final Pattern POM_ENTRY_PATTERN = Pattern.compile("META-INF/maven/.*/pom\\.xml");
301
302 private static final Predicate<JarEntry> IS_POM_ENTRY =
303 entry -> POM_ENTRY_PATTERN.matcher(entry.getName()).matches();
304
305 private File readingPomFromJarFile() throws MojoExecutionException {
306
307 String base = file.getName();
308 if (base.contains(".")) {
309 base = base.substring(0, base.lastIndexOf('.'));
310 }
311
312 try (JarFile jarFile = new JarFile(file)) {
313
314 JarEntry pomEntry = jarFile.stream().filter(IS_POM_ENTRY).findAny().orElse(null);
315
316 if (isNull(pomEntry)) {
317
318 log.info("pom.xml not found in {}", file.getName());
319 return null;
320 }
321
322 Path tempPomFile = Files.createTempFile(base, ".pom");
323
324 Files.copy(jarFile.getInputStream(pomEntry), tempPomFile, StandardCopyOption.REPLACE_EXISTING);
325
326 log.debug("Loading {}", pomEntry.getName());
327 processModel(readModel(tempPomFile.toFile()));
328 return tempPomFile.toFile();
329
330 } catch (IOException e) {
331
332 return null;
333 }
334 }
335
336
337
338
339
340
341
342
343 private Model readModel(File pomFile) throws MojoExecutionException {
344 try (InputStream reader = Files.newInputStream(pomFile.toPath())) {
345 return new MavenXpp3Reader().read(reader);
346 } catch (FileNotFoundException e) {
347 throw new MojoExecutionException("File not found " + pomFile, e);
348 } catch (IOException e) {
349 throw new MojoExecutionException("Error reading POM " + pomFile, e);
350 } catch (XmlPullParserException e) {
351 throw new MojoExecutionException("Error parsing POM " + pomFile, e);
352 }
353 }
354
355
356
357
358
359
360 private void processModel(Model model) {
361 Parent parent = model.getParent();
362
363 if (this.groupId == null) {
364 this.groupId = model.getGroupId();
365 if (this.groupId == null && parent != null) {
366 this.groupId = parent.getGroupId();
367 }
368 }
369 if (this.artifactId == null) {
370 this.artifactId = model.getArtifactId();
371 }
372 if (this.version == null) {
373 this.version = model.getVersion();
374 if (this.version == null && parent != null) {
375 this.version = parent.getVersion();
376 }
377 }
378 if (this.packaging == null) {
379 this.packaging = model.getPackaging();
380 }
381 }
382
383
384
385
386
387
388 private Model generateModel() {
389 Model model = new Model();
390
391 model.setModelVersion("4.0.0");
392
393 model.setGroupId(groupId);
394 model.setArtifactId(artifactId);
395 model.setVersion(version);
396 model.setPackaging(packaging);
397
398 model.setDescription("POM was created from install:install-file");
399
400 return model;
401 }
402
403
404
405
406
407
408
409
410 private File generatePomFile() throws MojoExecutionException {
411 Model model = generateModel();
412 try {
413 Path tempPomFile = Files.createTempFile("mvninstall", ".pom");
414
415 try (OutputStream writer = Files.newOutputStream(tempPomFile)) {
416 new MavenXpp3Writer().write(writer, model);
417 return tempPomFile.toFile();
418 }
419 } catch (IOException e) {
420 throw new MojoExecutionException("Error writing temporary POM file: " + e.getMessage(), e);
421 }
422 }
423
424
425
426
427
428 private File getLocalRepositoryFile(RepositorySystemSession session, Artifact artifact) {
429 String path = session.getLocalRepositoryManager().getPathForLocalArtifact(artifact);
430 return new File(session.getLocalRepository().getBasedir(), path);
431 }
432
433
434
435
436
437 private File getPomLocalRepositoryFile(RepositorySystemSession session, Artifact artifact) {
438 SubArtifact pomArtifact = new SubArtifact(artifact, "", "pom");
439 String path = session.getLocalRepositoryManager().getPathForLocalArtifact(pomArtifact);
440 return new File(session.getLocalRepository().getBasedir(), path);
441 }
442
443
444
445
446
447
448 private String getExtension(final File file) {
449 String filename = file.getName();
450 if (filename.contains(".tar.")) {
451 return "tar." + FileUtils.getExtension(filename);
452 } else {
453 return FileUtils.getExtension(filename);
454 }
455 }
456
457
458
459
460 private boolean isValidId(String id) {
461 if (id == null) {
462 return false;
463 }
464 for (int i = 0; i < id.length(); i++) {
465 char c = id.charAt(i);
466 if (!(c >= 'a' && c <= 'z'
467 || c >= 'A' && c <= 'Z'
468 || c >= '0' && c <= '9'
469 || c == '-'
470 || c == '_'
471 || c == '.')) {
472 return false;
473 }
474 }
475 return true;
476 }
477
478 private static final String ILLEGAL_VERSION_CHARS = "\\/:\"<>|?*[](){},";
479
480
481
482
483 private boolean isValidVersion(String version) {
484 if (version == null) {
485 return false;
486 }
487 for (int i = version.length() - 1; i >= 0; i--) {
488 if (ILLEGAL_VERSION_CHARS.indexOf(version.charAt(i)) >= 0) {
489 return false;
490 }
491 }
492 return true;
493 }
494 }