changeset 3369:e2abef6f10b9 jdk8u141-b08

8176329: jdeps to detect MR jar file and output a warning Reviewed-by: mchung
author bchristi
date Thu, 27 Apr 2017 16:18:18 -0700
parents e4d2d5a018e3
children 1df48afb34a0
files src/share/classes/com/sun/tools/jdeps/ClassFileReader.java src/share/classes/com/sun/tools/jdeps/JdepsTask.java src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties test/tools/jdeps/MRJarWarning.java
diffstat 5 files changed, 226 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java	Mon May 01 10:55:28 2017 -0700
+++ b/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java	Thu Apr 27 16:18:18 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -34,8 +34,10 @@
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.*;
+import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
+import java.util.jar.Manifest;
 
 /**
  * ClassFileReader reads ClassFile(s) of a given path that can be
@@ -154,6 +156,8 @@
         }
     }
 
+    public boolean isMultiReleaseJar() throws IOException { return false; }
+
     public String toString() {
         return path.toString();
     }
@@ -290,6 +294,16 @@
                 }
             };
         }
+
+        @Override
+        public boolean isMultiReleaseJar() throws IOException {
+            Manifest mf = this.jarfile.getManifest();
+            if (mf != null) {
+                Attributes atts = mf.getMainAttributes();
+                return "true".equalsIgnoreCase(atts.getValue("Multi-Release"));
+            }
+            return false;
+        }
     }
 
     class JarFileIterator implements Iterator<ClassFile> {
--- a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Mon May 01 10:55:28 2017 -0700
+++ b/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Thu Apr 27 16:18:18 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -515,6 +515,13 @@
         // add all classpath archives to the source locations for reporting
         sourceLocations.addAll(classpaths);
 
+        // warn about Multi-Release jars
+        for (Archive a : sourceLocations) {
+            if (a.reader().isMultiReleaseJar()) {
+                warning("warn.mrjar.usejdk9", a.getPathName());
+            }
+        }
+
         // Work queue of names of classfiles to be searched.
         // Entries will be unique, and for classes that do not yet have
         // dependencies in the results map.
--- a/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Mon May 01 10:55:28 2017 -0700
+++ b/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties	Thu Apr 27 16:18:18 2017 -0700
@@ -99,6 +99,10 @@
 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}
+warn.mrjar.usejdk9=\
+{0} is a multi-release jar file.\n\
+All versioned entries are analyzed. To analyze the entries for a specific\n\
+version, use a newer version of jdeps (JDK 9 or later) \"--multi-release\" option.
 
 artifact.not.found=not found
 jdeps.wiki.url=https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
--- a/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Mon May 01 10:55:28 2017 -0700
+++ b/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties	Thu Apr 27 16:18:18 2017 -0700
@@ -1,22 +1,45 @@
 // 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.org.apache.xml.internal.resolver=Use javax.xml.catalog @since 9
 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.doclets.standard=Use jdk.javadoc.doclets.StandardDoclet @since 9.
 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
+java.awt.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
+java.awt.dnd.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
+jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
+sun.awt.CausedFocusEvent=Use java.awt.event.FocusEvent::getCause @since 9
+sun.font.FontUtilities=See java.awt.Font.textRequiresLayout @since 9
+sun.reflect.Reflection=See http://openjdk.java.net/jeps/260
+sun.reflect.ReflectionFactory=See http://openjdk.java.net/jeps/260
+sun.misc.Unsafe=See http://openjdk.java.net/jeps/260
+sun.misc.Signal=See http://openjdk.java.net/jeps/260
+sun.misc.SignalHandler=See http://openjdk.java.net/jeps/260
 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.HostnameChecker=Use javax.net.ssl.SSLParameters.setEndpointIdentificationAlgorithm("HTTPS") @since 1.7\n\
+or javax.net.ssl.HttpsURLConnection.setHostnameVerifier() @since 1.4
 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
+sun.tools.jar=Use java.util.jar @since 1.2
+sun.tools.jar.Main=Use java.util.spi.ToolProvider @since 9
+# Internal APIs removed in JDK 9
+com.apple.eawt=Use java.awt.Desktop @since 9.  See http://openjdk.java.net/jeps/272
+com.apple.concurrent=Removed in JDK 9. 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
+sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
+sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
+sun.misc.ClassLoaderUtil=Use java.net.URLClassLoader.close() @since 1.7
+sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9.\n\
+See http://openjdk.java.net/jeps/260.
+sun.misc.Service=Use java.util.ServiceLoader @since 1.6
+sun.misc=Removed in JDK 9. See http://openjdk.java.net/jeps/260
+sun.reflect=Removed in JDK 9. See http://openjdk.java.net/jeps/260
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jdeps/MRJarWarning.java	Thu Apr 27 16:18:18 2017 -0700
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8176329
+ * @summary Test for jdeps warning when it encounters a multi-release jar
+ * @run testng MRJarWarning
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import org.testng.Assert;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class MRJarWarning {
+    private static final String WARNING = " is a multi-release jar file";
+    private static final String MRJAR_ATTR = "Multi-Release";
+
+    Path mrjar1;
+    Path mrjar2;
+    Path nonMRjar;
+    Path mrjarAllCaps;
+
+    private Attributes defaultAttributes;
+
+    @BeforeSuite
+    public void setup() throws IOException {
+        defaultAttributes = new Attributes();
+        defaultAttributes.putValue("Manifest-Version", "1.0");
+        defaultAttributes.putValue("Created-By", "1.8.0-internal");
+
+        mrjar1   = Paths.get("mrjar1.jar");
+        mrjar2   = Paths.get("mrjar2.jar");
+        nonMRjar = Paths.get("nonMRjar.jar");
+        mrjarAllCaps = Paths.get("mrjarAllCaps.jar");
+
+        Attributes mrJarAttrs = new Attributes(defaultAttributes);
+        mrJarAttrs.putValue(MRJAR_ATTR, "true");
+
+        build(mrjar1, mrJarAttrs);
+        build(mrjar2, mrJarAttrs);
+        build(nonMRjar, defaultAttributes);
+
+        // JEP 238 - "Multi-Release JAR Files" states that the attribute name
+        // and value are case insensitive.  Try with all caps to ensure that
+        // jdeps still recognizes a multi-release jar.
+        Attributes allCapsAttrs = new Attributes(defaultAttributes);
+        allCapsAttrs.putValue(MRJAR_ATTR.toUpperCase(), "TRUE");
+        build(mrjarAllCaps, allCapsAttrs);
+    }
+
+    @DataProvider(name="provider")
+    private Object[][] args() {
+        // jdeps warning messages may be localized.
+        // This test only checks for the English version.  Return an empty
+        // array (skip testing) if the default language is not English.
+        String language = Locale.getDefault().getLanguage();
+        System.out.println("Language: " + language);
+
+        if ("en".equals(language)) {
+            return new Object[][] {
+                // one mrjar arg
+                {   Arrays.asList(mrjar1.toString()),
+                    Arrays.asList(mrjar1)},
+                // two mrjar args
+                {   Arrays.asList(mrjar1.toString(), mrjar2.toString()),
+                    Arrays.asList(mrjar1, mrjar2)},
+                // one mrjar arg, with mrjar on classpath
+                {   Arrays.asList("-cp", mrjar2.toString(), mrjar1.toString()),
+                    Arrays.asList(mrjar1, mrjar2)},
+                // non-mrjar arg, with mrjar on classpath
+                {   Arrays.asList("-cp", mrjar1.toString(), nonMRjar.toString()),
+                    Arrays.asList(mrjar1)},
+                // mrjar arg with jar attribute name/value in ALL CAPS
+                {   Arrays.asList(mrjarAllCaps.toString()),
+                    Arrays.asList(mrjarAllCaps)},
+                // non-mrjar arg
+                {   Arrays.asList(nonMRjar.toString()),
+                    Collections.emptyList()}
+            };
+        } else {
+            System.out.println("Non-English language \""+ language +
+                    "\"; test passes superficially");
+            return new Object[][]{};
+        }
+    }
+
+    /* Run jdeps with the arguments given in 'args', and confirm that a warning
+     * is issued for each Multi-Release jar in 'expectedMRpaths'.
+     */
+    @Test(dataProvider="provider")
+    public void checkWarning(List<String> args, List<Path> expectedMRpaths) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+
+        int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[args.size()]), pw);
+        pw.close();
+
+        expectedMRJars(sw.toString(), expectedMRpaths);
+        Assert.assertEquals(rc, 0, "non-zero exit code from jdeps");
+    }
+
+    /* Confirm that warnings for the specified paths are in the output (or that
+     * warnings are absent if 'paths' is empty).
+     * Doesn't check for extra, unexpected warnings.
+     */
+    private static void expectedMRJars(String output, List<Path> paths) {
+        if (paths.isEmpty()) {
+            Assert.assertFalse(output.contains(WARNING),
+                               "Expected no mrjars, but found:\n" + output);
+        } else {
+            for (Path path : paths) {
+                String expect = "Warning: " + path.toString() + WARNING;
+                Assert.assertTrue(output.contains(expect),
+                        "Did not find:\n" + expect + "\nin:\n" + output + "\n");
+            }
+        }
+    }
+
+    /* Build a jar at the expected path, containing the given attributes */
+    private static void build(Path path, Attributes attributes) throws IOException {
+        try (OutputStream os = Files.newOutputStream(path);
+             JarOutputStream jos = new JarOutputStream(os)) {
+
+            JarEntry me = new JarEntry("META-INF/MANIFEST.MF");
+            jos.putNextEntry(me);
+            Manifest manifest = new Manifest();
+            manifest.getMainAttributes().putAll(attributes);
+            manifest.write(jos);
+            jos.closeEntry();
+        }
+    }
+}