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 05:00:22 UTC

svn commit: r1836867 - 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 05:00:21 2018
New Revision: 1836867

URL: http://svn.apache.org/viewvc?rev=1836867&view=rev
Log:
[BCEL-304] ClassPath.getClassFile does not work with Java 9 and higher.

Added:
    commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java
Modified:
    commons/proper/bcel/trunk/src/changes/changes.xml
    commons/proper/bcel/trunk/src/main/java/org/apache/bcel/util/ClassPath.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=1836867&r1=1836866&r2=1836867&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 05:00:21 2018
@@ -62,7 +62,8 @@ The <action> type attribute can be add,u
    -->
 
   <body>
-    <release version="6.3" date="2017-MM-DD" description="?">
+    <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.</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=1836867&r1=1836866&r2=1836867&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 05:00:21 2018
@@ -35,8 +35,7 @@ import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 /**
- * Responsible for loading (class) files from the CLASSPATH. Inspired by
- * sun.tools.ClassPath.
+ * Responsible for loading (class) files from the CLASSPATH. Inspired by sun.tools.ClassPath.
  *
  * @version $Id$
  */
@@ -47,14 +46,23 @@ public class ClassPath {
     private static final FilenameFilter ARCHIVE_FILTER = new FilenameFilter() {
 
         @Override
-        public boolean accept( final File dir, String name ) {
+        public boolean accept(final File dir, String name) {
             name = name.toLowerCase(Locale.ENGLISH);
             return name.endsWith(".zip") || name.endsWith(".jar");
         }
     };
 
-    private final PathEntry[] paths;
-    private final String class_path;
+    private static final FilenameFilter MODULES_FILTER = new FilenameFilter() {
+
+        @Override
+        public boolean accept(final File dir, String name) {
+            name = name.toLowerCase(Locale.ENGLISH);
+            return name.endsWith(".jmod");
+        }
+    };
+
+    private final AbstractPathEntry[] paths;
+    private final String classPath;
     private ClassPath parent;
 
     public ClassPath(final ClassPath parent, final String class_path) {
@@ -68,8 +76,8 @@ public class ClassPath {
      * @param class_path
      */
     public ClassPath(final String class_path) {
-        this.class_path = class_path;
-        final List<PathEntry> list = new ArrayList<>();
+        this.classPath = class_path;
+        final List<AbstractPathEntry> list = new ArrayList<>();
         for (final StringTokenizer tok = new StringTokenizer(class_path, File.pathSeparator); tok.hasMoreTokens();) {
             final String path = tok.nextToken();
             if (!path.isEmpty()) {
@@ -78,8 +86,10 @@ public class ClassPath {
                     if (file.exists()) {
                         if (file.isDirectory()) {
                             list.add(new Dir(path));
+                        } else if (path.endsWith(".jmod")) {
+                            list.add(new Module(new ZipFile(file)));
                         } else {
-                            list.add(new Zip(new ZipFile(file)));
+                            list.add(new Jar(new ZipFile(file)));
                         }
                     }
                 } catch (final IOException e) {
@@ -89,12 +99,13 @@ public class ClassPath {
                 }
             }
         }
-        paths = new PathEntry[list.size()];
+        paths = new AbstractPathEntry[list.size()];
         list.toArray(paths);
     }
 
     /**
      * Search for classes in CLASSPATH.
+     *
      * @deprecated Use SYSTEM_CLASS_PATH constant
      */
     @Deprecated
@@ -102,36 +113,35 @@ public class ClassPath {
         this(getClassPath());
     }
 
-    /** @return used class path string
+    /**
+     * @return used class path string
      */
     @Override
     public String toString() {
         if (parent != null) {
-            return parent + File.pathSeparator + class_path;
+            return parent + File.pathSeparator + classPath;
         }
-        return class_path;
+        return classPath;
     }
 
     @Override
     public int hashCode() {
         if (parent != null) {
-            return class_path.hashCode() + parent.hashCode();
+            return classPath.hashCode() + parent.hashCode();
         }
-        return class_path.hashCode();
+        return classPath.hashCode();
     }
 
-
     @Override
-    public boolean equals( final Object o ) {
+    public boolean equals(final Object o) {
         if (o instanceof ClassPath) {
-            final ClassPath cp = (ClassPath)o;
-            return class_path.equals(cp.toString());
+            final ClassPath cp = (ClassPath) o;
+            return classPath.equals(cp.toString());
         }
         return false;
     }
 
-
-    private static void getPathComponents( final String path, final List<String> list ) {
+    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()) {
@@ -144,9 +154,9 @@ public class ClassPath {
         }
     }
 
-
-    /** Checks for class path components in the following properties:
-     * "java.class.path", "sun.boot.class.path", "java.ext.dirs"
+    /**
+     * Checks for class path components in the following properties: "java.class.path", "sun.boot.class.path",
+     * "java.ext.dirs"
      *
      * @return class path as used by default by BCEL
      */
@@ -169,6 +179,19 @@ public class ClassPath {
                 }
             }
         }
+        // Starting in JDK 9, .class files are in the jmods directory. Add them to the path.
+        String modules_path = System.getProperty("java.modules.path");
+        if (modules_path == null || modules_path.trim().isEmpty()) {
+            // Default to looking in JAVA_HOME/jmods
+            modules_path = System.getProperty("java.home") + File.separator + "jmods";
+        }
+        final File modules_dir = new File(modules_path);
+        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) {
@@ -179,24 +202,25 @@ public class ClassPath {
         return buf.toString().intern();
     }
 
-
     /**
-     * @param name fully qualified class name, e.g. java.lang.String
+     * @param name
+     *            fully qualified class name, e.g. java.lang.String
      * @return input stream for class
      */
-    public InputStream getInputStream( final String name ) throws IOException {
-        return getInputStream(name.replace('.', '/'), ".class");
+    public InputStream getInputStream(final String name) throws IOException {
+        return getInputStream(packageToFolder(name), ".class");
     }
 
-
     /**
      * Return stream for class or resource on CLASSPATH.
      *
-     * @param name fully qualified file name, e.g. java/lang/String
-     * @param suffix file name ends with suff, e.g. .java
+     * @param name
+     *            fully qualified file name, e.g. java/lang/String
+     * @param suffix
+     *            file name ends with suff, e.g. .java
      * @return input stream for file on class path
      */
-    public InputStream getInputStream( final String name, final String suffix ) throws IOException {
+    public InputStream getInputStream(final String name, final String suffix) throws IOException {
         InputStream is = null;
         try {
             is = getClass().getClassLoader().getResourceAsStream(name + suffix); // may return null
@@ -210,12 +234,13 @@ public class ClassPath {
     }
 
     /**
-     * @param name fully qualified resource name, e.g. java/lang/String.class
+     * @param name
+     *            fully qualified resource name, e.g. java/lang/String.class
      * @return InputStream supplying the resource, or null if no resource with that name.
      * @since 6.0
      */
     public InputStream getResourceAsStream(final String name) {
-        for (final PathEntry path : paths) {
+        for (final AbstractPathEntry path : paths) {
             InputStream is;
             if ((is = path.getResourceAsStream(name)) != null) {
                 return is;
@@ -225,12 +250,13 @@ public class ClassPath {
     }
 
     /**
-     * @param name fully qualified resource name, e.g. java/lang/String.class
+     * @param name
+     *            fully qualified resource name, e.g. java/lang/String.class
      * @return URL supplying the resource, or null if no resource with that name.
      * @since 6.0
      */
     public URL getResource(final String name) {
-        for (final PathEntry path : paths) {
+        for (final AbstractPathEntry path : paths) {
             URL url;
             if ((url = path.getResource(name)) != null) {
                 return url;
@@ -240,14 +266,14 @@ public class ClassPath {
     }
 
     /**
-     * @param name fully qualified resource name, e.g. java/lang/String.class
-     * @return An Enumeration of URLs supplying the resource, or an
-     * empty Enumeration if no resource with that name.
+     * @param name
+     *            fully qualified resource name, e.g. java/lang/String.class
+     * @return An Enumeration of URLs supplying the resource, or an empty Enumeration if no resource with that name.
      * @since 6.0
      */
     public Enumeration<URL> getResources(final String name) {
         final Vector<URL> results = new Vector<>();
-        for (final PathEntry path : paths) {
+        for (final AbstractPathEntry path : paths) {
             URL url;
             if ((url = path.getResource(name)) != null) {
                 results.add(url);
@@ -257,11 +283,13 @@ public class ClassPath {
     }
 
     /**
-     * @param name fully qualified file name, e.g. java/lang/String
-     * @param suffix file name ends with suff, e.g. .java
+     * @param name
+     *            fully qualified file name, e.g. java/lang/String
+     * @param suffix
+     *            file name ends with suff, e.g. .java
      * @return class file for the java class
      */
-    public ClassFile getClassFile( final String name, final String suffix ) throws IOException {
+    public ClassFile getClassFile(final String name, final String suffix) throws IOException {
         ClassFile cf = null;
 
         if (parent != null) {
@@ -281,30 +309,31 @@ public class ClassPath {
 
     private ClassFile getClassFileInternal(final String name, final String suffix) throws IOException {
 
-      for (final PathEntry path : paths) {
-          final ClassFile cf = path.getClassFile(name, suffix);
+        for (final AbstractPathEntry path : paths) {
+            final ClassFile cf = path.getClassFile(name, suffix);
 
-          if(cf != null) {
-              return cf;
-          }
-      }
-
-      return null;
-   }
+            if (cf != null) {
+                return cf;
+            }
+        }
 
+        return null;
+    }
 
     /**
-     * @param name fully qualified class name, e.g. java.lang.String
+     * @param name
+     *            fully qualified class name, e.g. java.lang.String
      * @return input stream for class
      */
-    public ClassFile getClassFile( final String name ) throws IOException {
+    public ClassFile getClassFile(final String name) throws IOException {
         return getClassFile(name, ".class");
     }
 
-
     /**
-     * @param name fully qualified file name, e.g. java/lang/String
-     * @param suffix file name ends with suffix, e.g. .java
+     * @param name
+     *            fully qualified file name, e.g. java/lang/String
+     * @param suffix
+     *            file name ends with suffix, e.g. .java
      * @return byte array for file on class path
      */
     public byte[] getBytes(final String name, final String suffix) throws IOException {
@@ -324,20 +353,19 @@ public class ClassPath {
         }
     }
 
-
     /**
      * @return byte array for class
      */
-    public byte[] getBytes( final String name ) throws IOException {
+    public byte[] getBytes(final String name) throws IOException {
         return getBytes(name, ".class");
     }
 
-
     /**
-     * @param name name of file to search for, e.g. java/lang/String.java
+     * @param name
+     *            name of file to search for, e.g. java/lang/String.java
      * @return full (canonical) path for file
      */
-    public String getPath( String name ) throws IOException {
+    public String getPath(String name) throws IOException {
         final int index = name.lastIndexOf('.');
         String suffix = "";
         if (index > 0) {
@@ -347,58 +375,62 @@ public class ClassPath {
         return getPath(name, suffix);
     }
 
-
     /**
-     * @param name name of file to search for, e.g. java/lang/String
-     * @param suffix file name suffix, e.g. .java
+     * @param name
+     *            name of file to search for, e.g. java/lang/String
+     * @param suffix
+     *            file name suffix, e.g. .java
      * @return full (canonical) path for file, if it exists
      */
-    public String getPath( final String name, final String suffix ) throws IOException {
+    public String getPath(final String name, final String suffix) throws IOException {
         return getClassFile(name, suffix).getPath();
     }
 
-    private abstract static class PathEntry {
+    private abstract static class AbstractPathEntry {
+
+        abstract ClassFile getClassFile(String name, String suffix) throws IOException;
 
-        abstract ClassFile getClassFile( String name, String suffix ) throws IOException;
         abstract URL getResource(String name);
+
         abstract InputStream getResourceAsStream(String name);
     }
 
-    /** Contains information about file/ZIP entry of the Java class.
+    /**
+     * Contains information about file/ZIP entry of the Java class.
      */
     public interface ClassFile {
 
-        /** @return input stream for class file.
+        /**
+         * @return input stream for class file.
          */
         InputStream getInputStream() throws IOException;
 
-
-        /** @return canonical path to class file.
+        /**
+         * @return canonical path to class file.
          */
         String getPath();
 
-
-        /** @return base path of found class, i.e. class is contained relative
-         * to that path, which may either denote a directory, or zip file
+        /**
+         * @return base path of found class, i.e. class is contained relative to that path, which may either denote a
+         *         directory, or zip file
          */
         String getBase();
 
-
-        /** @return modification time of class file.
+        /**
+         * @return modification time of class file.
          */
         long getTime();
 
-
-        /** @return size of class file.
+        /**
+         * @return size of class file.
          */
         long getSize();
     }
 
-    private static class Dir extends PathEntry {
+    private static class Dir extends AbstractPathEntry {
 
         private final String dir;
 
-
         Dir(final String d) {
             dir = d;
         }
@@ -406,29 +438,28 @@ public class ClassPath {
         @Override
         URL getResource(final String name) {
             // Resource specification uses '/' whatever the platform
-            final File file = new File(dir + File.separatorChar + name.replace('/', File.separatorChar));
+            final File file = toFile(name);
             try {
                 return file.exists() ? file.toURI().toURL() : null;
             } catch (final MalformedURLException e) {
-               return null;
+                return null;
             }
         }
 
         @Override
         InputStream getResourceAsStream(final String name) {
             // Resource specification uses '/' whatever the platform
-            final File file = new File(dir + File.separatorChar + name.replace('/', File.separatorChar));
+            final File file = toFile(name);
             try {
-               return file.exists() ? new FileInputStream(file) : null;
+                return file.exists() ? new FileInputStream(file) : null;
             } catch (final IOException e) {
-               return null;
+                return null;
             }
         }
 
         @Override
-        ClassFile getClassFile( final String name, final String suffix ) throws IOException {
-            final File file = new File(dir + File.separatorChar
-                    + name.replace('.', File.separatorChar) + suffix);
+        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() {
 
                 @Override
@@ -436,7 +467,6 @@ public class ClassPath {
                     return new FileInputStream(file);
                 }
 
-
                 @Override
                 public String getPath() {
                     try {
@@ -446,19 +476,16 @@ public class ClassPath {
                     }
                 }
 
-
                 @Override
                 public long getTime() {
                     return file.lastModified();
                 }
 
-
                 @Override
                 public long getSize() {
                     return file.length();
                 }
 
-
                 @Override
                 public String getBase() {
                     return dir;
@@ -466,6 +493,9 @@ public class ClassPath {
             } : null;
         }
 
+        private File toFile(final String name) {
+            return new File(dir + File.separatorChar + name.replace('/', File.separatorChar));
+        }
 
         @Override
         public String toString() {
@@ -473,38 +503,63 @@ public class ClassPath {
         }
     }
 
-    private static class Zip extends PathEntry {
+    private static class Module extends AbstractZip {
 
-        private final ZipFile zip;
+        Module(final ZipFile zip) {
+            super(zip);
+        }
+
+        @Override
+        protected String toEntryName(final String name, final String suffix) {
+            return "classes/" + packageToFolder(name) + suffix;
+        }
+
+    }
 
+    private static class Jar extends AbstractZip {
 
-        Zip(final ZipFile z) {
-            zip = z;
+        Jar(final ZipFile zip) {
+            super(zip);
+        }
+
+        @Override
+        protected String toEntryName(final String name, final String suffix) {
+            return packageToFolder(name) + suffix;
+        }
+
+    }
+
+    private static abstract class AbstractZip extends AbstractPathEntry {
+
+        private final ZipFile zip;
+
+        AbstractZip(final ZipFile zip) {
+            this.zip = zip;
         }
 
         @Override
         URL getResource(final String name) {
             final ZipEntry entry = zip.getEntry(name);
             try {
-                return (entry != null) ? new URL("jar:file:" + zip.getName() + "!/" + name) : null;
+                return entry != null ? new URL("jar:file:" + zip.getName() + "!/" + name) : null;
             } catch (final MalformedURLException e) {
                 return null;
-           }
+            }
         }
 
         @Override
         InputStream getResourceAsStream(final String name) {
             final ZipEntry entry = zip.getEntry(name);
             try {
-                return (entry != null) ? zip.getInputStream(entry) : null;
+                return entry != null ? zip.getInputStream(entry) : null;
             } catch (final IOException e) {
                 return null;
             }
         }
 
         @Override
-        ClassFile getClassFile( final String name, final String suffix ) throws IOException {
-            final ZipEntry entry = zip.getEntry(name.replace('.', '/') + suffix);
+        ClassFile getClassFile(final String name, final String suffix) throws IOException {
+            final ZipEntry entry = zip.getEntry(toEntryName(name, suffix));
 
             if (entry == null) {
                 return null;
@@ -517,30 +572,38 @@ public class ClassPath {
                     return zip.getInputStream(entry);
                 }
 
-
                 @Override
                 public String getPath() {
                     return entry.toString();
                 }
 
-
                 @Override
                 public long getTime() {
                     return entry.getTime();
                 }
 
-
                 @Override
                 public long getSize() {
                     return entry.getSize();
                 }
 
-
                 @Override
                 public String getBase() {
                     return zip.getName();
                 }
             };
         }
+
+        protected abstract String toEntryName(final String name, final String suffix);
+
+        @Override
+        public String toString() {
+            return zip.getName();
+        }
+
+    }
+
+    static String packageToFolder(final String name) {
+        return name.replace('.', '/');
     }
 }

Added: 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=1836867&view=auto
==============================================================================
--- commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java (added)
+++ commons/proper/bcel/trunk/src/test/java/org/apache/bcel/util/ClassPathTestCase.java Sat Jul 28 05:00:21 2018
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.bcel.util;
+
+import java.io.IOException;
+
+import org.apache.bcel.AbstractTestCase;
+
+public class ClassPathTestCase extends AbstractTestCase {
+
+    public void testGetClassFile() throws IOException {
+        ClassPath.SYSTEM_CLASS_PATH.getClassFile("java.lang.String");
+    }
+}
\ No newline at end of file