tl;dr: Is there a direct way to check if a directory is in the classpath in Java 11?
I have the situation that I want to migrate a project from Java 8 to Java 11. I have Code that performs a check to see if a certain directory is in the classpath, as this is required in the program at a later stage.
This is how it looks:
// Step 002.1: Ensuring that runtime directory exists or at least can be created.
File clRootDirectory = new File(m_strRootDirectory);
clRootDirectory.mkdirs();
// Step 002.2: Checking if ROOT directory is in ClassPath
if ((clRootDirectory.isDirectory() == true) && (clRootDirectory.exists() == true))
{
URL[] clClassPathURLs = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
boolean bFoundRootInClassPath = false;
for (URL clCurrentPathElement : clClassPathURLs)
{
try
{
if (java.net.URLDecoder.decode(clCurrentPathElement.getFile(), StandardCharsets.UTF_8.name()).endsWith(m_strRootDirectory) == true) // endsWith seems a bit odd. But this way it works on Mac OS X as well.
{ // URLDecode.decode is necessary if there is a space on our classpath - as URLs use stuff like "%20" instead of " ".
bFoundRootInClassPath = true;
}
}
catch (UnsupportedEncodingException e)
{
// STATUS: Doesn't matter
}
}
if (bFoundRootInClassPath == false)
{
exitByFatalError("ROOT Directory is NOT in ClassPath. ROOT Directory is '"+m_strRootDirectory+"'.",SANITY_CHECK_ERROR_SYSTEM_ROOT_NOT_IN_CLASSPATH);
}
}
else
{
exitByFatalError("RootDirectory ("+m_strRootDirectory+") is not a valid Directory.", SANITY_CHECK_ERROR_SYSTEM_ROOT_NOT_A_DIRECTORY);
}
This seems pretty straightforward to me, and it works. However, The ClassLoader no longer has URLClassLoader as a base class. This makes the code completely unusable, since it heavily relies on this function. I have tried to replace the code with this:
private static boolean isInClasspath(File file) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
String directoryPath = file.getAbsolutePath();
String normalizedPath = directoryPath.endsWith("/") ? directoryPath : directoryPath + "/";
try {
Enumeration<URL> resources = classLoader.getResources(normalizedPath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
System.out.println("url = " + url);
String resourcePath = url.getPath();
if (resourcePath.startsWith(normalizedPath)) {
return true;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
However, this simply doesn't work. My resources enumeration is always empty, and I explicitly add my directory to the classpath in my run config. I haven't changed that, and with Java 8 the run config does its job.
I know that there are a number of ways to check if a certain file is in the classpath, but I want to know about an entire directory. I'd rather not implement a completely custom class loader...
The class path is available through System.getProperty("java.class.path")
. So you can use, e.g.
private static boolean isInClasspath(File file) {
List<String> classPath =
Arrays.asList(System.getProperty("java.class.path")
.split(File.pathSeparator));
return classPath.contains(file.toString());
}
This checks for a literal match only. If you want to check whether a path element refers to the same directory after resolving relative paths like ..
and links and/or perform a case insensitive match when the filesystem is case insensitive, you may use
private static boolean isInClasspath(File file) {
List<String> classPath =
Arrays.asList(System.getProperty("java.class.path")
.split(Pattern.quote(File.pathSeparator)));
if(classPath.contains(file.toString()))
return true;
Path path = check(file.toPath());
for(String s: classPath) {
if(check(Paths.get(s)).equals(path))
return true;
}
return false;
}
private static Path check(Path path) {
try {
return path.toRealPath();
} catch(IOException ex) {
return path.toAbsolutePath();
}
}