View Javadoc

1   package org.apache.maven.plugin.gpg;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.IOException;
25  import java.io.Reader;
26  import java.io.Writer;
27  import java.util.Map;
28  
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.artifact.deployer.ArtifactDeployer;
31  import org.apache.maven.artifact.deployer.ArtifactDeploymentException;
32  import org.apache.maven.artifact.factory.ArtifactFactory;
33  import org.apache.maven.artifact.metadata.ArtifactMetadata;
34  import org.apache.maven.artifact.repository.ArtifactRepository;
35  import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
36  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
37  import org.apache.maven.model.Model;
38  import org.apache.maven.model.Parent;
39  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
40  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
41  import org.apache.maven.plugin.MojoExecutionException;
42  import org.apache.maven.plugin.MojoFailureException;
43  import org.apache.maven.project.artifact.ProjectArtifactMetadata;
44  import org.apache.maven.project.validation.ModelValidationResult;
45  import org.apache.maven.project.validation.ModelValidator;
46  import org.codehaus.plexus.util.IOUtil;
47  import org.codehaus.plexus.util.ReaderFactory;
48  import org.codehaus.plexus.util.StringUtils;
49  import org.codehaus.plexus.util.WriterFactory;
50  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
51  
52  /**
53   * Signs artifacts and installs the artifact in the remote repository.
54   * 
55   * @author Daniel Kulp
56   * @goal sign-and-deploy-file
57   * @requiresProject false
58   * @threadSafe
59   * @since 1.0-beta-4
60   */
61  public class SignAndDeployFileMojo
62      extends AbstractGpgMojo
63  {
64  
65      /**
66       * The directory where to store signature files.
67       * 
68       * @parameter expression="${gpg.ascDirectory}"
69       */
70      private File ascDirectory;
71  
72      /**
73       * Flag whether Maven is currently in online/offline mode.
74       * 
75       * @parameter default-value="${settings.offline}"
76       * @readonly
77       */
78      private boolean offline;
79  
80      /**
81       * GroupId of the artifact to be deployed. Retrieved from POM file if specified.
82       * 
83       * @parameter expression="${groupId}"
84       */
85      private String groupId;
86  
87      /**
88       * ArtifactId of the artifact to be deployed. Retrieved from POM file if specified.
89       * 
90       * @parameter expression="${artifactId}"
91       */
92      private String artifactId;
93  
94      /**
95       * Version of the artifact to be deployed. Retrieved from POM file if specified.
96       * 
97       * @parameter expression="${version}"
98       */
99      private String version;
100 
101     /**
102      * Type of the artifact to be deployed. Retrieved from POM file if specified.
103      * 
104      * @parameter expression="${packaging}"
105      */
106     private String packaging;
107 
108     /**
109      * Add classifier to the artifact
110      * 
111      * @parameter expression="${classifier}";
112      */
113     private String classifier;
114 
115     /**
116      * Description passed to a generated POM file (in case of generatePom=true).
117      * 
118      * @parameter expression="${generatePom.description}"
119      */
120     private String description;
121 
122     /**
123      * File to be deployed.
124      * 
125      * @parameter expression="${file}"
126      * @required
127      */
128     private File file;
129 
130     /**
131      * Location of an existing POM file to be deployed alongside the main artifact, given by the ${file} parameter.
132      * 
133      * @parameter expression="${pomFile}"
134      */
135     private File pomFile;
136 
137     /**
138      * Upload a POM for this artifact. Will generate a default POM if none is supplied with the pomFile argument.
139      * 
140      * @parameter expression="${generatePom}" default-value="true"
141      */
142     private boolean generatePom;
143 
144     /**
145      * Whether to deploy snapshots with a unique version or not.
146      * 
147      * @parameter expression="${uniqueVersion}" default-value="true"
148      */
149     private boolean uniqueVersion;
150 
151     /**
152      * URL where the artifact will be deployed. <br/>
153      * ie ( file:///C:/m2-repo or scp://host.com/path/to/repo )
154      * 
155      * @parameter expression="${url}"
156      * @required
157      */
158     private String url;
159 
160     /**
161      * Server Id to map on the &lt;id&gt; under &lt;server&gt; section of <code>settings.xml</code>. In most cases, this
162      * parameter will be required for authentication.
163      * 
164      * @parameter expression="${repositoryId}" default-value="remote-repository"
165      * @required
166      */
167     private String repositoryId;
168 
169     /**
170      * The type of remote repository layout to deploy to. Try <i>legacy</i> for a Maven 1.x-style repository layout.
171      * 
172      * @parameter expression="${repositoryLayout}" default-value="default"
173      */
174     private String repositoryLayout;
175 
176     /**
177      * @component
178      */
179     private ArtifactDeployer deployer;
180 
181     /**
182      * @parameter default-value="${localRepository}"
183      * @required
184      * @readonly
185      */
186     private ArtifactRepository localRepository;
187 
188     /**
189      * Map that contains the layouts.
190      * 
191      * @component role="org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout"
192      */
193     private Map repositoryLayouts;
194 
195     /**
196      * Component used to create an artifact
197      * 
198      * @component
199      */
200     private ArtifactFactory artifactFactory;
201 
202     /**
203      * Component used to create a repository
204      * 
205      * @component
206      */
207     private ArtifactRepositoryFactory repositoryFactory;
208 
209     /**
210      * The component used to validate the user-supplied artifact coordinates.
211      * 
212      * @component
213      */
214     private ModelValidator modelValidator;
215 
216     private void initProperties()
217         throws MojoExecutionException
218     {
219         // Process the supplied POM (if there is one)
220         if ( pomFile != null )
221         {
222             generatePom = false;
223 
224             Model model = readModel( pomFile );
225 
226             processModel( model );
227         }
228     }
229 
230     public void execute()
231         throws MojoExecutionException, MojoFailureException
232     {
233         GpgSigner signer = newSigner( null );
234         signer.setOutputDirectory( ascDirectory );
235         signer.setBaseDirectory( new File( "" ).getAbsoluteFile() );
236 
237         if ( offline )
238         {
239             throw new MojoFailureException( "Cannot deploy artifacts when Maven is in offline mode" );
240         }
241 
242         initProperties();
243 
244         validateArtifactInformation();
245 
246         if ( !file.exists() )
247         {
248             throw new MojoFailureException( file.getPath() + " not found." );
249         }
250 
251         ArtifactRepositoryLayout layout = (ArtifactRepositoryLayout) repositoryLayouts.get( repositoryLayout );
252         if ( layout == null )
253         {
254             throw new MojoFailureException( "Invalid repository layout: " + repositoryLayout );
255         }
256 
257         ArtifactRepository deploymentRepository =
258             repositoryFactory.createDeploymentArtifactRepository( repositoryId, url, layout, uniqueVersion );
259 
260         if ( StringUtils.isEmpty( deploymentRepository.getProtocol() ) )
261         {
262             throw new MojoFailureException( "No transfer protocol found." );
263         }
264 
265         Artifact artifact =
266             artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, packaging, classifier );
267 
268         if ( file.equals( getLocalRepoFile( artifact ) ) )
269         {
270             throw new MojoFailureException( "Cannot deploy artifact from the local repository: " + file );
271         }
272 
273         File fileSig = signer.generateSignatureForArtifact( file );
274         ArtifactMetadata metadata = new AscArtifactMetadata( artifact, fileSig, false );
275         artifact.addMetadata( metadata );
276 
277         if ( !"pom".equals( packaging ) )
278         {
279             if ( pomFile == null && generatePom )
280             {
281                 pomFile = generatePomFile();
282             }
283             if ( pomFile != null )
284             {
285                 metadata = new ProjectArtifactMetadata( artifact, pomFile );
286                 artifact.addMetadata( metadata );
287 
288                 fileSig = signer.generateSignatureForArtifact( pomFile );
289                 metadata = new AscArtifactMetadata( artifact, fileSig, true );
290                 artifact.addMetadata( metadata );
291             }
292         }
293 
294         try
295         {
296             deployer.deploy( file, artifact, deploymentRepository, localRepository );
297         }
298         catch ( ArtifactDeploymentException e )
299         {
300             throw new MojoExecutionException( e.getMessage(), e );
301         }
302     }
303 
304     /**
305      * Gets the path of the specified artifact within the local repository. Note that the returned path need not exist
306      * (yet).
307      * 
308      * @param artifact The artifact whose local repo path should be determined, must not be <code>null</code>.
309      * @return The absolute path to the artifact when installed, never <code>null</code>.
310      */
311     private File getLocalRepoFile( Artifact artifact )
312     {
313         String path = localRepository.pathOf( artifact );
314         return new File( localRepository.getBasedir(), path );
315     }
316 
317     /**
318      * Process the supplied pomFile to get groupId, artifactId, version, and packaging
319      *
320      * @param model The POM to extract missing artifact coordinates from, must not be <code>null</code>.
321      */
322     private void processModel( Model model )
323     {
324         Parent parent = model.getParent();
325 
326         if ( this.groupId == null )
327         {
328             this.groupId = model.getGroupId();
329             if ( this.groupId == null && parent != null )
330             {
331                 this.groupId = parent.getGroupId();
332             }
333         }
334         if ( this.artifactId == null )
335         {
336             this.artifactId = model.getArtifactId();
337         }
338         if ( this.version == null )
339         {
340             this.version = model.getVersion();
341             if ( this.version == null && parent != null )
342             {
343                 this.version = parent.getVersion();
344             }
345         }
346         if ( this.packaging == null )
347         {
348             this.packaging = model.getPackaging();
349         }
350     }
351 
352     /**
353      * Extract the model from the specified POM file.
354      * 
355      * @param pomFile The path of the POM file to parse, must not be <code>null</code>.
356      * @return The model from the POM file, never <code>null</code>.
357      * @throws MojoExecutionException If the file doesn't exist of cannot be read.
358      */
359     private Model readModel( File pomFile )
360         throws MojoExecutionException
361     {
362         Reader reader = null;
363         try
364         {
365             reader = ReaderFactory.newXmlReader( pomFile );
366             return new MavenXpp3Reader().read( reader );
367         }
368         catch ( FileNotFoundException e )
369         {
370             throw new MojoExecutionException( "POM not found " + pomFile, e );
371         }
372         catch ( IOException e )
373         {
374             throw new MojoExecutionException( "Error reading POM " + pomFile, e );
375         }
376         catch ( XmlPullParserException e )
377         {
378             throw new MojoExecutionException( "Error parsing POM " + pomFile, e );
379         }
380         finally
381         {
382             IOUtil.close( reader );
383         }
384     }
385 
386     /**
387      * Generates a minimal POM from the user-supplied artifact information.
388      * 
389      * @return The path to the generated POM file, never <code>null</code>.
390      * @throws MojoExecutionException If the generation failed.
391      */
392     private File generatePomFile()
393         throws MojoExecutionException
394     {
395         Model model = generateModel();
396 
397         Writer fw = null;
398         try
399         {
400             File tempFile = File.createTempFile( "mvndeploy", ".pom" );
401             tempFile.deleteOnExit();
402 
403             fw = WriterFactory.newXmlWriter( tempFile );
404             new MavenXpp3Writer().write( fw, model );
405 
406             return tempFile;
407         }
408         catch ( IOException e )
409         {
410             throw new MojoExecutionException( "Error writing temporary pom file: " + e.getMessage(), e );
411         }
412         finally
413         {
414             IOUtil.close( fw );
415         }
416     }
417 
418     /**
419      * Generates a minimal model from the user-supplied artifact information.
420      * 
421      * @return The generated model, never <code>null</code>.
422      */
423     private Model generateModel()
424     {
425         Model model = new Model();
426 
427         model.setModelVersion( "4.0.0" );
428 
429         model.setGroupId( groupId );
430         model.setArtifactId( artifactId );
431         model.setVersion( version );
432         model.setPackaging( packaging );
433 
434         model.setDescription( description );
435 
436         return model;
437     }
438 
439     /**
440      * Validates the user-supplied artifact information.
441      * 
442      * @throws MojoFailureException If any artifact coordinate is invalid.
443      */
444     private void validateArtifactInformation()
445         throws MojoFailureException
446     {
447         Model model = generateModel();
448 
449         ModelValidationResult result = modelValidator.validate( model );
450 
451         if ( result.getMessageCount() > 0 )
452         {
453             throw new MojoFailureException( "The artifact information is incomplete or not valid:\n"
454                 + result.render( "  " ) );
455         }
456     }
457 
458 }