View Javadoc

1   package org.codehaus.mojo.jboss.packaging;
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.Artifact;
25  import org.apache.maven.artifact.handler.ArtifactHandler;
26  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
27  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
28  import org.apache.maven.plugin.AbstractMojo;
29  import org.apache.maven.plugin.MojoExecutionException;
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.project.MavenProjectHelper;
32  import org.codehaus.plexus.archiver.jar.JarArchiver;
33  import org.codehaus.plexus.util.FileUtils;
34  import org.codehaus.plexus.util.StringUtils;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.util.ArrayList;
39  import java.util.Collections;
40  import java.util.HashSet;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.Set;
44  
45  /**
46   * Abstract super class for all the packaging mojos. This class contains the logic for actually building the packaging
47   * types.
48   */
49  public abstract class AbstractPackagingMojo
50      extends AbstractMojo
51  {
52  
53      /**
54       * The maven project.
55       * 
56       * @parameter default-value="${project}"
57       * @readonly
58       */
59      private MavenProject project;
60  
61      /**
62       * The directory for the generated packaging.
63       * 
64       * @parameter default-value="${project.build.directory}"
65       */
66      private File outputDirectory;
67  
68      /**
69       * The directory containing generated classes.
70       * 
71       * @parameter default-value="${project.build.outputDirectory}"
72       * @readonly
73       */
74      private File classesDirectory;
75  
76      /**
77       * The directory where the JBoss packaging is built.
78       * 
79       * @parameter default-value="${project.build.directory}/${project.build.finalName}"
80       */
81      private File packagingDirectory;
82  
83      /**
84       * The filename for the output deployment descriptor. By default the deployment descriptor will retain the same
85       * filename.
86       * 
87       * @parameter
88       */
89      private String deploymentDescriptorDestName;
90  
91      /**
92       * The destination of the deployment descriptor file.
93       * 
94       * @parameter default-value="${project.build.directory}/${project.build.finalName}/META-INF"
95       */
96      private File deploymentDescriptorDest;
97  
98      /**
99       * The directory where to put the libs.
100      * 
101      * @parameter default-value="${project.build.directory}/${project.build.finalName}/lib"
102      */
103     private File libDirectory;
104 
105     /**
106      * The name of the generated packaging archive.
107      * 
108      * @parameter default-value="${project.build.finalName}"
109      */
110     private String archiveName;
111 
112     /**
113      * All artifacts are excluded.
114      * 
115      * @parameter expression="${excludeAll}" default-value="false"
116      */
117     private boolean excludeAll;
118 
119     /**
120      * Dependency Artifacts excluded from packaging within the generated archive file. Use artifactId:groupId in nested
121      * exclude tags.
122      * 
123      * @parameter
124      */
125     private Set excludes;
126 
127     /**
128      * The Jar archiver.
129      * 
130      * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
131      */
132     private JarArchiver jarArchiver;
133 
134     /**
135      * The maven archive configuration to use.
136      * 
137      * @parameter
138      */
139     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
140 
141     /**
142      * The manifest file for the archive.
143      * 
144      * @parameter
145      */
146     private File manifest;
147 
148     /**
149      * Classifier to add to the generated artifact. If given, the artifact will not be the primary project artifact.
150      * 
151      * @parameter
152      */
153     private String classifier;
154 
155     /**
156      * Whether this is the main artifact of the current project.
157      * 
158      * @parameter default-value="true"
159      */
160     private boolean primaryArtifact;
161 
162     /**
163      * The project helper.
164      * 
165      * @component
166      */
167     private MavenProjectHelper projectHelper;
168 
169     /**
170      * The artifact handler manager.
171      * 
172      * @component
173      */
174     private ArtifactHandlerManager artifactHandlerManager;
175 
176     /**
177      * Whether to remove the version numbers from the filenames of the included dependencies. By default the included
178      * dependencies will have the format [artifactId]-[version]-[classifier].[type] If this parameter is set to true,
179      * the jar name will be in the format [artifactId]-[classifier].[type]
180      * 
181      * @parameter default-value="false"
182      */
183     private boolean removeDependencyVersions;
184 
185     /**
186      * Whether to generate only the exploded archive format. By default both an exploded directory and a zipped file
187      * will be created. If set to "true" only the exploded directory will be created.
188      * 
189      * @parameter default-value="false" expression="${explodedOnly}"
190      * @since 2.0
191      */
192     private boolean explodedOnly;
193 
194     /**
195      * @return Whether only the exploded format should be created
196      */
197     public boolean isExplodedOnly()
198     {
199         return explodedOnly;
200     }
201 
202     /**
203      * @return the maven project
204      */
205     public MavenProject getProject()
206     {
207         return project;
208     }
209 
210     /**
211      * @return the packaging directory
212      */
213     public File getPackagingDirectory()
214     {
215         return packagingDirectory;
216     }
217 
218     /**
219      * @return the packaging directory
220      */
221     public File getClassesDirectory()
222     {
223         return classesDirectory;
224     }
225 
226     /**
227      * @return the lib directory
228      */
229     public File getLibDirectory()
230     {
231         return libDirectory;
232     }
233 
234     /**
235      * Get the deployment descriptor file. Subclasses may override this method to provide a different name for their
236      * type of archive packaging.
237      * 
238      * @return deployment descriptor File
239      */
240     public abstract File getDeploymentDescriptor();
241 
242     /**
243      * Get the type of the artifact.
244      * 
245      * @return The type of the generated artifact
246      */
247     public abstract String getArtifactType();
248 
249     /**
250      * @return The directory where to write the archive
251      */
252     public File getOutputDirectory()
253     {
254         return outputDirectory;
255     }
256 
257     /**
258      * Build the package in an exploded format.
259      * 
260      * @throws MojoExecutionException if an error occurred
261      * @throws MojoFailureException if an error occurred
262      */
263     public void buildExplodedPackaging()
264         throws MojoExecutionException
265     {
266         buildExplodedPackaging( Collections.EMPTY_SET );
267     }
268 
269     /**
270      * Build the package in an exploded format.
271      * 
272      * @param excludes File patterns to exclude from the packaging.
273      * @throws MojoExecutionException if an error occurred
274      * @throws MojoFailureException if an error occurred
275      */
276     public void buildExplodedPackaging( Set excludes )
277         throws MojoExecutionException
278     {
279         getLog().info( "Assembling JBoss packaging " + project.getArtifactId() + " in " + packagingDirectory );
280 
281         if ( excludes == null )
282         {
283             excludes = Collections.EMPTY_SET;
284         }
285 
286         packagingDirectory.mkdirs();
287         libDirectory.mkdirs();
288 
289         try
290         {
291             packageResources();
292         }
293         catch ( Exception e1 )
294         {
295             throw new MojoExecutionException( "Failed while packaging resources", e1 );
296         }
297 
298         if ( classesDirectory.exists() && !classesDirectory.equals( packagingDirectory ) )
299         {
300             try
301             {
302                 packageClasses();
303             }
304             catch ( Exception e )
305             {
306                 throw new MojoExecutionException( "Unable to copy classes directory", e );
307             }
308         }
309 
310         File deploymentDescriptorFile = this.getDeploymentDescriptor();
311         if ( deploymentDescriptorFile == null || !deploymentDescriptorFile.exists() )
312         {
313             throw new MojoExecutionException( "Could not find descriptor file: " + deploymentDescriptorFile );
314         }
315 
316         String destName = this.getDeploymentDescriptorDestName();
317         if ( destName == null )
318         {
319             destName = deploymentDescriptorFile.getName();
320         }
321         File deploymentDescriptorTarget = new File( getDeploymentDescriptorDest(), destName );
322 
323         if ( !deploymentDescriptorTarget.exists() )
324         {
325             deploymentDescriptorTarget.getParentFile().mkdirs();
326 
327             try
328             {
329                 FileUtils.copyFile( deploymentDescriptorFile, deploymentDescriptorTarget );
330             }
331             catch ( IOException e )
332             {
333                 throw new MojoExecutionException( "Could not copy deployment descriptor", e );
334             }
335         }
336 
337         Set artifacts = project.getArtifacts();
338         List rejects = new ArrayList();
339         final Set includedArtifacts = new HashSet();
340         final ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME );
341         getLog().debug( "" );
342         getLog().debug( "    Including artifacts: " );
343         getLog().debug( "    -------------------" );
344         for ( Iterator iter = artifacts.iterator(); iter.hasNext(); )
345         {
346             Artifact artifact = (Artifact) iter.next();
347             if ( !artifact.isOptional() && filter.include( artifact ) )
348             {
349                 String descriptor = artifact.getGroupId() + ":" + artifact.getArtifactId();
350 
351                 if ( !excludeAll && artifact.getArtifactHandler().isAddedToClasspath()
352                     && !excludes.contains( descriptor ) )
353                 {
354                     getLog().debug( "        o " + descriptor );
355 
356                     String name = getArtifactName( artifact );
357                     if ( !includedArtifacts.add( name ) )
358                     {
359                         name = artifact.getGroupId() + "-" + name;
360                         getLog().info( "Duplicate artifact discovered, using full name: " + name );
361                     }
362 
363                     try
364                     {
365                         packageLib( artifact, name );
366                     }
367                     catch ( Exception e )
368                     {
369                         throw new MojoExecutionException( "Could not copy dependency", e );
370                     }
371                 }
372                 else
373                 {
374                     rejects.add( artifact );
375                 }
376             }
377         }
378 
379         if ( !excludes.isEmpty() )
380         {
381             getLog().debug( "" );
382             getLog().debug( "    Excluded artifacts: " );
383             getLog().debug( "    ------------------" );
384             for ( int ii = 0; ii < rejects.size(); ii++ )
385             {
386                 getLog().debug( "        o " + rejects.get( ii ) );
387             }
388         }
389         else
390         {
391             getLog().debug( "No artifacts have been excluded." );
392         }
393 
394         getLog().debug( "" );
395 
396         buildSpecificPackaging( excludes );
397 
398         if ( libDirectory.isDirectory() )
399         {
400             String[] files = libDirectory.list();
401 
402             if ( files.length == 0 )
403             {
404                 libDirectory.delete();
405             }
406         }
407     }
408 
409     /**
410      * Perform any packaging specific to this type.
411      * 
412      * @param excludes The exclude list.
413      * @throws MojoExecutionException For plugin failures.
414      * @throws MojoFailureException For unexpected plugin failures.
415      * @throws IOException For exceptions during IO operations.
416      */
417     protected void buildSpecificPackaging( final Set excludes )
418         throws MojoExecutionException
419     {
420     }
421 
422     /**
423      * @return The name of the archive
424      */
425     public String getArchiveName()
426     {
427         return archiveName;
428     }
429 
430     /**
431      * Generates the packaged archive.
432      * 
433      * @throws MojoExecutionException if there is a problem
434      */
435     protected void performPackaging()
436         throws MojoExecutionException
437     {
438 
439         ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler( getArtifactType() );
440         String extension = artifactHandler.getExtension();
441         String type = getArtifactType();
442 
443         final File archiveFile = calculateFile( outputDirectory, archiveName, classifier, extension );
444 
445         // generate archive file
446         getLog().debug( "Generating JBoss packaging " + archiveFile.getAbsolutePath() );
447         MavenArchiver archiver = new MavenArchiver();
448         archiver.setArchiver( jarArchiver );
449         archiver.setOutputFile( archiveFile );
450         try
451         {
452             jarArchiver.addDirectory( getPackagingDirectory() );
453             if ( manifest != null )
454             {
455                 jarArchiver.setManifest( manifest );
456             }
457             archiver.createArchive( getProject(), archive );
458         }
459         catch ( Exception e )
460         {
461             throw new MojoExecutionException( "Problem generating archive file.", e );
462         }
463 
464         // If there is a classifier, then this archive is not the primary project artifact.
465         if ( classifier != null && !classifier.equals( "" ) )
466         {
467             primaryArtifact = false;
468         }
469 
470         if ( primaryArtifact )
471         {
472             Artifact artifact = project.getArtifact();
473             artifact.setFile( archiveFile );
474             artifact.setArtifactHandler( artifactHandler );
475         }
476         else
477         {
478             projectHelper.attachArtifact( project, type, classifier, archiveFile );
479         }
480     }
481 
482     /**
483      * Calculate the name of the archive file.
484      * 
485      * @param outputDirectory The output directory.
486      * @param archiveName The name of the artifact archive.
487      * @param classifier The classifier of the artifact.
488      * @param extension The artifact archive extension.
489      * @return The archive file.
490      */
491     private static File calculateFile( final File outputDirectory, final String archiveName, final String classifier,
492                                        final String extension )
493     {
494         final String basename;
495         if ( StringUtils.isEmpty( classifier ) )
496         {
497             basename = archiveName;
498         }
499         else
500         {
501             basename = archiveName + '-' + classifier;
502         }
503         return new File( outputDirectory, basename + '.' + extension );
504     }
505 
506     /**
507      * Get the name of the artifact.
508      * 
509      * @param artifact The current artifact.
510      * @return The name of the artifact.
511      */
512     private String getArtifactName( Artifact artifact )
513     {
514         String artifactName = artifact.getArtifactId();
515 
516         if ( !this.removeDependencyVersions )
517         {
518             artifactName += "-" + artifact.getVersion();
519         }
520 
521         if ( !StringUtils.isEmpty( artifact.getClassifier() ) )
522         {
523             artifactName += "-" + artifact.getClassifier();
524         }
525 
526         artifactName += "." + artifact.getArtifactHandler().getExtension();
527 
528         return artifactName;
529     }
530 
531     public String getDeploymentDescriptorDestName()
532     {
533         return deploymentDescriptorDestName;
534     }
535 
536     public File getDeploymentDescriptorDest()
537     {
538         return deploymentDescriptorDest;
539     }
540 
541     /**
542      * Main execution for the goal.
543      * 
544      * @throws MojoExecutionException if an error occurred while building the webapp
545      */
546     public void execute()
547         throws MojoExecutionException
548     {
549 
550         buildExplodedPackaging( excludes );
551 
552         if ( !isExplodedOnly() )
553         {
554             performPackaging();
555         }
556     }
557 
558     /**
559      * Routine that packages resources not handled by default resource handling.
560      */
561     protected void packageResources()
562         throws Exception
563     {
564         // Do nothing, override to handle specific resources
565     }
566 
567     /**
568      * Routine that includes the specified artifact into the exploded packaging.
569      * 
570      * @param artifact
571      * @param name
572      * @throws Exception
573      */
574     protected void packageLib( Artifact artifact, String name )
575         throws Exception
576     {
577         FileUtils.copyFile( artifact.getFile(), new File( libDirectory, name ) );
578     }
579 
580     /**
581      * Routine that includes the specified artifact into the exploded packaging.
582      * 
583      * @throws Exception
584      */
585     protected void packageClasses()
586         throws Exception
587     {
588         FileUtils.copyDirectoryStructure( classesDirectory, packagingDirectory );
589     }
590 }