You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2018/07/28 15:34:40 UTC

svn commit: r1836927 - in /commons/proper/bcel/trunk/src: changes/changes.xml main/java/org/apache/bcel/util/ClassPath.java test/java/org/apache/bcel/util/ClassPathTestCase.java

Author: ggregory
Date: Sat Jul 28 15:34:40 2018
New Revision: 1836927

URL: http://svn.apache.org/viewvc?rev=1836927&view=rev
Log:
[BCEL-305] ClassPath.getClassFile() and friends do not work with JRE 9 and higher.

Modified:
    commons/proper/bcel/trunk/src/changes/changes.xml
    commons/proper/bcel/trunk/src/main/java/org/apache/bcel/util/ClassPath.java
    commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java

Modified: commons/proper/bcel/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/changes/changes.xml?rev=1836927&r1=1836926&r2=1836927&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/changes/changes.xml [utf-8] (original)
+++ commons/proper/bcel/trunk/src/changes/changes.xml [utf-8] Sat Jul 28 15:34:40 2018
@@ -63,7 +63,8 @@ The <action> type attribute can be add,u
 
   <body>
     <release version="6.3" date="2018-MM-DD" description="?">
-      <action issue="BCEL-304" type="fix" dev="ggregory" due-to="Gary Gregory, Ed Pavlak">ClassPath.getClassFile does not work with JDK 9 and higher (PR #22.)</action>
+      <action issue="BCEL-304" type="fix" dev="ggregory" due-to="Gary Gregory, Ed Pavlak">ClassPath.getClassFile() and friends do not work with JDK 9 and higher (PR #22.)</action>
+      <action issue="BCEL-305" type="fix" dev="ggregory" due-to="Gary Gregory">ClassPath.getClassFile() and friends do not work with JRE 9 and higher</action>
     </release>
 
     <release version="6.2" date="2017-12-08" description="Experimental Java 9 Support">

Modified: commons/proper/bcel/trunk/src/main/java/org/apache/bcel/util/ClassPath.java
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/main/java/org/apache/bcel/util/ClassPath.java?rev=1836927&r1=1836926&r2=1836927&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/main/java/org/apache/bcel/util/ClassPath.java (original)
+++ commons/proper/bcel/trunk/src/main/java/org/apache/bcel/util/ClassPath.java Sat Jul 28 15:34:40 2018
@@ -17,6 +17,7 @@
  */
 package org.apache.bcel.util;
 
+import java.io.Closeable;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -24,11 +25,24 @@ import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.net.URI;
 import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.zip.ZipEntry;
@@ -39,9 +53,9 @@ import java.util.zip.ZipFile;
  *
  * @version $Id$
  */
-public class ClassPath {
+public class ClassPath implements Closeable {
 
-    private abstract static class AbstractPathEntry {
+    private abstract static class AbstractPathEntry implements Closeable {
 
         abstract ClassFile getClassFile(String name, String suffix) throws IOException;
 
@@ -52,15 +66,23 @@ public class ClassPath {
 
     private static abstract class AbstractZip extends AbstractPathEntry {
 
-        private final ZipFile zip;
+        private final ZipFile zipFile;
+
+        AbstractZip(final ZipFile zipFile) {
+            this.zipFile = Objects.requireNonNull(zipFile, "zipFile");
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (zipFile != null) {
+                zipFile.close();
+            }
 
-        AbstractZip(final ZipFile zip) {
-            this.zip = zip;
         }
 
         @Override
         ClassFile getClassFile(final String name, final String suffix) throws IOException {
-            final ZipEntry entry = zip.getEntry(toEntryName(name, suffix));
+            final ZipEntry entry = zipFile.getEntry(toEntryName(name, suffix));
 
             if (entry == null) {
                 return null;
@@ -70,12 +92,12 @@ public class ClassPath {
 
                 @Override
                 public String getBase() {
-                    return zip.getName();
+                    return zipFile.getName();
                 }
 
                 @Override
                 public InputStream getInputStream() throws IOException {
-                    return zip.getInputStream(entry);
+                    return zipFile.getInputStream(entry);
                 }
 
                 @Override
@@ -97,9 +119,9 @@ public class ClassPath {
 
         @Override
         URL getResource(final String name) {
-            final ZipEntry entry = zip.getEntry(name);
+            final ZipEntry entry = zipFile.getEntry(name);
             try {
-                return entry != null ? new URL("jar:file:" + zip.getName() + "!/" + name) : null;
+                return entry != null ? new URL("jar:file:" + zipFile.getName() + "!/" + name) : null;
             } catch (final MalformedURLException e) {
                 return null;
             }
@@ -107,9 +129,9 @@ public class ClassPath {
 
         @Override
         InputStream getResourceAsStream(final String name) {
-            final ZipEntry entry = zip.getEntry(name);
+            final ZipEntry entry = zipFile.getEntry(name);
             try {
-                return entry != null ? zip.getInputStream(entry) : null;
+                return entry != null ? zipFile.getInputStream(entry) : null;
             } catch (final IOException e) {
                 return null;
             }
@@ -119,7 +141,7 @@ public class ClassPath {
 
         @Override
         public String toString() {
-            return zip.getName();
+            return zipFile.getName();
         }
 
     }
@@ -165,6 +187,12 @@ public class ClassPath {
         }
 
         @Override
+        public void close() throws IOException {
+            // Nothing to do
+
+        }
+
+        @Override
         ClassFile getClassFile(final String name, final String suffix) throws IOException {
             final File file = new File(dir + File.separatorChar + name.replace('.', File.separatorChar) + suffix);
             return file.exists() ? new ClassFile() {
@@ -231,6 +259,7 @@ public class ClassPath {
             return dir;
         }
     }
+
     private static class Jar extends AbstractZip {
 
         Jar(final ZipFile zip) {
@@ -243,6 +272,195 @@ public class ClassPath {
         }
 
     }
+
+    private static class JrtModule extends AbstractPathEntry {
+
+        private final Path modulePath;
+
+        public JrtModule(final Path modulePath) {
+            this.modulePath = Objects.requireNonNull(modulePath, "modulePath");
+        }
+
+        @Override
+        public void close() throws IOException {
+            // Nothing to do.
+
+        }
+
+        @Override
+        ClassFile getClassFile(final String name, final String suffix) throws IOException {
+            final Path resolved = modulePath.resolve(packageToFolder(name) + suffix);
+            if (Files.exists(resolved)) {
+                return new ClassFile() {
+
+                    @Override
+                    public String getBase() {
+                        return resolved.getFileName().toString();
+                    }
+
+                    @Override
+                    public InputStream getInputStream() throws IOException {
+                        return Files.newInputStream(resolved);
+                    }
+
+                    @Override
+                    public String getPath() {
+                        return resolved.toString();
+                    }
+
+                    @Override
+                    public long getSize() {
+                        try {
+                            return Files.size(resolved);
+                        } catch (final IOException e) {
+                            return 0;
+                        }
+                    }
+
+                    @Override
+                    public long getTime() {
+                        try {
+                            return Files.getLastModifiedTime(resolved).toMillis();
+                        } catch (final IOException e) {
+                            return 0;
+                        }
+                    }
+                };
+            }
+            return null;
+        }
+
+        @Override
+        URL getResource(final String name) {
+            final Path resovled = modulePath.resolve(name);
+            try {
+                return Files.exists(resovled) ? new URL("jrt:" + modulePath + "/" + name) : null;
+            } catch (final MalformedURLException e) {
+                return null;
+            }
+        }
+
+        @Override
+        InputStream getResourceAsStream(final String name) {
+            try {
+                return Files.newInputStream(modulePath.resolve(name));
+            } catch (final IOException e) {
+                return null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return modulePath.toString();
+        }
+
+    }
+
+    private static class JrtModules extends AbstractPathEntry {
+
+        @SuppressWarnings("resource")
+        private static JrtModule[] getJreModules() {
+            final List<JrtModule> list = new ArrayList<>();
+            final Path jrePath = Paths.get(System.getProperty("java.home"));
+            try {
+                final Path jrtFsPath = jrePath.resolve("lib").resolve("jrt-fs.jar");
+                if (Files.exists(jrtFsPath)) {
+                    final Map<String, ?> emptyMap = Collections.emptyMap();
+                    try (URLClassLoader classLoader = new URLClassLoader(new URL[] {jrtFsPath.toUri().toURL() });
+                            FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), emptyMap, classLoader)) {
+                        try (DirectoryStream<Path> ds = Files.newDirectoryStream(fs.getPath("/modules"))) {
+                            final Iterator<Path> iterator = ds.iterator();
+                            while (iterator.hasNext()) {
+                                list.add(new JrtModule(iterator.next()));
+                            }
+                        }
+                    }
+                }
+            } catch (final Exception e) {
+                // Log?
+                e.printStackTrace();
+            }
+            return list.toArray(new JrtModule[list.size()]);
+        }
+        URLClassLoader classLoader;
+        FileSystem fs;
+
+        private final JrtModule[] modules;
+
+        @SuppressWarnings("resource")
+        public JrtModules() {
+            final List<JrtModule> list = new ArrayList<>();
+            try {
+                final Map<String, ?> emptyMap = Collections.emptyMap();
+                final Path jrePath = Paths.get(System.getProperty("java.home"));
+                final Path jrtFsPath = jrePath.resolve("lib").resolve("jrt-fs.jar");
+                this.classLoader = new URLClassLoader(new URL[] {jrtFsPath.toUri().toURL() });
+                this.fs = FileSystems.newFileSystem(URI.create("jrt:/"), emptyMap, classLoader);
+                try (DirectoryStream<Path> ds = Files.newDirectoryStream(fs.getPath("/modules"))) {
+                    final Iterator<Path> iterator = ds.iterator();
+                    while (iterator.hasNext()) {
+                        list.add(new JrtModule(iterator.next()));
+                    }
+                }
+            } catch (final IOException e) {
+                // TODO ?
+                e.printStackTrace();
+            }
+            this.modules = list.toArray(new JrtModule[list.size()]);
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (modules != null) {
+                for (final JrtModule module : modules) {
+                    module.close();
+                }
+            }
+        }
+
+        @Override
+        ClassFile getClassFile(final String name, final String suffix) throws IOException {
+            // don't use a for each loop to avoid creating an iterator for the GC to collect.
+            for (final JrtModule module : modules) {
+                final ClassFile classFile = module.getClassFile(name, suffix);
+                if (classFile != null) {
+                    return classFile;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        URL getResource(final String name) {
+            // don't use a for each loop to avoid creating an iterator for the GC to collect.
+            for (final JrtModule module : modules) {
+                final URL url = module.getResource(name);
+                if (url != null) {
+                    return url;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        InputStream getResourceAsStream(final String name) {
+            // don't use a for each loop to avoid creating an iterator for the GC to collect.
+            for (final JrtModule module : modules) {
+                final InputStream inputStream = module.getResourceAsStream(name);
+                if (inputStream != null) {
+                    return inputStream;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.toString(modules);
+        }
+
+    }
+
     private static class Module extends AbstractZip {
 
         Module(final ZipFile zip) {
@@ -276,6 +494,21 @@ public class ClassPath {
 
     public static final ClassPath SYSTEM_CLASS_PATH = new ClassPath(getClassPath());
 
+    private static void addJdkModules(final String javaHome, final List<String> list) {
+        String modulesPath = System.getProperty("java.modules.path");
+        if (modulesPath == null || modulesPath.trim().isEmpty()) {
+            // Default to looking in JAVA_HOME/jmods
+            modulesPath = javaHome + File.separator + "jmods";
+        }
+        final File modulesDir = new File(modulesPath);
+        if (modulesDir.exists()) {
+            final String[] modules = modulesDir.list(MODULES_FILTER);
+            for (final String module : modules) {
+                list.add(modulesDir.getPath() + File.separatorChar + module);
+            }
+        }
+    }
+
     /**
      * Checks for class path components in the following properties: "java.class.path", "sun.boot.class.path",
      * "java.ext.dirs"
@@ -287,7 +520,21 @@ public class ClassPath {
         final String classPathProp = System.getProperty("java.class.path");
         final String bootClassPathProp = System.getProperty("sun.boot.class.path");
         final String extDirs = System.getProperty("java.ext.dirs");
+        // System.out.println("java.version = " + System.getProperty("java.version"));
+        // System.out.println("java.class.path = " + classPathProp);
+        // System.out.println("sun.boot.class.path=" + bootClassPathProp);
+        // System.out.println("java.ext.dirs=" + extDirs);
+        final String javaHome = System.getProperty("java.home");
         final List<String> list = new ArrayList<>();
+
+        // Starting in JRE 9, .class files are in the modules directory. Add them to the path.
+        final Path modulesPath = Paths.get(javaHome).resolve("lib/modules");
+        if (Files.exists(modulesPath) && Files.isRegularFile(modulesPath)) {
+            list.add(modulesPath.toAbsolutePath().toString());
+        }
+        // Starting in JDK 9, .class files are in the jmods directory. Add them to the path.
+        addJdkModules(javaHome, list);
+
         getPathComponents(classPathProp, list);
         getPathComponents(bootClassPathProp, list);
         final List<String> dirs = new ArrayList<>();
@@ -301,20 +548,7 @@ public class ClassPath {
                 }
             }
         }
-        final String javaHome = System.getProperty("java.home");
-        // Starting in JDK 9, .class files are in the jmods directory. Add them to the path.
-        String modulesPath = System.getProperty("java.modules.path");
-        if (modulesPath == null || modulesPath.trim().isEmpty()) {
-            // Default to looking in JAVA_HOME/jmods
-            modulesPath = javaHome + File.separator + "jmods";
-        }
-        final File modules_dir = new File(modulesPath);
-        if (modules_dir.exists()) {
-            final String[] modules = modules_dir.list(MODULES_FILTER);
-            for (final String module : modules) {
-                list.add(modules_dir.getPath() + File.separatorChar + module);
-            }
-        }
+
         final StringBuilder buf = new StringBuilder();
         String separator = "";
         for (final String path : list) {
@@ -327,9 +561,9 @@ public class ClassPath {
 
     private static void getPathComponents(final String path, final List<String> list) {
         if (path != null) {
-            final StringTokenizer tok = new StringTokenizer(path, File.pathSeparator);
-            while (tok.hasMoreTokens()) {
-                final String name = tok.nextToken();
+            final StringTokenizer tokenizer = new StringTokenizer(path, File.pathSeparator);
+            while (tokenizer.hasMoreTokens()) {
+                final String name = tokenizer.nextToken();
                 final File file = new File(name);
                 if (file.exists()) {
                     list.add(name);
@@ -358,20 +592,21 @@ public class ClassPath {
         this(getClassPath());
     }
 
-    public ClassPath(final ClassPath parent, final String class_path) {
-        this(class_path);
+    public ClassPath(final ClassPath parent, final String classPath) {
+        this(classPath);
         this.parent = parent;
     }
 
     /**
      * Search for classes in given path.
      *
-     * @param class_path
+     * @param classPath
      */
-    public ClassPath(final String class_path) {
-        this.classPath = class_path;
+    @SuppressWarnings("resource")
+    public ClassPath(final String classPath) {
+        this.classPath = classPath;
         final List<AbstractPathEntry> list = new ArrayList<>();
-        for (final StringTokenizer tokenizer = new StringTokenizer(class_path, File.pathSeparator); tokenizer
+        for (final StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); tokenizer
                 .hasMoreTokens();) {
             final String path = tokenizer.nextToken();
             if (!path.isEmpty()) {
@@ -382,6 +617,8 @@ public class ClassPath {
                             list.add(new Dir(path));
                         } else if (path.endsWith(".jmod")) {
                             list.add(new Module(new ZipFile(file)));
+                        } else if (path.endsWith(File.separator + "modules")) {
+                            list.add(new JrtModules());
                         } else {
                             list.add(new Jar(new ZipFile(file)));
                         }
@@ -398,6 +635,16 @@ public class ClassPath {
     }
 
     @Override
+    public void close() throws IOException {
+        if (paths != null) {
+            for (final AbstractPathEntry path : paths) {
+                path.close();
+            }
+        }
+
+    }
+
+    @Override
     public boolean equals(final Object o) {
         if (o instanceof ClassPath) {
             final ClassPath cp = (ClassPath) o;

Modified: commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java?rev=1836927&r1=1836926&r2=1836927&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java (original)
+++ commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java Sat Jul 28 15:34:40 2018
@@ -19,12 +19,25 @@
 package org.apache.bcel.util;
 
 import java.io.IOException;
+import java.io.InputStream;
 
 import org.apache.bcel.AbstractTestCase;
+import org.junit.Assert;
 
 public class ClassPathTestCase extends AbstractTestCase {
 
     public void testGetClassFile() throws IOException {
-        ClassPath.SYSTEM_CLASS_PATH.getClassFile("java.lang.String");
+        Assert.assertNotNull(ClassPath.SYSTEM_CLASS_PATH.getClassFile("java.lang.String"));
+    }
+
+    public void testGetResource() throws IOException {
+        Assert.assertNotNull(ClassPath.SYSTEM_CLASS_PATH.getResource("java/lang/String.class"));
+    }
+
+    public void testGetResourceAsStream() throws IOException {
+        try (final InputStream inputStream = ClassPath.SYSTEM_CLASS_PATH
+                .getResourceAsStream("java/lang/String.class")) {
+            Assert.assertNotNull(inputStream);
+        }
     }
 }
\ No newline at end of file