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