package org.codehaus.plexus.archiver.diags;

/*
 * 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.archiver.ArchivedFileSet;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.FileSet;
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.IOException;

/**
 * A dry run archiver that does nothing. Some methods fall through to the underlying
 * archiver, but no actions are executed.
 *
dry-running (where the delegate archiver is never actually called)

 *

 */
public class DryRunArchiver
    extends DelgatingArchiver
{

    private final Logger logger;

    public DryRunArchiver( final Archiver target, final Logger logger )
    {
        super( target );
        this.logger = logger;
    }

    /**
     * {@inheritDoc}
     */
    public void addArchivedFileSet( final @Nonnull File archiveFile, final String prefix, final String[] includes,
                                    final String[] excludes )
    {

        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    private void debug( final String message )
    {
        if ( ( logger != null ) && logger.isDebugEnabled() )
        {
            logger.debug( message );
        }
    }

    /**
     * {@inheritDoc}
     */
    public void addArchivedFileSet( final @Nonnull File archiveFile, final String prefix )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addArchivedFileSet( final File archiveFile, final String[] includes, final String[] excludes )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addArchivedFileSet( final @Nonnull File archiveFile )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addDirectory( final @Nonnull File directory, final String prefix, final String[] includes,
                              final String[] excludes )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );

    }

    /**
     * {@inheritDoc}
     */
    public void addSymlink( String symlinkName, String symlinkDestination )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addSymlink( String symlinkName, int permissions, String symlinkDestination )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addDirectory( final @Nonnull File directory, final String prefix )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addDirectory( final @Nonnull File directory, final String[] includes, final String[] excludes )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addDirectory( final @Nonnull File directory )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addFile( final @Nonnull File inputFile, final @Nonnull String destFileName, final int permissions )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addFile( final @Nonnull File inputFile, final @Nonnull String destFileName )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void createArchive()
        throws ArchiverException, IOException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@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
    {

        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    /**
     * {@inheritDoc}
     */
    public void addFileSet( final @Nonnull FileSet fileSet )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    @Override
    public void addResource( PlexusIoResource resource, String destFileName, int permissions )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    @Override
    public void addResources( PlexusIoResourceCollection resources )
        throws ArchiverException
    {
        debug( "DRY RUN: Skipping delegated call to: " + getMethodName() );
    }

    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() + ")";
    }

}


package org.codehaus.plexus.archiver;

/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import javax.annotation.Nonnull;

import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.components.io.attributes.Java7Reflector;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.components.io.functions.ResourceAttributeSupplier;
import org.codehaus.plexus.components.io.resources.AbstractPlexusIoResourceCollection;
import org.codehaus.plexus.components.io.resources.PlexusIoArchivedResourceCollection;
import org.codehaus.plexus.components.io.resources.PlexusIoFileResourceCollection;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection;
import org.codehaus.plexus.components.io.resources.proxy.PlexusIoProxyResourceCollection;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.util.Os;

import static org.codehaus.plexus.archiver.util.DefaultArchivedFileSet.archivedFileSet;
import static org.codehaus.plexus.archiver.util.DefaultFileSet.fileSet;

/**
 * @version $Id$
 */
