javacommand-line-interfaceclassnotfoundexception

ClassNotFoundException when calling java code from a different folder than the one holding the code


The problem

I want to run java code from the command line, and I don't understand why I manage to do that from within the folder holding the code, but not from another folder. I am on Windows and am using Cygwin.

Example 1: What works

The folder /usr/local/lib/mytoolfolder/ contains two folders with java code:

If I do

$ cd /usr/local/lib/mytoolfolder/
$ java -cp ".;utils/bin;utils/lib/*;mytool/bin;mytool/lib/*" mytool.MyTool

... the class MyTool is called without any problems, and I get the expected output.

Example 2: What does not work

If I cd to a random other folder on my computer and try to call MyTool again, now including the full paths in the class path, like so:

$ java -cp "/usr/local/lib/mytoolfolder/.;/usr/local/lib/mytoolfolder/utils/bin;/usr/local/lib/mytoolfolder/utils/lib/*;/usr/local/lib/mytoolfolder/mytool/bin;/usr/local/lib/mytoolfolder/mytool/lib/*" mytool.MyTool

... I get this ClassNotFoundException:

Error: Could not find or load main class mytool.MyTool
Caused by: java.lang.ClassNotFoundException: mytool.MyTool

What I have tried

I have checked that the classpaths are correct and I have checked permissions.

Question

I understand that this error means that java can't find the class (obviously). I assume that this is because there is something wrong with the classpath (as Example 1 rules out that there is something wrong with the code itself).

I don't understand WHAT is wrong with the classpath, or with this approach. Where is the flaw in my logic? And how can I make it work?

Additional tips to debug this further are also appreciated.

EDIT

As requested by g00se below -- the output of find /usr/local/lib/mytoolfolder -name \*.jar -o -name \*.class looks like this (the actual output contains more of the same because my example above is minimised):

/usr/local/lib/mytoolfolder/mytool/bin/mytool/MyTool$Connection.class
/usr/local/lib/mytoolfolder/mytool/bin/mytool/MyTool.class
/usr/local/lib/mytoolfolder/utils/bin/tools/math/StatUtils.class
/usr/local/lib/mytoolfolder/utils/bin/tools/path/PathTools$1.class
/usr/local/lib/mytoolfolder/utils/bin/tools/path/PathTools.class
...
/usr/local/lib/mytoolfolder/utils/bin/tools/text/StringTools.class
/usr/local/lib/mytoolfolder/utils/bin/tools/ToolBox.class
/usr/local/lib/mytoolfolder/utils/bin/tools/ToolBox.class
/usr/local/lib/mytoolfolder/utils/lib/jackson-annotations-2.17.1.jar
/usr/local/lib/mytoolfolder/utils/lib/jackson-core-2.17.1.jar
/usr/local/lib/mytoolfolder/utils/lib/jackson-databind-2.17.1.jar

Solution

  • java is a Windows program so it doesn't understand Unix-style paths. Cygwin comes with a utility, cygpath that will convert paths, either individually, or even an entire PATH-style path, with the -p switch. But, it expects such a path to be delimited by colons. It will turn it into a Windows-style semicolon-delimited path.

    So you could use:

    cygpath -pw "/usr/local/lib/mytoolfolder:/usr/local/lib/mytoolfolder/utils/bin:/usr/local/lib/mytoolfolder/utils/lib/*:/usr/local/lib/mytoolfolder/mytool/bin:/usr/local/lib/mytoolfolder/mytool/lib/*"
    

    to convert the path, or even:

    java -cp "$(cygpath -pw '/usr/local/lib/mytoolfolder:/usr/local/lib/mytoolfolder/utils/bin:/usr/local/lib/mytoolfolder/utils/lib/*:/usr/local/lib/mytoolfolder/mytool/bin:/usr/local/lib/mytoolfolder/mytool/lib/*")"  mytool.MyTool
    

    to run your program.