diff --git a/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java b/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java index 435045b6014c2b8661e2b5454e474b1154a1c8cc..f41f2d0ba10d8a383d1e65b28c3d509a6955e0ac 100644 --- a/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java +++ b/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.Path; 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 class ClassFileReader { } } + public boolean isMultiReleaseJar() throws IOException { return false; } + public String toString() { return path.toString(); } @@ -290,6 +294,16 @@ public class ClassFileReader { } }; } + + @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 { diff --git a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java b/src/share/classes/com/sun/tools/jdeps/JdepsTask.java index 2c72d57e5a98463ff0cef9ac09443f04cff138bf..91002d31912c423de0a3dfd36017084ae432053d 100644 --- a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java +++ b/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 @@ class JdepsTask { // 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/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties b/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties index ccd14abe43a457befed274fb8615b0b9062b025b..51d11b88a8dfcc10da5611ce0d850c7b53315aeb 100644 --- a/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties +++ b/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties @@ -99,6 +99,10 @@ subject to be removed or changed incompatibly and could break your application.\ 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/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties b/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties index 56d7d0af432a2d441e3431dafd5b8a3c057b392e..868b39a0f68b0644c51a1b159dad5193f7f56e8b 100644 --- a/src/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties +++ b/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/test/tools/jdeps/MRJarWarning.java b/test/tools/jdeps/MRJarWarning.java new file mode 100644 index 0000000000000000000000000000000000000000..c3218cfab0f46f25bf3ac94cfb316508de8d8987 --- /dev/null +++ b/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 args, List 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 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(); + } + } +}