Sunday, January 22, 2017

Java ClassLoader

java.lang.ClassLoader: 
It is the class responsible for finding and loading class files at run time. Creating your own ClassLoader lets you customize the JVM in useful and interesting ways, allowing you to completely redefine how class files are brought into the system.

 Java program, unlike one written in C or C++, isn't a single executable file, but instead is composed of many individual class files, each of which corresponds to a single Java class.
Additionally, these class files are not loaded into memory all at once, but rather are loaded on demand, as needed by the program. The ClassLoader is the part of the JVM that loads classes into memory.

Why write a ClassLoader?

If the JVM has a ClassLoader, then why would you want to write another one?  The default ClassLoader only knows how to load class files from the local filesystem. This is fine for regular situations, when you have your Java program fully compiled and waiting on your computer.
But it easy for the JVM to get classes from places other than the local hard drive or network. For example, browsers use a custom ClassLoader to load executable content from a Web site.
There are many other ways to get class files. Besides simply loading files from the local disk or from a network, you can use a custom ClassLoader to:
  • Automatically verify a digital signature before executing untrusted code
  • Transparently decrypt code with a user-supplied password
  • Create dynamically built classes customized to the user's specific needs
Anything you can think of to write that can generate Java bytecode can be integrated into your application.

There are three categories of class loaders :
  1. BootStrap of Premodial class loaders : Deals with rt.jar or primitive classes that java offers.
  2. Extension Class loaders: Next comes the Java extension class loader. We can store extension libraries, those that provide features that go beyond the core Java runtime code, in the path given by the java.ext.dirs property. The ExtClassLoader is responsible for loading all .jar files kept in the java.ext.dirs path. A developer can add his or her own application .jar files or whatever libraries he or she might need to add to the classpath to this extension directory so that they will be loaded by the extension class loader.
  3. Application class loaders:
    The third and most important class loader from the developer perspective is the AppClassLoader. The application class loader is responsible for loading all of the classes kept in the path corresponding to the java.class.path system property.

    How class loader works in Java - class loading

    A java classloader works on chiefly three principle: delegation,visibility and uniqueness.

    Delegation principles
    As discussed on when a class is loaded and initialized in Java, a class is loaded in Java, when its needed. Suppose you have an application specific class called Abc.class, first request of loading this class will come to Application ClassLoader which will delegate to its parent Extension ClassLoader which further delegates to Primordial or Bootstrap class loader. Primordial will look for that class in rt.jar and since that class is not there, request comes to Extension class loader which looks on jre/lib/ext directory and tries to locate this class there, if class is found there than Extension class loader will load that class and Application class loader will never load that class but if its not loaded by extension class-loader than Application class loader loads it from Classpath in Java. Remember Classpath is used to load class files while PATH is used to locate executable like javac or java command.
    Visibility Principle
    According to visibility principle, Child ClassLoader can see class loaded by Parent ClassLoader but vice-versa is not true. Which mean if class Abc is loaded by Application class loader than trying to load class ABC explicitly using extension ClassLoader will throw either java.lang.ClassNotFoundException. as shown in below Example
    Uniqueness Principle
    According to this principle a class loaded by Parent should not be loaded by Child ClassLoader again. Though its completely possible to write class loader which violates Delegation and Uniqueness principles and loads class by itself, its not something which is beneficial.

    Security in Class Loader:
    Class loaders in the sandbox
    In Java's sandbox, the class loader architecture is the first line of defense against malicious code. It is the class loader, after all, that brings code into the JVM -- code that could be hostile.
    The class loader architecture contributes to Java's sandbox in two ways:
    1. It prevents malicious code from interfering with benevolent code.
    2. It guards the borders of the trusted class libraries.
    The class loader architecture guards the borders of the trusted class libraries by making sure untrusted classes can't pretend to be trusted. If a malicious class could successfully trick the JVM into believing it was a trusted class from the Java API, that malicious class potentially could break through the sandbox barrier. By preventing untrusted classes from impersonating trusted classes, the class loader architecture blocks one potential approach to compromising the security of the Java runtime.
    Name-spaces and shields
    The class loader architecture prevents malicious code from interfering with benevolent code by providing protected name-spaces for classes loaded by different class loaders. As mentioned above, name-space is a set of unique names for loaded classes that is maintained by the JVM.
    Name-spaces contribute to security because you can, in effect, place a shield between classes loaded into different name-spaces. Inside the JVM, classes in the same name-space can interact with one another directly. Classes in different name-spaces, however, can't even detect each other's presence unless you explicitly provide a mechanism that allows the classes to interact. If a malicious class, once loaded, had guaranteed access to every other class currently loaded by the virtual machine, that class potentially could learn things it shouldn't know, or it could interfere with the proper execution of your program.
    Creating a secure environment
    When you write an application that uses class loaders, you create an environment in which the dynamically loaded code runs. If you want the environment to be free of security holes, you must follow certain rules when you write your application and class loaders. In general, you will want to write your application so that malicious code will be shielded from benevolent code. Also, you will want to write class loaders such that they protect the borders of trusted class libraries, such as those of the Java API.
    Name-spaces and code sources
    To get the security benefits offered by name-spaces, you need to make sure you load classes from different sources through different class loaders. This is the scheme, described above, used by Java-enabled Web browsers. The Java application fired off by a Web browser usually creates a different applet class loader object for each source of classes it downloads across the network. For example, a browser would use one class loader object to download classes from http://www.niceapplets.com, and another class loader object to download classes from http://www.meanapplets.com.
    Guarding restricted packages
    Java allows classes in the same package to grant each other special access privileges that aren't granted to classes outside the package. So, if your class loader receives a request to load a class that by its name brazenly declares itself to be part of the Java API (for example, a class named java.lang.Virus), your class loader should proceed cautiously. If loaded, such a class could gain special access to the trusted classes of java.lang and could possibly use that special access for devious purposes.
    Consequently, you would normally write a class loader so that it simply refuses to load any class that claims to be part of the Java API (or any other trusted runtime library) but that doesn't exist in the local trusted repository. In other words, after your class loader passes a request to the primordial class loader, and the primordial class loader indicates it can't load the class, your class loader should check to make sure the class doesn't declare itself to be a member of a trusted package. If it does, your class loader, instead of trying to download the class across the network, should throw a security exception.
    Guarding forbidden packages
    In addition, you may have installed some packages in the trusted repository that contain classes you want your application to be able to load through the primordial class loader, but that you don't want to be accessible to classes loaded through your class loader. For example, assume you have created a package named absolutepower and installed it on the local repository accessible by the primordial class loader. Assume also that you don't want classes loaded by your class loader to be able to load any class from the absolutepower package. In this case, you would write your class loader such that the very first thing it does is to make sure the requested class doesn't declare itself as a member of the absolutepower package. If such a class is requested, your class loader, rather than passing the class name to the primordial class loader, should throw a security exception.
    The only way a class loader can know whether or not a class is from a restricted package, such as java.lang, or a forbidden package, such as absolutepower, is by the name of the class. Thus, a class loader must be given a list of the names of restricted and forbidden packages. Because the name of class java.lang.Virus indicates it is from the java.lang package, and java.lang is on the list of restricted packages, your class loader should throw a security exception if the primordial class loader can't load it. Likewise, because the name of class absolutepower.FancyClassLoader indicates it is part of the absolutepower package, and the absolutepower package is on the list of forbidden packages, your class loader should throw a security exception.
