log.siuda.net

java classpath separation part 2

A demo project for classpath (classloader) separation for plain java.

This is a continuation of part 1, where I have briefly described the concept.

about the demo

The demo consists of two projects: ClazzLoad, and additional internal one Separated. Both are build with a single gradle build script. The latter project has a simple dependency on a jar library, which is not compatible with the main project.

what’s inside

This is the separated, internal project. It is using the [com.google.common.net.HostAndPort.fromParts(String, int)](http://google.github.io/guava/releases/14.0.1/api/docs/com/google/common/net/HostAndPort.html#fromParts(java.lang.String, int)) from guava 14.0.1, removed from more recent guava releases. This code cannot compile nor run against guava 17.0.

package net.siuda.clazzes.separated;

import com.google.common.net.HostAndPort;

public class Separated {

    public void run() {
        System.out.println(\"RUN!\");
        HostAndPort hostAndPort = HostAndPort.fromParts(\"localhost\", 8080);
        System.out.println(\"result:\" + hostAndPort.getHostText());
    }

}

This is the program entry-point creating the SeparatingClassLoader for a list of .jar files' URLs (poinitng to the Separated project and it’s dependencies).

After invocation of Separated code it calls com.google.common.net.HostAndPort.fromHost(String) which was added in guava 17.0 and would fail with older releases. This code cannot compile nor run against guava 14.0.1.

package net.siuda.clazzes;

import com.google.common.net.HostAndPort;

import java.lang.reflect.Method;
import java.net.URL;

public class Main {

    private URL[] urls;

    public static void main(String ... args) throws Exception {
        URL[] urls = new URL[args.length];
        for(int c = 0; c < args.length; c++) {
            urls[c] = new URL(args[c]);
        }
        new Main(urls).run();
    }

    private Main(URL[] urls) {
        this.urls = urls;
    }

    private void run() throws Exception {
        ClassLoader cl = new SeparatingClassLoader(urls, this.getClass().getClassLoader());
        Class clazz = cl.loadClass(\"net.siuda.clazzes.separated.Separated\");
        Object object = clazz.newInstance();
        Method method = clazz.getMethod(\"run\");
        method.invoke(object);

        System.out.println(\"CONTINUE PARENT RUN:\");
        HostAndPort hostAndPort = HostAndPort.fromHost(\"localhost\");
        System.out.println(\"result:\" + hostAndPort.getHost());
    }

}

running the demo

[ClazzLoad]$ gradle jar
Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for details
:compileJavaNote: /mnt/drive/wlochaty/GIT/log/2017/java-classpathseparation/ClazzLoad/src/main/java/net/siuda/clazzes/Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

:processResources NO-SOURCE
:classes
:jar
:Separated:compileJava
:Separated:processResources NO-SOURCE
:Separated:classes
:Separated:jar

BUILD SUCCESSFUL in 11s
4 actionable tasks: 4 executed

[ClazzLoad]$ java -classpath file:///home/wlochaty/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/23.5-jre/e9ce4989adf6092a3dab6152860e93d989e8cf88/guava-23.5-jre.jar:build/libs/ClazzLoad-1.0-SNAPSHOT.jar net.siuda.clazzes.Main file:///home/wlochaty/h/IdeaProjects/ClazzLoad/Separated/build/libs/Separated-1.0-SNAPSHOT.jar file:///home/wlochaty/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/14.0.1/69e12f4c6aeac392555f1ea86fab82b5e5e31ad4/guava-14.0.1.jar 
RUN!
result:localhost
CONTINUE PARENT RUN:
result:localhost
[wlochaty@desklamp ClazzLoad]$ 

After the building jars with gradle I invoke the program with:

(note: for simplicity I’ve used paths to jars from gradle cache, the paths may need to be changed to run on your machine)

Enjoy.