public abstract class AbstractArchiver
    extends AbstractLogEnabled
    implements Archiver, Contextualizable, FinalizerEnabled
{

    private Logger logger;

    private File destFile;

    /**
     * A list of the following objects:
     *

     *
Instances of {@link ArchiveEntry}, which are passed back by {@link #getResources()} without modifications.

     *
Instances of {@link PlexusIoResourceCollection}, which are converted into an {@link Iterator} over instances
     * of {@link ArchiveEntry} by {@link #getResources()}.
     *

     */
    private final List<Object> resources = new ArrayList<Object>();

    private boolean includeEmptyDirs = true;

    private int fileMode = -1;

    private int directoryMode = -1;

    private int defaultFileMode = -1;

    private int defaultDirectoryMode = -1;

    private boolean forced = true;

    private List<ArchiveFinalizer> finalizers;

    private File dotFileDirectory;

    private String duplicateBehavior = Archiver.DUPLICATES_SKIP;

    // On lunix-like systems, we replace windows backslashes with forward slashes
    private final boolean replacePathSlashesToJavaPaths = File.separatorChar == '/';

    /**
     * since 2.2 is on by default
     *
     * @since 1.1
     */
    private boolean useJvmChmod = true;

    // contextualized.
    private ArchiverManager archiverManager;

    /**
     * @since 1.1
     */
    private boolean ignorePermissions = false;

    public String getDuplicateBehavior()
    {
        return duplicateBehavior;
    }

    public void setDuplicateBehavior( final String duplicate )
    {
        if ( !Archiver.DUPLICATES_VALID_BEHAVIORS.contains( duplicate ) )
        {
            throw new IllegalArgumentException(
                "Invalid duplicate-file behavior: \'" + duplicate + "\'. Please specify one of: "
                    + Archiver.DUPLICATES_VALID_BEHAVIORS );
        }

        duplicateBehavior = duplicate;
    }

    public final void setFileMode( final int mode )
    {
        if ( mode >= 0 )
        {
            fileMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.FILE_FLAG;
        }
        else
        {
            fileMode = -1;
        }
    }

    public final void setDefaultFileMode( final int mode )
    {
        defaultFileMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.FILE_FLAG;
    }

    public final int getOverrideFileMode()
    {
        return fileMode;
    }

    public final int getFileMode()
    {
        if ( fileMode < 0 )
        {
            if ( defaultFileMode < 0 )
            {
                return DEFAULT_FILE_MODE;
            }

            return defaultFileMode;
        }

        return fileMode;
    }

    public final int getDefaultFileMode()
    {
        return defaultFileMode;
    }

    /**
     * @deprecated Use {@link Archiver#getDefaultFileMode()}.
     */
    public final int getRawDefaultFileMode()
    {
        return getDefaultFileMode();
    }

    public final void setDirectoryMode( final int mode )
    {
        if ( mode >= 0 )
        {
            directoryMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.DIR_FLAG;
        }
        else
        {
            directoryMode = -1;
        }
    }

    public final void setDefaultDirectoryMode( final int mode )
    {
        defaultDirectoryMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.DIR_FLAG;
    }

    public final int getOverrideDirectoryMode()
    {
        return directoryMode;
    }

    public final int getDirectoryMode()
    {
        if ( directoryMode < 0 )
        {
            if ( defaultDirectoryMode < 0 )
            {
                return DEFAULT_DIR_MODE;
            }

            return defaultDirectoryMode;
        }

        return directoryMode;
    }

    public final int getDefaultDirectoryMode()
    {
        if ( defaultDirectoryMode < 0 )
        {
            return DEFAULT_DIR_MODE;
        }
        else
        {
            return defaultDirectoryMode;
        }
    }

    public boolean getIncludeEmptyDirs()
    {
        return includeEmptyDirs;
    }

    public void setIncludeEmptyDirs( final boolean includeEmptyDirs )
    {
        this.includeEmptyDirs = includeEmptyDirs;
    }

    public void addDirectory( @Nonnull final File directory )
        throws ArchiverException
    {
        addFileSet(
            fileSet( directory ).prefixed( "" ).includeExclude( null, null ).includeEmptyDirs( includeEmptyDirs ) );
    }

    public void addDirectory( @Nonnull final File directory, final String prefix )
        throws ArchiverException
    {
        addFileSet(
            fileSet( directory ).prefixed( prefix ).includeExclude( null, null ).includeEmptyDirs( includeEmptyDirs ) );
    }

    public void addDirectory( @Nonnull final File directory, final String[] includes, final String[] excludes )
        throws ArchiverException
    {
        addFileSet( fileSet( directory ).prefixed( "" ).includeExclude( includes, excludes ).includeEmptyDirs(
            includeEmptyDirs ) );
    }

    public void addDirectory( @Nonnull final File directory, final String prefix, final String[] includes,
                              final String[] excludes )
        throws ArchiverException
    {
        addFileSet( fileSet( directory ).prefixed( prefix ).includeExclude( includes, excludes ).includeEmptyDirs(
            includeEmptyDirs ) );
    }

    public void addFileSet( @Nonnull final FileSet fileSet )
        throws ArchiverException
    {
        final File directory = fileSet.getDirectory();
        if ( directory == null )
        {
            throw new ArchiverException( "The file sets base directory is null." );
        }

        if ( !directory.isDirectory() )
        {
            throw new ArchiverException( directory.getAbsolutePath() + " isn't a directory." );
        }

        // The PlexusIoFileResourceCollection contains platform-specific File.separatorChar which
        // is an interesting cause of grief, see PLXCOMP-192
        final PlexusIoFileResourceCollection collection = new PlexusIoFileResourceCollection();
        collection.setFollowingSymLinks( !isSymlinkSupported() );

        collection.setIncludes( fileSet.getIncludes() );
        collection.setExcludes( fileSet.getExcludes() );
        collection.setBaseDir( directory );
        collection.setFileSelectors( fileSet.getFileSelectors() );
        collection.setIncludingEmptyDirectories( fileSet.isIncludingEmptyDirectories() );
        collection.setPrefix( fileSet.getPrefix() );
        collection.setCaseSensitive( fileSet.isCaseSensitive() );
        collection.setUsingDefaultExcludes( fileSet.isUsingDefaultExcludes() );
        collection.setStreamTransformer( fileSet.getStreamTransformer() );

        if ( getOverrideDirectoryMode() > -1 || getOverrideFileMode() > -1 )
        {
            collection.setOverrideAttributes( -1, null, -1, null, getOverrideFileMode(), getOverrideDirectoryMode() );
        }

        if ( getDefaultDirectoryMode() > -1 || getDefaultFileMode() > -1 )
        {
            collection.setDefaultAttributes( -1, null, -1, null, getDefaultFileMode(), getDefaultDirectoryMode() );
        }

        addResources( collection );
    }

    private boolean isSymlinkSupported()
    {
        return Os.isFamily( Os.FAMILY_UNIX ) && Java7Reflector.isAtLeastJava7();
    }

    public void addFile( @Nonnull final File inputFile, @Nonnull final String destFileName )
        throws ArchiverException
    {
        final int fileMode = getOverrideFileMode();

        addFile( inputFile, destFileName, fileMode );
    }

    public void addSymlink( String symlinkName, String symlinkDestination )
        throws ArchiverException
    {
        final int fileMode = getOverrideFileMode();

        addSymlink( symlinkName, fileMode, symlinkDestination );
    }

    public void addSymlink( String symlinkName, int permissions, String symlinkDestination )
        throws ArchiverException
    {
        doAddResource( ArchiveEntry.createSymlinkEntry( symlinkName, permissions, symlinkDestination,
                                                        getDirectoryMode() ) );
    }

    protected ArchiveEntry asArchiveEntry( @Nonnull final PlexusIoResource resource, final String destFileName,
                                           final int permissions, PlexusIoResourceCollection collection )
        throws ArchiverException
    {
        if ( !resource.isExisting() )
        {
            throw new ArchiverException( resource.getName() + " not found." );
        }

        if ( resource.isFile() )
        {
            return ArchiveEntry.createFileEntry( destFileName, resource, permissions, collection,
                                                 getDirectoryMode() );
        }
        else
        {
            return ArchiveEntry.createDirectoryEntry( destFileName, resource, permissions, getDirectoryMode() );
        }
    }

    protected ArchiveEntry asArchiveEntry( final PlexusIoResourceCollection collection,
                                           final PlexusIoResource resource )
        throws ArchiverException
    {
        final String destFileName = collection.getName( resource );

        int permissions = -1;
        if ( resource instanceof ResourceAttributeSupplier)
        {
            final PlexusIoResourceAttributes attrs = ( (ResourceAttributeSupplier) resource ).getAttributes();

            if ( attrs != null )
            {
                permissions = attrs.getOctalMode();
            }
        }

        return asArchiveEntry( resource, destFileName, permissions, collection );
    }

    public void addResource( final PlexusIoResource resource, final String destFileName, final int permissions )
        throws ArchiverException
    {
        doAddResource( asArchiveEntry( resource, destFileName, permissions, null ) );
    }

    public void addFile( @Nonnull final File inputFile, @Nonnull String destFileName, int permissions )
        throws ArchiverException
    {
        if ( !inputFile.isFile() || !inputFile.exists() )
        {
            throw new ArchiverException( inputFile.getAbsolutePath() + " isn't a file." );
        }

        if ( replacePathSlashesToJavaPaths )
        {
            destFileName = destFileName.replace( '\\', '/' );
        }

        if ( permissions < 0 )
        {
            permissions = getOverrideFileMode();
        }

        try
        {
            // do a null check here, to avoid creating a file stream if there are no filters...
                doAddResource(
                    ArchiveEntry.createFileEntry( destFileName, inputFile, permissions, getDirectoryMode() ) );
        }
        catch ( final IOException e )
        {
            throw new ArchiverException( "Failed to determine inclusion status for: " + inputFile, e );
        }
    }

    @Nonnull
    public ResourceIterator getResources()
        throws ArchiverException
    {
        return new ResourceIterator()
        {
            private final Iterator addedResourceIter = resources.iterator();

            private PlexusIoResourceCollection currentResourceCollection;

            private Iterator ioResourceIter;

            private ArchiveEntry nextEntry;

            private final Set<String> seenEntries = new HashSet<String>();

            public boolean hasNext()
            {
                do
                {
                    if ( nextEntry == null )
                    {
                        if ( ioResourceIter == null )
                        {
                            if ( addedResourceIter.hasNext() )
                            {
                                final Object o = addedResourceIter.next();
                                if ( o instanceof ArchiveEntry )
                                {
                                    nextEntry = (ArchiveEntry) o;
                                }
                                else if ( o instanceof PlexusIoResourceCollection )
                                {
                                    currentResourceCollection = (PlexusIoResourceCollection) o;

                                    try
                                    {
                                        ioResourceIter = currentResourceCollection.getResources();
                                    }
                                    catch ( final IOException e )
                                    {
                                        throw new ArchiverException( e.getMessage(), e );
                                    }
                                }
                                else
                                {
                                    return throwIllegalResourceType( o );
                                }
                            }
                            else
                            {
                                nextEntry = null;
                            }
                        }
                        else
                        {
                            if ( ioResourceIter.hasNext() )
                            {
                                final PlexusIoResource resource = (PlexusIoResource) ioResourceIter.next();
                                nextEntry = asArchiveEntry( currentResourceCollection, resource );
                            }
                            else
                            {
                                ioResourceIter = null;
                            }
                        }
                    }

                    if ( nextEntry != null && seenEntries.contains( normalizedForDuplicateCheck(nextEntry) ))
                    {
                        final String path = nextEntry.getName();

                        if ( Archiver.DUPLICATES_PRESERVE.equals( duplicateBehavior )
                            || Archiver.DUPLICATES_SKIP.equals( duplicateBehavior ) )
                        {
                            if ( nextEntry.getType() == ArchiveEntry.FILE )
                            {
                                getLogger().debug( path + " already added, skipping" );
                            }

                            nextEntry = null;
                        }
                        else if ( Archiver.DUPLICATES_FAIL.equals( duplicateBehavior ) )
                        {
                            throw new ArchiverException(
                                "Duplicate file " + path + " was found and the duplicate " + "attribute is 'fail'." );
                        }
                        else
                        {
                            // duplicate equal to add, so we continue
                            getLogger().debug( "duplicate file " + path + " found, adding." );
                        }
                    }
                }
                while ( nextEntry == null && !( ioResourceIter == null && !addedResourceIter.hasNext() ) );

                return nextEntry != null;
            }

            private boolean throwIllegalResourceType( Object o )
            {
                throw new IllegalStateException(
                    "An invalid resource of type: " + o.getClass().getName() + " was added to archiver: "
                        + getClass().getName() );
            }

            public ArchiveEntry next()
            {
                if ( !hasNext() )
                {
                    throw new NoSuchElementException();
                }

                final ArchiveEntry next = nextEntry;
                nextEntry = null;

                seenEntries.add( normalizedForDuplicateCheck(next) );

                return next;
            }

            public void remove()
            {
                throw new UnsupportedOperationException( "Does not support iterator" );
            }

            private String normalizedForDuplicateCheck(ArchiveEntry entry){
                return entry.getName().replace( '\\', '/'  );
            }

        };

    }

    public Map<String, ArchiveEntry> getFiles()
    {
        try
        {
            final Map<String, ArchiveEntry> map = new HashMap<String, ArchiveEntry>();
            for ( final ResourceIterator iter = getResources(); iter.hasNext(); )
            {
                final ArchiveEntry entry = iter.next();
                if ( includeEmptyDirs || entry.getType() == ArchiveEntry.FILE )
                {
                    map.put( entry.getName(), entry );
                }
            }
            return map;
        }
        catch ( final ArchiverException e )
        {
            throw new UndeclaredThrowableException( e );
        }
    }

    public File getDestFile()
    {
        return destFile;
    }

    public void setDestFile( final File destFile )
    {
        this.destFile = destFile;

        if ( destFile != null )
        {
            destFile.getParentFile().mkdirs();
        }
    }

    protected Logger getLogger()
    {
        if ( logger == null )
        {
            if ( super.getLogger() != null )
            {
                logger = super.getLogger();
            }
            else
            {
                logger = new ConsoleLogger( Logger.LEVEL_INFO, "console" );
            }
        }

        return logger;
    }

    protected PlexusIoResourceCollection asResourceCollection( final ArchivedFileSet fileSet )
        throws ArchiverException
    {
        final File archiveFile = fileSet.getArchive();

        final PlexusIoResourceCollection resources;
        try
        {
            resources = archiverManager.getResourceCollection( archiveFile );
        }
        catch ( final NoSuchArchiverException e )
        {
            throw new ArchiverException(
                "Error adding archived file-set. PlexusIoResourceCollection not found for: " + archiveFile, e );
        }

        if ( resources instanceof PlexusIoArchivedResourceCollection )
        {
            ( (PlexusIoArchivedResourceCollection) resources ).setFile( fileSet.getArchive() );
        }
        else
        {
            throw new ArchiverException( "Expected " + PlexusIoArchivedResourceCollection.class.getName() + ", got "
                                             + resources.getClass().getName() );
        }

        if ( resources instanceof AbstractPlexusIoResourceCollection )
        {
            ( (AbstractPlexusIoResourceCollection) resources ).setStreamTransformer( fileSet.getStreamTransformer() );
        }
        final PlexusIoProxyResourceCollection proxy = new PlexusIoProxyResourceCollection( resources );

        proxy.setExcludes( fileSet.getExcludes() );
        proxy.setIncludes( fileSet.getIncludes() );
        proxy.setIncludingEmptyDirectories( fileSet.isIncludingEmptyDirectories() );
        proxy.setCaseSensitive( fileSet.isCaseSensitive() );
        proxy.setPrefix( fileSet.getPrefix() );
        proxy.setUsingDefaultExcludes( fileSet.isUsingDefaultExcludes() );
        proxy.setFileSelectors( fileSet.getFileSelectors() );
        proxy.setStreamTransformer( fileSet.getStreamTransformer() );

        if ( getOverrideDirectoryMode() > -1 || getOverrideFileMode() > -1 )
        {
            proxy.setOverrideAttributes( -1, null, -1, null, getOverrideFileMode(), getOverrideDirectoryMode() );
        }

        if ( getDefaultDirectoryMode() > -1 || getDefaultFileMode() > -1 )
        {
            proxy.setDefaultAttributes( -1, null, -1, null, getDefaultFileMode(), getDefaultDirectoryMode() );
        }

        return proxy;
    }

    /**
     * Adds a resource collection to the archive.
     */
    public void addResources( final PlexusIoResourceCollection collection )
        throws ArchiverException
    {
        doAddResource( collection );
    }

    private void doAddResource(Object item){
        resources.add( item);
    }

    public void addArchivedFileSet( final ArchivedFileSet fileSet )
        throws ArchiverException
    {
        final PlexusIoResourceCollection resourceCollection = asResourceCollection( fileSet );
        addResources( resourceCollection );
    }

    /**
     * @since 1.0-alpha-7
     */
    public void addArchivedFileSet( @Nonnull final File archiveFile, final String prefix, final String[] includes,
                                    final String[] excludes )
        throws ArchiverException
    {
        addArchivedFileSet(
            archivedFileSet( archiveFile ).prefixed( prefix ).includeExclude( includes, excludes ).includeEmptyDirs(
                includeEmptyDirs ) );
    }

    /**
     * @since 1.0-alpha-7
     */
    public void addArchivedFileSet( @Nonnull final File archiveFile, final String prefix )
        throws ArchiverException
    {
        addArchivedFileSet( archivedFileSet( archiveFile ).prefixed( prefix ).includeEmptyDirs( includeEmptyDirs ) );
    }

    /**
     * @since 1.0-alpha-7
     */
    public void addArchivedFileSet( @Nonnull final File archiveFile, final String[] includes, final String[] excludes )
        throws ArchiverException
    {
        addArchivedFileSet(
            archivedFileSet( archiveFile ).includeExclude( includes, excludes ).includeEmptyDirs( includeEmptyDirs ) );
    }

    /**
     * @since 1.0-alpha-7
     */
    public void addArchivedFileSet( @Nonnull final File archiveFile )
        throws ArchiverException
    {
        addArchivedFileSet( archivedFileSet( archiveFile ).includeEmptyDirs( includeEmptyDirs ) );
    }

    /**
     * Allows us to pull the ArchiverManager instance out of the container without causing a chicken-and-egg
     * instantiation/composition problem.
     */
    public void contextualize( final Context context )
        throws ContextException
    {
        final PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );

        try
        {
            archiverManager = (ArchiverManager) container.lookup( ArchiverManager.ROLE );
        }
        catch ( final ComponentLookupException e )
        {
            throw new ContextException( "Error retrieving ArchiverManager instance: " + e.getMessage(), e );
        }
    }

    public boolean isForced()
    {
        return forced;
    }

    public void setForced( final boolean forced )
    {
        this.forced = forced;
    }

    public void addArchiveFinalizer( final ArchiveFinalizer finalizer )
    {
        if ( finalizers == null )
        {
            finalizers = new ArrayList<ArchiveFinalizer>();
        }

        finalizers.add( finalizer );
    }

    public void setArchiveFinalizers( final List<ArchiveFinalizer> archiveFinalizers )
    {
        finalizers = archiveFinalizers;
    }

    public void setDotFileDirectory( final File dotFileDirectory )
    {
        this.dotFileDirectory = dotFileDirectory;
    }

    protected boolean isUptodate()
        throws ArchiverException
    {
        final File zipFile = getDestFile();
        final long destTimestamp = zipFile.lastModified();
        if ( destTimestamp == 0 )
        {
            getLogger().debug( "isUp2date: false (Destination " + zipFile.getPath() + " not found.)" );
            return false; // File doesn't yet exist
        }

        final Iterator it = resources.iterator();
        if ( !it.hasNext() )
        {
            getLogger().debug( "isUp2date: false (No input files.)" );
            return false; // No timestamp to compare
        }

        while ( it.hasNext() )
        {
            final Object o = it.next();
            final long l;
            if ( o instanceof ArchiveEntry )
            {
                l = ( (ArchiveEntry) o ).getResource().getLastModified();
            }
            else if ( o instanceof PlexusIoResourceCollection )
            {
                try
                {
                    l = ( (PlexusIoResourceCollection) o ).getLastModified();
                }
                catch ( final IOException e )
                {
                    throw new ArchiverException( e.getMessage(), e );
                }
            }
            else
            {
                throw new IllegalStateException( "Invalid object type: " + o.getClass().getName() );
            }
            if ( l == PlexusIoResource.UNKNOWN_MODIFICATION_DATE )
            {
                // Don't know what to do. Safe thing is to assume not up2date.
                getLogger().debug( "isUp2date: false (Resource with unknown modification date found.)" );
                return false;
            }
            if ( l > destTimestamp )
            {
                getLogger().debug( "isUp2date: false (Resource with newer modification date found.)" );
                return false;
            }
        }

        getLogger().debug( "isUp2date: true" );
        return true;
    }

    protected boolean checkForced()
        throws ArchiverException
    {
        if ( !isForced() && isSupportingForced() && isUptodate() )
        {
            getLogger().debug( "Archive " + getDestFile() + " is uptodate." );
            return false;
        }
        return true;
    }

    public boolean isSupportingForced()
    {
        return false;
    }

    protected void runArchiveFinalizers()
        throws ArchiverException
    {
        if ( finalizers != null )
        {
            for ( final ArchiveFinalizer finalizer : finalizers )
            {
                finalizer.finalizeArchiveCreation( this );
            }
        }
    }

    public final void createArchive()
        throws ArchiverException, IOException
    {
        validate();
        try
        {
            try
            {
                if ( dotFileDirectory != null )
                {
                    addArchiveFinalizer( new DotDirectiveArchiveFinalizer( dotFileDirectory ) );
                }

                runArchiveFinalizers();

                execute();
            }
            finally
            {
                close();
            }
        }
        catch ( final IOException e )
        {
            String msg = "Problem creating " + getArchiveType() + ": " + e.getMessage();

            final StringBuffer revertBuffer = new StringBuffer();
            if ( !revert( revertBuffer ) )
            {
                msg += revertBuffer.toString();
            }

            throw new ArchiverException( msg, e );
        }
        finally
        {
            cleanUp();
        }
    }

    protected boolean hasVirtualFiles()
    {
        if ( finalizers != null )
        {
            for ( final ArchiveFinalizer finalizer : finalizers )
            {
                final List virtualFiles = finalizer.getVirtualFiles();

                if ( ( virtualFiles != null ) && !virtualFiles.isEmpty() )
                {
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean revert( final StringBuffer messageBuffer )
    {
        return true;
    }

    protected void validate()
        throws ArchiverException, IOException
    {
    }

    protected abstract String getArchiveType();

    protected abstract void close()
        throws IOException;

    protected void cleanUp()
        throws IOException
    {
        for ( Object resource : resources )
        {
            if ( resource instanceof PlexusIoProxyResourceCollection )
            {
                resource = ( (PlexusIoProxyResourceCollection) resource ).getSrc();
            }
            if ( resource instanceof Closeable )
            {
                ( (Closeable) resource ).close();
            }
        }
        resources.clear();
    }

    protected abstract void execute()
        throws ArchiverException, IOException;

    /**
     * @since 1.1
     */
    public boolean isUseJvmChmod()
    {
        return useJvmChmod;
    }

    /**
     * @since 1.1
     */
    public void setUseJvmChmod( final boolean useJvmChmod )
    {
        this.useJvmChmod = useJvmChmod;
    }

    /**
     * @since 1.1
     */
    public boolean isIgnorePermissions()
    {
        return ignorePermissions;
    }

    /**
     * @since 1.1
     */
    public void setIgnorePermissions( final boolean ignorePermissions )
    {
        this.ignorePermissions = ignorePermissions;
    }

}


package org.codehaus.plexus.archiver.gzip;

/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.archiver.ArchiverException;
import org.codehaus.plexus.archiver.util.Streams;
import org.codehaus.plexus.archiver.util.Compressor;
import org.codehaus.plexus.util.IOUtil;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

/**
 * @version $Revision$ $Date$
 */
public class GZipCompressor
    extends Compressor
{
    private GZIPOutputStream zOut;

    /**
     * perform the GZip compression operation.
     */
    public void compress()
        throws ArchiverException
    {
        try
        {
            zOut = new GZIPOutputStream( Streams.bufferedOutputStream( new FileOutputStream( getDestFile() ) ));
            compress( getSource(), zOut );
        }
        catch ( IOException ioe )
        {
            String msg = "Problem creating gzip " + ioe.getMessage();
            throw new ArchiverException( msg, ioe );
        }
    }

    public void close()
    {
        IOUtil.close( zOut );
        zOut = null;
    }
}


package org.codehaus.plexus.archiver.jar;
/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.jar.Attributes;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.util.IOUtil;

/**
 * Not part of any public API
 * @author Kristian Rosenvold
 */
class JdkManifestFactory
{
    public static java.util.jar.Manifest getDefaultManifest()
        throws ArchiverException
    {
        try
        {
            String defManifest = "/org/codehaus/plexus/archiver/jar/defaultManifest.mf";
            InputStream in = JdkManifestFactory.class.getResourceAsStream( defManifest );
            if ( in == null )
            {
                throw new ArchiverException( "Could not find default manifest: " + defManifest );
            }
            try
            {
                java.util.jar.Manifest defaultManifest = new java.util.jar.Manifest( in );

                defaultManifest.getMainAttributes().putValue( "Created-By",
                                                              System.getProperty( "java.vm.version" ) + " ("
                                                                  + System.getProperty( "java.vm.vendor" ) + ")" );
                return defaultManifest;
            }
            finally
            {
                IOUtil.close( in );
            }
        }
        catch ( IOException e )
        {
            throw new ArchiverException( "Unable to read default manifest", e );
        }
    }

    public static void merge( java.util.jar.Manifest target, java.util.jar.Manifest other, boolean overwriteMain )
    {
        if ( other != null )
        {
            final Attributes mainAttributes = target.getMainAttributes();
            if ( overwriteMain )
            {
                mainAttributes.clear();
                mainAttributes.putAll( other.getMainAttributes() );
            }
            else
            {
                mergeAttributes( mainAttributes, other.getMainAttributes() );
            }

            for ( Map.Entry<String, Attributes> o : other.getEntries().entrySet() )
            {
                Attributes ourSection = target.getAttributes( o.getKey() );
                Attributes otherSection = o.getValue();
                if ( ourSection == null )
                {
                    if ( otherSection != null )
                    {
                        target.getEntries().put( o.getKey(), (Attributes) otherSection.clone() );
                    }
                }
                else
                {
                    mergeAttributes( ourSection, otherSection );
                }
            }
        }
    }

    /**
     * Merge in another section
     *
     * @param target The target manifest of the merge
     * @param section the section to be merged with this one.
     */
    public static void mergeAttributes( java.util.jar.Attributes target, java.util.jar.Attributes section )
    {
        for ( Object o : section.keySet() )
        {
            java.util.jar.Attributes.Name key = (Attributes.Name) o;
            final Object value = section.get( o );
            // the merge file always wins
            target.put( key, value );
        }
    }

}


package org.codehaus.plexus.archiver.util;

/*
 * Copyright 2014 The Codehaus Foundation.
 *
 * Licensed 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.archiver.ArchiverException;
import org.codehaus.plexus.components.io.attributes.Java7AttributeUtils;
import org.codehaus.plexus.components.io.attributes.Java7Reflector;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

@SuppressWarnings("JavaDoc")
public final class ArchiveEntryUtils
{

    public static boolean jvmFilePermAvailable;

    static
    {
        try
        {
            jvmFilePermAvailable = File.class.getMethod( "setReadable", Boolean.TYPE) != null;
        }
        catch ( final Exception e )
        {
            // ignore exception log this ?
        }
    }

    private ArchiveEntryUtils()
    {
        // no op
    }

    /**
     * @since 1.1
     * @param file
     * @param mode
     * @param logger
     * @param useJvmChmod
     *            will use jvm file permissions not available for group level
     * @throws ArchiverException
     */
    public static void chmod( final File file, final int mode, final Logger logger, boolean useJvmChmod )
        throws ArchiverException
    {
        if ( !Os.isFamily( Os.FAMILY_UNIX ) )
        {
            return;
        }

		if (Java7Reflector.isAtLeastJava7())
		{
			try
			{
				Java7AttributeUtils.chmod(file, mode);
				return;
			} catch (IOException e)
			{
				throw new ArchiverException("Failed setting file attributes with java7+", e);
			}
		}

        final String m = Integer.toOctalString( mode & 0xfff );

        if ( useJvmChmod && !jvmFilePermAvailable )
        {
            useJvmChmod = false;
        }

        if (useJvmChmod)
        {
            applyPermissionsWithJvm( file, m, logger );
            return;
        }

        try
        {
            final Commandline commandline = new Commandline();

            commandline.setWorkingDirectory( file.getParentFile().getAbsolutePath() );

            if ( logger.isDebugEnabled() )
            {
                logger.debug( file + ": mode " + Integer.toOctalString( mode ) + ", chmod " + m );
            }

            commandline.setExecutable( "chmod" );

            commandline.createArg().setValue( m );

            final String path = file.getAbsolutePath();

            commandline.createArg().setValue( path );

            // commenting this debug statement, since it can produce VERY verbose output...
            // this method is called often during archive creation.
            // logger.debug( "Executing:\n\n" + commandline.toString() + "\n\n" );

            final CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();

            final CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer();

            final int exitCode = CommandLineUtils.executeCommandLine( commandline, stderr, stdout );

            if ( exitCode != 0 )
            {
                logger.warn( "-------------------------------" );
                logger.warn( "Standard error:" );
                logger.warn( "-------------------------------" );
                logger.warn( stderr.getOutput() );
                logger.warn( "-------------------------------" );
                logger.warn( "Standard output:" );
                logger.warn( "-------------------------------" );
                logger.warn( stdout.getOutput() );
                logger.warn( "-------------------------------" );

                throw new ArchiverException( "chmod exit code was: " + exitCode );
            }
        }
        catch ( final CommandLineException e )
        {
            throw new ArchiverException( "Error while executing chmod.", e );
        }

    }

    /**
     * jvm chmod will be used only if System property useJvmChmod set to true
     *
     * @param file
     * @param mode
     * @param logger
     * @throws ArchiverException
     */
    public static void chmod( final File file, final int mode, final Logger logger )
        throws ArchiverException
    {
        chmod( file, mode, logger, Boolean.getBoolean( "useJvmChmod" ) && jvmFilePermAvailable );
    }

    private static void applyPermissionsWithJvm( final File file, final String mode, final Logger logger )
        throws ArchiverException
    {
        final FilePermission filePermission;
        try
        {
             filePermission = FilePermissionUtils.getFilePermissionFromMode(mode, logger);
        } catch (IllegalArgumentException e)
        {
            throw new ArchiverException("Problem getting permission from mode for " + file.getPath(), e);
        }

        Method method;
        try
        {
            method = File.class.getMethod( "setReadable", Boolean.TYPE, Boolean.TYPE);

            method.invoke( file,
					filePermission.isReadable(),
					filePermission.isOwnerOnlyReadable());

            method = File.class.getMethod( "setExecutable", Boolean.TYPE, Boolean.TYPE);
            method.invoke( file,
					filePermission.isExecutable(),
					filePermission.isOwnerOnlyExecutable());

            method = File.class.getMethod( "setWritable", Boolean.TYPE, Boolean.TYPE);
            method.invoke( file,
					filePermission.isWritable(),
					filePermission.isOwnerOnlyWritable());
        }
        catch ( final Exception e )
        {
            logger.error( "error calling dynamically file permissons with jvm " + e.getMessage(), e );
            throw new ArchiverException( "error calling dynamically file permissons with jvm " + e.getMessage(), e );
        }
    }

}


package org.codehaus.plexus.archiver.zip;

/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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 java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Enumeration;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.utils.IOUtils;
import org.codehaus.plexus.archiver.AbstractUnArchiver;
import org.codehaus.plexus.archiver.ArchiveFilterException;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.util.IOUtil;

/**
 * @author Emmanuel Venisse
 * @version $Id$
 */
public abstract class AbstractZipUnArchiver
    extends AbstractUnArchiver
{
    private static final String NATIVE_ENCODING = "native-encoding";

    private String encoding = "UTF8";

    public AbstractZipUnArchiver()
    {
    }

    public AbstractZipUnArchiver( final File sourceFile )
    {
        super( sourceFile );
    }

    /**
     * Sets the encoding to assume for file names and comments.
     *

     *

     * Set to native-encoding if you want your platform's native encoding, defaults to UTF8.
     *

     */
    public void setEncoding( String encoding )
    {
        if ( NATIVE_ENCODING.equals( encoding ) )
        {
            encoding = null;
        }
        this.encoding = encoding;
    }

    private static class ZipEntryFileInfo
        implements PlexusIoResource
    {
        private final org.apache.commons.compress.archivers.zip.ZipFile  zipFile;

        private final ZipArchiveEntry zipEntry;

        ZipEntryFileInfo( final org.apache.commons.compress.archivers.zip.ZipFile zipFile, final ZipArchiveEntry zipEntry )
        {
            this.zipFile = zipFile;
            this.zipEntry = zipEntry;
        }

        public String getName()
        {
            return zipEntry.getName();
        }

        public boolean isDirectory()
        {
            return zipEntry.isDirectory();
        }

        public boolean isFile()
        {
            return !zipEntry.isDirectory() && !zipEntry.isUnixSymlink();
        }

        public boolean isSymbolicLink()
        {
            return zipEntry.isUnixSymlink();
        }

        public InputStream getContents()
            throws IOException
        {
            return zipFile.getInputStream( zipEntry );
        }

        public long getLastModified()
        {
            final long l = zipEntry.getTime();
            return l == 0 ? PlexusIoResource.UNKNOWN_MODIFICATION_DATE : l;
        }

        public long getSize()
        {
            final long l = zipEntry.getSize();
            return l == -1 ? PlexusIoResource.UNKNOWN_RESOURCE_SIZE : l;
        }

        public URL getURL()
            throws IOException
        {
            return null;
        }

        public boolean isExisting()
        {
            return true;
        }
    }

    protected void execute()
        throws ArchiverException
    {
        getLogger().debug( "Expanding: " + getSourceFile() + " into " + getDestDirectory() );
        org.apache.commons.compress.archivers.zip.ZipFile zf = null;
        try
        {
            zf = new org.apache.commons.compress.archivers.zip.ZipFile( getSourceFile(), encoding );
            final Enumeration e = zf.getEntries();
            while ( e.hasMoreElements() )
            {
                final ZipArchiveEntry ze = (ZipArchiveEntry) e.nextElement();
                final ZipEntryFileInfo fileInfo = new ZipEntryFileInfo( zf, ze );
                if ( isSelected( ze.getName(), fileInfo ) )
                {
					InputStream in = zf.getInputStream( ze );
					extractFileIfIncluded(getSourceFile(), getDestDirectory(), in, ze.getName(),
							new Date(ze.getTime()), ze.isDirectory(), ze.getUnixMode() != 0 ? ze.getUnixMode() : null,
                            resolveSymlink( zf, ze ) );
					IOUtil.close(in);
				}

			}

            getLogger().debug( "expand complete" );
        }
        catch ( final IOException ioe )
        {
            throw new ArchiverException( "Error while expanding " + getSourceFile().getAbsolutePath(), ioe );
        }
        finally
        {
			IOUtils.closeQuietly( zf);
        }
    }

    private String resolveSymlink( ZipFile zf, ZipArchiveEntry ze )
        throws IOException
    {
        if  (ze.isUnixSymlink())
            return zf.getUnixSymlink( ze );
        else return null;
    }

    private void extractFileIfIncluded( final File sourceFile, final File destDirectory, final InputStream inputStream,
                                        final String name, final Date time, final boolean isDirectory,
                                        final Integer mode, String symlinkDestination )
        throws IOException, ArchiverException
    {
          extractFile( sourceFile, destDirectory, inputStream, name, time, isDirectory, mode, symlinkDestination );
    }

    protected void execute( final String path, final File outputDirectory )
        throws ArchiverException
    {
        org.apache.commons.compress.archivers.zip.ZipFile zipFile = null;

        try
        {
            zipFile = new org.apache.commons.compress.archivers.zip.ZipFile( getSourceFile(), encoding );

            final Enumeration e = zipFile.getEntries();

            while ( e.hasMoreElements() )
            {
                final ZipArchiveEntry ze = (ZipArchiveEntry) e.nextElement();
                final ZipEntryFileInfo fileInfo = new ZipEntryFileInfo( zipFile, ze );
                if ( !isSelected( ze.getName(), fileInfo ) )
                {
                    continue;
                }

                if ( ze.getName().startsWith( path ) )
                {
                    final InputStream inputStream = zipFile.getInputStream( ze );
                    extractFileIfIncluded( getSourceFile(), outputDirectory, inputStream,
                                           ze.getName(), new Date( ze.getTime() ), ze.isDirectory(),
                                           ze.getUnixMode() != 0 ? ze.getUnixMode() : null, resolveSymlink( zipFile, ze ) );
					IOUtil.close(inputStream);
                }
            }
        }
        catch ( final IOException ioe )
        {
            throw new ArchiverException( "Error while expanding " + getSourceFile().getAbsolutePath(), ioe );
        }
        finally
        {
			IOUtils.closeQuietly( zipFile);
        }
    }
}


package org.codehaus.plexus.archiver.ear;

/*
 * Copyright  2001-2004 The Apache Software Foundation
 *
 *  Licensed 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.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.codehaus.plexus.archiver.ArchiveEntry;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.archiver.util.ResourceUtils;

import java.io.File;
import java.io.IOException;

/**
 * Creates a EAR archive. Based on WAR task
 */
public class EarArchiver
    extends JarArchiver
{

    private File deploymentDescriptor;

    private boolean descriptorAdded;

    /**
     * Create an Ear.
     */
    public EarArchiver()
    {
        super();
        archiveType = "ear";
    }

    /**
     * File to incorporate as application.xml.
     */
    public void setAppxml( File descr )
        throws ArchiverException
    {
        deploymentDescriptor = descr;
        if ( !deploymentDescriptor.exists() )
        {
            throw new ArchiverException( "Deployment descriptor: " + deploymentDescriptor + " does not exist." );
        }

        addFile( descr, "META-INF/application.xml" );
    }

    /**
     * Adds archive.
     */
    public void addArchive( File fileName )
        throws ArchiverException
    {
        addDirectory( fileName.getParentFile(), "/", new String[]{fileName.getName()}, null );
    }

    /**
     * Adds archives.
     */
    public void addArchives( File directoryName, String[] includes, String[] excludes )
        throws ArchiverException
    {
        addDirectory( directoryName, "/", includes, excludes );
    }

    protected void initZipOutputStream( ZipArchiveOutputStream zOut )
        throws ArchiverException, IOException
    {
        // If no webxml file is specified, it's an error.
        if ( deploymentDescriptor == null && !isInUpdateMode() )
        {
            throw new ArchiverException( "appxml attribute is required" );
        }

        super.initZipOutputStream( zOut );
    }

    /**
     * Overridden from ZipArchiver class to deal with application.xml
     */
    protected void zipFile( ArchiveEntry entry, ZipArchiveOutputStream zOut, String vPath, int mode )
        throws IOException, ArchiverException
    {
        // If the file being added is META-INF/application.xml, we
        // warn if it's not the one specified in the "appxml"
        // attribute - or if it's being added twice, meaning the same
        // file is specified by the "appxml" attribute and in a
        // <fileset> element.
        if ( vPath.equalsIgnoreCase( "META-INF/application.xml" ) )
        {
            if ( deploymentDescriptor == null
                 || !ResourceUtils.isCanonicalizedSame( entry.getResource(), deploymentDescriptor )
                 || descriptorAdded )
            {
                getLogger().warn( "Warning: selected " + archiveType
                                      + " files include a META-INF/application.xml which will be ignored "
                                      + "(please use appxml attribute to " + archiveType + " task)" );
            }
            else
            {
                super.zipFile( entry, zOut, vPath );
                descriptorAdded = true;
            }
        }
        else
        {
            super.zipFile( entry, zOut, vPath );
        }
    }

    /**
     * Make sure we don't think we already have a application.xml next
     * time this task gets executed.
     */
    protected void cleanUp()
        throws IOException
    {
        descriptorAdded = false;
        super.cleanUp();
    }
}


package org.codehaus.plexus.archiver.snappy;

/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.archiver.ArchiverException;
import org.codehaus.plexus.archiver.util.Compressor;
import org.codehaus.plexus.util.IOUtil;
import org.xerial.snappy.SnappyOutputStream;

import java.io.IOException;

import static org.codehaus.plexus.archiver.util.Streams.bufferedOutputStream;
import static org.codehaus.plexus.archiver.util.Streams.fileOutputStream;

/**
 * Snappy compression
 */
public class SnappyCompressor
    extends Compressor
{
    private SnappyOutputStream zOut;

    /**
     * perform the Snappy compression operation.
     */
    public void compress()
        throws ArchiverException
    {
        try
        {
            zOut = new SnappyOutputStream( bufferedOutputStream( fileOutputStream( getDestFile() ) ) );
            compress( getSource(), zOut );
        }
        catch ( IOException ioe )
        {
            String msg = "Problem creating snappy " + ioe.getMessage();
            throw new ArchiverException( msg, ioe );
        }
    }

    public void close()
    {
        IOUtil.close( zOut );
        zOut = null;
    }
}


package org.codehaus.plexus.archiver.zip;

/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.*;
import java.util.*;
import java.util.zip.CRC32;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.codehaus.plexus.archiver.AbstractArchiver;
import org.codehaus.plexus.archiver.ArchiveEntry;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.ResourceIterator;
import org.codehaus.plexus.archiver.UnixStat;
import org.codehaus.plexus.archiver.util.ResourceUtils;
import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;

import static org.codehaus.plexus.archiver.util.Streams.bufferedOutputStream;
import static org.codehaus.plexus.archiver.util.Streams.fileOutputStream;

/**
 * @version $Revision$ $Date$
 */
@SuppressWarnings({"NullableProblems", "UnusedDeclaration"})
public abstract class AbstractZipArchiver
    extends AbstractArchiver
{

    private String comment;

    /**
     * Encoding to use for filenames, defaults to the platform's
     * default encoding.
     */
    private String encoding = "UTF8";

    private boolean doCompress = true;

    private boolean recompressAddedZips = true;

    private boolean doUpdate = false;

    // shadow of the above if the value is altered in execute
    private boolean savedDoUpdate = false;

    protected String archiveType = "zip";

    /*
     * Whether the original compression of entries coming from a ZIP
     * archive should be kept (for example when updating an archive).
     */
    //not used: private boolean keepCompression = false;
    private boolean doFilesonly = false;

    protected final Hashtable<String, String> entries = new Hashtable<String, String>();

    protected final Hashtable<String, String> addedDirs = new Hashtable<String, String>();

    private static final long EMPTY_CRC = new CRC32().getValue();

    protected boolean doubleFilePass = false;

    protected boolean skipWriting = false;

    /**
     * @deprecated Use {@link Archiver#setDuplicateBehavior(String)} instead.
     */
    protected final String duplicate = Archiver.DUPLICATES_SKIP;

    /**
     * true when we are adding new files into the Zip file, as opposed
     * to adding back the unchanged files
     */
    protected boolean addingNewFiles = false;

    /**
     * Whether the file modification times will be rounded up to the
     * next even number of seconds.
	 *
	 *
Zip archives store file modification times with a
	 * granularity of two seconds, so the times will either be rounded
	 * up or down.  If you round down, the archive will always seem
	 * out-of-date when you rerun the task, so the default is to round
	 * up.  Rounding up may lead to a different type of problems like
	 * JSPs inside a web archive that seem to be slightly more recent
	 * than precompiled pages, rendering precompilation useless.
	 *
	 *
	 * plexus-archiver chooses to round up.
	 *
	 * Java versions up to java7 round timestamp down, which means we add a heuristic value (which is slightly questionable)
	 * Java versions from 8 and up round timestamp up.
	 * s
     */
    private static final boolean isJava7OrLower = Integer.parseInt(System.getProperty("java.version").split("\\.")[1]) <= 7;

	// Renamed version of original file, if it exists
    private File renamedFile = null;

    private File zipFile;

    private boolean success;

    private ZipArchiveOutputStream zOut;

    public String getComment()
    {
        return comment;
    }

    public void setComment( String comment )
    {
        this.comment = comment;
    }

    public String getEncoding()
    {
        return encoding;
    }

    public void setEncoding( String encoding )
    {
        this.encoding = encoding;
    }

    public void setCompress( boolean compress )
    {
        this.doCompress = compress;
    }

    public boolean isCompress()
    {
        return doCompress;
    }

    public boolean isRecompressAddedZips()
    {
        return recompressAddedZips;
    }

    public void setRecompressAddedZips( boolean recompressAddedZips )
    {
        this.recompressAddedZips = recompressAddedZips;
    }

    public void setUpdateMode( boolean update )
    {
        this.doUpdate = update;
        savedDoUpdate = doUpdate;
    }

    public boolean isInUpdateMode()
    {
        return doUpdate;
    }

    /**
     * If true, emulate Sun's jar utility by not adding parent directories;
     * optional, defaults to false.
     * @param f true to emilate sun jar utility
     */
    public void setFilesonly( boolean f )
    {
        doFilesonly = f;
    }

    public boolean isFilesonly()
    {
        return doFilesonly;
    }

    protected void execute()
        throws ArchiverException, IOException
    {
        if ( ! checkForced() )
        {
            return;
        }

        if ( doubleFilePass )
        {
            skipWriting = true;
            createArchiveMain();
            skipWriting = false;
            createArchiveMain();
        }
        else
        {
            createArchiveMain();
        }

        finalizeZipOutputStream( zOut );
    }

    protected void finalizeZipOutputStream( ZipArchiveOutputStream zOut )
        throws IOException, ArchiverException
    {
    }

    private void createArchiveMain()
        throws ArchiverException, IOException
    {
        //noinspection deprecation
        if ( !Archiver.DUPLICATES_SKIP.equals( duplicate ) )
        {
            //noinspection deprecation
            setDuplicateBehavior( duplicate );
        }

        ResourceIterator iter = getResources();
        if ( !iter.hasNext() && !hasVirtualFiles() )
        {
            throw new ArchiverException( "You must set at least one file." );
        }

        zipFile = getDestFile();

        if ( zipFile == null )
        {
            throw new ArchiverException( "You must set the destination " + archiveType + "file." );
        }

        if ( zipFile.exists() && !zipFile.isFile() )
        {
            throw new ArchiverException( zipFile + " isn't a file." );
        }

        if ( zipFile.exists() && !zipFile.canWrite() )
        {
            throw new ArchiverException( zipFile + " is read-only." );
        }

        // Whether or not an actual update is required -
        // we don't need to update if the original file doesn't exist

        addingNewFiles = true;

        if ( doUpdate && !zipFile.exists() )
        {
            doUpdate = false;
            getLogger().debug( "ignoring update attribute as " + archiveType + " doesn't exist." );
        }

        success = false;

        if ( doUpdate )
        {
            renamedFile = FileUtils.createTempFile( "zip", ".tmp", zipFile.getParentFile() );
            renamedFile.deleteOnExit();

            try
            {
                FileUtils.rename( zipFile, renamedFile );
            }
            catch ( SecurityException e )
            {
                getLogger().debug( e.toString() );
                throw new ArchiverException( "Not allowed to rename old file (" + zipFile.getAbsolutePath()
                    + ") to temporary file", e );
            }
            catch ( IOException e )
            {
                getLogger().debug( e.toString() );
                throw new ArchiverException( "Unable to rename old file (" + zipFile.getAbsolutePath()
                    + ") to temporary file", e );
            }
        }

        String action = doUpdate ? "Updating " : "Building ";

        getLogger().info( action + archiveType + ": " + zipFile.getAbsolutePath() );

        if ( !skipWriting )
        {
            zOut = new ZipArchiveOutputStream( bufferedOutputStream( fileOutputStream( zipFile, "zip" ) ) );
            zOut.setCreateUnicodeExtraFields( ZipArchiveOutputStream.UnicodeExtraFieldPolicy.NOT_ENCODEABLE );
            zOut.setEncoding( encoding );
            if ( doCompress )
            {
                zOut.setMethod( ZipArchiveOutputStream.DEFLATED );
            }
            else
            {
                zOut.setMethod( ZipArchiveOutputStream.STORED );
            }
        }
        initZipOutputStream( zOut );

        // Add the new files to the archive.
        addResources( iter, zOut );

        // If we've been successful on an update, delete the
        // temporary file
        if ( doUpdate )
        {
            if ( !renamedFile.delete() )
            {
                getLogger().warn( "Warning: unable to delete temporary file " + renamedFile.getName() );
            }
        }
        success = true;
    }

    protected Map<String, Long> getZipEntryNames( File file )
        throws IOException
    {
        if ( !file.exists()  ||  !doUpdate )
        {
            //noinspection unchecked
            return Collections.EMPTY_MAP;
        }
        final Map<String, Long> entries = new HashMap<String, Long>();
        final org.apache.commons.compress.archivers.zip.ZipFile zipFile = new org.apache.commons.compress.archivers.zip.ZipFile( file );
        for ( Enumeration en = zipFile.getEntries();  en.hasMoreElements();  )
        {
            ZipArchiveEntry ze = (ZipArchiveEntry) en.nextElement();
            entries.put( ze.getName(), ze.getLastModifiedDate().getTime());
        }
        return entries;
    }

    protected static boolean isFileAdded(ArchiveEntry entry, Map entries)
    {
        return !entries.containsKey( entry.getName() );
    }

    protected static boolean isFileUpdated(ArchiveEntry entry, Map entries)
    {
        Long l = (Long) entries.get( entry.getName() );
        return l != null && (l == -1 || !ResourceUtils.isUptodate(entry.getResource(), l));
    }

    /**
     * Add the given resources.
     *
     * @param resources the resources to add
     * @param zOut      the stream to write to
     */
    @SuppressWarnings({"JavaDoc"})
    protected final void addResources( ResourceIterator resources, ZipArchiveOutputStream zOut )
        throws IOException, ArchiverException
    {
        File base = null;

        while ( resources.hasNext() )
        {
            ArchiveEntry entry = resources.next();
            String name = entry.getName();
            name = name.replace( File.separatorChar, '/' );

            if ( "".equals( name ) )
            {
                continue;
            }

            if ( entry.getResource().isDirectory() && !name.endsWith( "/" ) )
            {
                name = name + "/";
            }

            addParentDirs( entry, base, name, zOut, "" );

            if ( entry.getResource().isFile() )
            {
                zipFile( entry, zOut, name );
            }
            else
            {
                zipDir( entry.getResource(), zOut, name, entry.getMode() );
            }
        }
    }

    /**
     * Ensure all parent dirs of a given entry have been added.
	 *
	 * This method is computed in terms of the potentially remapped entry (that may be disconnected from the file system)
	 * we do not *relly* know the entry's connection to the file system so establishing the attributes of the parents can
	 * be impossible and is not really supported.
	 *
     */
    @SuppressWarnings({"JavaDoc"})
    private void addParentDirs( ArchiveEntry archiveEntry, File baseDir, String entry, ZipArchiveOutputStream zOut,
                                String prefix )
        throws IOException
    {
        if ( !doFilesonly && getIncludeEmptyDirs() )
        {
            Stack<String> directories = new Stack<String>();

            // Don't include the last entry itself if it's
            // a dir; it will be added on its own.
            int slashPos = entry.length() - ( entry.endsWith( "/" ) ? 1 : 0 );

            while ( ( slashPos = entry.lastIndexOf( '/', slashPos - 1 ) ) != -1 )
            {
                String dir = entry.substring( 0, slashPos + 1 );

                if ( addedDirs.contains( prefix + dir ) )
                {
                    break;
                }

                directories.push( dir );
            }

            while ( !directories.isEmpty() )
            {
                String dir = directories.pop();
                File f;
                if ( baseDir != null )
                {
                    f = new File( baseDir, dir );
                }
                else
                {
                    f = new File( dir );
                }
				// the
				// At this point we could do something like read the atr
				final PlexusIoResource res = new AnonymousResource( f);
                zipDir( res, zOut, prefix + dir, archiveEntry.getDefaultDirMode() );
            }
        }
    }

    private void readWithZipStats( InputStream in, byte[] header, int headerRead, ZipArchiveEntry ze,
                                   ByteArrayOutputStream bos ) throws IOException {
        byte[] buffer = new byte[8 * 1024];

        CRC32 cal2 = new CRC32();

        long size = 0;

        for (int i = 0; i < headerRead; i++){
            cal2.update(header[i]);
            size++;
        }

        int count = 0;
        do
        {
            size += count;
            cal2.update( buffer, 0, count );
            if (bos != null)
            {
                bos.write( buffer, 0, count );
            }
            count = in.read( buffer, 0, buffer.length );
        }
        while ( count != -1 );
        ze.setSize(size);
        ze.setCrc(cal2.getValue());
    }

    public static long copy( final InputStream input,
                             final OutputStream output,
                             final int bufferSize )
            throws IOException
    {
        final byte[] buffer = new byte[bufferSize];
        long size = 0;
        int n;
        while ( -1 != ( n = input.read( buffer ) ) )
        {
            size += n;
            output.write( buffer, 0, n );
        }
        return size;
    }

    /**
     * Adds a new entry to the archive, takes care of duplicates as well.
     *
     * @param in           the stream to read data for the entry from.
     * @param zOut         the stream to write to.
     * @param vPath        the name this entry shall have in the archive.
     * @param lastModified last modification time for the entry.
     * @param fromArchive  the original archive we are copying this
     * @param symlinkDestination
     */
    @SuppressWarnings({"JavaDoc"})
    protected void zipFile( InputStream in, ZipArchiveOutputStream zOut, String vPath, long lastModified, File fromArchive,
                            int mode, String symlinkDestination )
        throws IOException, ArchiverException
    {
        getLogger().debug( "adding entry " + vPath );

        entries.put( vPath, vPath );

        if ( !skipWriting )
        {
            ZipArchiveEntry ze = new ZipArchiveEntry( vPath );
            setTime(ze, lastModified);

            byte[] header = new byte[4];
            int read = in.read(header);

            boolean compressThis = doCompress;
            if (!recompressAddedZips && isZipHeader(header)){
                compressThis = false;
            }

            ze.setMethod( compressThis ? ZipArchiveEntry.DEFLATED : ZipArchiveEntry.STORED );
            ze.setUnixMode( UnixStat.FILE_FLAG | mode );
            /*
             * ZipOutputStream.putNextEntry expects the ZipEntry to
             * know its size and the CRC sum before you start writing
             * the data when using STORED mode - unless it is seekable.
             *
             * This forces us to process the data twice.
             */

            if (ze.isUnixSymlink()){
                zOut.putArchiveEntry( ze );
                ZipEncoding enc = ZipEncodingHelper.getZipEncoding( getEncoding() );
                final byte[] bytes =  enc.encode( symlinkDestination).array();
                zOut.write( bytes, 0, bytes.length);
            } else if (zOut.isSeekable() || compressThis) {
                zOut.putArchiveEntry( ze );
                if (read > 0) zOut.write(header, 0, read);
                IOUtil.copy( in, zOut, 8 * 1024);
            } else {
                if (in.markSupported())
                {
                    in.mark( Integer.MAX_VALUE );
                    readWithZipStats(in, header, read, ze, null);
                    in.reset();
                    zOut.putArchiveEntry( ze);
                    if (read > 0) zOut.write(header, 0, read);
                    IOUtil.copy(in, zOut, 8 * 1024);
                }
                else
                {
                    // Store data into a byte[]
                    // todo: explain how on earth this code works with zip streams > 128KB ???
                    ByteArrayOutputStream bos = new ByteArrayOutputStream(128 * 1024);
                    readWithZipStats(in, header,read, ze, bos);
                    zOut.putArchiveEntry(ze);
                    if (read > 0) zOut.write(header, 0, read);
                    bos.writeTo( zOut);
                }
            }

            zOut.closeArchiveEntry();
        }
    }

    private boolean isZipHeader(byte[] header) {
        return header[0] == 0x50 && header[1] == 0x4b && header[2] == 03 && header[3] == 04;
    }

    /**
     * Method that gets called when adding from java.io.File instances.
     *

     *
This implementation delegates to the six-arg version.

     *
     * @param entry the file to add to the archive
     * @param zOut  the stream to write to
     * @param vPath the name this entry shall have in the archive
     */
    @SuppressWarnings({"JavaDoc"})
    protected void zipFile( ArchiveEntry entry, ZipArchiveOutputStream zOut, String vPath )
        throws IOException, ArchiverException
    {
		final PlexusIoResource resource = entry.getResource();
		if ( ResourceUtils.isSame(resource, getDestFile() ) )
        {
            throw new ArchiverException( "A zip file cannot include itself" );
        }

        final boolean b = entry.getResource() instanceof SymlinkDestinationSupplier;
        String symlinkTarget = b ? ((SymlinkDestinationSupplier)entry.getResource()).getSymlinkDestination() : null;
        InputStream in = entry.getInputStream();
        try
        {
			zipFile( in, zOut, vPath, resource.getLastModified(), null, entry.getMode(), symlinkTarget );
        }
        catch ( IOException e )
        {
            throw new ArchiverException( "IOException when zipping r" + entry.getName() + ": " + e.getMessage(), e );
        }
        finally
        {
            IOUtil.close( in );
        }
    }

	private void setTime(java.util.zip.ZipEntry zipEntry, long lastModified){
		zipEntry.setTime( lastModified + ( isJava7OrLower ? 1999 : 0 ) );

		/*   Consider adding extended file stamp support.....

		X5455_ExtendedTimestamp ts =  new X5455_ExtendedTimestamp();
		ts.setModifyJavaTime(new Date(lastModified));
		if (zipEntry.getExtra() != null){
			// Uh-oh. What do we do now.
			throw new IllegalStateException("DIdnt expect to see xtradata here ?");

		}  else {
			zipEntry.setExtra(ts.getLocalFileDataData());
		}
        */
	}

    protected void zipDir( PlexusIoResource dir, ZipArchiveOutputStream zOut, String vPath, int mode )
        throws IOException
    {
        if ( addedDirs.get( vPath ) != null )
        {
            // don't add directories we've already added.
            // no warning if we try, it is harmless in and of itself
            return;
        }

        getLogger().debug( "adding directory " + vPath );
        addedDirs.put( vPath, vPath );

        if ( !skipWriting )
        {
            final boolean isSymlink = dir instanceof SymlinkDestinationSupplier;

            if (isSymlink && vPath.endsWith(File.separator))
            {
                vPath= vPath.substring(0, vPath.length() -1);
            }

            ZipArchiveEntry ze = new ZipArchiveEntry( vPath );

            /*
             * ZipOutputStream.putNextEntry expects the ZipEntry to
             * know its size and the CRC sum before you start writing
             * the data when using STORED mode - unless it is seekable.
             *
             * This forces us to process the data twice.
             */

            if (isSymlink) mode = UnixStat.LINK_FLAG | mode;

            if ( dir != null && dir.isExisting() )
            {
                setTime( ze, dir.getLastModified());
			}
            else
            {
                // ZIPs store time with a granularity of 2 seconds, round up
                setTime( ze, System.currentTimeMillis() );
            }
            if (!isSymlink){
                ze.setSize( 0 );
                ze.setMethod( ZipArchiveEntry.STORED );
                // This is faintly ridiculous:
                ze.setCrc( EMPTY_CRC );
            }
            ze.setUnixMode( mode );

            zOut.putArchiveEntry( ze );

            if ( isSymlink )
            {
                String symlinkDestination = ( (SymlinkDestinationSupplier) dir ).getSymlinkDestination();
                ZipEncoding enc = ZipEncodingHelper.getZipEncoding( zOut.getEncoding() );
                final byte[] bytes = enc.encode( symlinkDestination ).array();
                zOut.write( bytes, 0, bytes.length );

            }

            zOut.closeArchiveEntry();
        }
    }

    /**
     * Create an empty zip file
     *
     * @param zipFile The file
     * @return true for historic reasons
     */
    @SuppressWarnings({"JavaDoc"})
    protected boolean createEmptyZip( File zipFile )
        throws ArchiverException
    {
        // In this case using java.util.zip will not work
        // because it does not permit a zero-entry archive.
        // Must create it manually.
        getLogger().info( "Note: creating empty " + archiveType + " archive " + zipFile );
        OutputStream os = null;
        try
        {
            os = new FileOutputStream( zipFile );
            // Cf. PKZIP specification.
            byte[] empty = new byte[22];
            empty[0] = 80; // P
            empty[1] = 75; // K
            empty[2] = 5;
            empty[3] = 6;
            // remainder zeros
            os.write( empty );
        }
        catch ( IOException ioe )
        {
            throw new ArchiverException( "Could not create empty ZIP archive " + "(" + ioe.getMessage() + ")", ioe );
        }
        finally
        {
            IOUtil.close( os );
        }
        return true;
    }

    /**
     * Do any clean up necessary to allow this instance to be used again.
     *

     *
When we get here, the Zip file has been closed and all we
     * need to do is to reset some globals.

     *

     *
This method will only reset globals that have been changed
     * during execute(), it will not alter the attributes or nested
     * child elements.  If you want to reset the instance so that you
     * can later zip a completely different set of files, you must use
     * the reset method.

     *
     * @see #reset
     */
    protected void cleanUp()
        throws IOException
    {
        super.cleanUp();
        addedDirs.clear();
        entries.clear();
        addingNewFiles = false;
        doUpdate = savedDoUpdate;
        success = false;
        zOut = null;
        renamedFile = null;
        zipFile = null;
    }

    /**
     * Makes this instance reset all attributes to their default
     * values and forget all children.
     *
     * @see #cleanUp
     */
    public void reset()
    {
        setDestFile( null );
//        duplicate = "add";
        archiveType = "zip";
        doCompress = true;
        doUpdate = false;
        doFilesonly = false;
        encoding = null;
    }

    /**
     * method for subclasses to override
     */
    protected void initZipOutputStream( ZipArchiveOutputStream zOut )
        throws ArchiverException, IOException
    {
    }

    /**
     * method for subclasses to override
     */
    public boolean isSupportingForced()
    {
        return true;
    }

    protected boolean revert( StringBuffer messageBuffer )
    {
        int initLength = messageBuffer.length();

        // delete a bogus ZIP file (but only if it's not the original one)
        if ( ( !doUpdate || renamedFile != null ) && !zipFile.delete() )
        {
            messageBuffer.append( " (and the archive is probably corrupt but I could not delete it)" );
        }

        if ( doUpdate && renamedFile != null )
        {
            try
            {
                FileUtils.rename( renamedFile, zipFile );
            }
            catch ( IOException e )
            {
                messageBuffer.append( " (and I couldn't rename the temporary file " );
                messageBuffer.append( renamedFile.getName() );
                messageBuffer.append( " back)" );
            }
        }

        return messageBuffer.length() == initLength;
    }

    protected void close()
        throws IOException
    {
        // Close the output stream.
        try
        {
            if ( zOut != null )
            {
                zOut.close();
            }
        }
        catch ( IOException ex )
        {
            // If we're in this finally clause because of an
            // exception, we don't really care if there's an
            // exception when closing the stream. E.g. if it
            // throws "ZIP file must have at least one entry",
            // because an exception happened before we added
            // any files, then we must swallow this
            // exception. Otherwise, the error that's reported
            // will be the close() error, which is not the
            // real cause of the problem.
            if ( success )
            {
                throw ex;
            }
        }
    }

    protected String getArchiveType()
    {
        return archiveType;
    }

}


package org.codehaus.plexus.archiver.util;

/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 *  Licensed 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.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.components.io.resources.PlexusIoFileResource;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.util.IOUtil;

import static org.codehaus.plexus.components.io.resources.ResourceFactory.createResource;

/**
 * @version $Revision$ $Date$
 */
public abstract class Compressor
    extends AbstractLogEnabled
{
    private File destFile;

    private PlexusIoResource source;

    /**
     * the required destination file.
     *
     * @param compressFile
     */
    public void setDestFile( File compressFile )
    {
        this.destFile = compressFile;
    }

    public File getDestFile()
    {
        return destFile;
    }

    /**
     * The resource to compress; required.
     */
    public void setSource( PlexusIoResource source )
    {
        this.source = source;
    }

    /**
     * The resource to compress; required.
     */
    public PlexusIoResource getSource()
    {
        return source;
    }

    /**
     * compress a stream to an output stream
     *
     * @param in
     * @param zOut
     * @throws IOException
     */
    private void compressFile( InputStream in, OutputStream zOut )
        throws IOException
    {
        byte[] buffer = new byte[8 * 1024];
        int count = 0;
        do
        {
            zOut.write( buffer, 0, count );
            count = in.read( buffer, 0, buffer.length );
        }
        while ( count != -1 );
    }

    /**
     * compress a resource to an output stream
     */
    protected void compress( PlexusIoResource resource, OutputStream zOut )
        throws IOException
    {
        InputStream in = Streams.bufferedInputStream( resource.getContents() );
        try
        {
            compressFile( in, zOut );
        }
        finally
        {
            IOUtil.close( in );
        }
    }

    /**
     * subclasses must implement this method to do their compression
     *
     * this is public so the process of compression and closing can be dealt with separately.
     */
    public abstract void compress()
        throws ArchiverException;

    /**
     * subclasses must implement this method to cleanup after compression
     *
     * this is public so the process of compression and closing can be dealt with separately.
     */
    public abstract void close();
}