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