For another thread I created an example ClassLoader to demonstrate how you can have two different instances of a static field running in the same JVM. There is a zip file with code at: http://flick.nerdc.ufl.edu/jgo/ClassLoaderNamespaces.zip but incase the server is offline I’ll include the code below.
File: /game/foo/Bar.java
package foo;
public class Bar {
private static int count = 0;
public String toString() {
return String.valueOf(++count);
}
}
File: /loader/Test.java
import java.io.File;
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
String myPackage = "foo.";
String baseDir = ".." + File.separator + "game" + File.separator; // ../game/ on UNIX or ..\game\ on DOS
// Create two ClassLoaders so we have two different namespaces
ClassLoader cl1 = new SimpleClassLoader(myPackage, baseDir);
ClassLoader cl2 = new SimpleClassLoader(myPackage, baseDir);
// Create three Objects, two of which share the same ClassLoader
Object o1 = cl1.loadClass("foo.Bar").newInstance();
Object o2 = cl2.loadClass("foo.Bar").newInstance();
Object o3 = cl1.loadClass("foo.Bar").newInstance(); // Same ClassLoader as o1
// Print the output
System.out.println("o1: " + o1.toString());
System.out.println("o2: " + o2.toString());
System.out.println("=======");
System.out.println("o1: " + o1.toString());
System.out.println("o2: " + o2.toString());
System.out.println("=======");
System.out.println("o1: " + o1.toString());
System.out.println("o2: " + o2.toString());
System.out.println("=======");
System.out.println("o1: " + o1.toString());
System.out.println("o2: " + o2.toString());
System.out.println("o3: " + o3.toString());
System.out.println("=======");
System.out.println("o1: " + o1.toString());
System.out.println("o2: " + o2.toString()); // Will be different than o1 because of o3 above
System.out.println("o3: " + o3.toString());
System.out.println("=======");
System.out.println("o1: " + o1.toString());
System.out.println("o2: " + o2.toString());
System.out.println("o3: " + o3.toString());
}
}
File: /loader/SimpleClassLoader.java which is based on: http://www.javaworld.com/javaworld/jw-10-1996/indepth/indepth.src.html
/*
* SimpleClassLoader.java - a bare bones class loader.
*
* Copyright (c) 1996 Chuck McManis, All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies.
*
* CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS
* SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
* OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
// EDIT: Whatever was here before was a mess
import java.io.FileInputStream;
import java.io.File;
import java.util.Hashtable;
public class SimpleClassLoader extends ClassLoader {
private Hashtable classes = new Hashtable();
// NEW
private String myPackage;
private String codeBase;
public SimpleClassLoader(String myPackage, String codeBase) {
// NEW
this.myPackage = myPackage;
this.codeBase = codeBase;
}
/**
* This sample function for reading class implementations reads
* them from the local file system
*/
private byte getClassImplFromDataBase(String className)[] {
System.err.println(" >>>>>> Fetching the implementation of " + className);
byte result[];
// NEW: convert package seperators '.' to directory seperators
StringBuffer sb = new StringBuffer(className);
for (int i=0, n=sb.length(); i < n; i++) {
if (sb.charAt(i) == '.') {
sb.setCharAt(i, File.separatorChar);
}
}
className = sb.append(".class").toString();
System.err.println("New className: " + className); // to double check
try {
// EDIT: Moved some stuff up a few lines
// FileInputStream fi = new FileInputStream("store\\"+className+".impl");
FileInputStream fi = new FileInputStream(codeBase + className);
result = new byte[fi.available()];
fi.read(result);
return result;
} catch (Exception e) {
/*
* If we caught an exception, either the class wasnt found or it
* was unreadable by our process.
*/
return null;
}
}
/**
* This is a simple version for external clients since they
* will always want the class resolved before it is returned
* to them.
*/
public Class loadClass(String className) throws ClassNotFoundException {
return (loadClass(className, true));
}
/**
* This is the required version of loadClass which is called
* both from loadClass above and from the internal function
* FindClassFromClass.
*/
public synchronized Class loadClass(String className, boolean resolveIt) throws ClassNotFoundException {
Class result;
byte classData[];
System.err.println(" >>>>>> Load class : " + className);
/* Check our local cache of classes */
result = (Class)classes.get(className);
if (result != null) {
System.err.println(" >>>>>> returning cached result.");
return result;
}
// NEW: Added the if startsWith
if (!className.startsWith(myPackage)) {
/* Check with the primordial class loader */
try {
result = super.findSystemClass(className);
System.err.println(" >>>>>> returning system class (in CLASSPATH).");
return result;
} catch (ClassNotFoundException e) {
System.err.println(" >>>>>> Not a system class.");
}
}
/* Try to load it from our repository */
classData = getClassImplFromDataBase(className);
if (classData == null) {
throw new ClassNotFoundException();
}
/* Define it (parse the class file) */
result = defineClass(classData, 0, classData.length);
if (result == null) {
throw new ClassFormatError();
}
if (resolveIt) {
resolveClass(result);
}
classes.put(className, result);
System.err.println(" >>>>>> Returning newly loaded class.");
return result;
}
}
And when you run java Test
you get output something like:
[quote]o1: 1
o2: 1
o1: 2
o2: 2
o1: 3
o2: 3
o1: 4
o2: 4
o3: 5
o1: 6
o2: 5
o3: 7
o1: 8
o2: 6
o3: 9
[/quote]
Hope this is useful.