javaclassloaderjava-11urlclassloader

How to check if a directory is in the classpath after java 11?


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...


Solution

  • 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();
        }
    }