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