1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.deploy;
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.Writer;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.nio.file.StandardCopyOption;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.jar.JarEntry;
33 import java.util.jar.JarFile;
34 import java.util.regex.Pattern;
35
36 import org.apache.maven.api.Artifact;
37 import org.apache.maven.api.ProducedArtifact;
38 import org.apache.maven.api.RemoteRepository;
39 import org.apache.maven.api.model.Model;
40 import org.apache.maven.api.model.Parent;
41 import org.apache.maven.api.plugin.MojoException;
42 import org.apache.maven.api.plugin.annotations.Mojo;
43 import org.apache.maven.api.plugin.annotations.Parameter;
44 import org.apache.maven.api.services.ArtifactDeployer;
45 import org.apache.maven.api.services.ArtifactDeployerException;
46 import org.apache.maven.api.services.ArtifactDeployerRequest;
47 import org.apache.maven.api.services.ArtifactManager;
48 import org.apache.maven.api.services.xml.ModelXmlFactory;
49 import org.apache.maven.api.services.xml.XmlReaderException;
50
51
52
53
54
55
56 @Mojo(name = "deploy-file", projectRequired = false)
57 @SuppressWarnings("unused")
58 public class DeployFileMojo extends AbstractDeployMojo {
59 private static final String TAR = "tar.";
60 private static final String ILLEGAL_VERSION_CHARS = "\\/:\"<>|?*[](){},";
61
62
63
64
65 @Parameter(property = "groupId")
66 private String groupId;
67
68
69
70
71 @Parameter(property = "artifactId")
72 private String artifactId;
73
74
75
76
77 @Parameter(property = "version")
78 private String version;
79
80
81
82
83
84
85
86 @Parameter(property = "packaging")
87 private String packaging;
88
89
90
91
92 @Parameter(property = "generatePom.description")
93 private String description;
94
95
96
97
98 @Parameter(property = "file", required = true)
99 Path file;
100
101
102
103
104
105
106 @Parameter(property = "javadoc")
107 private Path javadoc;
108
109
110
111
112
113
114 @Parameter(property = "sources")
115 private Path sources;
116
117
118
119
120
121 @Parameter(property = "repositoryId", defaultValue = "remote-repository", required = true)
122 private String repositoryId;
123
124
125
126
127
128 @Parameter(property = "url", required = true)
129 private String url;
130
131
132
133
134 @Parameter(property = "pomFile")
135 private Path pomFile;
136
137
138
139
140 @Parameter(property = "generatePom", defaultValue = "true")
141 private boolean generatePom;
142
143
144
145
146 @Parameter(property = "classifier")
147 private String classifier;
148
149
150
151
152
153 @Parameter(property = "types")
154 private String types;
155
156
157
158
159
160 @Parameter(property = "classifiers")
161 private String classifiers;
162
163
164
165
166
167 @Parameter(property = "files")
168 private String files;
169
170
171
172
173
174
175
176
177
178
179
180
181 @Parameter(property = "maven.deploy.file.skip", defaultValue = "false")
182 private String skip = Boolean.FALSE.toString();
183
184 void initProperties() throws MojoException {
185 Path deployedPom;
186 if (pomFile != null) {
187 deployedPom = pomFile;
188 processModel(readModel(deployedPom));
189 } else {
190 deployedPom = readingPomFromJarFile();
191 if (deployedPom != null) {
192 pomFile = deployedPom;
193 }
194 }
195
196 if (packaging == null && file != null) {
197 packaging = getExtension(file);
198 }
199 }
200
201 private Path readingPomFromJarFile() {
202 Pattern pomEntry = Pattern.compile("META-INF/maven/.*/pom\\.xml");
203 try {
204 try (JarFile jarFile = new JarFile(file.toFile())) {
205 JarEntry entry = jarFile.stream()
206 .filter(e -> pomEntry.matcher(e.getName()).matches())
207 .findFirst()
208 .orElse(null);
209 if (entry != null) {
210 getLog().debug("Using " + entry.getName() + " as pomFile");
211
212 try (InputStream pomInputStream = jarFile.getInputStream(entry)) {
213 String base = file.getFileName().toString();
214 if (base.indexOf('.') > 0) {
215 base = base.substring(0, base.lastIndexOf('.'));
216 }
217 Path pomFile = File.createTempFile(base, ".pom").toPath();
218
219 Files.copy(pomInputStream, pomFile, StandardCopyOption.REPLACE_EXISTING);
220
221 processModel(readModel(pomFile));
222
223 return pomFile;
224 }
225 } else {
226 getLog().info("pom.xml not found in " + file.getFileName());
227 }
228 }
229 } catch (IOException e) {
230
231 }
232 return null;
233 }
234
235 @SuppressWarnings("checkstyle:MethodLength")
236 public void execute() throws MojoException {
237 if (Boolean.parseBoolean(skip)
238 || ("releases".equals(skip) && !session.isVersionSnapshot(version))
239 || ("snapshots".equals(skip) && session.isVersionSnapshot(version))) {
240 getLog().info("Skipping artifact deployment");
241 return;
242 }
243
244 if (!Files.exists(file)) {
245 String message = "The specified file '" + file + "' does not exist";
246 getLog().error(message);
247 throw new MojoException(message);
248 }
249
250 initProperties();
251
252 RemoteRepository deploymentRepository =
253 createDeploymentArtifactRepository(repositoryId, url.replace(File.separator, "/"));
254
255 if (deploymentRepository.getProtocol().isEmpty()) {
256 throw new MojoException("No transfer protocol found.");
257 }
258
259 Path deployedPom;
260 if (pomFile != null) {
261 deployedPom = pomFile;
262 processModel(readModel(deployedPom));
263 } else {
264 deployedPom = readingPomFromJarFile();
265 }
266
267 if (groupId == null || artifactId == null || version == null || packaging == null) {
268 throw new MojoException("The artifact information is incomplete: 'groupId', 'artifactId', "
269 + "'version' and 'packaging' are required.");
270 }
271
272 if (!isValidId(groupId) || !isValidId(artifactId) || !isValidVersion(version)) {
273 throw new MojoException("The artifact information is not valid: uses invalid characters.");
274 }
275
276 failIfOffline();
277 warnIfAffectedPackagingAndMaven(packaging);
278
279 List<ProducedArtifact> deployables = new ArrayList<>();
280
281 boolean isFilePom = classifier == null && "pom".equals(packaging);
282 ProducedArtifact artifact = session.createProducedArtifact(
283 groupId, artifactId, version, classifier, isFilePom ? "pom" : getExtension(file), packaging);
284
285 if (file.equals(getLocalRepositoryFile(artifact))) {
286 throw new MojoException("Cannot deploy artifact from the local repository: " + file);
287 }
288
289 ArtifactManager artifactManager = session.getService(ArtifactManager.class);
290 artifactManager.setPath(artifact, file);
291 deployables.add(artifact);
292
293 ProducedArtifact pomArtifact = null;
294 if (!isFilePom) {
295 pomArtifact = session.createProducedArtifact(groupId, artifactId, version, "", "pom", null);
296 if (deployedPom != null) {
297 artifactManager.setPath(pomArtifact, deployedPom);
298 deployables.add(pomArtifact);
299 } else {
300 deployedPom = generatePomFile();
301 artifactManager.setPath(pomArtifact, deployedPom);
302 if (generatePom) {
303 getLog().debug("Deploying generated POM");
304 deployables.add(pomArtifact);
305 } else {
306 getLog().debug("Skipping deploying POM");
307 }
308 }
309 }
310
311 if (sources != null) {
312 ProducedArtifact sourcesArtifact =
313 session.createProducedArtifact(groupId, artifactId, version, "sources", "jar", null);
314 artifactManager.setPath(sourcesArtifact, sources);
315 deployables.add(sourcesArtifact);
316 }
317
318 if (javadoc != null) {
319 ProducedArtifact javadocArtifact =
320 session.createProducedArtifact(groupId, artifactId, version, "javadoc", "jar", null);
321 artifactManager.setPath(javadocArtifact, javadoc);
322 deployables.add(javadocArtifact);
323 }
324
325 if (files != null) {
326 if (types == null) {
327 throw new MojoException("You must specify 'types' if you specify 'files'");
328 }
329 if (classifiers == null) {
330 throw new MojoException("You must specify 'classifiers' if you specify 'files'");
331 }
332 int filesLength = countCommas(files);
333 int typesLength = countCommas(types);
334 int classifiersLength = countCommas(classifiers);
335 if (typesLength != filesLength) {
336 throw new MojoException("You must specify the same number of entries in 'files' and "
337 + "'types' (respectively " + filesLength + " and " + typesLength + " entries )");
338 }
339 if (classifiersLength != filesLength) {
340 throw new MojoException("You must specify the same number of entries in 'files' and "
341 + "'classifiers' (respectively " + filesLength + " and " + classifiersLength + " entries )");
342 }
343 int fi = 0;
344 int ti = 0;
345 int ci = 0;
346 for (int i = 0; i <= filesLength; i++) {
347 int nfi = files.indexOf(',', fi);
348 if (nfi == -1) {
349 nfi = files.length();
350 }
351 int nti = types.indexOf(',', ti);
352 if (nti == -1) {
353 nti = types.length();
354 }
355 int nci = classifiers.indexOf(',', ci);
356 if (nci == -1) {
357 nci = classifiers.length();
358 }
359 Path file = Paths.get(files.substring(fi, nfi).replace("/", File.separator));
360 if (!Files.isRegularFile(file)) {
361
362 file = Paths.get(files.substring(fi, nfi));
363 }
364 if (Files.isRegularFile(file)) {
365 String extension = getExtension(file);
366 String type = types.substring(ti, nti).trim();
367
368 ProducedArtifact deployable = session.createProducedArtifact(
369 artifact.getGroupId(),
370 artifact.getArtifactId(),
371 artifact.getVersion().asString(),
372 classifiers.substring(ci, nci).trim(),
373 extension,
374 type);
375 artifactManager.setPath(deployable, file);
376 deployables.add(deployable);
377 } else {
378 throw new MojoException("Specified side artifact " + file + " does not exist");
379 }
380 fi = nfi + 1;
381 ti = nti + 1;
382 ci = nci + 1;
383 }
384 } else {
385 if (types != null) {
386 throw new MojoException("You must specify 'files' if you specify 'types'");
387 }
388 if (classifiers != null) {
389 throw new MojoException("You must specify 'files' if you specify 'classifiers'");
390 }
391 }
392
393 try {
394 ArtifactDeployerRequest deployRequest = ArtifactDeployerRequest.builder()
395 .session(session)
396 .repository(deploymentRepository)
397 .artifacts(deployables)
398 .retryFailedDeploymentCount(Math.max(1, Math.min(10, getRetryFailedDeploymentCount())))
399 .build();
400
401 getLog().info("Deploying artifacts " + deployables + " to repository " + deploymentRepository);
402 ArtifactDeployer artifactDeployer = session.getService(ArtifactDeployer.class);
403 artifactDeployer.deploy(deployRequest);
404 } catch (ArtifactDeployerException e) {
405 throw new MojoException(e.getMessage(), e);
406 } finally {
407 if (pomFile == null && deployedPom != null) {
408 try {
409 Files.deleteIfExists(deployedPom);
410 } catch (IOException e) {
411
412 }
413 if (pomArtifact != null) {
414 artifactManager.setPath(pomArtifact, null);
415 }
416 }
417 }
418 }
419
420
421
422
423
424 private Path getLocalRepositoryFile(Artifact artifact) {
425 return session.getPathForLocalArtifact(artifact);
426 }
427
428
429
430
431
432
433 private void processModel(Model model) {
434 Parent parent = model.getParent();
435
436 if (this.groupId == null) {
437 this.groupId = model.getGroupId();
438 if (this.groupId == null && parent != null) {
439 this.groupId = parent.getGroupId();
440 }
441 }
442 if (this.artifactId == null) {
443 this.artifactId = model.getArtifactId();
444 }
445 if (this.version == null) {
446 this.version = model.getVersion();
447 if (this.version == null && parent != null) {
448 this.version = parent.getVersion();
449 }
450 }
451 if (this.packaging == null) {
452 this.packaging = model.getPackaging();
453 }
454 }
455
456
457
458
459
460
461
462
463 Model readModel(Path pomFile) throws MojoException {
464 try (InputStream is = Files.newInputStream(pomFile)) {
465 ModelXmlFactory modelXmlFactory = session.getService(ModelXmlFactory.class);
466 return modelXmlFactory.read(is);
467 } catch (FileNotFoundException e) {
468 throw new MojoException("POM not found " + pomFile, e);
469 } catch (IOException e) {
470 throw new MojoException("Error reading POM " + pomFile, e);
471 } catch (XmlReaderException e) {
472 throw new MojoException("Error parsing POM " + pomFile, e);
473 }
474 }
475
476
477
478
479
480
481
482 private Path generatePomFile() throws MojoException {
483 Model model = generateModel();
484 try {
485 Path pomFile = File.createTempFile("mvndeploy", ".pom").toPath();
486 try (Writer writer = Files.newBufferedWriter(pomFile)) {
487 ModelXmlFactory modelXmlFactory = session.getService(ModelXmlFactory.class);
488 modelXmlFactory.write(model, writer);
489 }
490 return pomFile;
491 } catch (IOException e) {
492 throw new MojoException("Error writing temporary POM file: " + e.getMessage(), e);
493 }
494 }
495
496
497
498
499
500
501 private Model generateModel() {
502 return Model.newBuilder()
503 .modelVersion("4.0.0")
504 .groupId(groupId)
505 .artifactId(artifactId)
506 .version(version)
507 .packaging(packaging)
508 .description(description)
509 .build();
510 }
511
512 void setGroupId(String groupId) {
513 this.groupId = groupId;
514 }
515
516 void setArtifactId(String artifactId) {
517 this.artifactId = artifactId;
518 }
519
520 void setVersion(String version) {
521 this.version = version;
522 }
523
524 void setPackaging(String packaging) {
525 this.packaging = packaging;
526 }
527
528 void setPomFile(Path pomFile) {
529 this.pomFile = pomFile;
530 }
531
532 String getGroupId() {
533 return groupId;
534 }
535
536 String getArtifactId() {
537 return artifactId;
538 }
539
540 String getVersion() {
541 return version;
542 }
543
544 String getPackaging() {
545 return packaging;
546 }
547
548 Path getFile() {
549 return file;
550 }
551
552 String getClassifier() {
553 return classifier;
554 }
555
556 void setClassifier(String classifier) {
557 this.classifier = classifier;
558 }
559
560
561
562 private static int countCommas(String str) {
563 int count = 0;
564 int idx = 0;
565 while ((idx = str.indexOf(',', idx)) != -1) {
566 count++;
567 idx++;
568 }
569 return count;
570 }
571
572
573
574
575 private String getExtension(final Path file) {
576 String filename = file.getFileName().toString();
577 int lastDot = filename.lastIndexOf('.');
578 if (lastDot > 0 && lastDot < filename.length() - 1) {
579 String ext = filename.substring(lastDot + 1);
580 return filename.regionMatches(lastDot + 1 - TAR.length(), TAR, 0, TAR.length()) ? TAR + ext : ext;
581 }
582 return "";
583 }
584
585
586
587
588 private boolean isValidId(String id) {
589 if (id == null) {
590 return false;
591 }
592 for (int i = 0; i < id.length(); i++) {
593 char c = id.charAt(i);
594 if (!(c >= 'a' && c <= 'z'
595 || c >= 'A' && c <= 'Z'
596 || c >= '0' && c <= '9'
597 || c == '-'
598 || c == '_'
599 || c == '.')) {
600 return false;
601 }
602 }
603 return true;
604 }
605
606
607
608
609 private boolean isValidVersion(String version) {
610 if (version == null) {
611 return false;
612 }
613 for (int i = version.length() - 1; i >= 0; i--) {
614 if (ILLEGAL_VERSION_CHARS.indexOf(version.charAt(i)) >= 0) {
615 return false;
616 }
617 }
618 return true;
619 }
620 }