View Javadoc

1   package org.apache.maven.plugin.source;
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.archiver.MavenArchiveConfiguration;
23  import org.apache.maven.archiver.MavenArchiver;
24  import org.apache.maven.artifact.DependencyResolutionRequiredException;
25  import org.apache.maven.model.Resource;
26  import org.apache.maven.plugin.AbstractMojo;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.project.MavenProject;
29  import org.apache.maven.project.MavenProjectHelper;
30  import org.codehaus.plexus.archiver.Archiver;
31  import org.codehaus.plexus.archiver.ArchiverException;
32  import org.codehaus.plexus.archiver.jar.JarArchiver;
33  import org.codehaus.plexus.archiver.jar.ManifestException;
34  import org.codehaus.plexus.util.FileUtils;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Iterator;
41  import java.util.List;
42  
43  /**
44   * Base class for bundling sources into a jar archive.
45   * 
46   * @version $Id: AbstractSourceJarMojo.java 824199 2009-10-11 23:30:47Z aheritier $
47   * @since 2.0.3
48   */
49  public abstract class AbstractSourceJarMojo
50      extends AbstractMojo
51  {
52      private static final String[] DEFAULT_INCLUDES = new String[] { "**/*" };
53  
54      private static final String[] DEFAULT_EXCLUDES = new String[] {};
55  
56      /**
57       * List of files to include. Specified as fileset patterns which are relative to the input directory whose contents
58       * is being packaged into the JAR.
59       * 
60       * @parameter
61       * @since 2.1
62       */
63      private String[] includes;
64  
65      /**
66       * List of files to exclude. Specified as fileset patterns which are relative to the input directory whose contents
67       * is being packaged into the JAR.
68       * 
69       * @parameter
70       * @since 2.1
71       */
72      private String[] excludes;
73  
74      /**
75       * Exclude commonly excluded files such as SCM configuration. These are defined in the plexus
76       * FileUtils.getDefaultExcludes()
77       * 
78       * @parameter default-value="true"
79       * @since 2.1
80       */
81      private boolean useDefaultExcludes;
82  
83      /**
84       * The Maven Project Object
85       * 
86       * @parameter expression="${project}"
87       * @readonly
88       * @required
89       */
90      protected MavenProject project;
91  
92      /**
93       * The Jar archiver.
94       *
95       * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
96       */
97      private JarArchiver jarArchiver;
98  
99      /**
100      * The archive configuration to use. See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven
101      * Archiver Reference</a>.
102      * 
103      * @parameter
104      * @since 2.1
105      */
106     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
107 
108     /**
109      * Path to the default MANIFEST file to use. It will be used if <code>useDefaultManifestFile</code> is set to
110      * <code>true</code>.
111      *
112      * @parameter default-value="${project.build.outputDirectory}/META-INF/MANIFEST.MF"
113      * @required
114      * @readonly
115      * @since 2.1
116      */
117     private File defaultManifestFile;
118 
119     /**
120      * Set this to <code>true</code> to enable the use of the <code>defaultManifestFile</code>. <br/>
121      *
122      * @parameter default-value="false"
123      * @since 2.1
124      */
125     private boolean useDefaultManifestFile;
126 
127     /**
128      * Specifies whether or not to attach the artifact to the project
129      *
130      * @parameter expression="${attach}" default-value="true"
131      */
132     private boolean attach;
133 
134     /**
135      * Specifies whether or not to exclude resources from the sources-jar. This
136      * can be convenient if your project includes large resources, such as
137      * images, and you don't want to include them in the sources-jar.
138      *
139      * @parameter expression="${source.excludeResources}" default-value="false"
140      * @since 2.0.4
141      */
142     protected boolean excludeResources;
143 
144     /**
145      * Specifies whether or not to include the POM file in the sources-jar.
146      *
147      * @parameter expression="${source.includePom}" default-value="false"
148      * @since 2.1
149      */
150     protected boolean includePom;
151 
152     /**
153      * Used for attaching the source jar to the project.
154      *
155      * @component
156      */
157     private MavenProjectHelper projectHelper;
158 
159     /**
160      * The directory where the generated archive file will be put.
161      *
162      * @parameter default-value="${project.build.directory}"
163      */
164     protected File outputDirectory;
165 
166     /**
167      * The filename to be used for the generated archive file.
168      * For the source:jar goal, "-sources" is appended to this filename.
169      * For the source:test-jar goal, "-test-sources" is appended.
170      *
171      * @parameter default-value="${project.build.finalName}"
172      */
173     protected String finalName;
174 
175     /**
176      * Contains the full list of projects in the reactor.
177      *
178      * @parameter expression="${reactorProjects}"
179      * @readonly
180      */
181     protected List reactorProjects;
182 
183     /**
184      * Whether creating the archive should be forced.  If set to true, the jar will
185      * always be created.  If set to false, the jar will only be created when the
186      * sources are newer than the jar.
187      *
188      * @parameter expression="${source.forceCreation}" default-value="false"
189      * @since 2.1
190      */
191     private boolean forceCreation;
192 
193     // ----------------------------------------------------------------------
194     // Public methods
195     // ----------------------------------------------------------------------
196 
197     /** {@inheritDoc} */
198     public void execute()
199         throws MojoExecutionException
200     {
201         packageSources( project );
202     }
203 
204     // ----------------------------------------------------------------------
205     // Protected methods
206     // ----------------------------------------------------------------------
207 
208     /**
209      * @return the wanted classifier, ie <code>sources</code> or <code>test-sources</code>
210      */
211     protected abstract String getClassifier();
212 
213     /**
214      * @param p not null
215      * @return the compile or test sources
216      */
217     protected abstract List getSources( MavenProject p )
218         throws MojoExecutionException;
219 
220     /**
221      * @param p not null
222      * @return the compile or test resources
223      */
224     protected abstract List getResources( MavenProject p )
225         throws MojoExecutionException;
226 
227     protected void packageSources( MavenProject p )
228         throws MojoExecutionException
229     {
230         if ( !"pom".equals( p.getPackaging() ) )
231         {
232             packageSources( Arrays.asList( new Object[] { p } ) );
233         }
234     }
235 
236     protected void packageSources( List projects )
237         throws MojoExecutionException
238     {
239         if ( project.getArtifact().getClassifier() != null )
240         {
241             getLog().warn( "NOT adding sources to artifacts with classifier as Maven only supports one classifier "
242                 + "per artifact. Current artifact [" + project.getArtifact().getId() + "] has a ["
243                 + project.getArtifact().getClassifier() + "] classifier." );
244 
245             return;
246         }
247 
248         MavenArchiver archiver = createArchiver();
249 
250         for ( Iterator i = projects.iterator(); i.hasNext(); )
251         {
252             MavenProject subProject = getProject( (MavenProject) i.next() );
253 
254             if ( "pom".equals( subProject.getPackaging() ) )
255             {
256                 continue;
257             }
258 
259             archiveProjectContent( subProject, archiver.getArchiver() );
260         }
261 
262         if(!archiver.getArchiver().getFiles().isEmpty()){
263         
264             if ( useDefaultManifestFile && defaultManifestFile.exists() && archive.getManifestFile() == null )
265             {
266                 getLog().info( "Adding existing MANIFEST to archive. Found under: " + defaultManifestFile.getPath() );
267                 archive.setManifestFile( defaultManifestFile );
268             }
269     
270             File outputFile = new File( outputDirectory, finalName + "-" + getClassifier() + getExtension() );
271             
272             try
273             {
274                 archiver.setOutputFile( outputFile );
275     
276                 archive.setAddMavenDescriptor( false );
277                 archive.setForced( forceCreation );
278     
279                 archiver.createArchive( project, archive );
280             }
281             catch ( IOException e )
282             {
283                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
284             }
285             catch ( ArchiverException e )
286             {
287                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
288             }
289             catch ( DependencyResolutionRequiredException e )
290             {
291                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
292             }
293             catch ( ManifestException e )
294             {
295                 throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
296             }
297     
298             if ( attach )
299             {
300                 projectHelper.attachArtifact( project, getType(), getClassifier(), outputFile );
301             }
302             else
303             {
304                 getLog().info( "NOT adding java-sources to attached artifacts list." );
305             }
306         }
307         else
308         {
309             getLog().info( "No sources in project. Archive not created." );
310         }
311     }
312 
313     protected void archiveProjectContent( MavenProject p, Archiver archiver )
314         throws MojoExecutionException
315     {
316         if ( includePom )
317         {
318             try
319             {
320                 archiver.addFile( p.getFile(), p.getFile().getName() );
321             }
322             catch ( ArchiverException e )
323             {
324                 throw new MojoExecutionException( "Error adding POM file to target jar file.", e );
325             }
326         }
327 
328         for ( Iterator i = getSources( p ).iterator(); i.hasNext(); )
329         {
330             String s = (String) i.next();
331 
332             File sourceDirectory = new File( s );
333 
334             if ( sourceDirectory.exists() )
335             {
336                 addDirectory( archiver, sourceDirectory, getCombinedIncludes( null ), getCombinedExcludes( null ) );
337             }
338         }
339 
340         //MAPI: this should be taken from the resources plugin
341         for ( Iterator i = getResources( p ).iterator(); i.hasNext(); )
342         {
343             Resource resource = (Resource) i.next();
344 
345             File sourceDirectory = new File( resource.getDirectory() );
346 
347             if ( !sourceDirectory.exists() )
348             {
349                 continue;
350             }
351 
352             List resourceIncludes = resource.getIncludes();
353 
354             String[] combinedIncludes = getCombinedIncludes( resourceIncludes );
355 
356             List resourceExcludes = resource.getExcludes();
357 
358             String[] combinedExcludes = getCombinedExcludes( resourceExcludes );
359 
360             String targetPath = resource.getTargetPath();
361             if ( targetPath != null )
362             {
363                 if ( !targetPath.trim().endsWith( "/" ) )
364                 {
365                     targetPath += "/";
366                 }
367                 addDirectory( archiver, sourceDirectory, targetPath, combinedIncludes, combinedExcludes );
368             }
369             else
370             {
371                 addDirectory( archiver, sourceDirectory, combinedIncludes, combinedExcludes );
372             }
373         }
374     }
375 
376     protected MavenArchiver createArchiver()
377         throws MojoExecutionException
378     {
379         MavenArchiver archiver = new MavenArchiver();
380         archiver.setArchiver( jarArchiver );
381 
382         if ( project.getBuild() != null )
383         {
384             List resources = project.getBuild().getResources();
385 
386             for ( Iterator i = resources.iterator(); i.hasNext(); )
387             {
388                 Resource r = (Resource) i.next();
389 
390                 if ( r.getDirectory().endsWith( "maven-shared-archive-resources" ) )
391                 {
392                     addDirectory( archiver.getArchiver(), new File( r.getDirectory() ), getCombinedIncludes( null ),
393                                   getCombinedExcludes( null ) );
394                 }
395             }
396         }
397 
398         return archiver;
399     }
400 
401     protected void addDirectory( Archiver archiver, File sourceDirectory, String[] includes, String[] excludes )
402         throws MojoExecutionException
403     {
404         try
405         {
406             archiver.addDirectory( sourceDirectory, includes, excludes );
407         }
408         catch ( ArchiverException e )
409         {
410             throw new MojoExecutionException( "Error adding directory to source archive.", e );
411         }
412     }
413 
414     protected void addDirectory( Archiver archiver, File sourceDirectory, String prefix, String[] includes,
415                                  String[] excludes )
416         throws MojoExecutionException
417     {
418         try
419         {
420             archiver.addDirectory( sourceDirectory, prefix, includes, excludes );
421         }
422         catch ( ArchiverException e )
423         {
424             throw new MojoExecutionException( "Error adding directory to source archive.", e );
425         }
426     }
427 
428     protected String getExtension()
429     {
430         return ".jar";
431     }
432 
433     protected MavenProject getProject( MavenProject p )
434     {
435         if ( p.getExecutionProject() != null )
436         {
437             return p.getExecutionProject();
438         }
439 
440         return p;
441     }
442 
443     protected String getType()
444     {
445         return "java-source";
446     }
447 
448     /**
449      * Combines the includes parameter and additional includes. Defaults to {@link #DEFAULT_INCLUDES} If the
450      * additionalIncludes parameter is null, it is not added to the combined includes.
451      * 
452      * @param additionalIncludes The includes specified in the pom resources section
453      * @return The combined array of includes.
454      */
455     private String[] getCombinedIncludes( List additionalIncludes )
456     {
457         ArrayList combinedIncludes = new ArrayList();
458 
459         if ( includes != null && includes.length > 0 )
460         {
461             combinedIncludes.addAll( Arrays.asList( includes ) );
462         }
463 
464         if ( additionalIncludes != null && additionalIncludes.size() > 0 )
465         {
466             combinedIncludes.addAll( additionalIncludes );
467         }
468 
469         // If there are no other includes, use the default.
470         if ( combinedIncludes.size() == 0 )
471         {
472             combinedIncludes.addAll( Arrays.asList( DEFAULT_INCLUDES ) );
473         }
474 
475         return (String[]) combinedIncludes.toArray( new String[combinedIncludes.size()] );
476     }
477 
478     /**
479      * Combines the user parameter {@link #excludes}, the default excludes from plexus FileUtils,
480      * and the contents of the parameter addionalExcludes.
481      * 
482      * @param additionalExcludes Additional excludes to add to the array
483      * @return The combined list of excludes.
484      */
485 
486     private String[] getCombinedExcludes( List additionalExcludes )
487     {
488         ArrayList combinedExcludes = new ArrayList();
489 
490         if ( useDefaultExcludes )
491         {
492             combinedExcludes.addAll( FileUtils.getDefaultExcludesAsList() );
493         }
494 
495         if ( excludes != null && excludes.length > 0 )
496         {
497             combinedExcludes.addAll( Arrays.asList( excludes ) );
498         }
499 
500         if ( additionalExcludes != null && additionalExcludes.size() > 0 )
501         {
502             combinedExcludes.addAll( additionalExcludes );
503         }
504 
505         if ( combinedExcludes.size() == 0 )
506         {
507             combinedExcludes.addAll( Arrays.asList( DEFAULT_EXCLUDES ) );
508         }
509 
510         return (String[]) combinedExcludes.toArray( new String[combinedExcludes.size()] );
511     }
512 }