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