/**
* Copyright (c) 2014, the Railo Company Ltd.
* Copyright (c) 2015, Lucee Assosication Switzerland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
package lucee.commons.lang;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import lucee.commons.io.IOUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.io.res.util.FileWrapper;
// TODO umbauen auf ZipInputStream oder ein wrapper schreiben fuer resorces der das file interface einhaelt
public final class ArchiveClassLoader extends ClassLoader implements Closeable {
private final ZipFile zip;
private final ClassLoader pcl;
/**
* constructor of the class
* @param file
* @param parent
* @throws IOException
*/
public ArchiveClassLoader(Resource file, ClassLoader parent) throws IOException {
super(parent);
this.pcl=parent;
//this.file=file;
//print.ln("archive:"+file.getPath());
if(!file.exists())
throw new FileNotFoundException("file "+file.getAbsolutePath()+" doesn't exist");
if(!file.isFile())
throw new IOException(file.getAbsolutePath()+" is not a file");
if(!file.isReadable())
throw new IOException("no access to "+file.getAbsolutePath()+" file");
this.zip=new ZipFile(FileWrapper.toFile(file));
}
/**
* Loads the class with the specified name. This method searches for
* classes in the same manner as the {@link #loadClass(String, boolean)}
* method. It is called by the Java virtual machine to resolve class
* references. Calling this method is equivalent to calling
* <code>loadClass(name, false)</code>.
*
* @param name the name of the class
* @return the resulting <code>Class</code> object
* @exception ClassNotFoundException if the class was not found
*/
@Override
public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* Loads the class with the specified name. The default implementation of
* this method searches for classes in the following order:<p>
*
* <ol>
* <li> Call {@link #findLoadedClass(String)} to check if the class has
* already been loaded. <p>
* <li> Call the <code>loadClass</code> method on the parent class
* loader. If the parent is <code>null</code> the class loader
* built-in to the virtual machine is used, instead. <p>
* <li> Call the {@link #findClass(String)} method to find the class. <p>
* </ol>
*
* If the class was found using the above steps, and the
* <code>resolve</code> flag is true, this method will then call the
* {@link #resolveClass(Class)} method on the resulting class object.
* <p>
* From the Java 2 SDK, v1.2, subclasses of ClassLoader are
* encouraged to override
* {@link #findClass(String)}, rather than this method.<p>
*
* @param name the name of the class
* @param resolve if <code>true</code> then resolve the class
* @return the resulting <code>Class</code> object
* @exception ClassNotFoundException if the class could not be found
*/
@Override
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
c=findClassEL(name);
if(c==null) {
c =pcl.loadClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
@Override
protected Class findClass(String name) throws ClassNotFoundException {
Class clazz=findClassEL(name);
if(clazz!=null) return clazz;
throw new ClassNotFoundException("class "+name+" not found");
}
private Class findClassEL(String name) {
byte[] barr = getBytes(name.replace('.','/').concat(".class"));
if(barr!=null) return null;
try {
int start=ClassUtil.hasCF33Prefix(barr)?10:0;
return defineClass(name,barr,start,barr.length-start);
}
catch(Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
return null;
}
}
@Override
public InputStream getResourceAsStream(String name) {
InputStream is = super.getResourceAsStream(name);
if(is!=null) return is;
byte[] barr = getBytes(name);
int start=ClassUtil.hasCF33Prefix(barr)?10:0;
if(barr!=null) return new ByteArrayInputStream(barr,start,barr.length-start);
return null;
}
@Override
public URL getResource(String name) {
return null;
}
private byte[] getBytes(String name) {
ZipEntry entry= zip.getEntry(name);
if (entry == null)return null;
int size= (int) entry.getSize();
InputStream is=null;
try {
is= zip.getInputStream(entry);
byte[] data= new byte[size];
int pos= 0;
while (pos < size) {
int n= is.read(data, pos, data.length - pos);
pos += n;
}
return data;
}
catch(IOException ioe) {}
finally {
IOUtil.closeEL(is);
}
return null;
}
@Override
public void close() throws IOException {
zip.close();
}
}