Really long classpa…!
The maximum classpath length that Java can handle depends on many factors, including platform and JDK implementation. As far as I can tell, the limit is usually undocumented, but can be as low as a few thousand characters. Additionally, elements beyond Java’s control, such as the shell, can also affect how many characters can safely be used in a classpath.
In practice, this means that the classpath you request can be truncated before the JRE processes it… and you might not find out until you get a very confusing ClassNotFoundException.
If circumstances dictate an uncomfortably long classpath at launch time, there are a few ways to condense it without resorting to single-character symbolic links.
The java.ext.dirs system property or classpath wildcards might be handy if you don’t care about the ordering of the entities on the classpath. If you do care about ordering, one approach is to package everything up into a single jar, taking care to select the correct versions of each class from your existing jars and class directories (the leftmost occurrence takes precedence). In practice this can get a little tricky, particularly if you need to respect all the jar manifests – in which case consider using a pathing jar.
But if you’re in a position to modify the code you’re running, another option is to simply load the classes at runtime.
I needed to handle huge, varied, dynamically-generated classpaths in thousands JUnit tests forked from Ant. The tests ran fine in most contexts – but on z/OS, where the classpath length limit appears to be significantly lower, many tests failed because their classpaths were truncated. I didn’t want to redesign the build process just for the sake of z/OS.
So I fashioned a few classes to shorten the classpath that is used when Ant forks JUnit processes. These include a slightly modified implementation of the Ant 1.7.1 Junit task. Instead of just launching a JUnit process with a huge classpath, the modified task writes the classpath to file, and launches a wrapper application, passing it the path to the generated file, as well as the class to launch complete with its application args.
The wrapper application reads the file containing the desired classpath entries, and loads them programatically. Finally, it invokes the class that was originally supposed to run via reflection.
Download “Jawful” (bin & src)
To use it, put jawful.jar on the classpath before Ant’s jars when you run Ant. You can achieve this by setting the CLASSPATH environment variable to point to jawful.jar before launching Ant.
Hopefully this will save you a few minutes should you ever run into mysterious ClassNotFoundExceptions in JUnit processes forked from Ant! 🙂
2 thoughts on “Java Classpath Length”
I was about to write this when I found this blog post. This is exactly what I was looking for. I made two trivial changes: use FILE_UTILS.createTempFile to create the temporary file (where FILE_UTILS is an instance of org.apache.tools.ant.util.FileUtils), and delete the temporary file from inside the wrapper program after reading it and closing the readers. Porting the changes to JUnitTask to work with ant 1.8.3 was trivial: the diff against pristine 1.7.1 applied with offsets to 1.8.3. I also created a small build.xml. Anyway, thanks for doing this and sharing it!