View Javadoc

1   package org.apache.maven.plugin.deploy;
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.List;
28  
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.artifact.deployer.ArtifactDeploymentException;
31  import org.apache.maven.artifact.metadata.ArtifactMetadata;
32  import org.apache.maven.artifact.repository.ArtifactRepository;
33  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
34  import org.apache.maven.model.Model;
35  import org.apache.maven.model.Parent;
36  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
37  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
38  import org.apache.maven.plugin.MojoExecutionException;
39  import org.apache.maven.plugin.MojoFailureException;
40  import org.apache.maven.plugins.annotations.Component;
41  import org.apache.maven.plugins.annotations.Mojo;
42  import org.apache.maven.plugins.annotations.Parameter;
43  import org.apache.maven.project.MavenProject;
44  import org.apache.maven.project.MavenProjectHelper;
45  import org.apache.maven.project.artifact.ProjectArtifactMetadata;
46  import org.apache.maven.project.validation.ModelValidationResult;
47  import org.apache.maven.project.validation.ModelValidator;
48  import org.codehaus.plexus.util.FileUtils;
49  import org.codehaus.plexus.util.IOUtil;
50  import org.codehaus.plexus.util.ReaderFactory;
51  import org.codehaus.plexus.util.StringUtils;
52  import org.codehaus.plexus.util.WriterFactory;
53  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
54  
55  /**
56   * Installs the artifact in the remote repository.
57   * 
58   * @author <a href="mailto:aramirez@apache.org">Allan Ramirez</a>
59   */
60  @Mojo( name = "deploy-file", requiresProject = false, threadSafe = true )
61  public class DeployFileMojo
62      extends AbstractDeployMojo
63  {
64      /**
65       * The default Maven project created when building the plugin
66       */
67      @Component
68      private MavenProject project;
69  
70      /**
71       * Used for attaching the source and javadoc jars to the project.
72       */
73      @Component
74      private MavenProjectHelper projectHelper;
75  
76      /**
77       * GroupId of the artifact to be deployed. Retrieved from POM file if specified.
78       */
79      @Parameter( property = "groupId" )
80      private String groupId;
81  
82      /**
83       * ArtifactId of the artifact to be deployed. Retrieved from POM file if specified.
84       */
85      @Parameter( property = "artifactId" )
86      private String artifactId;
87  
88      /**
89       * Version of the artifact to be deployed. Retrieved from POM file if specified.
90       */
91      @Parameter( property = "version" )
92      private String version;
93  
94      /**
95       * Type of the artifact to be deployed. Retrieved from the &lt;packaging&gt element of the POM file if a POM file
96       * specified. Defaults to the file extension if it is not specified via command line or POM.<br/>
97       * Maven uses two terms to refer to this datum: the &lt;packaging&gt; element for the entire POM, and the
98       * &lt;type&gt; element in a dependency specification.
99       */
100     @Parameter( property = "packaging" )
101     private String packaging;
102 
103     /**
104      * Description passed to a generated POM file (in case of generatePom=true)
105      */
106     @Parameter( property = "generatePom.description" )
107     private String description;
108 
109     /**
110      * File to be deployed.
111      */
112     @Parameter( property = "file", required = true )
113     private File file;
114 
115     /**
116      * The bundled API docs for the artifact.
117      */
118     @Parameter( property = "javadoc" )
119     private File javadoc;
120 
121     /**
122      * The bundled sources for the artifact.
123      */
124     @Parameter( property = "sources" )
125     private File sources;
126 
127     /**
128      * Server Id to map on the &lt;id&gt; under &lt;server&gt; section of settings.xml In most cases, this parameter
129      * will be required for authentication.
130      */
131     @Parameter( property = "repositoryId", defaultValue = "remote-repository", required = true )
132     private String repositoryId;
133 
134     /**
135      * The type of remote repository layout to deploy to. Try <i>legacy</i> for a Maven 1.x-style repository layout.
136      */
137     @Parameter( property = "repositoryLayout", defaultValue = "default" )
138     private String repositoryLayout;
139 
140     /**
141      * URL where the artifact will be deployed. <br/>
142      * ie ( file:///C:/m2-repo or scp://host.com/path/to/repo )
143      */
144     @Parameter( property = "url" )
145     private String url;
146 
147     /**
148      * Location of an existing POM file to be deployed alongside the main artifact, given by the ${file} parameter.
149      */
150     @Parameter( property = "pomFile" )
151     private File pomFile;
152 
153     /**
154      * Upload a POM for this artifact. Will generate a default POM if none is supplied with the pomFile argument.
155      */
156     @Parameter( property = "generatePom", defaultValue = "true" )
157     private boolean generatePom;
158 
159     /**
160      * Add classifier to the artifact
161      */
162     @Parameter( property = "classifier" )
163     private String classifier;
164 
165     /**
166      * Whether to deploy snapshots with a unique version or not.
167      */
168     @Parameter( property = "uniqueVersion", defaultValue = "true" )
169     private boolean uniqueVersion;
170 
171     /**
172      * The component used to validate the user-supplied artifact coordinates.
173      */
174     @Component
175     private ModelValidator modelValidator;
176 
177     /**
178      * A comma separated list of types for each of the extra side artifacts to deploy. If there is a mis-match in the
179      * number of entries in {@link #files} or {@link #classifiers}, then an error will be raised.
180      */
181     @Parameter( property = "types" )
182     private String types;
183 
184     /**
185      * A comma separated list of classifiers for each of the extra side artifacts to deploy. If there is a mis-match in
186      * the number of entries in {@link #files} or {@link #types}, then an error will be raised.
187      */
188     @Parameter( property = "classifiers" )
189     private String classifiers;
190 
191     /**
192      * A comma separated list of files for each of the extra side artifacts to deploy. If there is a mis-match in the
193      * number of entries in {@link #types} or {@link #classifiers}, then an error will be raised.
194      */
195     @Parameter( property = "files" )
196     private String files;
197 
198     void initProperties()
199         throws MojoExecutionException
200     {
201         // Process the supplied POM (if there is one)
202         if ( pomFile != null )
203         {
204             generatePom = false;
205 
206             Model model = readModel( pomFile );
207 
208             processModel( model );
209         }
210 
211         if ( packaging == null && file != null )
212         {
213             packaging = FileUtils.getExtension( file.getName() );
214         }
215     }
216 
217     public void execute()
218         throws MojoExecutionException, MojoFailureException
219     {
220         failIfOffline();
221 
222         if ( !file.exists() )
223         {
224             throw new MojoExecutionException( file.getPath() + " not found." );
225         }
226 
227         initProperties();
228 
229         validateArtifactInformation();
230 
231         ArtifactRepositoryLayout layout = getLayout( repositoryLayout );
232 
233         ArtifactRepository deploymentRepository =
234             repositoryFactory.createDeploymentArtifactRepository( repositoryId, url, layout, uniqueVersion );
235 
236         String protocol = deploymentRepository.getProtocol();
237 
238         if ( StringUtils.isEmpty( protocol ) )
239         {
240             throw new MojoExecutionException( "No transfer protocol found." );
241         }
242 
243         // Create the artifact
244         Artifact artifact =
245             artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, packaging, classifier );
246 
247         if ( file.equals( getLocalRepoFile( artifact ) ) )
248         {
249             throw new MojoFailureException( "Cannot deploy artifact from the local repository: " + file );
250         }
251 
252         // Upload the POM if requested, generating one if need be
253         if ( !"pom".equals( packaging ) )
254         {
255             if ( pomFile != null )
256             {
257                 ArtifactMetadata metadata = new ProjectArtifactMetadata( artifact, pomFile );
258                 artifact.addMetadata( metadata );
259             }
260             else if ( generatePom )
261             {
262                 ArtifactMetadata metadata = new ProjectArtifactMetadata( artifact, generatePomFile() );
263                 artifact.addMetadata( metadata );
264             }
265         }
266 
267         if ( updateReleaseInfo )
268         {
269             artifact.setRelease( true );
270         }
271 
272         project.setArtifact( artifact );
273 
274         try
275         {
276             deploy( file, artifact, deploymentRepository, getLocalRepository(), getRetryFailedDeploymentCount() );
277         }
278         catch ( ArtifactDeploymentException e )
279         {
280             throw new MojoExecutionException( e.getMessage(), e );
281         }
282 
283         if ( sources != null )
284         {
285             projectHelper.attachArtifact( project, "jar", "sources", sources );
286         }
287 
288         if ( javadoc != null )
289         {
290             projectHelper.attachArtifact( project, "jar", "javadoc", javadoc );
291         }
292 
293         if ( files != null )
294         {
295             if ( types == null )
296             {
297                 throw new MojoExecutionException( "You must specify 'types' if you specify 'files'" );
298             }
299             if ( classifiers == null )
300             {
301                 throw new MojoExecutionException( "You must specify 'classifiers' if you specify 'files'" );
302             }
303             int filesLength = StringUtils.countMatches( files, "," );
304             int typesLength = StringUtils.countMatches( types, "," );
305             int classifiersLength = StringUtils.countMatches( classifiers, "," );
306             if ( typesLength != filesLength )
307             {
308                 throw new MojoExecutionException( "You must specify the same number of entries in 'files' and "
309                     + "'types' (respectively " + filesLength + " and " + typesLength + " entries )" );
310             }
311             if ( classifiersLength != filesLength )
312             {
313                 throw new MojoExecutionException( "You must specify the same number of entries in 'files' and "
314                     + "'classifiers' (respectively " + filesLength + " and " + classifiersLength + " entries )" );
315             }
316             int fi = 0;
317             int ti = 0;
318             int ci = 0;
319             for ( int i = 0; i <= filesLength; i++ )
320             {
321                 int nfi = files.indexOf( ',', fi );
322                 if ( nfi == -1 )
323                 {
324                     nfi = files.length();
325                 }
326                 int nti = types.indexOf( ',', ti );
327                 if ( nti == -1 )
328                 {
329                     nti = types.length();
330                 }
331                 int nci = classifiers.indexOf( ',', ci );
332                 if ( nci == -1 )
333                 {
334                     nci = classifiers.length();
335                 }
336                 File file = new File( files.substring( fi, nfi ) );
337                 if ( !file.isFile() )
338                 {
339                     // try relative to the project basedir just in case
340                     file = new File( project.getBasedir(), files.substring( fi, nfi ) );
341                 }
342                 if ( file.isFile() )
343                 {
344                     if ( StringUtils.isWhitespace( classifiers.substring( ci, nci ) ) )
345                     {
346                         projectHelper.attachArtifact( project, types.substring( ti, nti ).trim(), file );
347                     }
348                     else
349                     {
350                         projectHelper.attachArtifact( project, types.substring( ti, nti ).trim(),
351                                                       classifiers.substring( ci, nci ).trim(), file );
352                     }
353                 }
354                 else
355                 {
356                     throw new MojoExecutionException( "Specified side artifact " + file + " does not exist" );
357                 }
358                 fi = nfi + 1;
359                 ti = nti + 1;
360                 ci = nci + 1;
361             }
362         }
363         else
364         {
365             if ( types != null )
366             {
367                 throw new MojoExecutionException( "You must specify 'files' if you specify 'types'" );
368             }
369             if ( classifiers != null )
370             {
371                 throw new MojoExecutionException( "You must specify 'files' if you specify 'classifiers'" );
372             }
373         }
374 
375         @SuppressWarnings( "unchecked" )
376         List<Artifact> attachedArtifacts = project.getAttachedArtifacts();
377 
378         for ( Artifact attached : attachedArtifacts )
379         {
380             try
381             {
382                 deploy( attached.getFile(), attached, deploymentRepository, getLocalRepository(), getRetryFailedDeploymentCount() );
383             }
384             catch ( ArtifactDeploymentException e )
385             {
386                 throw new MojoExecutionException( "Error deploying attached artifact " + attached.getFile() + ": "
387                     + e.getMessage(), e );
388             }
389         }
390 
391     }
392 
393     /**
394      * Gets the path of the specified artifact within the local repository. Note that the returned path need not exist
395      * (yet).
396      * 
397      * @param artifact The artifact whose local repo path should be determined, must not be <code>null</code>.
398      * @return The absolute path to the artifact when installed, never <code>null</code>.
399      */
400     private File getLocalRepoFile( Artifact artifact )
401     {
402         String path = getLocalRepository().pathOf( artifact );
403         return new File( getLocalRepository().getBasedir(), path );
404     }
405 
406     /**
407      * Process the supplied pomFile to get groupId, artifactId, version, and packaging
408      * 
409      * @param model The POM to extract missing artifact coordinates from, must not be <code>null</code>.
410      */
411     private void processModel( Model model )
412     {
413         Parent parent = model.getParent();
414 
415         if ( this.groupId == null )
416         {
417             this.groupId = model.getGroupId();
418             if ( this.groupId == null && parent != null )
419             {
420                 this.groupId = parent.getGroupId();
421             }
422         }
423         if ( this.artifactId == null )
424         {
425             this.artifactId = model.getArtifactId();
426         }
427         if ( this.version == null )
428         {
429             this.version = model.getVersion();
430             if ( this.version == null && parent != null )
431             {
432                 this.version = parent.getVersion();
433             }
434         }
435         if ( this.packaging == null )
436         {
437             this.packaging = model.getPackaging();
438         }
439     }
440 
441     /**
442      * Extract the model from the specified POM file.
443      * 
444      * @param pomFile The path of the POM file to parse, must not be <code>null</code>.
445      * @return The model from the POM file, never <code>null</code>.
446      * @throws MojoExecutionException If the file doesn't exist of cannot be read.
447      */
448     Model readModel( File pomFile )
449         throws MojoExecutionException
450     {
451         Reader reader = null;
452         try
453         {
454             reader = ReaderFactory.newXmlReader( pomFile );
455             return new MavenXpp3Reader().read( reader );
456         }
457         catch ( FileNotFoundException e )
458         {
459             throw new MojoExecutionException( "POM not found " + pomFile, e );
460         }
461         catch ( IOException e )
462         {
463             throw new MojoExecutionException( "Error reading POM " + pomFile, e );
464         }
465         catch ( XmlPullParserException e )
466         {
467             throw new MojoExecutionException( "Error parsing POM " + pomFile, e );
468         }
469         finally
470         {
471             IOUtil.close( reader );
472         }
473     }
474 
475     /**
476      * Generates a minimal POM from the user-supplied artifact information.
477      * 
478      * @return The path to the generated POM file, never <code>null</code>.
479      * @throws MojoExecutionException If the generation failed.
480      */
481     private File generatePomFile()
482         throws MojoExecutionException
483     {
484         Model model = generateModel();
485 
486         Writer fw = null;
487         try
488         {
489             File tempFile = File.createTempFile( "mvndeploy", ".pom" );
490             tempFile.deleteOnExit();
491 
492             fw = WriterFactory.newXmlWriter( tempFile );
493             new MavenXpp3Writer().write( fw, model );
494 
495             return tempFile;
496         }
497         catch ( IOException e )
498         {
499             throw new MojoExecutionException( "Error writing temporary pom file: " + e.getMessage(), e );
500         }
501         finally
502         {
503             IOUtil.close( fw );
504         }
505     }
506 
507     /**
508      * Validates the user-supplied artifact information.
509      * 
510      * @throws MojoExecutionException If any artifact coordinate is invalid.
511      */
512     private void validateArtifactInformation()
513         throws MojoExecutionException
514     {
515         Model model = generateModel();
516 
517         ModelValidationResult result = modelValidator.validate( model );
518 
519         if ( result.getMessageCount() > 0 )
520         {
521             throw new MojoExecutionException( "The artifact information is incomplete or not valid:\n"
522                 + result.render( "  " ) );
523         }
524     }
525 
526     /**
527      * Generates a minimal model from the user-supplied artifact information.
528      * 
529      * @return The generated model, never <code>null</code>.
530      */
531     private Model generateModel()
532     {
533         Model model = new Model();
534 
535         model.setModelVersion( "4.0.0" );
536 
537         model.setGroupId( groupId );
538         model.setArtifactId( artifactId );
539         model.setVersion( version );
540         model.setPackaging( packaging );
541 
542         model.setDescription( description );
543 
544         return model;
545     }
546 
547     void setGroupId( String groupId )
548     {
549         this.groupId = groupId;
550     }
551 
552     void setArtifactId( String artifactId )
553     {
554         this.artifactId = artifactId;
555     }
556 
557     void setVersion( String version )
558     {
559         this.version = version;
560     }
561 
562     void setPackaging( String packaging )
563     {
564         this.packaging = packaging;
565     }
566 
567     void setPomFile( File pomFile )
568     {
569         this.pomFile = pomFile;
570     }
571 
572     String getGroupId()
573     {
574         return groupId;
575     }
576 
577     String getArtifactId()
578     {
579         return artifactId;
580     }
581 
582     String getVersion()
583     {
584         return version;
585     }
586 
587     String getPackaging()
588     {
589         return packaging;
590     }
591 
592     File getFile()
593     {
594         return file;
595     }
596 
597     String getClassifier()
598     {
599         return classifier;
600     }
601 
602     void setClassifier( String classifier )
603     {
604         this.classifier = classifier;
605     }
606 }