blob: 45dd4ae64c5ea5a9da054319d3881799bc931fb3 [file] [log] [blame]
package org.apache.maven.plugin.assembly.archive.archiver;
/*
* 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.assembly.filter.ContainerDescriptorHandler;
import org.codehaus.plexus.archiver.ArchiveEntry;
import org.codehaus.plexus.archiver.ArchiveFinalizer;
import org.codehaus.plexus.archiver.ArchivedFileSet;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.FileSet;
import org.codehaus.plexus.archiver.FinalizerEnabled;
import org.codehaus.plexus.archiver.ResourceIterator;
import org.codehaus.plexus.archiver.util.DefaultArchivedFileSet;
import org.codehaus.plexus.archiver.util.DefaultFileSet;
import org.codehaus.plexus.components.io.fileselectors.FileInfo;
import org.codehaus.plexus.components.io.fileselectors.FileSelector;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection;
import org.codehaus.plexus.logging.Logger;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Delegating archiver implementation that supports:
* <ul>
* <li>dry-running (where the delegate archiver is never actually called)</li>
* <li>prefixing (where all paths have a set global prefix prepended before addition)</li>
* <li>duplication checks on archive additions (for archive-file path + prefix)</li>
* </ul>
*
* @author jdcasey
* @version $Id$
*/
public class AssemblyProxyArchiver
implements Archiver
{
private final Archiver delegate;
private String rootPrefix;
private FileSelector[] selectors;
private final ThreadLocal<Boolean> inPublicApi = new ThreadLocal<Boolean>();
private final Logger logger;
private final boolean dryRun;
private boolean forced;
private final Set<String> seenPaths = new HashSet<String>();
private final String assemblyWorkPath;
/**
* @since 2.2
*/
private boolean useJvmChmod;
public AssemblyProxyArchiver( final String rootPrefix, final Archiver delegate,
final List<ContainerDescriptorHandler> containerDescriptorHandlers,
final List<FileSelector> extraSelectors, final List<ArchiveFinalizer> extraFinalizers,
final File assemblyWorkDir, final Logger logger, final boolean dryRun )
{
this.rootPrefix = rootPrefix;
this.delegate = delegate;
assemblyWorkPath = assemblyWorkDir.getAbsolutePath().replace( '\\', '/' );
this.logger = logger;
this.dryRun = dryRun;
if ( !"".equals( rootPrefix ) && !rootPrefix.endsWith( "/" ) )
{
this.rootPrefix += "/";
}
final List<FileSelector> selectors = new ArrayList<FileSelector>();
FinalizerEnabled finalizer = ( delegate instanceof FinalizerEnabled ) ? (FinalizerEnabled) delegate : null;
if ( containerDescriptorHandlers != null )
{
for ( final ContainerDescriptorHandler handler : containerDescriptorHandlers )
{
selectors.add( handler );
if ( finalizer != null )
{
finalizer.addArchiveFinalizer( handler );
}
}
}
if ( extraSelectors != null )
{
for ( final FileSelector selector : extraSelectors )
{
selectors.add( selector );
}
}
if ( ( extraFinalizers != null ) && finalizer != null )
{
for ( ArchiveFinalizer extraFinalizer : extraFinalizers )
{
finalizer.addArchiveFinalizer( extraFinalizer );
}
}
if ( !selectors.isEmpty() )
{
this.selectors = selectors.toArray( new FileSelector[selectors.size()] );
}
}
/**
* {@inheritDoc}
*/
public void addArchivedFileSet( final @Nonnull File archiveFile, final String prefix, final String[] includes,
final String[] excludes )
{
final String archiveKey = getArchiveKey( archiveFile, prefix );
if ( seenPaths.contains( archiveKey ) )
{
warn( "Archive: " + archiveFile + " has already been added. Skipping." );
return;
}
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultArchivedFileSet fs = new DefaultArchivedFileSet();
fs.setArchive( archiveFile );
fs.setIncludes( includes );
fs.setExcludes( excludes );
fs.setPrefix( rootPrefix + prefix );
fs.setFileSelectors( selectors );
debug( "Adding archived file-set in: " + archiveFile + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addArchivedFileSet( fs );
seenPaths.add( archiveKey );
}
}
finally
{
inPublicApi.set( null );
}
}
private String getArchiveKey( final File archiveFile, final String prefix )
{
return archiveFile.getAbsolutePath() + ":" + prefix;
}
private void debug( final String message )
{
if ( ( logger != null ) && logger.isDebugEnabled() )
{
logger.debug( message );
}
}
private void warn( final String message )
{
if ( ( logger != null ) && logger.isWarnEnabled() )
{
logger.warn( message );
}
}
/**
* {@inheritDoc}
*/
public void addArchivedFileSet( final @Nonnull File archiveFile, final String prefix )
throws ArchiverException
{
final String archiveKey = getArchiveKey( archiveFile, prefix );
if ( seenPaths.contains( archiveKey ) )
{
warn( "Archive: " + archiveFile + " has already been added. Skipping." );
return;
}
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultArchivedFileSet fs = new DefaultArchivedFileSet();
fs.setArchive( archiveFile );
fs.setPrefix( rootPrefix + prefix );
fs.setFileSelectors( selectors );
debug( "Adding archived file-set in: " + archiveFile + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addArchivedFileSet( fs );
seenPaths.add( archiveKey );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addArchivedFileSet( final File archiveFile, final String[] includes, final String[] excludes )
throws ArchiverException
{
final String archiveKey = getArchiveKey( archiveFile, "" );
if ( seenPaths.contains( archiveKey ) )
{
warn( "Archive: " + archiveFile + " has already been added. Skipping." );
return;
}
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultArchivedFileSet fs = new DefaultArchivedFileSet();
fs.setArchive( archiveFile );
fs.setIncludes( includes );
fs.setExcludes( excludes );
fs.setPrefix( rootPrefix );
fs.setFileSelectors( selectors );
debug( "Adding archived file-set in: " + archiveFile + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addArchivedFileSet( fs );
seenPaths.add( archiveKey );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addArchivedFileSet( final @Nonnull File archiveFile )
throws ArchiverException
{
final String archiveKey = getArchiveKey( archiveFile, "" );
if ( seenPaths.contains( archiveKey ) )
{
warn( "Archive: " + archiveFile + " has already been added. Skipping." );
return;
}
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultArchivedFileSet fs = new DefaultArchivedFileSet();
fs.setArchive( archiveFile );
fs.setPrefix( rootPrefix );
fs.setFileSelectors( selectors );
debug( "Adding archived file-set in: " + archiveFile + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addArchivedFileSet( fs );
seenPaths.add( archiveKey );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addDirectory( final @Nonnull File directory, final String prefix, final String[] includes,
final String[] excludes )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultFileSet fs = new DefaultFileSet();
fs.setDirectory( directory );
fs.setIncludes( includes );
fs.setExcludes( excludes );
fs.setPrefix( rootPrefix + prefix );
fs.setFileSelectors( selectors );
debug( "Adding directory file-set in: " + directory + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
doAddFileSet( fs );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addSymlink( String symlinkName, String symlinkDestination )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addSymlink( symlinkName, symlinkDestination );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addSymlink( String symlinkName, int permissions, String symlinkDestination )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addSymlink( symlinkName, permissions, symlinkDestination );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addDirectory( final @Nonnull File directory, final String prefix )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultFileSet fs = new DefaultFileSet();
fs.setDirectory( directory );
fs.setPrefix( rootPrefix + prefix );
fs.setFileSelectors( selectors );
debug( "Adding directory file-set in: " + directory + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
doAddFileSet( fs );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addDirectory( final @Nonnull File directory, final String[] includes, final String[] excludes )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultFileSet fs = new DefaultFileSet();
fs.setDirectory( directory );
fs.setIncludes( includes );
fs.setExcludes( excludes );
fs.setPrefix( rootPrefix );
fs.setFileSelectors( selectors );
debug( "Adding directory file-set in: " + directory + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
doAddFileSet( fs );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addDirectory( final @Nonnull File directory )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
final DefaultFileSet fs = new DefaultFileSet();
fs.setDirectory( directory );
fs.setPrefix( rootPrefix );
fs.setFileSelectors( selectors );
debug( "Adding directory file-set in: " + directory + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
doAddFileSet( fs );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addFile( final @Nonnull File inputFile, final @Nonnull String destFileName, final int permissions )
throws ArchiverException
{
if ( acceptFile( inputFile ) )
{
inPublicApi.set( Boolean.TRUE );
try
{
debug( "Adding file: " + inputFile + " to archive location: " + rootPrefix + destFileName );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addFile( inputFile, rootPrefix + destFileName, permissions );
}
}
finally
{
inPublicApi.set( null );
}
}
}
/**
* {@inheritDoc}
*/
public void addFile( final @Nonnull File inputFile, final @Nonnull String destFileName )
throws ArchiverException
{
if ( acceptFile( inputFile ) )
{
inPublicApi.set( Boolean.TRUE );
try
{
debug( "Adding file: " + inputFile + " to archive location: " + rootPrefix + destFileName );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addFile( inputFile, rootPrefix + destFileName );
}
}
finally
{
inPublicApi.set( null );
}
}
}
/**
* {@inheritDoc}
*/
public void createArchive()
throws ArchiverException, IOException
{
inPublicApi.set( Boolean.TRUE );
try
{
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.setForced( forced );
delegate.createArchive();
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public int getDefaultDirectoryMode()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.getDefaultDirectoryMode();
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public int getDefaultFileMode()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.getDefaultFileMode();
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public File getDestFile()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.getDestFile();
}
finally
{
inPublicApi.set( null );
}
}
@SuppressWarnings( { "rawtypes", "deprecation" } )
/** {@inheritDoc} */ public Map<String, ArchiveEntry> getFiles()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.getFiles();
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public boolean getIncludeEmptyDirs()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.getIncludeEmptyDirs();
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public boolean isForced()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.isForced();
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public boolean isSupportingForced()
{
inPublicApi.set( Boolean.TRUE );
try
{
return delegate.isSupportingForced();
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setDefaultDirectoryMode( final int mode )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setDefaultDirectoryMode( mode );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setDefaultFileMode( final int mode )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setDefaultFileMode( mode );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setDestFile( final File destFile )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setDestFile( destFile );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setForced( final boolean forced )
{
inPublicApi.set( Boolean.TRUE );
try
{
this.forced = forced;
delegate.setForced( forced );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setIncludeEmptyDirs( final boolean includeEmptyDirs )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setIncludeEmptyDirs( includeEmptyDirs );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setDotFileDirectory( final File dotFileDirectory )
{
throw new UnsupportedOperationException(
"Undocumented feature of plexus-archiver; this is not yet supported." );
}
/**
* {@inheritDoc}
*/
public void addArchivedFileSet( final ArchivedFileSet fileSet )
throws ArchiverException
{
final String archiveKey = getArchiveKey( fileSet.getArchive(), "" );
if ( seenPaths.contains( archiveKey ) )
{
warn( "Archive: " + fileSet.getArchive() + " has already been added. Skipping." );
return;
}
inPublicApi.set( Boolean.TRUE );
try
{
final PrefixedArchivedFileSet fs = new PrefixedArchivedFileSet( fileSet, rootPrefix, selectors );
debug( "Adding archived file-set in: " + fileSet.getArchive() + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
delegate.addArchivedFileSet( fs );
seenPaths.add( archiveKey );
}
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addFileSet( final @Nonnull FileSet fileSet )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
final PrefixedFileSet fs = new PrefixedFileSet( fileSet, rootPrefix, selectors );
debug( "Adding file-set in: " + fileSet.getDirectory() + " to archive location: " + fs.getPrefix() );
if ( dryRun )
{
debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
}
else
{
doAddFileSet( fs );
}
}
finally
{
inPublicApi.set( null );
}
}
private void doAddFileSet( final FileSet fs )
throws ArchiverException
{
final String fsPath = fs.getDirectory().getAbsolutePath().replace( '\\', '/' );
if ( fsPath.equals( assemblyWorkPath ) )
{
logger.debug( "SKIPPING fileset with source directory matching assembly working-directory: " + fsPath );
}
else if ( assemblyWorkPath.startsWith( fsPath ) )
{
final List<String> newEx = new ArrayList<String>();
if ( fs.getExcludes() != null )
{
newEx.addAll( Arrays.asList( fs.getExcludes() ) );
}
final String workDirExclude = assemblyWorkPath.substring( fsPath.length() + 1 );
logger.debug(
"Adding exclude for assembly working-directory: " + workDirExclude + "\nFile-Set source directory: "
+ fsPath );
newEx.add( workDirExclude );
final List<String> newIn = new ArrayList<String>();
if ( fs.getIncludes() != null )
{
for ( final String include : fs.getIncludes() )
{
if ( !include.startsWith( workDirExclude ) )
{
newIn.add( include );
}
}
}
final DefaultFileSet dfs = new DefaultFileSet();
dfs.setCaseSensitive( fs.isCaseSensitive() );
dfs.setDirectory( fs.getDirectory() );
dfs.setExcludes( newEx.toArray( new String[newEx.size()] ) );
dfs.setFileSelectors( fs.getFileSelectors() );
dfs.setIncludes( newIn.toArray( new String[newIn.size()] ) );
dfs.setIncludingEmptyDirectories( fs.isIncludingEmptyDirectories() );
dfs.setPrefix( fs.getPrefix() );
dfs.setUsingDefaultExcludes( fs.isUsingDefaultExcludes() );
delegate.addFileSet( dfs );
}
else
{
delegate.addFileSet( fs );
}
}
private String getMethodName()
{
final NullPointerException npe = new NullPointerException();
final StackTraceElement[] trace = npe.getStackTrace();
final StackTraceElement methodElement = trace[1];
return methodElement.getMethodName() + " (archiver line: " + methodElement.getLineNumber() + ")";
}
private boolean acceptFile( final File inputFile )
throws ArchiverException
{
if ( !Boolean.TRUE.equals( inPublicApi.get()) )
{
if ( selectors != null )
{
final FileInfo fileInfo = new DefaultFileInfo( inputFile );
for ( final FileSelector selector : selectors )
{
try
{
if ( !selector.isSelected( fileInfo ) )
{
return false;
}
}
catch ( final IOException e )
{
throw new ArchiverException(
"Error processing file: " + inputFile + " using selector: " + selector, e );
}
}
}
}
return true;
}
private static final class DefaultFileInfo
implements FileInfo
{
private final File inputFile;
DefaultFileInfo( final File inputFile )
{
this.inputFile = inputFile;
}
public InputStream getContents()
throws IOException
{
return new FileInputStream( inputFile );
}
public String getName()
{
return inputFile.getName();
}
public boolean isDirectory()
{
return inputFile.isDirectory();
}
public boolean isFile()
{
return inputFile.isFile();
}
public boolean isSymbolicLink()
{
return false;
}
}
/**
* {@inheritDoc}
*/
public void addResource( final PlexusIoResource resource, final String destFileName, final int permissions )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.addResource( resource, destFileName, permissions );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void addResources( final PlexusIoResourceCollection resources )
throws ArchiverException
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.addResources( resources );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public
@Nonnull
ResourceIterator getResources()
throws ArchiverException
{
return delegate.getResources();
}
/**
* {@inheritDoc}
*/
public String getDuplicateBehavior()
{
return delegate.getDuplicateBehavior();
}
/**
* {@inheritDoc}
*/
public void setDuplicateBehavior( final String duplicate )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setDuplicateBehavior( duplicate );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public int getDirectoryMode()
{
return delegate.getDirectoryMode();
}
/**
* {@inheritDoc}
*/
public int getFileMode()
{
return delegate.getFileMode();
}
/**
* {@inheritDoc}
*/
public int getOverrideDirectoryMode()
{
return delegate.getOverrideDirectoryMode();
}
/**
* {@inheritDoc}
*/
public int getOverrideFileMode()
{
return delegate.getOverrideFileMode();
}
/**
* {@inheritDoc}
*/
public void setDirectoryMode( final int mode )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setDirectoryMode( mode );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public void setFileMode( final int mode )
{
inPublicApi.set( Boolean.TRUE );
try
{
delegate.setFileMode( mode );
}
finally
{
inPublicApi.set( null );
}
}
/**
* {@inheritDoc}
*/
public boolean isUseJvmChmod()
{
return useJvmChmod;
}
/**
* {@inheritDoc}
*/
public void setUseJvmChmod( final boolean useJvmChmod )
{
this.useJvmChmod = useJvmChmod;
}
/**
* {@inheritDoc}
*/
public boolean isIgnorePermissions()
{
return delegate.isIgnorePermissions();
}
/**
* {@inheritDoc}
*/
public void setIgnorePermissions( final boolean ignorePermissions )
{
delegate.setIgnorePermissions( ignorePermissions );
}
}