8160398: (jdeps) Replace a list of JDK 8 internal API for detecting if it's removed in JDK 9 or later

Reviewed-by: dfuchs
This commit is contained in:
Mandy Chung 2016-07-13 14:41:27 -07:00
parent e08b3b12fb
commit de4f424b8c
10 changed files with 1332 additions and 52 deletions

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package build.tools.listjdkinternals;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.net.URI;
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.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
/**
* Run this tool to generate the JDK internal APIs in the previous releases
* including platform-specific internal APIs.
*/
public class ListJDKInternals {
// Filter non-interesting JAR files
private final static List<String> excludes = Arrays.asList(
"deploy.jar",
"javaws.jar",
"plugin.jar",
"cldrdata.jar",
"localedata.jar"
);
private static void usage() {
System.out.println("ListJDKInternals [-o <outfile>] <javaHome> [<javaHome>]*");
}
private static final Set<String> EXPORTED_PACKAGES = new HashSet<>();
public static void main(String... args) throws IOException {
List<Path> paths = new ArrayList<>();
Path outFile = null;
int i=0;
while (i < args.length) {
String arg = args[i++];
if (arg.equals("-o")) {
outFile = Paths.get(args[i++]);
} else {
Path p = Paths.get(arg);
if (Files.notExists(p))
throw new IllegalArgumentException(p + " not exist");
paths.add(p);
}
}
if (paths.isEmpty()) {
usage();
System.exit(1);
}
// Get the exported APIs from the current JDK releases
Path javaHome = Paths.get(System.getProperty("java.home"));
ModuleFinder.ofSystem().findAll()
.stream()
.map(ModuleReference::descriptor)
.filter(md -> !md.name().equals("jdk.unsupported"))
.map(ModuleDescriptor::exports)
.flatMap(Set::stream)
.filter(exp -> !exp.isQualified())
.map(ModuleDescriptor.Exports::source)
.forEach(EXPORTED_PACKAGES::add);
ListJDKInternals listJDKInternals = new ListJDKInternals(paths);
if (outFile != null) {
try (OutputStream out = Files.newOutputStream(outFile);
PrintStream pw = new PrintStream(out)) {
listJDKInternals.write(pw);
}
} else {
listJDKInternals.write(System.out);
}
}
private final Set<String> packages = new HashSet<>();
ListJDKInternals(List<Path> dirs) throws IOException {
for (Path p : dirs) {
packages.addAll(list(p));
}
}
private void write(PrintStream pw) {
pw.println("# This file is auto-generated by ListJDKInternals tool on " +
LocalDateTime.now().toString());
packages.stream().sorted()
.forEach(pw::println);
}
private Set<String> list(Path javaHome) throws IOException {
Path jrt = javaHome.resolve("lib").resolve("modules");
Path jre = javaHome.resolve("jre");
if (Files.exists(jrt)) {
return listModularRuntime(javaHome);
} else if (Files.exists(jre.resolve("lib").resolve("rt.jar"))) {
return listLegacyRuntime(javaHome);
}
throw new IllegalArgumentException("invalid " + javaHome);
}
private Set<String> listModularRuntime(Path javaHome) throws IOException {
Map<String, String> env = new HashMap<>();
env.put("java.home", javaHome.toString());
FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env);
Path root = fs.getPath("packages");
return Files.walk(root, 1)
.map(Path::getFileName)
.map(Path::toString)
.filter(pn -> !EXPORTED_PACKAGES.contains(pn))
.collect(Collectors.toSet());
}
private Set<String> listLegacyRuntime(Path javaHome) throws IOException {
List<Path> dirs = new ArrayList<>();
Path jre = javaHome.resolve("jre");
Path lib = javaHome.resolve("lib");
dirs.add(jre.resolve("lib"));
dirs.add(jre.resolve("lib").resolve("ext"));
dirs.add(lib.resolve("tools.jar"));
dirs.add(lib.resolve("jconsole.jar"));
Set<String> packages = new HashSet<>();
for (Path d : dirs) {
Files.find(d, 1, (Path p, BasicFileAttributes attr)
-> p.getFileName().toString().endsWith(".jar") &&
!excludes.contains(p.getFileName().toString()))
.map(ListJDKInternals::walkJarFile)
.flatMap(Set::stream)
.filter(pn -> !EXPORTED_PACKAGES.contains(pn))
.forEach(packages::add);
}
return packages;
}
static Set<String> walkJarFile(Path jarfile) {
try (JarFile jf = new JarFile(jarfile.toFile())) {
return jf.stream()
.map(JarEntry::getName)
.filter(n -> n.endsWith(".class"))
.map(ListJDKInternals::toPackage)
.collect(Collectors.toSet());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
static String toPackage(String name) {
int i = name.lastIndexOf('/');
if (i < 0) {
System.err.format("Warning: unnamed package %s%n", name);
}
return i >= 0 ? name.substring(0, i).replace("/", ".") : "";
}
}

View File

@ -25,20 +25,19 @@
package com.sun.tools.jdeps;
import static com.sun.tools.jdeps.JdepsConfiguration.*;
import com.sun.tools.classfile.Dependency.Location;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -369,35 +368,29 @@ public class Analyzer {
}
}
static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
static final Jdk8Internals REMOVED_JDK_INTERNALS = new Jdk8Internals();
static class JdkInternals extends Module {
private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals";
private final Set<String> jdkinternals;
private final Set<String> jdkUnsupportedClasses;
private JdkInternals() {
static class Jdk8Internals extends Module {
private final String JDK8_INTERNALS = "/com/sun/tools/jdeps/resources/jdk8_internals.txt";
private final Set<String> jdk8Internals;
private Jdk8Internals() {
super("JDK removed internal API");
try {
ResourceBundle rb = ResourceBundle.getBundle(BUNDLE);
this.jdkinternals = rb.keySet();
} catch (MissingResourceException e) {
throw new InternalError("Cannot find jdkinternals resource bundle");
try (InputStream in = JdepsTask.class.getResourceAsStream(JDK8_INTERNALS);
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
this.jdk8Internals = reader.lines()
.filter(ln -> !ln.startsWith("#"))
.collect(Collectors.toSet());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
this.jdkUnsupportedClasses = getUnsupportedClasses();
}
public boolean contains(Location location) {
if (jdkUnsupportedClasses.contains(location.getName() + ".class")) {
return false;
}
String cn = location.getClassName();
int i = cn.lastIndexOf('.');
String pn = i > 0 ? cn.substring(0, i) : "";
return jdkinternals.contains(cn) || jdkinternals.contains(pn);
return jdk8Internals.contains(pn);
}
@Override
@ -414,25 +407,5 @@ public class Analyzer {
public boolean isExported(String pn) {
return false;
}
private Set<String> getUnsupportedClasses() {
// jdk.unsupported may not be observable
Optional<Module> om = Profile.FULL_JRE.findModule(JDK_UNSUPPORTED);
if (om.isPresent()) {
return om.get().reader().entries();
}
// find from local run-time image
SystemModuleFinder system = new SystemModuleFinder();
if (system.find(JDK_UNSUPPORTED).isPresent()) {
try {
return system.getClassReader(JDK_UNSUPPORTED).entries();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return Collections.emptySet();
}
}
}

View File

@ -28,7 +28,7 @@ sun.security.util.SecurityConstants=Use appropriate java.security.Permission sub
sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4
sun.tools.jar=Use java.util.jar or jar tool @since 1.2
# Internal APIs removed in JDK 9
com.apple.eawt=Use java.awt.desktop and JEP 272 @since 9
com.apple.eawt=Use java.awt.Desktop and JEP 272 @since 9
com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187
com.sun.image.codec.jpeg=Use javax.imageio @since 1.4
sun.awt.image.codec=Use javax.imageio @since 1.4

View File

@ -71,7 +71,7 @@ public class RemovedJDKInternals {
assertTrue(CompilerUtils.compile(codecSrc, codecDest));
// patch jdk.unsupported and set -cp to codec types
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src"),
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "p"),
CLASSES_DIR,
"-Xpatch:jdk.unsupported=" + patchDir,
"-cp", codecDest.toString()));

View File

@ -59,14 +59,17 @@ public class ShowReplacement {
public void compileAll() throws Exception {
CompilerUtils.cleanDir(CLASSES_DIR);
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "p"),
Path tmp = Paths.get("tmp");
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "apple"), tmp));
assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "q"),
CLASSES_DIR,
"-cp", tmp.toString(),
"-XaddExports:java.base/sun.security.util=ALL-UNNAMED"));
}
@Test
public void withReplacement() {
Path file = Paths.get("p", "WithRepl.class");
Path file = Paths.get("q", "WithRepl.class");
String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString());
int i = 0;
while (!output[i].contains("Suggested Replacement")) {
@ -90,9 +93,29 @@ public class ShowReplacement {
}
}
/*
* A JDK internal class has been removed while its package still exists.
*/
@Test
public void noReplacement() {
Path file = Paths.get("p", "NoRepl.class");
Path file = Paths.get("q", "NoRepl.class");
String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString());
int i = 0;
// expect no replacement
while (i < output.length && !output[i].contains("Suggested Replacement")) {
i++;
}
// no replacement
assertEquals(output.length-i, 0);
}
/*
* A JDK internal package has been removed.
*/
@Test
public void removedPackage() {
Path file = Paths.get("q", "RemovedPackage.class");
String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString());
int i = 0;
// expect no replacement

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package apple.applescript;
import javax.script.ScriptEngine;
public interface AppleScriptEngine extends ScriptEngine {
}

View File

@ -21,7 +21,7 @@
* questions.
*/
package p;
package q;
import java.io.IOException;
import java.io.OutputStream;

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package q;
import apple.applescript.AppleScriptEngine;
public class RemovedPackage {
AppleScriptEngine scriptEngine;
}

View File

@ -21,7 +21,7 @@
* questions.
*/
package p;
package q;
import sun.security.util.HostnameChecker;