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 763422 2009-04-08 21:59:54Z pgier $
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 ( useDefaultManifestFile && defaultManifestFile.exists() && archive.getManifestFile() == null )
263         {
264             getLog().info( "Adding existing MANIFEST to archive. Found under: " + defaultManifestFile.getPath() );
265             archive.setManifestFile( defaultManifestFile );
266         }
267 
268         File outputFile = new File( outputDirectory, finalName + "-" + getClassifier() + getExtension() );
269         
270         try
271         {
272             archiver.setOutputFile( outputFile );
273 
274             archive.setAddMavenDescriptor( false );
275             archive.setForced( forceCreation );
276 
277             archiver.createArchive( project, archive );
278         }
279         catch ( IOException e )
280         {
281             throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
282         }
283         catch ( ArchiverException e )
284         {
285             throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
286         }
287         catch ( DependencyResolutionRequiredException e )
288         {
289             throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
290         }
291         catch ( ManifestException e )
292         {
293             throw new MojoExecutionException( "Error creating source archive: " + e.getMessage(), e );
294         }
295 
296         if ( attach )
297         {
298             projectHelper.attachArtifact( project, getType(), getClassifier(), outputFile );
299         }
300         else
301         {
302             getLog().info( "NOT adding java-sources to attached artifacts list." );
303         }
304     }
305 
306     protected void archiveProjectContent( MavenProject p, Archiver archiver )
307         throws MojoExecutionException
308     {
309         if ( includePom )
310         {
311             try
312             {
313                 archiver.addFile( p.getFile(), p.getFile().getName() );
314             }
315             catch ( ArchiverException e )
316             {
317                 throw new MojoExecutionException( "Error adding POM file to target jar file.", e );
318             }
319         }
320 
321         for ( Iterator i = getSources( p ).iterator(); i.hasNext(); )
322         {
323             String s = (String) i.next();
324 
325             File sourceDirectory = new File( s );
326 
327             if ( sourceDirectory.exists() )
328             {
329                 addDirectory( archiver, sourceDirectory, getCombinedIncludes( null ), getCombinedExcludes( null ) );
330             }
331         }
332 
333         //MAPI: this should be taken from the resources plugin
334         for ( Iterator i = getResources( p ).iterator(); i.hasNext(); )
335         {
336             Resource resource = (Resource) i.next();
337 
338             File sourceDirectory = new File( resource.getDirectory() );
339 
340             if ( !sourceDirectory.exists() )
341             {
342                 continue;
343             }
344 
345             List resourceIncludes = resource.getIncludes();
346 
347             String[] combinedIncludes = getCombinedIncludes( resourceIncludes );
348 
349             List resourceExcludes = resource.getExcludes();
350 
351             String[] combinedExcludes = getCombinedExcludes( resourceExcludes );
352 
353             String targetPath = resource.getTargetPath();
354             if ( targetPath != null )
355             {
356                 if ( !targetPath.trim().endsWith( "/" ) )
357                 {
358                     targetPath += "/";
359                 }
360                 addDirectory( archiver, sourceDirectory, targetPath, combinedIncludes, combinedExcludes );
361             }
362             else
363             {
364                 addDirectory( archiver, sourceDirectory, combinedIncludes, combinedExcludes );
365             }
366         }
367     }
368 
369     protected MavenArchiver createArchiver()
370         throws MojoExecutionException
371     {
372         MavenArchiver archiver = new MavenArchiver();
373         archiver.setArchiver( jarArchiver );
374 
375         if ( project.getBuild() != null )
376         {
377             List resources = project.getBuild().getResources();
378 
379             for ( Iterator i = resources.iterator(); i.hasNext(); )
380             {
381                 Resource r = (Resource) i.next();
382 
383                 if ( r.getDirectory().endsWith( "maven-shared-archive-resources" ) )
384                 {
385                     addDirectory( archiver.getArchiver(), new File( r.getDirectory() ), getCombinedIncludes( null ),
386                                   getCombinedExcludes( null ) );
387                 }
388             }
389         }
390 
391         return archiver;
392     }
393 
394     protected void addDirectory( Archiver archiver, File sourceDirectory, String[] includes, String[] excludes )
395         throws MojoExecutionException
396     {
397         try
398         {
399             archiver.addDirectory( sourceDirectory, includes, excludes );
400         }
401         catch ( ArchiverException e )
402         {
403             throw new MojoExecutionException( "Error adding directory to source archive.", e );
404         }
405     }
406 
407     protected void addDirectory( Archiver archiver, File sourceDirectory, String prefix, String[] includes,
408                                  String[] excludes )
409         throws MojoExecutionException
410     {
411         try
412         {
413             archiver.addDirectory( sourceDirectory, prefix, includes, excludes );
414         }
415         catch ( ArchiverException e )
416         {
417             throw new MojoExecutionException( "Error adding directory to source archive.", e );
418         }
419     }
420 
421     protected String getExtension()
422     {
423         return ".jar";
424     }
425 
426     protected MavenProject getProject( MavenProject p )
427     {
428         if ( p.getExecutionProject() != null )
429         {
430             return p.getExecutionProject();
431         }
432 
433         return p;
434     }
435 
436     protected String getType()
437     {
438         return "java-source";
439     }
440 
441     /**
442      * Combines the includes parameter and additional includes. Defaults to {@link #DEFAULT_INCLUDES} If the
443      * additionalIncludes parameter is null, it is not added to the combined includes.
444      * 
445      * @param additionalIncludes The includes specified in the pom resources section
446      * @return The combined array of includes.
447      */
448     private String[] getCombinedIncludes( List additionalIncludes )
449     {
450         ArrayList combinedIncludes = new ArrayList();
451 
452         if ( includes != null && includes.length > 0 )
453         {
454             combinedIncludes.addAll( Arrays.asList( includes ) );
455         }
456 
457         if ( additionalIncludes != null && additionalIncludes.size() > 0 )
458         {
459             combinedIncludes.addAll( additionalIncludes );
460         }
461 
462         // If there are no other includes, use the default.
463         if ( combinedIncludes.size() == 0 )
464         {
465             combinedIncludes.addAll( Arrays.asList( DEFAULT_INCLUDES ) );
466         }
467 
468         return (String[]) combinedIncludes.toArray( new String[combinedIncludes.size()] );
469     }
470 
471     /**
472      * Combines the user parameter {@link #excludes}, the default excludes from plexus FileUtils,
473      * and the contents of the parameter addionalExcludes.
474      * 
475      * @param additionalExcludes Additional excludes to add to the array
476      * @return The combined list of excludes.
477      */
478 
479     private String[] getCombinedExcludes( List additionalExcludes )
480     {
481         ArrayList combinedExcludes = new ArrayList();
482 
483         if ( useDefaultExcludes )
484         {
485             combinedExcludes.addAll( FileUtils.getDefaultExcludesAsList() );
486         }
487 
488         if ( excludes != null && excludes.length > 0 )
489         {
490             combinedExcludes.addAll( Arrays.asList( excludes ) );
491         }
492 
493         if ( additionalExcludes != null && additionalExcludes.size() > 0 )
494         {
495             combinedExcludes.addAll( additionalExcludes );
496         }
497 
498         if ( combinedExcludes.size() == 0 )
499         {
500             combinedExcludes.addAll( Arrays.asList( DEFAULT_EXCLUDES ) );
501         }
502 
503         return (String[]) combinedExcludes.toArray( new String[combinedExcludes.size()] );
504     }
505 }