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