1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.gpg;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.nio.file.Files;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import org.apache.maven.artifact.handler.ArtifactHandler;
33 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
34 import org.apache.maven.model.Model;
35 import org.apache.maven.model.Parent;
36 import org.apache.maven.model.building.DefaultModelBuildingRequest;
37 import org.apache.maven.model.building.ModelBuildingRequest;
38 import org.apache.maven.model.building.ModelProblem;
39 import org.apache.maven.model.building.ModelProblemCollector;
40 import org.apache.maven.model.building.ModelProblemCollectorRequest;
41 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
42 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
43 import org.apache.maven.model.validation.ModelValidator;
44 import org.apache.maven.plugin.MojoExecutionException;
45 import org.apache.maven.plugin.MojoFailureException;
46 import org.apache.maven.plugins.annotations.Mojo;
47 import org.apache.maven.plugins.annotations.Parameter;
48 import org.apache.maven.project.MavenProject;
49 import org.codehaus.plexus.util.FileUtils;
50 import org.codehaus.plexus.util.StringUtils;
51 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
52 import org.eclipse.aether.RepositorySystem;
53 import org.eclipse.aether.artifact.Artifact;
54 import org.eclipse.aether.artifact.DefaultArtifact;
55 import org.eclipse.aether.deployment.DeployRequest;
56 import org.eclipse.aether.deployment.DeploymentException;
57 import org.eclipse.aether.repository.RemoteRepository;
58
59
60
61
62
63
64
65 @Mojo(name = "sign-and-deploy-file", requiresProject = false, threadSafe = true)
66 public class SignAndDeployFileMojo extends AbstractGpgMojo {
67
68
69
70
71 @Parameter(property = "gpg.ascDirectory")
72 private File ascDirectory;
73
74
75
76
77 @Parameter(property = "groupId")
78 private String groupId;
79
80
81
82
83 @Parameter(property = "artifactId")
84 private String artifactId;
85
86
87
88
89 @Parameter(property = "version")
90 private String version;
91
92
93
94
95
96 @Parameter(property = "packaging")
97 private String packaging;
98
99
100
101
102 @Parameter(property = "classifier")
103 private String classifier;
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 @Parameter(property = "pomFile")
121 private File pomFile;
122
123
124
125
126 @Parameter(property = "generatePom", defaultValue = "true")
127 private boolean generatePom;
128
129
130
131
132
133 @Parameter(property = "url", required = true)
134 private String url;
135
136
137
138
139
140 @Parameter(property = "repositoryId", defaultValue = "remote-repository", required = true)
141 private String repositoryId;
142
143
144
145
146
147
148 @Parameter(property = "javadoc")
149 private File javadoc;
150
151
152
153
154
155
156 @Parameter(property = "sources")
157 private File sources;
158
159
160
161
162
163
164
165 @Parameter(property = "retryFailedDeploymentCount", defaultValue = "1")
166 private int retryFailedDeploymentCount;
167
168
169
170
171
172 @Parameter(property = "types")
173 private String types;
174
175
176
177
178
179 @Parameter(property = "classifiers")
180 private String classifiers;
181
182
183
184
185
186 @Parameter(property = "files")
187 private String files;
188
189 private final RepositorySystem repositorySystem;
190
191
192
193
194 private final ModelValidator modelValidator;
195
196
197
198
199
200
201 private final MavenProject project;
202
203
204
205
206 private final ArtifactHandlerManager artifactHandlerManager;
207
208 @Inject
209 public SignAndDeployFileMojo(
210 RepositorySystem repositorySystem,
211 ModelValidator modelValidator,
212 MavenProject project,
213 ArtifactHandlerManager artifactHandlerManager) {
214 this.repositorySystem = repositorySystem;
215 this.modelValidator = modelValidator;
216 this.project = project;
217 this.artifactHandlerManager = artifactHandlerManager;
218 }
219
220 private void initProperties() throws MojoExecutionException {
221
222 if (pomFile != null) {
223 generatePom = false;
224
225 Model model = readModel(pomFile);
226
227 processModel(model);
228 }
229
230 if (packaging == null && file != null) {
231 packaging = FileUtils.getExtension(file.getName());
232 }
233 }
234
235 @Override
236 protected void doExecute() throws MojoExecutionException, MojoFailureException {
237 if (settings.isOffline()) {
238 throw new MojoFailureException("Cannot deploy artifacts when Maven is in offline mode");
239 }
240
241 initProperties();
242
243 validateArtifactInformation();
244
245 if (!file.exists()) {
246 throw new MojoFailureException(file.getPath() + " not found.");
247 }
248
249
250 List<Artifact> artifacts = new ArrayList<>();
251
252
253 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(packaging);
254 Artifact main = new DefaultArtifact(
255 groupId,
256 artifactId,
257 classifier == null || classifier.trim().isEmpty() ? handler.getClassifier() : classifier,
258 handler.getExtension(),
259 version)
260 .setFile(file);
261
262 File localRepoFile = new File(
263 session.getRepositorySession().getLocalRepository().getBasedir(),
264 session.getRepositorySession().getLocalRepositoryManager().getPathForLocalArtifact(main));
265 if (file.equals(localRepoFile)) {
266 throw new MojoFailureException("Cannot deploy artifact from the local repository: " + file);
267 }
268 artifacts.add(main);
269
270 if (!"pom".equals(packaging)) {
271 if (pomFile == null && generatePom) {
272 pomFile = generatePomFile();
273 }
274 if (pomFile != null) {
275 artifacts.add(
276 new DefaultArtifact(main.getGroupId(), main.getArtifactId(), null, "pom", main.getVersion())
277 .setFile(pomFile));
278 }
279 }
280
281 if (sources != null) {
282 artifacts.add(
283 new DefaultArtifact(main.getGroupId(), main.getArtifactId(), "sources", "jar", main.getVersion())
284 .setFile(sources));
285 }
286
287 if (javadoc != null) {
288 artifacts.add(
289 new DefaultArtifact(main.getGroupId(), main.getArtifactId(), "javadoc", "jar", main.getVersion())
290 .setFile(javadoc));
291 }
292
293 if (files != null) {
294 if (types == null) {
295 throw new MojoExecutionException("You must specify 'types' if you specify 'files'");
296 }
297 if (classifiers == null) {
298 throw new MojoExecutionException("You must specify 'classifiers' if you specify 'files'");
299 }
300 String[] files = this.files.split(",", -1);
301 String[] types = this.types.split(",", -1);
302 String[] classifiers = this.classifiers.split(",", -1);
303 if (types.length != files.length) {
304 throw new MojoExecutionException("You must specify the same number of entries in 'files' and "
305 + "'types' (respectively " + files.length + " and " + types.length + " entries )");
306 }
307 if (classifiers.length != files.length) {
308 throw new MojoExecutionException("You must specify the same number of entries in 'files' and "
309 + "'classifiers' (respectively " + files.length + " and " + classifiers.length + " entries )");
310 }
311 for (int i = 0; i < files.length; i++) {
312 File file = new File(files[i]);
313 if (!file.isFile()) {
314
315 file = new File(project.getBasedir(), files[i]);
316 }
317 if (file.isFile()) {
318 Artifact artifact;
319 String ext =
320 artifactHandlerManager.getArtifactHandler(types[i]).getExtension();
321 if (StringUtils.isWhitespace(classifiers[i])) {
322 artifact = new DefaultArtifact(
323 main.getGroupId(), main.getArtifactId(), null, ext, main.getVersion());
324 } else {
325 artifact = new DefaultArtifact(
326 main.getGroupId(), main.getArtifactId(), classifiers[i], ext, main.getVersion());
327 }
328 artifacts.add(artifact.setFile(file));
329 } else {
330 throw new MojoExecutionException("Specified side artifact " + file + " does not exist");
331 }
332 }
333 } else {
334 if (types != null) {
335 throw new MojoExecutionException("You must specify 'files' if you specify 'types'");
336 }
337 if (classifiers != null) {
338 throw new MojoExecutionException("You must specify 'files' if you specify 'classifiers'");
339 }
340 }
341
342
343 AbstractGpgSigner signer = newSigner(null);
344 signer.setOutputDirectory(ascDirectory);
345 signer.setBaseDirectory(new File("").getAbsoluteFile());
346
347 getLog().info("Signer '" + signer.signerName() + "' is signing " + artifacts.size() + " file"
348 + ((artifacts.size() > 1) ? "s" : "") + " with key " + signer.getKeyInfo());
349
350 ArrayList<Artifact> signatures = new ArrayList<>();
351 for (Artifact a : artifacts) {
352 signatures.add(new DefaultArtifact(
353 a.getGroupId(),
354 a.getArtifactId(),
355 a.getClassifier(),
356 a.getExtension() + AbstractGpgSigner.SIGNATURE_EXTENSION,
357 a.getVersion())
358 .setFile(signer.generateSignatureForArtifact(a.getFile())));
359 }
360 artifacts.addAll(signatures);
361
362
363 RemoteRepository deploymentRepository = repositorySystem.newDeploymentRepository(
364 session.getRepositorySession(), new RemoteRepository.Builder(repositoryId, "default", url).build());
365 try {
366 deploy(deploymentRepository, artifacts);
367 } catch (DeploymentException e) {
368 throw new MojoExecutionException(
369 "Error deploying attached artifacts " + artifacts + ": " + e.getMessage(), e);
370 }
371 }
372
373
374
375
376
377
378 private void processModel(Model model) {
379 Parent parent = model.getParent();
380
381 if (this.groupId == null) {
382 this.groupId = model.getGroupId();
383 if (this.groupId == null && parent != null) {
384 this.groupId = parent.getGroupId();
385 }
386 }
387 if (this.artifactId == null) {
388 this.artifactId = model.getArtifactId();
389 }
390 if (this.version == null) {
391 this.version = model.getVersion();
392 if (this.version == null && parent != null) {
393 this.version = parent.getVersion();
394 }
395 }
396 if (this.packaging == null) {
397 this.packaging = model.getPackaging();
398 }
399 }
400
401
402
403
404
405
406
407
408 private Model readModel(File pomFile) throws MojoExecutionException {
409 try (InputStream inputStream = Files.newInputStream(pomFile.toPath())) {
410 return new MavenXpp3Reader().read(inputStream);
411 } catch (FileNotFoundException e) {
412 throw new MojoExecutionException("POM not found " + pomFile, e);
413 } catch (IOException e) {
414 throw new MojoExecutionException("Error reading POM " + pomFile, e);
415 } catch (XmlPullParserException e) {
416 throw new MojoExecutionException("Error parsing POM " + pomFile, e);
417 }
418 }
419
420
421
422
423
424
425
426 private File generatePomFile() throws MojoExecutionException {
427 Model model = generateModel();
428
429 try {
430 File tempFile = Files.createTempFile("mvndeploy", ".pom").toFile();
431 tempFile.deleteOnExit();
432
433 try (OutputStream outputStream = Files.newOutputStream(tempFile.toPath())) {
434 new MavenXpp3Writer().write(outputStream, model);
435 }
436
437 return tempFile;
438 } catch (IOException e) {
439 throw new MojoExecutionException("Error writing temporary pom file: " + e.getMessage(), e);
440 }
441 }
442
443
444
445
446
447
448 private void validateArtifactInformation() throws MojoFailureException {
449 Model model = generateModel();
450
451 ModelBuildingRequest request =
452 new DefaultModelBuildingRequest().setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
453
454 List<String> result = new ArrayList<>();
455
456 SimpleModelProblemCollector problemCollector = new SimpleModelProblemCollector(result);
457
458 modelValidator.validateEffectiveModel(model, request, problemCollector);
459
460 if (!result.isEmpty()) {
461 StringBuilder msg = new StringBuilder("The artifact information is incomplete or not valid:\n");
462 for (String e : result) {
463 msg.append(" - " + e + '\n');
464 }
465 throw new MojoFailureException(msg.toString());
466 }
467 }
468
469
470
471
472
473
474 private Model generateModel() {
475 Model model = new Model();
476
477 model.setModelVersion("4.0.0");
478
479 model.setGroupId(groupId);
480 model.setArtifactId(artifactId);
481 model.setVersion(version);
482 model.setPackaging(packaging);
483
484 model.setDescription(description);
485
486 return model;
487 }
488
489
490
491
492
493
494
495
496 protected void deploy(RemoteRepository deploymentRepository, List<Artifact> artifacts) throws DeploymentException {
497 int retryFailedDeploymentCount = Math.max(1, Math.min(10, this.retryFailedDeploymentCount));
498 DeploymentException exception = null;
499 for (int count = 0; count < retryFailedDeploymentCount; count++) {
500 try {
501 if (count > 0) {
502
503 getLog().info("Retrying deployment attempt " + (count + 1) + " of " + retryFailedDeploymentCount);
504
505 }
506 DeployRequest deployRequest = new DeployRequest();
507 deployRequest.setRepository(deploymentRepository);
508 deployRequest.setArtifacts(artifacts);
509
510 repositorySystem.deploy(session.getRepositorySession(), deployRequest);
511 exception = null;
512 break;
513 } catch (DeploymentException e) {
514 if (count + 1 < retryFailedDeploymentCount) {
515 getLog().warn("Encountered issue during deployment: " + e.getLocalizedMessage());
516 getLog().debug(e);
517 }
518 if (exception == null) {
519 exception = e;
520 }
521 }
522 }
523 if (exception != null) {
524 throw exception;
525 }
526 }
527
528 private static class SimpleModelProblemCollector implements ModelProblemCollector {
529
530 private final List<String> result;
531
532 SimpleModelProblemCollector(List<String> result) {
533 this.result = result;
534 }
535
536 public void add(ModelProblemCollectorRequest req) {
537 if (!ModelProblem.Severity.WARNING.equals(req.getSeverity())) {
538 result.add(req.getMessage());
539 }
540 }
541 }
542 }