Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Custom ClassLoaders?

2 views
Skip to first unread message

Tyler Baker

unread,
Aug 8, 1997, 3:00:00 AM8/8/97
to

I have a custom ClassLoader which reads a zip file and stores the byte
arrays of the classes (entries) in a hashtable. Then when loadClass is
called, the class byte array is grabbed out of the hashtable and loaded
using defineClass(String name, byte[] data, int off, int len).

However, when I keep getting java.lang.NoClassDefFoundError excpetions
for some reason. Am I not registering my custom class loader
appropriately or something? As far as I understand, whenever you create
a ClassLoader instance, the JVM automatically registers it.

Note: a lot of this code is not exactly modular since I am only trying
to get this to test out right. The zip file being loaded is actually a
JAR file from the BDK which you can find in the /BDK/jars/ directory
with the name buttons.jar. If you have the BDK installed it will be
there.

Thanx in advance,

import java.io.*;
import java.util.zip.*;
import java.util.Hashtable;
import java.util.Enumeration;

public class ClassLoadingTest {
public static void main(String[] args) {
try {
ClassLoader loader = new MyClassLoader(args[0]);
Class c = loader.loadClass("sunw.demo.buttons.ExplicitButton");
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

class MyClassLoader extends ClassLoader {
private Hashtable classCache, classRawDataBuffer;

public MyClassLoader(String fileName) {
classCache = new Hashtable();
classRawDataBuffer = new Hashtable();

try {
InputStream classStream;
Class c;
ZipFile zipFile;
ZipEntry current;
zipFile = new ZipFile(fileName);
String className;
byte[] data;
Enumeration entries = zipFile.entries();
for (current = (ZipEntry)entries.nextElement();
entries.hasMoreElements(); current = (ZipEntry)entries.nextElement()) {
if (current.getName().indexOf(".class") == -1) {
System.out.println(current.getName());
}
else {
classStream = zipFile.getInputStream(current);
data = new byte[(int)current.getSize()];
classStream.read(data, 0, data.length);
className = convertPath(current.getName());
System.out.println(data.length + " " + className);
classRawDataBuffer.put(className, data);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

private byte[] getClassRawData(String name) {
return (byte[])classRawDataBuffer.get(name);
}

public synchronized Class loadClass(String name, boolean resolve) {
try {
Class c = (Class)classCache.get(name);
if (c == null) {
byte[] data = getClassRawData(name);
if (data == null) {
System.out.println("No Data");
return null;
}
c = defineClass(name, data, 0, data.length);
System.out.println("Data");
classCache.put(name, c);
}
if (resolve) {
resolveClass(c);
return c;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

public String convertPath(String path) {
path = path.replace('/', '.');
return path.substring(0, path.length()-6);
}
}

Patrick Lorrimer

unread,
Aug 11, 1997, 3:00:00 AM8/11/97
to Tyler Baker

This is a multi-part message in MIME format.
--------------84AA42CF1BFA3F4877273DD1
Content-Type: text/plain; charset=us-ascii
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Transfer-Encoding: 7bit

The call "loader.loadClass()" loads some of the class definition through
your class loader but not all of it. Later, when you manipulate the
object the JVM wants to load the rest of the definition. To do this it
uses the default class loader build into the JVM. I don't know why.
Obviously this loader fails to find the class file and throws an
exception. Perhaps a different JVM might work differently? There must be
an answer to this one but I haven't found it.

Patrick Lorrimer (plor...@uk.oracle.com).

Tyler Baker wrote:

--------------84AA42CF1BFA3F4877273DD1
Content-Type: text/x-vcard; charset=us-ascii; name="vcard.vcf"
Content-Transfer-Encoding: 7bit
Content-Description: Card for Patrick Lorrime
Content-Disposition: attachment; filename="vcard.vcf"

begin: vcard
fn: Patrick Lorrime
n: Lorrime;Patrick
org: Oracle UK
email;internet: plor...@uk.oracle.com
x-mozilla-cpt: ;0
x-mozilla-html: FALSE
end: vcard


--------------84AA42CF1BFA3F4877273DD1--


Amit B. Kulkarni

unread,
Aug 11, 1997, 3:00:00 AM8/11/97
to

> Tyler Baker wrote:
>
> > I have a custom ClassLoader which reads a zip file and stores the byte

--- snip ---

> > public class ClassLoadingTest {
> > public static void main(String[] args) {
> > try {
> > ClassLoader loader = new MyClassLoader(args[0]);
> > Class c = loader.loadClass("sunw.demo.buttons.ExplicitButton");

Here you are calling loadClass with one argument

> >
> > public synchronized Class loadClass(String name, boolean resolve) {

while you have declared it with 2 arguments. So the loadClass method
of the default ClassLoader gets called instead of yours!!

--
Amit B. Kulkarni
-----------------------------------------------------
Information and Telecommunication Technology Center
University of Kansas, Lawrence KS 66046
Home: (785) 832-9061 Office: (785) 864-7793
Web : http://www.ittc.ukans.edu/~kulkarn
Black holes are where God has divided by zero
-----------------------------------------------------

Thore Herberg

unread,
Aug 14, 1997, 3:00:00 AM8/14/97
to

Patrick Lorrimer wrote:
>
> I notice that as well. However, if you step through the code, the right
> one gets called.

>
> Amit B. Kulkarni wrote:
>
> > > Tyler Baker wrote:
> > >
> > > > I have a custom ClassLoader which reads a zip file and stores the
> > byte
> >
> > --- snip ---
> >
> > > > public class ClassLoadingTest {
> > > > public static void main(String[] args) {
> > > > try {
> > > > ClassLoader loader = new MyClassLoader(args[0]);

Is there a way of finding out how much free disk space is available
on a hd?

Thore

Patrick Lorrimer

unread,
Aug 14, 1997, 3:00:00 AM8/14/97
to Amit B. Kulkarni

This is a multi-part message in MIME format.
--------------1284772366BDB53D84858183

Content-Type: text/plain; charset=us-ascii
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Transfer-Encoding: 7bit

I notice that as well. However, if you step through the code, the right
one gets called.

Amit B. Kulkarni wrote:

> > Tyler Baker wrote:
> >
> > > I have a custom ClassLoader which reads a zip file and stores the
> byte
>
> --- snip ---
>
> > > public class ClassLoadingTest {
> > > public static void main(String[] args) {
> > > try {
> > > ClassLoader loader = new MyClassLoader(args[0]);

> > > Class c =
> loader.loadClass("sunw.demo.buttons.ExplicitButton");
>
> Here you are calling loadClass with one argument
>
> > >
> > > public synchronized Class loadClass(String name, boolean
> resolve) {
>
> while you have declared it with 2 arguments. So the loadClass method
> of the default ClassLoader gets called instead of yours!!
>
> --
> Amit B. Kulkarni
> -----------------------------------------------------
> Information and Telecommunication Technology Center
> University of Kansas, Lawrence KS 66046
> Home: (785) 832-9061 Office: (785) 864-7793
> Web : http://www.ittc.ukans.edu/~kulkarn
> Black holes are where God has divided by zero
> -----------------------------------------------------

--------------1284772366BDB53D84858183


Content-Type: text/x-vcard; charset=us-ascii; name="vcard.vcf"
Content-Transfer-Encoding: 7bit
Content-Description: Card for Patrick Lorrime
Content-Disposition: attachment; filename="vcard.vcf"

begin: vcard
fn: Patrick Lorrime
n: Lorrime;Patrick
org: Oracle UK
email;internet: plor...@uk.oracle.com
x-mozilla-cpt: ;0
x-mozilla-html: FALSE
end: vcard


--------------1284772366BDB53D84858183--


Joubin

unread,
Aug 16, 1997, 3:00:00 AM8/16/97
to Tyler Baker

Tyler Baker wrote:
>
> I have a custom ClassLoader which reads a zip file and stores the byte
> arrays of the classes (entries) in a hashtable. Then when loadClass is
> called, the class byte array is grabbed out of the hashtable and loaded
> using defineClass(String name, byte[] data, int off, int len).
>
> However, when I keep getting java.lang.NoClassDefFoundError excpetions
> for some reason. Am I not registering my custom class loader
> appropriately or something? As far as I understand, whenever you create
> a ClassLoader instance, the JVM automatically registers it.
>
> Note: a lot of this code is not exactly modular since I am only trying
> to get this to test out right. The zip file being loaded is actually a
> JAR file from the BDK which you can find in the /BDK/jars/ directory
> with the name buttons.jar. If you have the BDK installed it will be
> there.

Tyler,

I modified your code a bit, since you werent checking for
<ClassFormatError> when you where you were calling
<ClassLoader#defineClass()>. This method is a bit bizzare since the
(jdk) source code does not declare a throws clause, but the javaDoc
does! But it certainly does throw this & <NoClassDefFoundError>. Since
it is a wrapper for the native #defineClass0(), something else might be
going on ...

Anyway, you should catch this 'error' when you define a class. You
should also allow for the scenario that one of the dependencies of the
class you are loading might be a System class.

Given this, this is the typical result I get running the modified code
over a BDK jar:

sunw/demo/buttons/OrangeButton.ser
sunw/demo/buttons/BlueButton.ser
META-INF/MANIFEST.MF
sunw/demo/buttons/ExplicitButtonIcon32.gif
#loadClass: sunw.demo.buttons.ExplicitButton resolve flag: true
Not loaded, try classCache... sunw.demo.buttons.ExplicitButton
Not cached, try getting its data... sunw.demo.buttons.ExplicitButton
Got Data, try defining it... sunw.demo.buttons.ExplicitButton
#loadClass: sunw.demo.buttons.OurButton resolve flag: false
Not loaded, try classCache... sunw.demo.buttons.OurButton
Not cached, try getting its data... sunw.demo.buttons.OurButton
Got Data, try defining it... sunw.demo.buttons.OurButton
DATA FOR CLASS sunw.demo.buttons.OurButton is corrupted! <<< Error is
here
java.lang.ClassFormatError: Illegal constant pool type
at java.lang.ClassLoader.defineClass(ClassLoader.java:228)
at ZIPClassLoader.loadClass(ZIPClassLoader.java:85)
at
java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
at java.lang.ClassLoader.defineClass(ClassLoader.java:228)
at ZIPClassLoader.loadClass(ZIPClassLoader.java:85)
at java.lang.ClassLoader.loadClass(ClassLoader.java:167)
at ZIPClassLoader.main(ZIPClassLoader.java:18)
java.lang.NoClassDefFoundError

AND, this is the results I got running it over jce.zip for
java.security.SecretKey:

java/security/interfaces/
java/
sun/
java/security/
sun/security/pkcs/
sun/security/provider/
sun/security/
#loadClass: java.security.SecretKey resolve flag: true
Not loaded, try classCache... java.security.SecretKey
Not cached, try getting its data... java.security.SecretKey
Got Data, try defining it... java.security.SecretKey
#loadClass: java.lang.Object resolve flag: false
Not loaded, try classCache... java.lang.Object
Not cached, try getting its data... java.lang.Object
NO DATA, is it a System Class ??? java.lang.Object
LOADED CLASS java.lang.Object
Got Class, try caching it... java.security.SecretKey
Resolve it ... java.security.SecretKey
#loadClass: java.security.Key resolve flag: false
Not loaded, try classCache... java.security.Key
Not cached, try getting its data... java.security.Key
NO DATA, is it a System Class ??? java.security.Key
LOADED CLASS java.security.Key
LOADED CLASS java.security.SecretKey
LOADED CLASS: java.security.SecretKey

So, either your zip algorithm is not loading jar'd objects correctly, or
the bdk jars are corrupted. I don't know which is the case. (got to
resume my own work ;)

here is the code, which I consolidated into a ZIPClassLoader class. To
run it:

>java ZIPClassLoader <zipFile> [<classInZipFile>]

If you run it only with the zipfile arguement, it will list the content;
if you add the classname arguement, it will NOT list the contents. This
helps with large zip files.

Take care,

Joubin


[ZIPClassLoader.java] :

import java.io.*;
import java.util.zip.*;
import java.util.Hashtable;
import java.util.Enumeration;

public class ZIPClassLoader extends ClassLoader {
private Hashtable classCache, classRawDataBuffer;

public static void main(String[] args) {
if( args.length < 1) {
System.out.println ("Usage: java ZIPClassLoader <ZIPFile>
[<classInJarFile>]");
System.exit (1);
}
boolean list = args.length ==1 ? true : false;
try {
ClassLoader loader = new ZIPClassLoader(args[0], list);
if( args.length > 1) {
Class c = loader.loadClass(args[1]);
System.out.println("LOADED CLASS: " + c.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public ZIPClassLoader(String fileName, boolean list) {


classCache = new Hashtable();
classRawDataBuffer = new Hashtable();

try {
InputStream classStream;
Class c;

ZipEntry current;
ZipFile zipFile = new ZipFile(fileName);


String className;
byte[] data;
Enumeration entries = zipFile.entries();

for ( current = (ZipEntry)entries.nextElement();


entries.hasMoreElements();
current = (ZipEntry)entries.nextElement())
{
if (current.getName().indexOf(".class") == -1) {
System.out.println(current.getName());
}
else {
classStream = zipFile.getInputStream(current);
data = new byte[(int)current.getSize()];
classStream.read(data, 0, data.length);
className = convertPath(current.getName());

if(list)


System.out.println(data.length + " " + className);
classRawDataBuffer.put(className, data);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}

private byte[] getClassRawData(String name) {
return (byte[])classRawDataBuffer.get(name);
}

public


synchronized
Class loadClass(String name, boolean resolve)

throws ClassNotFoundException
{
System.out.println("#loadClass: " + name + " resolve flag: " +
resolve) ;
Class c = findLoadedClass (name);

System.out.println("Not loaded, try classCache... " + name) ;
if (c == null)
c = (Class) classCache.get(name);

System.out.println("Not cached, try getting its data... " + name) ;
if (c == null) {
byte[] data = null;
try {
data = getClassRawData(name);
if( data != null) {
System.out.println("Got Data, try defining it... " + name) ;
try {


c = defineClass(name, data, 0, data.length);
}

catch (ClassFormatError cfe) {
System.out.println("DATA FOR CLASS " + name + " is corrupted!");
cfe.printStackTrace ();
return null;
}
System.out.println("Got Class, try caching it... " + name) ;
classCache.put(name, c);
}
else {
System.out.println("NO DATA, is it a System Class ??? " + name) ;
c = findSystemClass (name);
if(c==null)
throw new ClassNotFoundException (name);
}
}
catch (Exception e) {
System.out.println("Error: " + name);
e.printStackTrace ();
throw new ClassNotFoundException (e.getMessage());
}
}

if (resolve){
System.out.println("Resolve it ... " + name) ;
resolveClass(c);
}
System.out.println("LOADED CLASS " + name) ;
return c;
}

public String convertPath(String path) {
path = path.replace('/', '.');
return path.substring(0, path.length()-6);
}
}


__________________________________
member, alpha zero LLC
jou...@inch.com
703.323.5204
NoVA
__________________________________

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.2

mQCNAzNEGP4AAAEEAMAsxUB5FstE5/ZnqpiplM0OAoy+OAGHFdVeQbtfE995HhWG
3y7JKa3vPE9NAmFbPBkL3X4iLDxP2G1pKxGaOp3N2hzNeyRI04U9YPZ2ZXVoy5u3
geHowPuiCgnE+dAzU5JrT5pzK1xLl+X+x66vXghwP67givRC8DzcyXZ33/IpAAUR
tBdqb3ViaW48am91YmluQGluY2guY29tPg==
=ym6B
-----END PGP PUBLIC KEY BLOCK-----

0 new messages