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