Search code examples
javajarnullpointerexceptionzipclassloader

NullPointerException in Custom ClassLoader - Can't find zipEntry


I'm making a classloader which can take a jar, and based on that, it can take a package name, and is allowed to load only classes that are in that package. When I try to load a class from there I get the error :

Exception in thread "main" java.lang.NullPointerException
at com.classloader.CustomClassLoader.getClassBytes(CustomClassLoader.java:64)
at com.classloader.CustomClassLoader.getClass(CustomClassLoader.java:48)
at com.classloader.CustomClassLoader.loadClass(CustomClassLoader.java:89)
at com.classloader.TestJar.main(TestJar.java:46)

Even though the package name and entry name are correct. Any ideas where the problem is and how to fix it ? The code is below and I believe it's pretty self explanatory.

CustomLoader

package com.classloader;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

public class CustomClassLoader extends ClassLoader implements LoaderConstraints {

private Map<String, Class> loaded = new HashMap<String, Class>();
private Map<String, String> available = new LinkedHashMap<String, String>();
private Set<String> allowed = new LinkedHashSet<String>();

public Set<String> getPermited() {
    return allowed;
}

public Map<String, String> getAvailable() {
    return available;
}

public Map<String, Class> getLoaded() {
    return loaded;
}

public CustomClassLoader() {
    super(CustomClassLoader.class.getClassLoader());
}

private Class<?> getClass(String className, String pack)  throws ClassNotFoundException {

    Class<?> c = null;
    String classPath = className.replace('.', File.separatorChar) + ".class";
    byte[] b = null;
    try {
        b = getClassBytes(classPath, pack);

         c = defineClass(className, b, 0, b.length);
        resolveClass(c);

        return c;
    } catch (IOException e) {
        e.printStackTrace();

    }
    return c;
 }

private byte[] getClassBytes(String classPath, String pack) throws IOException {
       ZipFile zip = new ZipFile(pack);
       // System.out.println(classPath); classPath is right , as well as pack
       ZipEntry entry = zip.getEntry(classPath);//This return null, for some reason ???       
       InputStream in = zip.getInputStream(zip.getEntry(classPath));       
       long size = entry.getSize();
       byte buff[] = new byte[(int)size];
       in.read(buff);
       in.close();
       return buff;
}   

    @Override
    public Class<?> loadClass(String className) throws ClassNotFoundException {



        Class<?> found = null;

        if(loaded.get(className) != null){
                        return loaded.get(className);
                }

        if(available.get(className ) != null){
                if(allowed.contains(className ) == true){
                                found = getClass(className, available.get(className));
                                if(found != null)
                                        loaded.put(className, found);
                }
        }
        else{
                        found = super.loadClass(className);
                        if(found != null)
                                loaded.put(className, found);
        }

        return found;
    }

public void files(ZipFile zip, Map<String,String> list) throws ZipException, IOException{

    Enumeration<? extends ZipEntry> entries = zip.entries();

    while(entries.hasMoreElements()) {
        ZipEntry entry = entries.nextElement();
        if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
            String className = entry.getName().replace('/', '.');
            list.put(className.substring(0, className.length() - ".class".length())
                                ,zip.getName());
        }               
    }

}

public void addJar (File jarFile) {
    try {
        files(new ZipFile(jarFile), available);
    } catch (ZipException e) {

        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

public void deleteJar(File jarFile) {
    Map<String,String> removes = new HashMap<String, String>();
    try {
        files(new ZipFile(jarFile), removes );
    } catch (ZipException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    available.keySet().removeAll(removes.keySet());

}

public void allowPackage(final String p) {
    for(String s : available.keySet()){
        if(s.startsWith(p))
            allowed.add(new String(s));
    }

}

public void denyPackage(final String p) {
    Set<String> newPermited = new HashSet<String>();
    for(String s : allowed){
        if(!s.startsWith(p))
            newPermited.add(new String(s));
    }
    allowed = newPermited;

}

}

Test

public static void main(String[] args) throws ZipException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {

CustomClassLoader ccl = new CustomClassLoader();
    ccl.addJar(new File("derby.jar"));

    ccl.allowPackage("org.apache.derby.jdbc");

    //System.out.println(ccl.getAvailable().get("org.apache.derby.jdbc.EmbeddedDriver")); --> returns "derby.jar"

    Class<?> clazz = ccl.loadClass("org.apache.derby.jdbc.EmbeddedDriver");//Gives exception , line 46 
    System.out.println(clazz.getClass());
    Object instance = clazz.newInstance();
    System.out.println(instance.getClass());
 }

The derby.jar is located in the project folder. Any help is welcomed :)


Solution

  • Try to replace File.separatorChar with '/'