diff options
author | antelder <antelder@13f79535-47bb-0310-9956-ffa450edef68> | 2009-04-05 06:48:25 +0000 |
---|---|---|
committer | antelder <antelder@13f79535-47bb-0310-9956-ffa450edef68> | 2009-04-05 06:48:25 +0000 |
commit | 11f0de486598b390274ef2d845cde2271f2b68d7 (patch) | |
tree | 71817327ae90321f28e2d4a4c81dd5ba188207ef /maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany | |
parent | 6ea29047da867592e0659e9149bd3d1a313bef03 (diff) |
Start of a maven plugin to create SCA ZIP contributions
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@762046 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany')
7 files changed, 1595 insertions, 0 deletions
diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/AbstractZipMojo.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/AbstractZipMojo.java new file mode 100644 index 0000000000..365ac2871e --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/AbstractZipMojo.java @@ -0,0 +1,896 @@ +package org.apache.tuscany.maven.zip; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.archiver.MavenArchiveConfiguration; +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.UnArchiver; +import org.codehaus.plexus.archiver.jar.JarArchiver; +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.IOUtil; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.StringUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * Based on code from the Maven WAR plugin 2.0.2 AbstractWarMojo + */ +public abstract class AbstractZipMojo + extends AbstractMojo +{ + /** + * The maven project. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + private MavenProject project; + + /** + * The directory containing generated classes. + * + * @parameter expression="${project.build.outputDirectory}" + * @required + * @readonly + */ + private File classesDirectory; + + /** + * Whether a JAR file will be created for the classes in the webapp. Using this optional configuration + * parameter will make the generated classes to be archived into a jar file + * and the classes directory will then be excluded from the webapp. + * + * @parameter expression="${archiveClasses}" default-value="false" + */ + private boolean archiveClasses; + + /** + * The Jar archiver needed for archiving classes directory into jar file. + * + * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}" + * @required + */ + private JarArchiver jarArchiver; + + /** + * The directory where the SCA ZIP contribution is built. + * + * @parameter expression="${project.build.directory}/${project.build.finalName}" + * @required + */ + private File zipDirectory; + + /** + * Filters (property files) to include during the interpolation of the pom.xml. + * + * @parameter expression="${project.build.filters}" + */ + private List filters; + + /** + * 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; + + private static final String META_INF = "META-INF"; + + private static final String[] DEFAULT_INCLUDES = {"**/**"}; + + /** + * The comma separated list of tokens to include in the SCA ZIP. + * Default is '**'. + * + * @parameter alias="includes" + */ + private String warSourceIncludes = "**"; + + /** + * The comma separated list of tokens to exclude from the WAR. + * + * @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 war overlay. + * + * @parameter + */ + private String dependentWarExcludes; + + /** + * The maven archive configuration to use. + * + * @parameter + */ + protected MavenArchiveConfiguration archive = new MavenArchiveConfiguration(); + + private static final String[] EMPTY_STRING_ARRAY = {}; + + + public MavenProject getProject() + { + return project; + } + + public void setProject( MavenProject project ) + { + this.project = project; + } + + public File getClassesDirectory() + { + return classesDirectory; + } + + public void setClassesDirectory( File classesDirectory ) + { + this.classesDirectory = classesDirectory; + } + + public File getZipDirectory() + { + return zipDirectory; + } + + public void setScaZipDirectory( File scaZipDirectory ) + { + this.zipDirectory = scaZipDirectory; + } + + /** + * Returns a string array of the excludes to be used + * when assembling/copying the war. + * + * @return an array of tokens to exclude + */ + protected String[] getExcludes() + { + List excludeList = new ArrayList(); + if ( StringUtils.isNotEmpty( warSourceExcludes ) ) + { + excludeList.addAll( Arrays.asList( StringUtils.split( warSourceExcludes, "," ) ) ); + } + + return (String[]) excludeList.toArray( EMPTY_STRING_ARRAY ); + } + + /** + * Returns a string array of the includes to be used + * when assembling/copying the war. + * + * @return an array of tokens to include + */ + protected String[] getIncludes() + { + return StringUtils.split( StringUtils.defaultString( 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() + { + String[] excludes; + if ( StringUtils.isNotEmpty( dependentWarExcludes ) ) + { + excludes = StringUtils.split( dependentWarExcludes, "," ); + } + else + { + excludes = EMPTY_STRING_ARRAY; + } + return excludes; + } + + /** + * 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 StringUtils.split( StringUtils.defaultString( dependentWarIncludes ), "," ); + } + + public void buildExplodedWebapp( File webappDirectory ) + throws MojoExecutionException, MojoFailureException + { + getLog().info( "Exploding webapp..." ); + + webappDirectory.mkdirs(); + + try + { + buildWebapp( project, webappDirectory ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not explode webapp...", e ); + } + } + + private Map getBuildFilterProperties() + throws MojoExecutionException + { + + Map filterProperties = new Properties(); + + // System properties + filterProperties.putAll( System.getProperties() ); + + // Project properties + filterProperties.putAll( project.getProperties() ); + + for ( Iterator i = filters.iterator(); i.hasNext(); ) + { + String filtersfile = (String) i.next(); + + try + { + Properties properties = PropertyUtils.loadPropertyFile( new File( filtersfile ), true, true ); + + filterProperties.putAll( properties ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error loading property file '" + filtersfile + "'", e ); + } + } + + // can't putAll, as ReflectionProperties doesn't enumerate - so we make a composite map with the project variables as dominant + return new CompositeMap( new ReflectionProperties( project ), filterProperties ); + } + + /** + * Copies webapp webResources from the specified directory. + * <p/> + * Note that the <tt>webXml</tt> parameter could be null and may + * specify a file which is not named <tt>web.xml<tt>. If the file + * exists, it will be copied to the <tt>META-INF</tt> directory and + * renamed accordingly. + * + * @param resource the resource to copy + * @param webappDirectory the target directory + * @param filterProperties + * @throws java.io.IOException if an error occured while copying webResources + */ + public void copyResources( Resource resource, File webappDirectory, Map filterProperties ) + throws IOException + { + if ( !resource.getDirectory().equals( webappDirectory.getPath() ) ) + { + getLog().info( "Copy webapp webResources to " + webappDirectory.getAbsolutePath() ); + if ( webappDirectory.exists() ) + { + String[] fileNames = getWarFiles( resource ); + String targetPath = (resource.getTargetPath() == null) ? "" : resource.getTargetPath(); + File destination = new File(webappDirectory,targetPath); + for ( int i = 0; i < fileNames.length; i++ ) + { + if ( resource.isFiltering() ) + { + copyFilteredFile( new File( resource.getDirectory(), fileNames[i] ), + new File( destination, fileNames[i] ), null, getFilterWrappers(), + filterProperties ); + } + else + { + copyFileIfModified( new File( resource.getDirectory(), fileNames[i] ), + new File( destination, fileNames[i] ) ); + } + } + } + } + } + + /** + * Generates the JAR. + * + * @todo Add license files in META-INF directory. + */ + public void createJarArchive( File libDirectory ) + throws MojoExecutionException + { + String archiveName = project.getBuild().getFinalName() + ".jar"; + + File jarFile = new File( libDirectory, archiveName ); + + MavenArchiver archiver = new MavenArchiver(); + + archiver.setArchiver( jarArchiver ); + + archiver.setOutputFile( jarFile ); + + try + { + archiver.getArchiver().addDirectory( classesDirectory, getIncludes(), getExcludes() ); + + archiver.createArchive( project, archive ); + } + catch ( Exception e ) + { + // TODO: improve error handling + throw new MojoExecutionException( "Error assembling JAR", e ); + } + } + + /** + * Builds the webapp for the specified project. + * <p/> + * Classes, libraries and tld files are copied to + * the <tt>webappDirectory</tt> during this phase. + * + * @param project the maven project + * @param webappDirectory + * @throws java.io.IOException if an error occured while building the webapp + */ + public void buildWebapp( MavenProject project, File webappDirectory ) + throws MojoExecutionException, IOException, MojoFailureException + { + getLog().info( "Assembling webapp " + project.getArtifactId() + " in " + webappDirectory ); + + File metainfDir = new File( webappDirectory, META_INF ); + metainfDir.mkdirs(); + + File libDirectory = new File( webappDirectory, "/lib" ); + + File webappClassesDirectory = new File( webappDirectory, "/" ); + + if ( classesDirectory.exists() && !classesDirectory.equals( webappClassesDirectory ) ) + { + if ( archiveClasses ) + { + createJarArchive( libDirectory ); + } + else + { + copyDirectoryStructureIfModified( classesDirectory, webappClassesDirectory ); + } + } + + Set artifacts = project.getArtifacts(); + + List duplicates = findDuplicates( artifacts ); + + List dependentWarDirectories = new ArrayList(); + + for ( Iterator iter = artifacts.iterator(); iter.hasNext(); ) + { + Artifact artifact = (Artifact) iter.next(); + String targetFileName = getDefaultFinalName( artifact ); + + getLog().debug( "Processing: " + targetFileName ); + + if ( duplicates.contains( targetFileName ) ) + { + getLog().debug( "Duplicate found: " + targetFileName ); + targetFileName = artifact.getGroupId() + "-" + targetFileName; + getLog().debug( "Renamed to: " + targetFileName ); + } + + // TODO: utilise appropriate methods from project builder + ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME ); + if ( !artifact.isOptional() && filter.include( artifact ) ) + { + String type = artifact.getType(); + if ( "jar".equals( type ) || "ejb".equals( type ) || "ejb-client".equals( type ) ) + { + copyFileIfModified( artifact.getFile(), new File( libDirectory, targetFileName ) ); + } + else + { + if ( "par".equals( type ) ) + { + targetFileName = targetFileName.substring( 0, targetFileName.lastIndexOf( '.' ) ) + ".jar"; + + getLog().debug( + "Copying " + artifact.getFile() + " to " + new File( libDirectory, targetFileName ) ); + + copyFileIfModified( artifact.getFile(), new File( libDirectory, targetFileName ) ); + } + 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 ); + } + } + } + + /** + * Searches a set of artifacts for duplicate filenames and returns a list of duplicates. + * + * @param artifacts set of artifacts + * @return List of duplicated artifacts + */ + private List findDuplicates( Set artifacts ) + { + List duplicates = new ArrayList(); + List identifiers = new ArrayList(); + for ( Iterator iter = artifacts.iterator(); iter.hasNext(); ) + { + Artifact artifact = (Artifact) iter.next(); + String candidate = getDefaultFinalName( artifact ); + if ( identifiers.contains( candidate ) ) + { + duplicates.add( candidate ); + } + else + { + identifiers.add( candidate ); + } + } + return duplicates; + } + + /** + * Unpacks war artifacts into a temporary directory inside <tt>workDirectory</tt> + * 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 = archiverManager.getUnArchiver( archiveExt ); + unArchiver.setSourceFile( file ); + unArchiver.setDestDirectory( location ); + unArchiver.setOverwrite( true ); + 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 <tt>srcDir</tt> into <tt>targetDir</tt>. + * 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 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] ); + + try + { + targetFile.getParentFile().mkdirs(); + copyFileIfModified( 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. + * + * @param sourceDir the directory to be scanned + * @return the array of filenames, relative to the sourceDir + */ + private String[] getWarFiles( File sourceDir ) + { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( sourceDir ); + scanner.setExcludes( getExcludes() ); + scanner.addDefaultExcludes(); + + scanner.setIncludes( getIncludes() ); + + scanner.scan(); + + return scanner.getIncludedFiles(); + } + + /** + * Returns a list of filenames that should be copied + * over to the destination directory. + * + * @param resource the resource to be scanned + * @return the array of filenames, relative to the sourceDir + */ + private String[] getWarFiles( Resource resource ) + { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( resource.getDirectory() ); + if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() ) + { + scanner.setIncludes( (String[]) resource.getIncludes().toArray( EMPTY_STRING_ARRAY ) ); + } + else + { + scanner.setIncludes( DEFAULT_INCLUDES ); + } + if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() ) + { + scanner.setExcludes( (String[]) resource.getExcludes().toArray( EMPTY_STRING_ARRAY ) ); + } + + scanner.addDefaultExcludes(); + + scanner.scan(); + + return scanner.getIncludedFiles(); + } + + /** + * Copy file from source to destination only if source is newer than the target file. + * If <code>destinationDirectory</code> does not exist, it + * (and any parent directories) will be created. If a file <code>source</code> in + * <code>destinationDirectory</code> exists, it will be overwritten. + * + * @param source An existing <code>File</code> to copy. + * @param destinationDirectory A directory to copy <code>source</code> into. + * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file. + * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory. + * @throws java.io.IOException if <code>source</code> does not exist, the file in + * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying. + * <p/> + * TO DO: Remove this method when Maven moves to plexus-utils version 1.4 + */ + private static void copyFileToDirectoryIfModified( File source, File destinationDirectory ) + throws IOException + { + // TO DO: Remove this method and use the method in WarFileUtils when Maven 2 changes + // to plexus-utils 1.2. + if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) + { + throw new IllegalArgumentException( "Destination is not a directory" ); + } + + copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); + } + + private FilterWrapper[] getFilterWrappers() + { + return new FilterWrapper[]{ + // support ${token} + new FilterWrapper() + { + public Reader getReader( Reader fileReader, Map filterProperties ) + { + return new InterpolationFilterReader( fileReader, filterProperties, "${", "}" ); + } + }, + // support @token@ + new FilterWrapper() + { + public Reader getReader( Reader fileReader, Map filterProperties ) + { + return new InterpolationFilterReader( fileReader, filterProperties, "@", "@" ); + } + }}; + } + + /** + * @param from + * @param to + * @param encoding + * @param wrappers + * @param filterProperties + * @throws IOException TO DO: Remove this method when Maven moves to plexus-utils version 1.4 + */ + private static void copyFilteredFile( File from, File to, String encoding, FilterWrapper[] wrappers, + Map filterProperties ) + throws IOException + { + // buffer so it isn't reading a byte at a time! + Reader fileReader = null; + Writer fileWriter = null; + try + { + // fix for MWAR-36, ensures that the parent dir are created first + to.getParentFile().mkdirs(); + + if ( encoding == null || encoding.length() < 1 ) + { + fileReader = new BufferedReader( new FileReader( from ) ); + fileWriter = new FileWriter( to ); + } + else + { + FileInputStream instream = new FileInputStream( from ); + + FileOutputStream outstream = new FileOutputStream( to ); + + fileReader = new BufferedReader( new InputStreamReader( instream, encoding ) ); + + fileWriter = new OutputStreamWriter( outstream, encoding ); + } + + Reader reader = fileReader; + for ( int i = 0; i < wrappers.length; i++ ) + { + FilterWrapper wrapper = wrappers[i]; + reader = wrapper.getReader( reader, filterProperties ); + } + + IOUtil.copy( reader, fileWriter ); + } + finally + { + IOUtil.close( fileReader ); + IOUtil.close( fileWriter ); + } + } + + /** + * Copy file from source to destination only if source timestamp is later than the destination timestamp. + * The directories up to <code>destination</code> will be created if they don't already exist. + * <code>destination</code> will be overwritten if it already exists. + * + * @param source An existing non-directory <code>File</code> to copy bytes from. + * @param destination A non-directory <code>File</code> to write bytes to (possibly + * overwriting). + * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be + * written to, or an IO error occurs during copying. + * @throws java.io.FileNotFoundException if <code>destination</code> is a directory + * <p/> + * TO DO: Remove this method when Maven moves to plexus-utils version 1.4 + */ + private static void copyFileIfModified( File source, File destination ) + throws IOException + { + // TO DO: Remove this method and use the method in WarFileUtils when Maven 2 changes + // to plexus-utils 1.2. + if ( destination.lastModified() < source.lastModified() ) + { + FileUtils.copyFile( source.getCanonicalFile(), destination ); + // preserve timestamp + destination.setLastModified( source.lastModified() ); + } + } + + /** + * Copies a entire directory structure but only source files with timestamp later than the destinations'. + * <p/> + * Note: + * <ul> + * <li>It will include empty directories. + * <li>The <code>sourceDirectory</code> must exists. + * </ul> + * + * @param sourceDirectory + * @param destinationDirectory + * @throws IOException TO DO: Remove this method when Maven moves to plexus-utils version 1.4 + */ + private static void copyDirectoryStructureIfModified( File sourceDirectory, File destinationDirectory ) + throws IOException + { + if ( !sourceDirectory.exists() ) + { + throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); + } + + File[] files = sourceDirectory.listFiles(); + + String sourcePath = sourceDirectory.getAbsolutePath(); + + for ( int i = 0; i < files.length; i++ ) + { + File file = files[i]; + + String dest = file.getAbsolutePath(); + + dest = dest.substring( sourcePath.length() + 1 ); + + File destination = new File( destinationDirectory, dest ); + + if ( file.isFile() ) + { + destination = destination.getParentFile(); + + copyFileToDirectoryIfModified( file, destination ); + } + else if ( file.isDirectory() ) + { + if ( !destination.exists() && !destination.mkdirs() ) + { + throw new IOException( + "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); + } + + copyDirectoryStructureIfModified( file, destination ); + } + else + { + throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); + } + } + } + + /** + * TO DO: Remove this interface when Maven moves to plexus-utils version 1.4 + */ + private interface FilterWrapper + { + Reader getReader( Reader fileReader, Map filterProperties ); + } + + /** + * Converts the filename of an artifact to artifactId-version.type format. + * + * @param artifact + * @return converted filename of the artifact + */ + private String getDefaultFinalName( Artifact artifact ) + { + String finalName = artifact.getArtifactId() + "-" + artifact.getVersion(); + + String classifier = artifact.getClassifier(); + if ( ( classifier != null ) && ! ( "".equals( classifier.trim() ) ) ) + { + finalName += "-" + classifier; + } + + finalName = finalName + "." + artifact.getArtifactHandler().getExtension(); + return finalName; + } + +} diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/CompositeMap.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/CompositeMap.java new file mode 100644 index 0000000000..813a1bd668 --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/CompositeMap.java @@ -0,0 +1,61 @@ +package org.apache.tuscany.maven.zip; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * @version $Id: CompositeMap.java 480784 2006-11-30 00:07:45Z jvanzyl $ + * @todo merge with resources/assembly plugin + */ +public class CompositeMap + extends AbstractMap +{ + private Map recessive; + + private Map dominant; + + public CompositeMap( Map dominant, Map recessive ) + { + this.dominant = Collections.unmodifiableMap( dominant ); + + this.recessive = Collections.unmodifiableMap( recessive ); + } + + public synchronized Object get( Object key ) + { + Object value = dominant.get( key ); + + if ( value == null ) + { + value = recessive.get( key ); + } + + return value; + } + + public Set entrySet() + { + throw new UnsupportedOperationException( "Cannot enumerate properties in a composite map" ); + } +} diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/PropertyUtils.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/PropertyUtils.java new file mode 100644 index 0000000000..8c204987f1 --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/PropertyUtils.java @@ -0,0 +1,150 @@ +package org.apache.tuscany.maven.zip; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.codehaus.plexus.util.IOUtil; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Properties; + +/** + * @author <a href="mailto:kenney@neonics.com">Kenney Westerhof</a> + * @version $Id: PropertyUtils.java 480784 2006-11-30 00:07:45Z jvanzyl $ + * @todo this is duplicated from the resources plugin - migrate to plexus-utils + */ +public final class PropertyUtils +{ + private PropertyUtils() + { + // prevent instantiation + } + + /** + * Reads a property file, resolving all internal variables. + * + * @param propfile The property file to load + * @param fail wheter to throw an exception when the file cannot be loaded or to return null + * @param useSystemProps wheter to incorporate System.getProperties settings into the returned Properties object. + * @return the loaded and fully resolved Properties object + */ + public static Properties loadPropertyFile( File propfile, boolean fail, boolean useSystemProps ) + throws IOException + { + Properties props = new Properties(); + + if ( useSystemProps ) + { + props = new Properties( System.getProperties() ); + } + + if ( propfile.exists() ) + { + FileInputStream inStream = new FileInputStream( propfile ); + try + { + props.load( inStream ); + } + finally + { + IOUtil.close( inStream ); + } + } + else if ( fail ) + { + throw new FileNotFoundException( propfile.toString() ); + } + + for ( Enumeration n = props.propertyNames(); n.hasMoreElements(); ) + { + String k = (String) n.nextElement(); + props.setProperty( k, PropertyUtils.getPropertyValue( k, props ) ); + } + + return props; + } + + + /** + * Retrieves a property value, replacing values like ${token} + * using the Properties to look them up. + * <p/> + * It will leave unresolved properties alone, trying for System + * properties, and implements reparsing (in the case that + * the value of a property contains a key), and will + * not loop endlessly on a pair like + * test = ${test}. + */ + private static String getPropertyValue( String k, Properties p ) + { + // This can also be done using InterpolationFilterReader, + // but it requires reparsing the file over and over until + // it doesn't change. + + String v = p.getProperty( k ); + String ret = ""; + int idx, idx2; + + while ( ( idx = v.indexOf( "${" ) ) >= 0 ) + { + // append prefix to result + ret += v.substring( 0, idx ); + + // strip prefix from original + v = v.substring( idx + 2 ); + + // if no matching } then bail + if ( ( idx2 = v.indexOf( '}' ) ) < 0 ) + { + break; + } + + // strip out the key and resolve it + // resolve the key/value for the ${statement} + String nk = v.substring( 0, idx2 ); + v = v.substring( idx2 + 1 ); + String nv = p.getProperty( nk ); + + // try global environment.. + if ( nv == null ) + { + nv = System.getProperty( nk ); + } + + // if the key cannot be resolved, + // leave it alone ( and don't parse again ) + // else prefix the original string with the + // resolved property ( so it can be parsed further ) + // taking recursion into account. + if ( nv == null || nv.equals( k ) ) + { + ret += "${" + nk + "}"; + } + else + { + v = nv + v; + } + } + return ret + v; + } +} diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ReflectionProperties.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ReflectionProperties.java new file mode 100644 index 0000000000..bceee39489 --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ReflectionProperties.java @@ -0,0 +1,60 @@ +package org.apache.tuscany.maven.zip; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.introspection.ReflectionValueExtractor; + +import java.util.AbstractMap; +import java.util.Set; + +/** + * @version $Id: ReflectionProperties.java 480784 2006-11-30 00:07:45Z jvanzyl $ + * @todo merge with resources/assembly plugin + */ +public class ReflectionProperties + extends AbstractMap +{ + private MavenProject project; + + public ReflectionProperties( MavenProject project ) + { + this.project = project; + } + + public synchronized Object get( Object key ) + { + Object value = null; + try + { + value = ReflectionValueExtractor.evaluate( String.valueOf( key ), project ); + } + catch ( Exception e ) + { + //TODO: remove the try-catch block when ReflectionValueExtractor.evaluate() throws no more exceptions + } + return value; + } + + public Set entrySet() + { + throw new UnsupportedOperationException( "Cannot enumerate properties in a project" ); + } +} diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipArchiver.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipArchiver.java new file mode 100644 index 0000000000..a22ae9835a --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipArchiver.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.maven.zip; + +import org.codehaus.plexus.archiver.ArchiveEntry; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.zip.ZipOutputStream; + +import java.io.File; +import java.io.IOException; + +/** + * An extension of <jar> to create a WAR archive. + * Contains special treatment for files that should end up in the + * <code>WEB-INF/lib</code>, <code>WEB-INF/classes</code> or + * <code>WEB-INF</code> directories of the Web Application Archive.</p> + * <p>(The War task is a shortcut for specifying the particular layout of a WAR file. + * The same thing can be accomplished by using the <i>prefix</i> and <i>fullpath</i> + * attributes of zipfilesets in a Zip or Jar task.)</p> + * <p>The extended zipfileset element from the zip task + * (with attributes <i>prefix</i>, <i>fullpath</i>, and <i>src</i>) + * is available in the War task.</p> + * + * @see JarArchiver + */ +public class ZipArchiver + extends JarArchiver +{ + + /** + * our web.xml deployment descriptor + */ + private File deploymentDescriptor; + + /** + * flag set if finding the webxml is to be expected. + */ + private boolean ignoreWebxml = true; + + /** + * flag set if the descriptor is added + */ + private boolean descriptorAdded; + + public void setIgnoreWebxml( boolean ignore ) { + ignoreWebxml = ignore; + } + + public ZipArchiver() + { + super(); + archiveType = "scazip"; + } + + /** + * set the deployment descriptor to use (WEB-INF/web.xml); + * required unless <tt>update=true</tt> + */ + public void setWebxml( File descr ) + throws ArchiverException + { + deploymentDescriptor = descr; + if ( !deploymentDescriptor.exists() ) + { + throw new ArchiverException( "Deployment descriptor: " + + deploymentDescriptor + + " does not exist." ); + } + + addFile( descr, "WEB-INF/web.xml" ); + } + + /** + * add a file under WEB-INF/lib/ + */ + + public void addLib( File fileName ) + throws ArchiverException + { + addDirectory( fileName.getParentFile(), "WEB-INF/lib/", new String[]{fileName.getName()}, null ); + } + + /** + * add files under WEB-INF/lib/ + */ + + public void addLibs( File directoryName, String[] includes, String[] excludes ) + throws ArchiverException + { + addDirectory( directoryName, "WEB-INF/lib/", includes, excludes ); + } + + /** + * add a file under WEB-INF/lib/ + */ + + public void addClass( File fileName ) + throws ArchiverException + { + addDirectory( fileName.getParentFile(), "WEB-INF/classes/", new String[]{fileName.getName()}, null ); + } + + /** + * add files under WEB-INF/classes + */ + public void addClasses( File directoryName, String[] includes, String[] excludes ) + throws ArchiverException + { + addDirectory( directoryName, "WEB-INF/classes/", includes, excludes ); + } + + /** + * files to add under WEB-INF; + */ + public void addWebinf( File directoryName, String[] includes, String[] excludes ) + throws ArchiverException + { + addDirectory( directoryName, "WEB-INF/", includes, excludes ); + } + + /** + * override of parent; validates configuration + * before initializing the output stream. + */ + protected void initZipOutputStream( ZipOutputStream zOut ) + throws IOException, ArchiverException + { + super.initZipOutputStream( zOut ); + } + + /** + * Overridden from ZipArchiver class to deal with web.xml + */ + protected void zipFile( ArchiveEntry entry, ZipOutputStream zOut, String vPath, int mode ) + throws IOException, ArchiverException + { + // If the file being added is WEB-INF/web.xml, we warn if it's + // not the one specified in the "webxml" attribute - or if + // it's being added twice, meaning the same file is specified + // by the "webxml" attribute and in a <fileset> element. + if ( vPath.equalsIgnoreCase( "WEB-INF/web.xml" ) ) + { + if ( descriptorAdded || ( ignoreWebxml + && ( deploymentDescriptor == null + || !deploymentDescriptor.getCanonicalPath().equals( entry.getFile().getCanonicalPath() ) ) ) ) + { + getLogger().warn( "Warning: selected " + archiveType + + " files include a WEB-INF/web.xml which will be ignored " + + "\n(webxml attribute is missing from " + + archiveType + " task, or ignoreWebxml attribute is specified as 'true')" ); + } + else + { + super.zipFile( entry, zOut, vPath ); + descriptorAdded = true; + } + } + else + { + super.zipFile( entry, zOut, vPath ); + } + } + + /** + * Make sure we don't think we already have a web.xml next time this task + * gets executed. + */ + protected void cleanUp() + { + descriptorAdded = false; + ignoreWebxml = true; + super.cleanUp(); + } +} diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipExplodedMojo.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipExplodedMojo.java new file mode 100644 index 0000000000..913653f11a --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipExplodedMojo.java @@ -0,0 +1,41 @@ +package org.apache.tuscany.maven.zip; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +/** + * Generate the exploded SCA ZIP contribution + * + * @goal exploded + * @phase package + * @requiresDependencyResolution runtime + */ +public class ZipExplodedMojo + extends AbstractZipMojo +{ + public void execute() + throws MojoExecutionException, MojoFailureException + { + buildExplodedWebapp( getZipDirectory() ); + } + +}
\ No newline at end of file diff --git a/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipMojo.java b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipMojo.java new file mode 100644 index 0000000000..c9f73f1fa7 --- /dev/null +++ b/maven-plugins/trunk/maven-zip-plugin/src/main/java/org/apache/tuscany/maven/zip/ZipMojo.java @@ -0,0 +1,195 @@ +package org.apache.tuscany.maven.zip; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProjectHelper; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.ManifestException; + +/** + * Build an SCA ZIP contribution. + * + * Based on code from the Maven WAR plugin 2.0.2 by Emmanuel Venisse + * + * @goal zip + * @phase package + * @requiresDependencyResolution runtime + */ +public class ZipMojo + extends AbstractZipMojo +{ + /** + * The directory for the generated contribution. + * + * @parameter expression="${project.build.directory}" + * @required + */ + private String outputDirectory; + + /** + * The name of the generated contribution. + * + * @parameter expression="${project.build.finalName}" + * @required + */ + private String contributionName; + + /** + * Classifier to add to the artifact generated. If given, the artifact will be an attachment instead. + * + * @parameter + */ + private String classifier; + + /** + * The Jar archiver. + * + */ + private ZipArchiver Archiver = new ZipArchiver(); + + + /** + * @component + */ + private MavenProjectHelper projectHelper; + + /** + * Whether this is the main artifact being built. Set to <code>false</code> if you don't want to install or + * deploy it to the local repository instead of the default one in an execution. + * + * @parameter expression="${primaryArtifact}" default-value="true" + */ + private boolean primaryArtifact; + + // ---------------------------------------------------------------------- + // Implementation + // ---------------------------------------------------------------------- + + /** + * Overload this to produce a test-war, for example. + */ + protected String getClassifier() + { + return classifier; + } + + protected static File getFile( File basedir, String finalName, String classifier ) + { + if ( classifier == null ) + { + classifier = ""; + } + else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) ) + { + classifier = "-" + classifier; + } + + return new File( basedir, finalName + classifier + ".zip" ); + } + + /** + * Executes the Mojo on the current project. + * + * @throws MojoExecutionException if an error occured while building the SCA ZIP contribution + */ + public void execute() + throws MojoExecutionException, MojoFailureException + { + File File = getFile( new File( outputDirectory ), contributionName, classifier ); + + try + { + performPackaging( File ); + } + catch ( DependencyResolutionRequiredException e ) + { + throw new MojoExecutionException( "Error assembling SCA ZIP: " + e.getMessage(), e ); + } + catch ( ManifestException e ) + { + throw new MojoExecutionException( "Error assembling SCA ZIP", e ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error assembling SCA ZIP", e ); + } + catch ( ArchiverException e ) + { + throw new MojoExecutionException( "Error assembling SCA ZIP: " + e.getMessage(), e ); + } + } + + /** + * Generates the SCA ZIP contribution according to the <tt>mode</tt> attribute. + * + * @param File the target war file + * @throws IOException + * @throws ArchiverException + * @throws ManifestException + * @throws DependencyResolutionRequiredException + * + */ + private void performPackaging( File File ) + throws IOException, ArchiverException, ManifestException, DependencyResolutionRequiredException, + MojoExecutionException, MojoFailureException + { + buildExplodedWebapp( getZipDirectory() ); + + //generate war file + getLog().info( "Generating SCA ZIP contribution " + File.getAbsolutePath() ); + + MavenArchiver archiver = new MavenArchiver(); + + archiver.setArchiver( Archiver ); + + archiver.setOutputFile( File ); + + Archiver.addDirectory( getZipDirectory(), getIncludes(), getExcludes() ); + + // create archive + archiver.createArchive( getProject(), archive ); + + String classifier = this.classifier; + if ( classifier != null ) + { + projectHelper.attachArtifact( getProject(), "", classifier, File ); + } + else + { + Artifact artifact = getProject().getArtifact(); + if ( primaryArtifact ) + { + artifact.setFile( File ); + } + else if ( artifact.getFile() == null || artifact.getFile().isDirectory() ) + { + artifact.setFile( File ); + } + } + } +} |