View Javadoc
1   package org.apache.maven.plugins.repository;
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.factory.ArtifactFactory;
24  import org.apache.maven.artifact.repository.ArtifactRepository;
25  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
26  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
27  import org.apache.maven.artifact.resolver.ArtifactResolver;
28  import org.apache.maven.model.License;
29  import org.apache.maven.model.Model;
30  import org.apache.maven.model.Scm;
31  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
32  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
33  import org.apache.maven.plugin.AbstractMojo;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugins.annotations.Component;
36  import org.apache.maven.plugins.annotations.Mojo;
37  import org.apache.maven.plugins.annotations.Parameter;
38  import org.apache.maven.settings.Settings;
39  import org.codehaus.plexus.archiver.Archiver;
40  import org.codehaus.plexus.archiver.ArchiverException;
41  import org.codehaus.plexus.archiver.jar.JarArchiver;
42  import org.codehaus.plexus.components.interactivity.InputHandler;
43  import org.codehaus.plexus.util.ReaderFactory;
44  import org.codehaus.plexus.util.WriterFactory;
45  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
46  
47  import java.io.File;
48  import java.io.FileNotFoundException;
49  import java.io.IOException;
50  import java.util.Collections;
51  import java.util.List;
52  
53  /**
54   * Packs artifacts already available in a local repository in a bundle for an
55   * upload requests. It requires that the artifact has a POM in the local
56   * repository. It will check for mandatory elements, asking interactively for
57   * missing values. Can be used to generate bundles for third parties artifacts
58   * that have been manually added to the local repository.
59   *
60   * @since 2.1
61   */
62  @Mojo( name = "bundle-pack", requiresProject = false )
63  public class BundlePackMojo
64      extends AbstractMojo
65  {
66      public static final String POM = "pom.xml";
67  
68      /**
69       * Jar archiver.
70       */
71      @Component( role = Archiver.class, hint = "jar" )
72      protected JarArchiver jarArchiver;
73  
74      /**
75       * Artifact resolver.
76       */
77      @Component
78      protected ArtifactResolver artifactResolver;
79  
80      /**
81       * Artifact factory.
82       */
83      @Component
84      protected ArtifactFactory artifactFactory;
85  
86      /**
87       * Local maven repository.
88       */
89      @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
90      protected ArtifactRepository localRepository;
91  
92      /**
93       */
94      @Component
95      protected InputHandler inputHandler;
96  
97      /**
98       * Directory where the upload-bundle will be created.
99       */
100     @Parameter( defaultValue = "${basedir}", readonly = true )
101     protected String basedir;
102 
103     /**
104      * GroupId for the artifact to create an upload bundle for.
105      */
106     @Parameter( property = "groupId" )
107     protected String groupId;
108 
109     /**
110      * ArtifactId for the artifact to create an upload bundle for.
111      */
112     @Parameter( property = "artifactId" )
113     protected String artifactId;
114 
115     /**
116      * Version for the artifact to create an upload bundle for.
117      */
118     @Parameter( property = "version" )
119     protected String version;
120 
121     /**
122      * Viewable URL for SCM connections, in cases where this isn't provided by the POM.
123      */
124     @Parameter( property = "scmUrl" )
125     protected String scmUrl;
126 
127     /**
128      * Read-only URL for SCM tool connections, in cases where this isn't provided by the POM.
129      * <br/>
130      * <b>NOTE:</b> This should be a standard maven-scm URL. See the
131      * <a href="http://maven.apache.org/scm/scm-url-format.html">format guidelines</a> for more
132      * information.
133      */
134     @Parameter( property = "scmConnection" )
135     protected String scmConnection;
136 
137     /**
138      */
139     @Parameter( defaultValue = "${settings}", readonly = true, required = true )
140     protected Settings settings;
141 
142     /**
143      * Disable validations to make sure bundle supports project materialization.
144      * <br/>
145      * <b>WARNING: This means your project will be MUCH harder to use.</b>
146      */
147     @Parameter( property = "bundle.disableMaterialization", defaultValue = "false" )
148     private boolean disableMaterialization;
149 
150     @SuppressWarnings( "unchecked" )
151     public void execute()
152         throws MojoExecutionException
153     {
154         readArtifactDataFromUser();
155 
156         Artifact artifact = artifactFactory.createProjectArtifact( groupId, artifactId, version );
157 
158         try
159         {
160             artifactResolver.resolve( artifact, Collections.EMPTY_LIST, localRepository );
161         }
162         catch ( ArtifactResolutionException e )
163         {
164             throw new MojoExecutionException( "Unable to resolve artifact " + artifact.getId(), e );
165         }
166         catch ( ArtifactNotFoundException e )
167         {
168             throw new MojoExecutionException( "Artifact " + artifact.getId() + " not found in local repository", e );
169         }
170 
171         File pom = artifact.getFile();
172 
173         File dir = pom.getParentFile();
174 
175         Model model = readPom( pom );
176 
177         boolean rewrite = false;
178         try
179         {
180 
181             if ( model.getPackaging() == null )
182             {
183                 model.setPackaging( "jar" );
184                 rewrite = true;
185             }
186             if ( model.getName() == null )
187             {
188                 getLog().info( "Project name is missing, please type the project name [" + artifactId + "]:" );
189                 model.setName( inputHandler.readLine() );
190                 if ( model.getName() == null )
191                 {
192                     model.setName( artifactId );
193                 }
194                 rewrite = true;
195             }
196             if ( model.getDescription() == null )
197             {
198                 getLog().info( "Project description is missing, please type the project description:" );
199                 model.setDescription( inputHandler.readLine() );
200                 rewrite = true;
201             }
202             if ( model.getUrl() == null )
203             {
204                 getLog().info( "Project URL is missing, please type the project URL:" );
205                 model.setUrl( inputHandler.readLine() );
206                 rewrite = true;
207             }
208 
209             List<License> licenses = model.getLicenses();
210             if ( licenses.isEmpty() )
211             {
212                 License license = new License();
213 
214                 getLog().info( "License name is missing, please type the license name:" );
215                 license.setName( inputHandler.readLine() );
216                 getLog().info( "License URL is missing, please type the license URL:" );
217                 license.setUrl( inputHandler.readLine() );
218                 licenses.add( license );
219                 rewrite = true;
220             }
221             
222             if ( disableMaterialization )
223             {
224                 getLog().warn( "Validations to confirm support for project materialization have been DISABLED."
225                        + "\n\nYour project may not provide the POM elements necessary to allow users to retrieve "
226                        + "sources on-demand,"
227                        + "\nor to easily checkout your project in an IDE. THIS CAN SERIOUSLY INCONVENIENCE YOUR USERS."
228                        + "\n\nContinue? [y/N]" );
229                 
230                 try
231                 {
232                     if ( 'y' != inputHandler.readLine().toLowerCase().charAt( 0 ) )
233                     {
234                         disableMaterialization = false;
235                     }
236                 }
237                 catch ( IOException e )
238                 {
239                     getLog().debug( "Error reading confirmation: " + e.getMessage(), e );
240                 }
241                 
242             }
243             
244             if ( !disableMaterialization )
245             {
246                 Scm scm = model.getScm();
247                 if ( scm == null )
248                 {
249                     scm = new Scm();
250                     model.setScm( scm );
251                 }
252                 
253                 if ( scm.getUrl() == null )
254                 {
255                     if ( scmUrl != null )
256                     {
257                         scm.setUrl( scmUrl );
258                     }
259                     else
260                     {
261                         getLog().info( "SCM view URL is missing, please type the URL for the viewable SCM interface:" );
262                         scm.setUrl( inputHandler.readLine() );
263                         rewrite = true;
264                     }
265                 }
266                 
267                 if ( scm.getConnection() == null )
268                 {
269                     if ( scmConnection != null )
270                     {
271                         scm.setConnection( scmConnection );
272                     }
273                     else
274                     {
275                         getLog().info( "SCM read-only connection URL is missing, please type the read-only SCM URL:" );
276                         scm.setConnection( inputHandler.readLine() );
277                         rewrite = true;
278                     }
279                 }
280             }
281         }
282         catch ( IOException e )
283         {
284             throw new MojoExecutionException( e.getMessage(), e );
285         }
286 
287         try
288         {
289             if ( rewrite )
290             {
291                 new MavenXpp3Writer().write( WriterFactory.newXmlWriter( pom ), model );
292             }
293 
294             String finalName = null;
295 
296             if ( model.getBuild() != null )
297             {
298                 finalName = model.getBuild().getFinalName();
299             }
300             if ( finalName == null )
301             {
302                 finalName = model.getArtifactId() + "-" + model.getVersion();
303             }
304             
305             boolean batchMode = settings == null ? false : !settings.isInteractiveMode();
306             List<File> files = BundleUtils.selectProjectFiles( dir, inputHandler, finalName, pom, getLog(), batchMode );
307 
308             File bundle = new File( basedir, finalName + "-bundle.jar" );
309 
310             jarArchiver.addFile( pom, POM );
311 
312             boolean artifactChecks = !"pom".equals( model.getPackaging() );
313             boolean sourcesFound = false;
314             boolean javadocsFound = false;
315             
316             for ( File f : files )
317             {
318                 if ( artifactChecks && f.getName().endsWith( finalName + "-sources.jar" ) )
319                 {
320                     sourcesFound = true;
321                 }
322                 else if ( artifactChecks && f.getName().equals( finalName + "-javadoc.jar" ) )
323                 {
324                     javadocsFound = true;
325                 }
326                 
327                 jarArchiver.addFile( f, f.getName() );
328             }
329             
330             if ( artifactChecks && !sourcesFound )
331             {
332                 getLog().warn( "Sources not included in upload bundle." );
333             }
334 
335             if ( artifactChecks && !javadocsFound )
336             {
337                 getLog().warn( "Javadoc not included in upload bundle." );
338             }
339 
340             jarArchiver.setDestFile( bundle );
341 
342             jarArchiver.createArchive();
343 
344         }
345         catch ( IOException e )
346         {
347             throw new MojoExecutionException( e.getMessage(), e );
348         }
349         catch ( ArchiverException e )
350         {
351             throw new MojoExecutionException( e.getMessage(), e );
352         }
353 
354     }
355 
356     /**
357      * Read groupId, artifactId and version from the user on the command line,
358      * if they were not provided as parameters.
359      *
360      * @throws MojoExecutionException If the values can't be read
361      */
362     private void readArtifactDataFromUser()
363         throws MojoExecutionException
364     {
365         try
366         {
367             if ( groupId == null )
368             {
369                 getLog().info( "groupId? " );
370 
371                 groupId = inputHandler.readLine();
372 
373             }
374 
375             if ( artifactId == null )
376             {
377                 getLog().info( "artifactId? " );
378                 artifactId = inputHandler.readLine();
379             }
380 
381             if ( version == null )
382             {
383                 getLog().info( "version? " );
384                 version = inputHandler.readLine();
385             }
386         }
387         catch ( IOException e )
388         {
389             throw new MojoExecutionException( e.getMessage(), e );
390         }
391     }
392 
393     /**
394      * Read the POM file.
395      *
396      * @param pom The file to read
397      * @return A Maven Model
398      * @throws MojoExecutionException if something goes wrong when reading the file
399      */
400     private Model readPom( File pom )
401         throws MojoExecutionException
402     {
403         Model model;
404         try
405         {
406             model = new MavenXpp3Reader().read( ReaderFactory.newXmlReader( pom ) );
407         }
408         catch ( XmlPullParserException e )
409         {
410             throw new MojoExecutionException( "Unable to parse POM at " + pom.getAbsolutePath() + ": " + e.getMessage(),
411                                               e );
412         }
413         catch ( FileNotFoundException e )
414         {
415             throw new MojoExecutionException( "Unable to read POM at " + pom.getAbsolutePath() + ": " + e.getMessage(),
416                                               e );
417         }
418         catch ( IOException e )
419         {
420             throw new MojoExecutionException( "Unable to read POM at " + pom.getAbsolutePath() + ": " + e.getMessage(),
421                                               e );
422         }
423         return model;
424     }
425 
426 }