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