A security-minded class loader
A common way to write a security-minded class loader is to use the following four steps:
  1. If packages exist that this class loader is not allowed to load from, the class loader checks whether the requested class is in one of those forbidden packages mentioned above. If so, it throws a security exception. If not, it continues on to step two.
  2. The class loader passes the request to the primordial class loader. If the primordial class loader successfully returns the class, the class loader returns that same class. Otherwise it continues on to step three.
  3. If trusted packages exist that this class loader is not allowed to add classes to, the class loader checks whether the requested class is in one of those restricted packages. If so, it throws a security exception. If not, it continues on to step four.
  4. Finally, the class loader attempts to load the class in the custom way, such as by downloading it across a network. If successful, it returns the class. If unsuccessful, it throws a "no class definition found" error.
By performing steps one and three as outlined above, the class loader guards the borders of the trusted packages. With step one, it prevents a class from a forbidden package to be loaded at all. With step three, it doesn't allow an untrusted class to insert itself into a trusted package.
Conclusion
The class loader architecture contributes to the JVM's security model in two ways:
  1. by separating code into multiple name-spaces and placing a "shield" between code in different name-spaces
  2. by guarding the borders of trusted class libraries, such as the Java API

 Examples:
package com.kunal.testclassloader;

public class DefaultMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println(java.lang.String.class.getClassLoader());//Prints null
        System.out.println(ClassABC.class.getClassLoader());//Prints sun.misc.Launcher$AppClassLoader@4e0e2f2a
    }

}



