View Javadoc

1   package org.apache.maven.plugin.install;
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.InputStream;
26  import java.io.Reader;
27  import java.io.Writer;
28  import java.net.MalformedURLException;
29  import java.util.Collection;
30  import java.util.Enumeration;
31  import java.util.LinkedHashSet;
32  import java.util.Map;
33  import java.util.jar.JarEntry;
34  import java.util.jar.JarFile;
35  import java.util.regex.Pattern;
36  
37  import org.apache.maven.artifact.Artifact;
38  import org.apache.maven.artifact.installer.ArtifactInstallationException;
39  import org.apache.maven.artifact.metadata.ArtifactMetadata;
40  import org.apache.maven.artifact.repository.DefaultArtifactRepository;
41  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
42  import org.apache.maven.model.Model;
43  import org.apache.maven.model.Parent;
44  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
45  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
46  import org.apache.maven.plugin.MojoExecutionException;
47  import org.apache.maven.plugin.MojoFailureException;
48  import org.apache.maven.plugins.annotations.Component;
49  import org.apache.maven.plugins.annotations.Mojo;
50  import org.apache.maven.plugins.annotations.Parameter;
51  import org.apache.maven.project.artifact.ProjectArtifactMetadata;
52  import org.apache.maven.project.validation.ModelValidationResult;
53  import org.apache.maven.project.validation.ModelValidator;
54  import org.apache.maven.shared.utils.io.IOUtil;
55  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
56  import org.apache.maven.shared.utils.ReaderFactory;
57  import org.apache.maven.shared.utils.WriterFactory;
58  
59  /**
60   * Installs a file in the local repository.
61   * 
62   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
63   * @version $Id: InstallFileMojo.java 1530417 2013-10-08 20:49:56Z rfscholte $
64   */
65  @Mojo( name = "install-file", requiresProject = false, aggregator = true, threadSafe = true )
66  public class InstallFileMojo
67      extends AbstractInstallMojo
68  {
69  
70      /**
71       * GroupId of the artifact to be installed. Retrieved from POM file if one is specified or extracted from
72       * {@code pom.xml} in jar if available.
73       */
74      @Parameter( property = "groupId" )
75      protected String groupId;
76  
77      /**
78       * ArtifactId of the artifact to be installed. Retrieved from POM file if one is specified or extracted from
79       * {@code pom.xml} in jar if available.
80       */
81      @Parameter( property = "artifactId" )
82      protected String artifactId;
83  
84      /**
85       * Version of the artifact to be installed. Retrieved from POM file if one is specified or extracted from
86       * {@code pom.xml} in jar if available.
87       */
88      @Parameter( property = "version" )
89      protected String version;
90  
91      /**
92       * Packaging type of the artifact to be installed. Retrieved from POM file if one is specified or extracted from
93       * {@code pom.xml} in jar if available.
94       */
95      @Parameter( property = "packaging" )
96      protected String packaging;
97  
98      /**
99       * Classifier type of the artifact to be installed. For example, "sources" or "javadoc". Defaults to none which
100      * means this is the project's main artifact.
101      * 
102      * @since 2.2
103      */
104     @Parameter( property = "classifier" )
105     protected String classifier;
106 
107     /**
108      * The file to be installed in the local repository.
109      */
110     @Parameter( property = "file", required = true )
111     private File file;
112 
113     /**
114      * The bundled API docs for the artifact.
115      * 
116      * @since 2.3
117      */
118     @Parameter( property = "javadoc" )
119     private File javadoc;
120 
121     /**
122      * The bundled sources for the artifact.
123      * 
124      * @since 2.3
125      */
126     @Parameter( property = "sources" )
127     private File sources;
128 
129     /**
130      * Location of an existing POM file to be installed alongside the main artifact, given by the {@link #file}
131      * parameter.
132      * 
133      * @since 2.1
134      */
135     @Parameter( property = "pomFile" )
136     private File pomFile;
137 
138     /**
139      * Generate a minimal POM for the artifact if none is supplied via the parameter {@link #pomFile}. Defaults to
140      * <code>true</code> if there is no existing POM in the local repository yet.
141      * 
142      * @since 2.1
143      */
144     @Parameter( property = "generatePom" )
145     private Boolean generatePom;
146 
147     /**
148      * The type of remote repository layout to install to. Try <code>legacy</code> for a Maven 1.x-style repository
149      * layout.
150      * 
151      * @since 2.2
152      */
153     @Parameter( property = "repositoryLayout", defaultValue = "default", required = true )
154     private String repositoryLayout;
155 
156     /**
157      * Map that contains the repository layouts.
158      */
159     @SuppressWarnings( "MismatchedQueryAndUpdateOfCollection" )
160     @Component( role = ArtifactRepositoryLayout.class )
161     private Map<String, ArtifactRepositoryLayout> repositoryLayouts;
162 
163     /**
164      * The path for a specific local repository directory. If not specified the local repository path configured in the
165      * Maven settings will be used.
166      * 
167      * @since 2.2
168      */
169     @Parameter( property = "localRepositoryPath" )
170     private File localRepositoryPath;
171 
172     /**
173      * The component used to validate the user-supplied artifact coordinates.
174      */
175     @Component
176     private ModelValidator modelValidator;
177 
178     /**
179      * @see org.apache.maven.plugin.Mojo#execute()
180      */
181     public void execute()
182         throws MojoExecutionException, MojoFailureException
183     {
184 
185         if ( !file.exists() )
186         {
187             String message = "The specified file '" + file.getPath() + "' not exists";
188             getLog().error( message );
189             throw new MojoFailureException( message );
190         }
191 
192         // ----------------------------------------------------------------------
193         // Override the default localRepository variable
194         // ----------------------------------------------------------------------
195         if ( localRepositoryPath != null )
196         {
197             try
198             {
199                 ArtifactRepositoryLayout layout = repositoryLayouts.get( repositoryLayout );
200                 getLog().debug( "Layout: " + layout.getClass() );
201 
202                 // noinspection deprecation
203                 localRepository =
204                     new DefaultArtifactRepository( localRepository.getId(), localRepositoryPath.toURL().toString(),
205                                                    layout );
206             }
207             catch ( MalformedURLException e )
208             {
209                 throw new MojoExecutionException( "MalformedURLException: " + e.getMessage(), e );
210             }
211         }
212 
213         if ( pomFile != null )
214         {
215             processModel( readModel( pomFile ) );
216         }
217         else
218         {
219             boolean foundPom = false;
220 
221             try
222             {
223                 Pattern pomEntry = Pattern.compile( "META-INF/maven/.*/pom\\.xml" );
224 
225                 JarFile jarFile = new JarFile( file );
226 
227                 Enumeration<JarEntry> jarEntries = jarFile.entries();
228 
229                 while ( jarEntries.hasMoreElements() )
230                 {
231                     JarEntry entry = jarEntries.nextElement();
232 
233                     if ( pomEntry.matcher( entry.getName() ).matches() )
234                     {
235                         getLog().debug( "Using " + entry.getName() + " for groupId, artifactId, packaging and version" );
236 
237                         foundPom = true;
238 
239                         InputStream pomInputStream = null;
240 
241                         try
242                         {
243                             pomInputStream = jarFile.getInputStream( entry );
244 
245                             processModel( readModel( pomInputStream ) );
246 
247                             break;
248                         }
249                         finally
250                         {
251                             if ( pomInputStream != null )
252                             {
253                                 pomInputStream.close();
254                             }
255                         }
256                     }
257                 }
258 
259                 if ( !foundPom )
260                 {
261                     getLog().info( "pom.xml not found in " + file.getName() );
262                 }
263             }
264             catch ( IOException e )
265             {
266                 // ignore, artifact not packaged by Maven
267             }
268 
269         }
270 
271         validateArtifactInformation();
272 
273         Artifact artifact =
274             artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, packaging, classifier );
275 
276         if ( file.equals( getLocalRepoFile( artifact ) ) )
277         {
278             throw new MojoFailureException( "Cannot install artifact. "
279                 + "Artifact is already in the local repository.\n\nFile in question is: " + file + "\n" );
280         }
281 
282         File generatedPomFile = null;
283 
284         if ( !"pom".equals( packaging ) )
285         {
286             if ( pomFile != null )
287             {
288                 ArtifactMetadata pomMetadata = new ProjectArtifactMetadata( artifact, pomFile );
289                 artifact.addMetadata( pomMetadata );
290             }
291             else
292             {
293                 generatedPomFile = generatePomFile();
294                 ArtifactMetadata pomMetadata = new ProjectArtifactMetadata( artifact, generatedPomFile );
295                 if ( Boolean.TRUE.equals( generatePom )
296                     || ( generatePom == null && !getLocalRepoFile( pomMetadata ).exists() ) )
297                 {
298                     getLog().debug( "Installing generated POM" );
299                     artifact.addMetadata( pomMetadata );
300                 }
301                 else if ( generatePom == null )
302                 {
303                     getLog().debug( "Skipping installation of generated POM, already present in local repository" );
304                 }
305             }
306         }
307 
308         if ( updateReleaseInfo )
309         {
310             artifact.setRelease( true );
311         }
312 
313         Collection<File> metadataFiles = new LinkedHashSet<File>();
314 
315         // TODO: maybe not strictly correct, while we should enforce that packaging has a type handler of the same id,
316         // we don't
317         try
318         {
319             installer.install( file, artifact, localRepository );
320             installChecksums( artifact, createChecksum );
321             addMetaDataFilesForArtifact( artifact, metadataFiles, createChecksum );
322 
323         }
324         catch ( ArtifactInstallationException e )
325         {
326             throw new MojoExecutionException( "Error installing artifact '" + artifact.getDependencyConflictId()
327                 + "': " + e.getMessage(), e );
328         }
329         finally
330         {
331             if ( generatedPomFile != null )
332             {
333                 // noinspection ResultOfMethodCallIgnored
334                 generatedPomFile.delete();
335             }
336         }
337 
338         if ( sources != null )
339         {
340             artifact = artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, "jar", "sources" );
341             try
342             {
343                 installer.install( sources, artifact, localRepository );
344                 installChecksums( artifact, createChecksum );
345                 addMetaDataFilesForArtifact( artifact, metadataFiles, createChecksum );
346 
347             }
348             catch ( ArtifactInstallationException e )
349             {
350                 throw new MojoExecutionException( "Error installing sources " + sources + ": " + e.getMessage(), e );
351             }
352         }
353 
354         if ( javadoc != null )
355         {
356             artifact = artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, "jar", "javadoc" );
357             try
358             {
359                 installer.install( javadoc, artifact, localRepository );
360                 installChecksums( artifact, createChecksum );
361                 addMetaDataFilesForArtifact( artifact, metadataFiles, createChecksum );
362 
363             }
364             catch ( ArtifactInstallationException e )
365             {
366                 throw new MojoExecutionException( "Error installing API docs " + javadoc + ": " + e.getMessage(), e );
367             }
368         }
369 
370         installChecksums( metadataFiles );
371     }
372 
373     /**
374      * Parses a POM.
375      * 
376      * @param pomFile The path of the POM file to parse, must not be <code>null</code>.
377      * @return The model from the POM file, never <code>null</code>.
378      * @throws MojoExecutionException If the POM could not be parsed.
379      */
380     private Model readModel( File pomFile )
381         throws MojoExecutionException
382     {
383         Reader reader = null;
384         try
385         {
386             reader = ReaderFactory.newXmlReader( pomFile );
387             return new MavenXpp3Reader().read( reader );
388         }
389         catch ( FileNotFoundException e )
390         {
391             throw new MojoExecutionException( "File not found " + pomFile, e );
392         }
393         catch ( IOException e )
394         {
395             throw new MojoExecutionException( "Error reading POM " + pomFile, e );
396         }
397         catch ( XmlPullParserException e )
398         {
399             throw new MojoExecutionException( "Error parsing POM " + pomFile, e );
400         }
401         finally
402         {
403             IOUtil.close( reader );
404         }
405     }
406 
407     /**
408      * Parses a POM.
409      * 
410      * @param pomFile The path of the POM file to parse, must not be <code>null</code>.
411      * @return The model from the POM file, never <code>null</code>.
412      * @throws MojoExecutionException If the POM could not be parsed.
413      */
414     private Model readModel( InputStream pomFile )
415         throws MojoExecutionException
416     {
417         Reader reader = null;
418         try
419         {
420             reader = ReaderFactory.newXmlReader( pomFile );
421             return new MavenXpp3Reader().read( reader );
422         }
423         catch ( FileNotFoundException e )
424         {
425             throw new MojoExecutionException( "File not found " + pomFile, e );
426         }
427         catch ( IOException e )
428         {
429             throw new MojoExecutionException( "Error reading POM " + pomFile, e );
430         }
431         catch ( XmlPullParserException e )
432         {
433             throw new MojoExecutionException( "Error parsing POM " + pomFile, e );
434         }
435         finally
436         {
437             IOUtil.close( reader );
438         }
439     }
440 
441     /**
442      * Populates missing mojo parameters from the specified POM.
443      * 
444      * @param model The POM to extract missing artifact coordinates from, must not be <code>null</code>.
445      */
446     private void processModel( Model model )
447     {
448         Parent parent = model.getParent();
449 
450         if ( this.groupId == null )
451         {
452             this.groupId = model.getGroupId();
453             if ( this.groupId == null && parent != null )
454             {
455                 this.groupId = parent.getGroupId();
456             }
457         }
458         if ( this.artifactId == null )
459         {
460             this.artifactId = model.getArtifactId();
461         }
462         if ( this.version == null )
463         {
464             this.version = model.getVersion();
465             if ( this.version == null && parent != null )
466             {
467                 this.version = parent.getVersion();
468             }
469         }
470         if ( this.packaging == null )
471         {
472             this.packaging = model.getPackaging();
473         }
474     }
475 
476     /**
477      * Validates the user-supplied artifact information.
478      * 
479      * @throws MojoExecutionException If any artifact coordinate is invalid.
480      */
481     private void validateArtifactInformation()
482         throws MojoExecutionException
483     {
484         Model model = generateModel();
485 
486         ModelValidationResult result = modelValidator.validate( model );
487 
488         if ( result.getMessageCount() > 0 )
489         {
490             throw new MojoExecutionException( "The artifact information is incomplete or not valid:\n"
491                 + result.render( "  " ) );
492         }
493     }
494 
495     /**
496      * Generates a minimal model from the user-supplied artifact information.
497      * 
498      * @return The generated model, never <code>null</code>.
499      */
500     private Model generateModel()
501     {
502         Model model = new Model();
503 
504         model.setModelVersion( "4.0.0" );
505 
506         model.setGroupId( groupId );
507         model.setArtifactId( artifactId );
508         model.setVersion( version );
509         model.setPackaging( packaging );
510 
511         model.setDescription( "POM was created from install:install-file" );
512 
513         return model;
514     }
515 
516     /**
517      * Generates a (temporary) POM file from the plugin configuration. It's the responsibility of the caller to delete
518      * the generated file when no longer needed.
519      * 
520      * @return The path to the generated POM file, never <code>null</code>.
521      * @throws MojoExecutionException If the POM file could not be generated.
522      */
523     private File generatePomFile()
524         throws MojoExecutionException
525     {
526         Model model = generateModel();
527 
528         Writer writer = null;
529         try
530         {
531             File pomFile = File.createTempFile( "mvninstall", ".pom" );
532 
533             writer = WriterFactory.newXmlWriter( pomFile );
534             new MavenXpp3Writer().write( writer, model );
535 
536             return pomFile;
537         }
538         catch ( IOException e )
539         {
540             throw new MojoExecutionException( "Error writing temporary POM file: " + e.getMessage(), e );
541         }
542         finally
543         {
544             IOUtil.close( writer );
545         }
546     }
547 
548     /**
549      * @return the localRepositoryPath
550      */
551     @SuppressWarnings( "UnusedDeclaration" )
552     public File getLocalRepositoryPath()
553     {
554         return this.localRepositoryPath;
555     }
556 
557     /**
558      * @param theLocalRepositoryPath the localRepositoryPath to set
559      */
560     public void setLocalRepositoryPath( File theLocalRepositoryPath )
561     {
562         this.localRepositoryPath = theLocalRepositoryPath;
563     }
564 
565 }