Classloader

0

In this article, you will know what is a class Loader, various class loaders available and the delegation model . Finally, I will show you how you can build your own classloader.

Trusted and Untrusted sources

A Java Virtual Machine’s main job is to load class files and execute the byte codes they contain.
classloader_component
There are two types of class sources. The first one is completely trusted one like the Java core APIs like those in packages java.lang, java.io, java.net, java.util, and so on. These are called system classes. Classes from untrusted sources include application classes, extension classes, and classes from remote network locations.

Primordial ClassLoader

The primordial class loader is the special one as it is responsible for loading the trusted classes of the Java runtime. It is built-in part of JVM itself. The primordial class loader is not a Java class, is not loaded by any class loader and is generated at JVM start-up. The custom class loaders are a subclass of the class java.lang.ClassLoader. In the last section, I will show you how to create a classloader but before that we need to know how trusted and untrusted classes are loaded and the rules that a classloaders follow while loading classes.

Other types of Java ClassLoaders

  1. Application ClassLoader – User classes are not considered fully trusted and are not loaded by the primordial class loader but instead loaded by the application class loader. JVM creates Application classloader during its startup which is nothing but an instance of java.net.URLClassLoader. It loads all the resources found in the classpath.
  2. Extensions ClassLoader – Extension classloader is used by the JVM to load JAR files from a specific extensions directory. The extension class path by default contains only the subdirectory lib/ext of the Java home directory. It can be overriden by using option -Djava.ext.dirs.
  3. Classes from remote locations – A custom ClassLoader is created to load remote classes from a specific set of URLs. You will see one in the example section.

Two-stage process – Loading and Linking

Classes loaded by the primordial class loader are regarded as special and they are not subjected to verification prior to execution. They are assumed to be well-formed, safe Java classes. In addition, they are not subjected to any security policy restrictions. All the untrusted classes need go through a two stage process – loading and linking. In the second phase, called linking or resolution, the class is verified to ensure that it is well formed and doesn’t try to violate any of the virtual machine’s security constraints.

Namespace

The set of classes loaded by a particular class loader is known as that class loader’s name space thus each classloader creates its own namespace.

Responsibilities of ClassLoader

    1. Protection of Trusted Classes – A trusted system class should not be overwritten with an identically named class downloaded from a remote location
    2. Protection from Name clashes – Since an application can extend itself dynamically by loading classes from remote locations, there are chances of class name collisions. Thus classloader should make sure the two classes can coexist without any possibility of confusion.
    3. Protection of Trusted Packages – Since classes that reside in the same package have special access permissions so it is important to protect a custom class pretending to be a part of java system package from intruding into the java core packages.
    4. Protection from accessing system resources – Class loaders have the ability to associate a set of authorizations with each loaded class.

ClassLoader API

Custom ClassLoader is a subclass of ClassLoader. One can create a new ClassLoader by extending URLClassLoader. It takes in an array of URL from which to load classes and resources and the the parent class loader for delegation. ClassLoaders follow delegation model which we will discuss in the next section.

ClassLoader Hierarchy

Since ClassLoader itself is a Java class it has to be loaded by some classloader. The primordial class loader, which in general is not a Java class, is not loaded by any class loader and is at the root. By default, when a program instantiates a ClassLoader object, the program’s class loader becomes the ClassLoader object’s parent. Both application classloader and extensions classloader are created by the same system program so ideally their parent should be the bootstrap classloader but in case of application classloader, extensions classloader is passed as parent.
Every ClassLoader instance, on receiving the request to load a Java class, first looks into its own cache. If it is already loaded it returns the class. If the class is not yet loaded it immediately delegates the request to its parent class loader which in turn delegates it to its parent and so on. This is to make sure that a more trusted class cannot be replaced with a less trusted class having the same fully qualified name.
Finally, when the request reaches the root, Bootstrap ClassLoader tries to load the class. If it can’t load the class, it returns the request back to the classloader from where the request has come from. This follows back down to the Custom ClassLoader which first received the request.
classloader_hieararch

Example

My test creates a custom classloader and loads a jar file from at an external path, not within the classpath. We will first see the classes within the jar file.
It contains class called SomeClass which extends SuperClass which in turn extends MoreSuperClass.

SomeClass:

package javarticles.external;
public class SomeClass extends SuperClass {
    static {
        System.out.println("SomeClass static block called");
    }
    public SomeClass() {
        System.out.println("SomeClass initialized");
    }
    public String toString() {
        return getClass().getCanonicalName();
    }
}

SuperClass:

package javarticles.external;
public class SuperClass extends MoreSuperClass {
    private MoreSuperClass mMoreSuperClass;
    static {
        System.out.println("SuperClass static block called");
    }

    public SuperClass() {
        System.out.println("SuperClass initialized");
    }
}

MoreSuperClass:

package javarticles.external;
public class MoreSuperClass {
    private SuperClass mSuperClass;
    static {
        System.out.println("MoreSuperClass static block called");
    }

    public MoreSuperClass() {
        System.out.println("MoreSuperClass initialized");
    }
}

We will jar above classes into javarticles.jar file located in C:/practise/javarticles. In the test below, we will create our own Classloader with parent as NULL which means when requested for class, it will delegate the request to bootstrap loader and no other ClassLoader. Test requests the ClassLoader to load SomeClass and the prints one of the private member class. It then instantiates the class and invokes toString(). From the test, we can see that the classloader loads the class requested and the other related classes.

ClassLoaderTest:

package classloaders;

import junit.framework.TestCase;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class ClassLoaderTest extends TestCase {
    public void testLoadClass() throws Exception {
        File jarFile = new File("C:/practise/javarticles/javarticles.jar");
        if (!jarFile.exists() || !jarFile.canRead()) {
            fail("not a valid jar");
        }
        URL[] urls = new URL[]{jarFile.toURI().toURL()};
        MyClassLoader classLoader = new MyClassLoader(urls, null);
        try {
            Class a = classLoader.loadClass("javarticles.external.SomeClass");
            System.out.println("Member class loaded: " + a.getSuperclass().getDeclaredFields()[0].getDeclaringClass());
            Object aObj = a.newInstance();
            Method toStringM = a.getMethod("toString", null);
            assertEquals("javarticles.external.SomeClass", toStringM.invoke(aObj, null));
        } catch (ClassNotFoundException e) {
            fail(e.toString());
        }
        try {
            Class c = classLoader.loadClass("prod.B");
            fail(c.getCanonicalName() + " shouldn't be found ");
        } catch (ClassNotFoundException e) {
        }
    }

    private class MyClassLoader extends URLClassLoader {
        public MyClassLoader(URL[] urls) {
            super(urls);
        }

        public MyClassLoader(URL[] urls, ClassLoader parent) {
            super(urls, parent);
        }

        public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            return super.loadClass(name, resolve);
        }
    }
}

Output:

Member class loaded: class javarticles.external.SuperClass
MoreSuperClass static block called
SuperClass static block called
SuperClass static block called
MoreSuperClass initialized
SuperClass initialized
SomeClass initialized
Share.

Leave A Reply