Index: src/main/java/org/apache/maven/plugin/war/AbstractWarMojo.java =================================================================== --- src/main/java/org/apache/maven/plugin/war/AbstractWarMojo.java (revision 332182) +++ src/main/java/org/apache/maven/plugin/war/AbstractWarMojo.java (working copy) @@ -20,8 +20,13 @@ import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.UnArchiver; +import org.codehaus.plexus.archiver.manager.ArchiverManager; +import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; import org.codehaus.plexus.util.DirectoryScanner; import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; import java.io.File; import java.io.IOException; @@ -74,6 +79,23 @@ * @parameter expression="${maven.war.webxml}" */ private String webXml; + + /** + * Directory to unpack dependent WARs into if needed + * + * @parameter expression="${project.build.directory}/war/work" + * @required + */ + private File workDirectory; + + /** + * To look up Archiver/UnArchiver implementations + * + * @parameter expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}" + * @required + */ + protected ArchiverManager archiverManager; + public static final String WEB_INF = "WEB-INF"; @@ -91,6 +113,23 @@ * @parameter alias="excludes" */ private String warSourceExcludes; + + /** + * The comma separated list of tokens to include when doing + * a war overlay. + * Default is '**' + * + * @parameter + */ + private String dependentWarIncludes = "**"; + + /** + * The comma separated list of tokens to exclude when doing + * a way overlay. + * + * @parameter + */ + private String dependentWarExcludes; private static final String[] EMPTY_STRING_ARRAY = {}; @@ -155,8 +194,8 @@ */ protected String[] getExcludes() { - List excludeList = new ArrayList( FileUtils.getDefaultExcludesAsList() ); - if ( warSourceExcludes != null && !"".equals( warSourceExcludes ) ) + List excludeList = new ArrayList(); + if ( StringUtils.isNotEmpty(warSourceExcludes) ) { excludeList.add( warSourceExcludes ); } @@ -180,7 +219,33 @@ { return new String[]{warSourceIncludes}; } + + /** + * Returns a string array of the excludes to be used + * when adding dependent wars as an overlay onto this war. + * + * @return an array of tokens to exclude + */ + protected String[] getDependentWarExcludes() { + List excludeList = new ArrayList(); + if ( StringUtils.isNotEmpty(dependentWarExcludes) ) + { + excludeList.add( dependentWarExcludes ); + } + return (String[]) excludeList.toArray( EMPTY_STRING_ARRAY ); + } + + /** + * Returns a string array of the includes to be used + * when adding dependent wars as an overlay onto this war. + * + * @return an array of tokens to include + */ + protected String[] getDependentWarIncludes() { + return new String[] { dependentWarIncludes }; + } + public void buildExplodedWebapp( File webappDirectory ) throws MojoExecutionException { @@ -253,7 +318,7 @@ * @throws java.io.IOException if an error occured while building the webapp */ public void buildWebapp( MavenProject project, File webappDirectory ) - throws IOException + throws MojoExecutionException, IOException { getLog().info( "Assembling webapp " + project.getArtifactId() + " in " + webappDirectory ); @@ -269,6 +334,8 @@ } Set artifacts = project.getArtifacts(); + + List dependentWarDirectories = new ArrayList(); for ( Iterator iter = artifacts.iterator(); iter.hasNext(); ) { @@ -287,15 +354,147 @@ { FileUtils.copyFileToDirectory( artifact.getFile(), libDirectory ); } + else if ( "war".equals( type ) ) + { + dependentWarDirectories.add( unpackWarToTempDirectory( artifact ) ); + } else { getLog().debug( "Skipping artifact of type " + type + " for WEB-INF/lib" ); } } } + + if ( dependentWarDirectories.size() > 0 ) + { + getLog().info( "Overlaying " + dependentWarDirectories.size() + " war(s)." ); + + // overlay dependent wars + for ( Iterator iter = dependentWarDirectories.iterator(); iter.hasNext(); ) + { + copyDependentWarContents( (File) iter.next(), webappDirectory ); + } + } } + + /** + * Unpacks war artifacts into a temporary directory inside workDirectory + * named with the name of the war. + * + * @param artifact War artifact to unpack. + * @return Directory containing the unpacked war. + * @throws MojoExecutionException + */ + private File unpackWarToTempDirectory( Artifact artifact ) + throws MojoExecutionException + { + String name = artifact.getFile().getName(); + File tempLocation = new File( workDirectory, name.substring( 0, name.length() - 4 ) ); + boolean process = false; + if ( !tempLocation.exists() ) + { + tempLocation.mkdirs(); + process = true; + } + else if ( artifact.getFile().lastModified() > tempLocation.lastModified() ) + { + process = true; + } + + if ( process ) + { + File file = artifact.getFile(); + try + { + unpack( file, tempLocation ); + } + catch ( NoSuchArchiverException e ) + { + this.getLog().info( "Skip unpacking dependency file with unknown extension: " + file.getPath() ); + } + } + + return tempLocation; + } + /** + * Unpacks the archive file. + * + * @param file File to be unpacked. + * @param location Location where to put the unpacked files. + */ + private void unpack( File file, File location ) + throws MojoExecutionException, NoSuchArchiverException + { + String archiveExt = FileUtils.getExtension( file.getAbsolutePath() ).toLowerCase(); + + try + { + UnArchiver unArchiver; + unArchiver = this.archiverManager.getUnArchiver( archiveExt ); + unArchiver.setSourceFile( file ); + unArchiver.setDestDirectory( location ); + unArchiver.extract(); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error unpacking file: " + file + "to: " + location, e ); + } + catch ( ArchiverException e ) + { + throw new MojoExecutionException( "Error unpacking file: " + file + "to: " + location, e ); + } + } + + /** + * Recursively copies contents of srcDir into targetDir. + * This will not overwrite any existing files. + * + * @param srcDir Directory containing unpacked dependent war contents + * @param targetDir Directory to overlay srcDir into + */ + private void copyDependentWarContents( File srcDir, File targetDir ) + throws IOException, MojoExecutionException + { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( srcDir ); + scanner.setExcludes( getDependentWarExcludes() ); + scanner.addDefaultExcludes(); + + scanner.setIncludes( getDependentWarIncludes() ); + + scanner.scan(); + + String[] dirs = scanner.getIncludedDirectories(); + for ( int j = 0; j < dirs.length; j++ ) + { + new File( targetDir, dirs[j] ).mkdirs(); + } + + String[] files = scanner.getIncludedFiles(); + + for ( int j = 0; j < files.length; j++ ) + { + File targetFile = new File( targetDir, files[j] ); + + // Do not overwrite existing files. + if (!targetFile.exists()) + { + try + { + targetFile.getParentFile().mkdirs(); + FileUtils.copyFile( new File( srcDir, files[j] ), targetFile ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error copying file '" + files[j] + "' to '" + targetFile + "'", e ); + } + } + } + } + + /** * Returns a list of filenames that should be copied * over to the destination directory. *