8050804: (jdeps) Recommend supported API to replace use of JDK internal API
authormchung
Thu, 17 Jul 2014 10:17:58 -0700
changeset 25692 39537fdca12c
parent 25691 8d4664cdd4ce
child 25693 b9f1261db3b4
8050804: (jdeps) Recommend supported API to replace use of JDK internal API Reviewed-by: dfuchs
langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java
langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties
langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
langtools/test/tools/jdeps/APIDeps.java
--- a/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java	Thu Jul 17 09:56:07 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java	Thu Jul 17 10:17:58 2014 -0700
@@ -24,8 +24,6 @@
  */
 package com.sun.tools.jdeps;
 
-import com.sun.tools.classfile.Dependency.Location;
-import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -33,6 +31,10 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.sun.tools.classfile.Dependency.Location;
+import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
 
 /**
  * Dependency Analyzer.
@@ -110,6 +112,13 @@
         return false;
     }
 
+    public Set<String> dependences(Archive source) {
+        ArchiveDeps result = results.get(source);
+        return result.dependencies().stream()
+                     .map(Dep::target)
+                     .collect(Collectors.toSet());
+    }
+
     public interface Visitor {
         /**
          * Visits a recorded dependency from origin to target which can be
--- a/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Thu Jul 17 09:56:07 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Thu Jul 17 10:17:58 2014 -0700
@@ -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 {
@@ -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");
+            }
         }
     }
 
@@ -928,4 +946,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.putIfAbsent(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());
+            }
+        }
+
+    }
 }
--- a/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Thu Jul 17 09:56:07 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Thu Jul 17 10:17:58 2014 -0700
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Thu Jul 17 10:17:58 2014 -0700
@@ -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
--- a/langtools/test/tools/jdeps/APIDeps.java	Thu Jul 17 09:56:07 2014 -0700
+++ b/langtools/test/tools/jdeps/APIDeps.java	Thu Jul 17 10:17:58 2014 -0700
@@ -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