org.codehaus.plexus.archiver.zip.PlexusIoZipFileResourceCollection Maven / Gradle / Ivy
/*
* Copyright 2007 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.
*/
package org.codehaus.plexus.archiver.zip;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.jar.JarFile;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier;
import org.codehaus.plexus.components.io.resources.AbstractPlexusIoArchiveResourceCollection;
import org.codehaus.plexus.components.io.resources.EncodingSupported;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.components.io.resources.PlexusIoURLResource;
public class PlexusIoZipFileResourceCollection
extends AbstractPlexusIoArchiveResourceCollection
implements EncodingSupported
{
/**
* The zip file resource collections role hint.
*/
public static final String ROLE_HINT = "zipFile";
/**
* The zip file resource collections role hint for jar files.
*/
public static final String JAR_ROLE_HINT = "jarFile";
private Charset charset = Charset.forName( "UTF-8" );
public PlexusIoZipFileResourceCollection()
{
}
@Override
public boolean isConcurrentAccessSupported()
{
// Maybe we could support concurrent some time in the future
return false;
}
@Override
protected Iterator getEntries()
throws IOException
{
final File f = getFile();
if ( f == null )
{
throw new IOException( "The zip file has not been set." );
}
final URLClassLoader urlClassLoader = new URLClassLoader( new URL[]
{
f.toURI().toURL()
}, null )
{
@Override
public URL getResource( String name )
{
return findResource( name );
}
};
final URL url = new URL( "jar:" + f.toURI().toURL() + "!/" );
final JarFile jarFile = new JarFile( f );
final ZipFile zipFile = new ZipFile( f, charset != null ? charset.name() : "UTF8" );
final Enumeration en = zipFile.getEntriesInPhysicalOrder();
return new ZipFileResourceIterator( en, url, jarFile, zipFile, urlClassLoader );
}
private static class ZipFileResourceIterator
implements Iterator, Closeable
{
private class ZipFileResource
extends PlexusIoURLResource
{
private final JarFile jarFile;
private ZipFileResource( JarFile jarFile, ZipArchiveEntry entry )
{
super( entry.getName(),
entry.getTime() == -1 ? PlexusIoResource.UNKNOWN_MODIFICATION_DATE : entry.getTime(),
entry.isDirectory() ? PlexusIoResource.UNKNOWN_RESOURCE_SIZE : entry.getSize(),
!entry.isDirectory(), entry.isDirectory(), true );
this.jarFile = jarFile;
}
@Override
public InputStream getContents() throws IOException {
// Uses the JarFile to get the input stream for the entry.
// The super method will do the same, why overriding it?
// But it will create new JarFile for every entry
// and that could be very expensive in some cases (when the JAR is signed).
// Enabling the URLConnection cache would solve the problem
// but the cache is global and shared during the build so
// if the AJR file changed during the causes another problem
// (see plexus-io#2).
// Using local JarFile instance solves the two issues -
// JarFile is initialized once so there is no performance penalty
// and it is local so if the file changed during the build
// would not be a problem.
// And we know the URL returned by getURL is part of the JAR
// because that is how we constructed it.
return jarFile.getInputStream( jarFile.getEntry( getName() ) );
}
@Override
public URL getURL()
throws IOException
{
String spec = getName();
if ( spec.startsWith( "/" ) )
{
// Code path for PLXCOMP-170. Note that urlClassloader does not seem to produce correct
// urls for this. Which again means files loaded via this path cannot have file names
// requiring url encoding
spec = "./" + spec;
return new URL( url, spec );
}
return urlClassLoader.getResource( spec );
}
}
private class ZipFileSymlinkResource
extends ZipFileResource
implements SymlinkDestinationSupplier
{
private final ZipArchiveEntry entry;
private ZipFileSymlinkResource( JarFile jarFile, ZipArchiveEntry entry )
{
super( jarFile, entry );
this.entry = entry;
}
@Override
public String getSymlinkDestination()
throws IOException
{
return zipFile.getUnixSymlink( entry );
}
@Override
public boolean isSymbolicLink()
{
return true;
}
}
private final Enumeration en;
private final URL url;
private final JarFile jarFile;
private final ZipFile zipFile;
private final URLClassLoader urlClassLoader;
public ZipFileResourceIterator( Enumeration en, URL url, JarFile jarFile, ZipFile zipFile,
URLClassLoader urlClassLoader )
{
this.en = en;
this.url = url;
this.jarFile = jarFile;
this.zipFile = zipFile;
this.urlClassLoader = urlClassLoader;
}
@Override
public boolean hasNext()
{
return en.hasMoreElements();
}
@Override
public PlexusIoResource next()
{
final ZipArchiveEntry entry = en.nextElement();
return entry.isUnixSymlink()
? new ZipFileSymlinkResource( jarFile, entry )
: new ZipFileResource( jarFile, entry );
}
@Override
public void remove()
{
throw new UnsupportedOperationException( "Removing isn't implemented." );
}
@Override
public void close()
throws IOException
{
try
{
urlClassLoader.close();
}
finally
{
try
{
zipFile.close();
} finally
{
jarFile.close();
}
}
}
}
@Override
public void setEncoding( Charset charset )
{
this.charset = charset;
}
}