-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
<posted & mailed>
Peter Bradley wrote:
Hi guys and gals,
I've never understood $CLASSPATH and packages and friends - but I
had the same problem, which is why I'm reading this thread.
After looking at the replies (and remembering some odd experiences
I'd had with a previous excursion into Javaland) I tried calling the
program from the directory above where it resides.
To illustrate. Here's the code:
<codeextract>
package ConnectorJTest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class LoadDriver {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver").newInstance ();
System.out.println("Success!");
} catch (Exception ex) {
// Handle the error
System.out.println("Failure!");
}
}
}
</codeextract>
The program is in
/home/peter/connector_j_test/ConnectorJTest/LoadDriver.class
Now, if I call the program by going to the ConnectorJTest and doing:
java LoadDriver
I get:
<output>
peter@linux:~/connector_j_test/ConnectorJTest> java LoadDriver
Exception in thread "main" java.lang.NoClassDefFoundError:
LoadDriver (wrong name: ConnectorJTest/LoadDriver)
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java :502)
at
java.security.SecureClassLoader.defineClass(Secure ClassLoader.java:123) at
java.net.URLClassLoader.defineClass(URLClassLoader .java:250)
at
java.net.URLClassLoader.access$100(URLClassLoader. java:54)
at java.net.URLClassLoader$1.run(URLClassLoader.java: 193) at
java.security.AccessController.doPrivileged(Native Method)
at
java.net.URLClassLoader.findClass(URLClassLoader.j ava:186)
at java.lang.ClassLoader.loadClass(ClassLoader.java:2 99) at
sun.misc.Launcher$AppClassLoader.loadClass(Launche r.java:265) at java.lang.ClassLoader.loadClass(ClassLoader.java:2 55) at
java.lang.ClassLoader.loadClassInternal(ClassLoade r.java:315)
</output>
And if I try the fully qualified name from the same directory, I
get:
<output>
peter@linux:~/connector_j_test/ConnectorJTest> java
ConnectorJTest.LoadDriver
Exception in thread "main" java.lang.NoClassDefFoundError:
ConnectorJTest/LoadDriver
</output>
However if I move up a directory and give the fully qualified name,
I get:
<output>
peter@linux:~/connector_j_test> java ConnectorJTest.LoadDriver
Success!
</output>
Any explanation (preferably rational, though :) ) would be
gratefully received
Cheers
Peter
Hi,
After working with Java for a while, I seem to understand the
CLASSPATH/packages thing. Here goes:
Every class has a package. If one isn't specified when it's created,
it's in the root package, but it still "has" a package.
If you create the following file in the current directory:
package com.somecompany.test;
public class Test {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
and run javac on it directly:
javac Test.java
It produces a file called Test.class in the current directory, which
fails utterly when you try to run it. See, each piece of the package
name is supposed to correspond to a physical directory name in the
filesystem. When you run javac as above, it outputs the class file in
the current directory. If, on the other hand, you were to put the
above source into a file com/somecompany/test/Test.java, staying in
the same directory (not descending in deeper), compiling it would
create com/somecompany/test/Test.class, which you could then run by
typing "java com.somecompany.test.Test". The idea is that by using
the filesystem like this, packages are useful. If two companies make
two classes with the same name, they put them in different packages.
Thus, inside the VM, everyone is sure which class is being referred
to. By using directories in the filesystem, both classes can be put
in "the same place" but they won't overwrite each other.
If you type in "java com.somecompany.test.Test", here's what happens:
The JVM needs to find the class. Ignoring the jre/lib/ext directory
for the time being, we'll assume a CLASSPATH variable set to
/home/me/java. The JVM does this internally:
REALPATH=$CLASSPATH/$PACKAGENAME/$CLASSNAME
Thus, the real path is
/home/me/java/com/somecompany/test/Test.class
which exists.
Why it doesn't work moving into that directory and just typing "java
Test", is because the class file has the package name that it was
meant to be inside of embedded when it's compiled. The JVM detects
that although it seems to be able to find the class, it must be the
wrong file because by typing the above command line, you're asking
for a class in the root package, and the file it found isn't in the
root package. It assumes something is wrong.
Now, personally, I don't actually even have the CLASSPATH set. This
means the JVM defaults to "." as its value. Thus:
java com.somecompany.test.Test
loads
../com/somecompany/test/Test.class
which contains the package name inside the file:
com.somecompany.test
which the JVM checks against the command line, and sees that it
matches, so everything is good. Thus, I can just invoke any class I
need by changing to its root directory (which, of course, isn't
necessarily the directory containing the class file--in the case of
packages, it's the directory containing the highest-level package
name).
For libraries, I find, easier than downloading the JAR file and adding
it to the CLASSPATH, just put it into the jre/lib/ext directory.
Every JAR file in jre/lib/ext is automatically put in the classpath.
Thus, if I use the MySQL connector, and the JVM tries to find this
class:
com.mysql.jdbc.Driver
it takes the classpath element like this (filename shortened):
/usr/java/j2sdk1.4.2/jre/lib/ext/mysql.jar
appends the names just like I described previously, producing this
physical path:
/usr/java/j2sdk1.4.2/jre/lib/ext/mysql.jar/com/mysql/jdbc/Driver
which looks ridiculous, since it describes a directory inside a file,
but, what happens, is that inside the mysql.jar file is a zipped
directory called com, which contains a directory called mysql, etc.
etc. etc. and everything works.
Basically, just remember:
- - When invoking a class that is in a package, you have to include the
package name on the command line.
- - When invoking a class that is in a package, the class file needs to
be contained in a directory structure matching the package name, OFF
OF whatever element in CLASSPATH is relevant. If there's a package
name, the class file isn't going to be right in the directory, it'll
be in a subdirectory.
- - To include a JAR file, just put the path to the JAR itself in the
CLASSPATH, and the file acts like a root directory in terms of
packaging. It doesn't work just putting the directory containing the
JAR in your classpath, primarily because that would imply the name of
the JAR file is part of the package name (good luck trying to get the
".jar" part to go into the package name without being converted into
a subdirectory <VBG>)
Rational explanation? Lunatic rambling? Probably both. Hope it helped
though :)
- --
Chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2 (GNU/Linux)
iD8DBQE/KYnBwxczzJRavJYRAvGGAKCy9PNnjXfk5jtxHm9utwrGBZXbzg CglmIS
+gbxA2LvEiioKieWoT9jsxY=
=s0Na
-----END PGP SIGNATURE-----