Loading Class Example :




Class to load :


package com.kunal.testclassloader;

public class ClassABC {
   
    public void testGreet(){
        System.out.println("From the loaded class");
    }

}




 Custom class Loader:
package com.kunal.testclassloader;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoadeer extends ClassLoader {

   
    public MyClassLoadeer(){
        super();
    }

    @Override
    public Class<?> loadClass(String name)
            throws ClassNotFoundException {
        System.out.println("loading class '" + name + "'");
        if (name.startsWith("com.kunal.")) {
            try {
                return getClass(name);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return super.loadClass(name);
       
    }

    private Class<?> getClass(String name) throws ClassNotFoundException, IOException {
        // We are getting a name that looks like
        // javablogging.package.ClassToLoad
        // and we have to convert it into the .class file name
        // like javablogging/package/ClassToLoad.class
        String file = name.replace('.', File.separatorChar)
            + ".class";
        byte[] b = null;
        // This loads the byte code data from the file
        b = loadClassData(file);
        // defineClass is inherited from the ClassLoader class
        // and converts the byte array into a Class
        Class<?> c = defineClass(name, b, 0, b.length);
        resolveClass(c);
        return c;
    }

    private byte[] loadClassData(String file) throws IOException {
        // Opening the file
        InputStream stream = getClass().getClassLoader()
            .getResourceAsStream(file);
        int size = stream.available();
        byte buff[] = new byte[size];
        DataInputStream in = new DataInputStream(stream);
        // Reading the binary data
        in.readFully(buff);
        in.close();
        return buff;
    }
   
   
}
Main Class named DefaultMain:

package com.kunal.testclassloader;

import java.lang.reflect.InvocationTargetException;

public class DefaultMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println(java.lang.String.class.getClassLoader());//Prints null
        System.out.println(ClassABC.class.getClassLoader());//Prints sun.misc.Launcher$AppClassLoader@4e0e2f2a
       
        MyClassLoadeer loader = new MyClassLoadeer();
        try {
            Class<?> classLoaded = loader.loadClass("com.kunal.testclassloader.ClassABC");
            try {
            Object abc = classLoaded.newInstance();
                System.out.println(abc.getClass().getClassLoader());
//                ((ClassABC)abc).testGreet();
                try {
                    classLoaded.getMethod("testGreet", null).invoke(abc, null);
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (SecurityException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}


Output:
null
sun.misc.Launcher$AppClassLoader@4e0e2f2a
loading class 'com.kunal.testclassloader.ClassABC'
loading class 'java.lang.Object'
com.kunal.testclassloader.MyClassLoadeer@6d06d69c
loading class 'java.lang.System'
loading class 'java.io.PrintStream'
From the loaded class


Comments for improvement are welcome.

No comments:

Post a Comment