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 }