8176329: jdeps to detect MR jar file and output a warning
Reviewed-by: mchung
diff --git a/langtools/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java b/langtools/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java
index 435045b..f41f2d0 100644
--- a/langtools/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java
@@ -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> {
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 2c72d57..91002d3 100644
--- a/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/JdepsTask.java
@@ -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.
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 ccd14ab..51d11b8 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
@@ -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
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
index 56d7d0a..868b39a 100644
--- a/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
@@ -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
+
diff --git a/langtools/test/tools/jdeps/MRJarWarning.java b/langtools/test/tools/jdeps/MRJarWarning.java
new file mode 100644
index 0000000..c3218cf
--- /dev/null
+++ b/langtools/test/tools/jdeps/MRJarWarning.java
@@ -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();
+ }
+ }
+}