8050804: (jdeps) Recommend supported API to replace use of JDK internal API
Reviewed-by: dfuchs
diff --git a/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java b/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java
index 04d6e71..afaa38e 100644
--- a/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java
@@ -114,6 +114,11 @@
return false;
}
+ public Set<String> dependences(Archive source) {
+ ArchiveDeps result = results.get(source);
+ return result.targetDependences();
+ }
+
public interface Visitor {
/**
* Visits a recorded dependency from origin to target which can be
@@ -179,6 +184,14 @@
return deps;
}
+ Set<String> targetDependences() {
+ Set<String> targets = new HashSet<>();
+ for (Dep d : deps) {
+ targets.add(d.target());
+ }
+ return targets;
+ }
+
Set<Archive> requires() {
return requires;
}
diff --git a/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java b/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
index c3110e7..e75e9c7 100644
--- a/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
@@ -236,6 +236,11 @@
task.options.showLabel = true;
}
},
+ new HiddenOption(false, "-q", "-quiet") {
+ void process(JdepsTask task, String opt, String arg) {
+ task.options.nowarning = true;
+ }
+ },
new HiddenOption(true, "-depth") {
void process(JdepsTask task, String opt, String arg) throws BadArgs {
try {
@@ -249,7 +254,7 @@
private static final String PROGNAME = "jdeps";
private final Options options = new Options();
- private final List<String> classes = new ArrayList<String>();
+ private final List<String> classes = new ArrayList<>();
private PrintWriter log;
void setLog(PrintWriter out) {
@@ -320,7 +325,9 @@
Analyzer analyzer = new Analyzer(options.verbose, new Analyzer.Filter() {
@Override
- public boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive) {
+ public boolean accepts(Location origin, Archive originArchive,
+ Location target, Archive targetArchive)
+ {
if (options.findJDKInternals) {
// accepts target that is JDK class but not exported
return isJDKArchive(targetArchive) &&
@@ -344,6 +351,10 @@
} else {
printRawOutput(log, analyzer);
}
+
+ if (options.findJDKInternals && !options.nowarning) {
+ showReplacements(analyzer);
+ }
return true;
}
@@ -693,6 +704,7 @@
boolean apiOnly;
boolean showLabel;
boolean findJDKInternals;
+ boolean nowarning;
// default is to show package-level dependencies
// and filter references from same package
Analyzer.Type verbose = PACKAGE;
@@ -709,6 +721,7 @@
private static class ResourceBundleHelper {
static final ResourceBundle versionRB;
static final ResourceBundle bundle;
+ static final ResourceBundle jdkinternals;
static {
Locale locale = Locale.getDefault();
@@ -722,6 +735,11 @@
} catch (MissingResourceException e) {
throw new InternalError("version.resource.missing");
}
+ try {
+ jdkinternals = ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdkinternals");
+ } catch (MissingResourceException e) {
+ throw new InternalError("Cannot find jdkinternals resource bundle");
+ }
}
}
@@ -935,4 +953,50 @@
}
return Profile.getProfile(pn);
}
+
+ /**
+ * Returns the recommended replacement API for the given classname;
+ * or return null if replacement API is not known.
+ */
+ private String replacementFor(String cn) {
+ String name = cn;
+ String value = null;
+ while (value == null && name != null) {
+ try {
+ value = ResourceBundleHelper.jdkinternals.getString(name);
+ } catch (MissingResourceException e) {
+ // go up one subpackage level
+ int i = name.lastIndexOf('.');
+ name = i > 0 ? name.substring(0, i) : null;
+ }
+ }
+ return value;
+ };
+
+ private void showReplacements(Analyzer analyzer) {
+ Map<String,String> jdkinternals = new TreeMap<>();
+ boolean useInternals = false;
+ for (Archive source : sourceLocations) {
+ useInternals = useInternals || analyzer.hasDependences(source);
+ for (String cn : analyzer.dependences(source)) {
+ String repl = replacementFor(cn);
+ if (repl != null && !jdkinternals.containsKey(cn)) {
+ jdkinternals.put(cn, repl);
+ }
+ }
+ }
+ if (useInternals) {
+ log.println();
+ warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
+ }
+ if (!jdkinternals.isEmpty()) {
+ log.println();
+ log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
+ log.format("%-40s %s%n", "----------------", "---------------------");
+ for (Map.Entry<String,String> e : jdkinternals.entrySet()) {
+ log.format("%-40s %s%n", e.getKey(), e.getValue());
+ }
+ }
+
+ }
}
diff --git a/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
index 7cc433e..ccd14ab 100644
--- a/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
@@ -93,5 +93,12 @@
err.invalid.path=invalid path: {0}
warn.invalid.arg=Invalid classname or pathname not exist: {0}
warn.split.package=package {0} defined in {1} {2}
+warn.replace.useJDKInternals=\
+JDK internal APIs are unsupported and private to JDK implementation that are\n\
+subject to be removed or changed incompatibly and could break your application.\n\
+Please modify your code to eliminate dependency on any JDK internal APIs.\n\
+For the most recent update on JDK internal API replacements, please check:\n\
+{0}
artifact.not.found=not found
+jdeps.wiki.url=https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
diff --git a/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
new file mode 100644
index 0000000..56d7d0a
--- /dev/null
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
@@ -0,0 +1,22 @@
+// No translation needed
+com.sun.crypto.provider.SunJCE=Use java.security.Security.getProvider(provider-name) @since 1.3
+com.sun.image.codec=Use javax.imageio @since 1.4
+com.sun.org.apache.xml.internal.security=Use java.xml.crypto @since 1.6
+com.sun.org.apache.xml.internal.security.utils.Base64=Use java.util.Base64 @since 1.8
+com.sun.net.ssl=Use javax.net.ssl @since 1.4
+com.sun.net.ssl.internal.ssl.Provider=Use java.security.Security.getProvider(provider-name) @since 1.3
+com.sun.rowset=Use javax.sql.rowset.RowSetProvider @since 1.7
+com.sun.tools.javac.tree=Use com.sun.source @since 1.6
+com.sun.tools.javac=Use javax.tools and javax.lang.model @since 1.6
+sun.awt.image.codec=Use javax.imageio @since 1.4
+sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
+sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
+sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2
+sun.misc.Service=Use java.util.ServiceLoader @since 1.6
+sun.security.action=Use java.security.PrivilegedAction @since 1.1
+sun.security.krb5=Use com.sun.security.jgss
+sun.security.provider.PolicyFile=Use java.security.Policy.getInstance("JavaPolicy", new URIParameter(uri)) @since 1.6
+sun.security.provider.Sun=Use java.security.Security.getProvider(provider-name) @since 1.3
+sun.security.util.SecurityConstants=Use appropriate java.security.Permission subclass @since 1.1
+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
diff --git a/langtools/test/tools/jdeps/APIDeps.java b/langtools/test/tools/jdeps/APIDeps.java
index bf01a77..c3d2c99 100644
--- a/langtools/test/tools/jdeps/APIDeps.java
+++ b/langtools/test/tools/jdeps/APIDeps.java
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8015912 8029216 8048063
+ * @bug 8015912 8029216 8048063 8050804
* @summary Test -apionly and -jdkinternals options
* @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G
* @run main APIDeps