changeset 41484:834b7539ada3

8164689: Retrofit jar, jlink, jmod as a ToolProvider Reviewed-by: alanb, lancea
author mchung
date Wed, 12 Oct 2016 15:41:00 -0700
parents 99e81f03a628
children 15a87b8ccd06
files jdk/src/java.base/share/classes/java/util/spi/ToolProvider.java jdk/src/jdk.jartool/share/classes/module-info.java jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java jdk/src/jdk.jartool/share/classes/sun/tools/jar/JarToolProvider.java jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Main.java jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/Main.java jdk/src/jdk.jlink/share/classes/module-info.java jdk/test/java/lang/module/ModuleReader/ModuleReaderTest.java jdk/test/tools/jar/ChangeDir.java jdk/test/tools/jar/InputFilesTest.java jdk/test/tools/jar/JarBackSlash.java jdk/test/tools/jar/JarEntryTime.java jdk/test/tools/jar/UpdateJar.java jdk/test/tools/jar/UpdateManifest.java jdk/test/tools/jar/index/MetaInf.java jdk/test/tools/jlink/JLinkTest.java jdk/test/tools/jlink/basic/BasicTest.java jdk/test/tools/jmod/JmodNegativeTest.java jdk/test/tools/jmod/JmodTest.java jdk/test/tools/jmod/hashes/HashesTest.java jdk/test/tools/launcher/modules/basic/BasicTest.java jdk/test/tools/launcher/modules/dryrun/DryRunTest.java jdk/test/tools/lib/tests/JImageGenerator.java
diffstat 26 files changed, 287 insertions(+), 124 deletions(-) [+]
line wrap: on
line diff
--- a/jdk/src/java.base/share/classes/java/util/spi/ToolProvider.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/util/spi/ToolProvider.java	Wed Oct 12 15:41:00 2016 -0700
@@ -56,8 +56,8 @@
     /**
      * Returns the name of this tool provider.
      *
-     * @apiNote It is recommended that the name be the same as would be used on
-     *      the command line: for example, "javac", "jar", "jlink".
+     * @apiNote It is recommended that the name be the same as would be
+     * used on the command line: for example, "javac", "jar", "jlink".
      *
      * @return the name of this tool provider
      */
@@ -67,12 +67,13 @@
      * Runs an instance of the tool, returning zero for a successful run.
      * Any non-zero return value indicates a tool-specific error during the
      * execution.
+     *
      * Two streams should be provided, for "expected" output, and for any
      * error messages. If it is not necessary to distinguish the output,
      * the same stream may be used for both.
      *
      * @apiNote The interpretation of the arguments will be specific to
-     *      each tool.
+     * each tool.
      *
      * @param out a stream to which "expected" output should be written
      *
@@ -81,12 +82,13 @@
      * @param args the command-line arguments for the tool
      *
      * @return the result of executing the tool.
-     *      A return value of 0 means the tool did not encounter any errors;
-     *      any other value indicates that at least one error occurred during
-     *      execution.
+     *         A return value of 0 means the tool did not encounter any errors;
+     *         any other value indicates that at least one error occurred
+     *         during execution.
      *
      * @throws NullPointerException if any of the arguments are {@code null},
-     *      or if there are any {@code null} values in the {@code args} array
+     *         or if there are any {@code null} values in the {@code args}
+     *         array
      */
     int run(PrintWriter out, PrintWriter err, String... args);
 
@@ -94,16 +96,17 @@
      * Runs an instance of the tool, returning zero for a successful run.
      * Any non-zero return value indicates a tool-specific error during the
      * execution.
+     *
      * Two streams should be provided, for "expected" output, and for any
      * error messages. If it is not necessary to distinguish the output,
      * the same stream may be used for both.
      *
      * @apiNote The interpretation of the arguments will be specific to
-     *      each tool.
+     * each tool.
      *
      * @implNote This implementation wraps the {@code out} and {@code err}
-     *      streams within {@link PrintWriter}s, and then calls
-     *      {@link run(PrintWriter, PrintWriter, String[])}.
+     * streams within {@link PrintWriter}s, and then calls
+     * {@link #run(PrintWriter, PrintWriter, String[])}.
      *
      * @param out a stream to which "expected" output should be written
      *
@@ -112,12 +115,13 @@
      * @param args the command-line arguments for the tool
      *
      * @return the result of executing the tool.
-     *      A return value of 0 means the tool did not encounter any errors;
-     *      any other value indicates that at least one error occurred during
-     *      execution.
+     *         A return value of 0 means the tool did not encounter any errors;
+     *         any other value indicates that at least one error occurred
+     *         during execution.
      *
      * @throws NullPointerException if any of the arguments are {@code null},
-     *      or if there are any {@code null} values in the {@code args} array
+     *         or if there are any {@code null} values in the {@code args}
+     *         array
      */
     default int run(PrintStream out, PrintStream err, String... args) {
         Objects.requireNonNull(out);
--- a/jdk/src/jdk.jartool/share/classes/module-info.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jartool/share/classes/module-info.java	Wed Oct 12 15:41:00 2016 -0700
@@ -26,5 +26,7 @@
 module jdk.jartool {
     exports com.sun.jarsigner;
     exports jdk.security.jarsigner;
+
+    provides java.util.spi.ToolProvider with sun.tools.jar.JarToolProvider;
 }
 
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java	Wed Oct 12 15:41:00 2016 -0700
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.lang.module.ModuleFinder;
 import java.lang.module.ModuleDescriptor.Version;
 import java.nio.file.Path;
@@ -289,7 +290,7 @@
         throw new BadArgs("error.unrecognized.option", name).showUsage(true);
     }
 
-    static void printHelp(PrintStream out) {
+    static void printHelp(PrintWriter out) {
         out.format("%s%n", Main.getMsg("main.help.preopt"));
         for (OptionType type : OptionType.values()) {
             boolean typeHeadingWritten = false;
@@ -312,16 +313,16 @@
         out.format("%n%s%n%n", Main.getMsg("main.help.postopt"));
     }
 
-    static void printCompatHelp(PrintStream out) {
+    static void printCompatHelp(PrintWriter out) {
         out.format("%s%n", Main.getMsg("usage.compat"));
     }
 
-    static void printUsageSummary(PrintStream out) {
+    static void printUsageSummary(PrintWriter out) {
         out.format("%s%n", Main.getMsg("main.usage.summary"));
         out.format("%s%n", Main.getMsg("main.usage.summary.try"));
     }
 
-    static void printVersion(PrintStream out) {
+    static void printVersion(PrintWriter out) {
         out.format("%s %s%n", "jar", System.getProperty("java.version"));
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/JarToolProvider.java	Wed Oct 12 15:41:00 2016 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.tools.jar;
+
+import java.io.PrintWriter;
+import java.util.spi.ToolProvider;
+
+public class JarToolProvider implements ToolProvider {
+    public String name() {
+        return "jar";
+    }
+
+    public int run(PrintWriter out, PrintWriter err, String... args) {
+        boolean ok = new Main(out, err, name()).run(args);
+        return ok ? 0 : 1;
+    }
+}
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Wed Oct 12 15:41:00 2016 -0700
@@ -76,7 +76,7 @@
 public
 class Main {
     String program;
-    PrintStream out, err;
+    PrintWriter out, err;
     String fname, mname, ename;
     String zname = "";
     String rootjar = null;
@@ -189,9 +189,9 @@
         USAGE_SUMMARY(GNUStyleOptions::printUsageSummary),
         VERSION(GNUStyleOptions::printVersion);
 
-        private Consumer<PrintStream> printFunction;
-        Info(Consumer<PrintStream> f) { this.printFunction = f; }
-        void print(PrintStream out) { printFunction.accept(out); }
+        private Consumer<PrintWriter> printFunction;
+        Info(Consumer<PrintWriter> f) { this.printFunction = f; }
+        void print(PrintWriter out) { printFunction.accept(out); }
     };
     Info info;
 
@@ -252,6 +252,12 @@
     }
 
     public Main(PrintStream out, PrintStream err, String program) {
+        this.out = new PrintWriter(out, true);
+        this.err = new PrintWriter(err, true);
+        this.program = program;
+    }
+
+    public Main(PrintWriter out, PrintWriter err, String program) {
         this.out = out;
         this.err = err;
         this.program = program;
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Wed Oct 12 15:41:00 2016 -0700
@@ -153,7 +153,7 @@
             = taskHelper.newOptionsHelper(JlinkTask.class, recognizedOptions);
     private PrintWriter log;
 
-    void setLog(PrintWriter out) {
+    void setLog(PrintWriter out, PrintWriter err) {
         log = out;
         taskHelper.setLog(log);
     }
@@ -182,7 +182,8 @@
 
     int run(String[] args) {
         if (log == null) {
-            setLog(new PrintWriter(System.out, true));
+            setLog(new PrintWriter(System.out, true),
+                   new PrintWriter(System.err, true));
         }
         try {
             optionsHelper.handleOptions(this, args);
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Main.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Main.java	Wed Oct 12 15:41:00 2016 -0700
@@ -26,6 +26,7 @@
 package jdk.tools.jlink.internal;
 
 import java.io.*;
+import java.util.spi.ToolProvider;
 
 public class Main {
     public static void main(String... args) throws Exception {
@@ -34,17 +35,27 @@
         System.exit(rc);
     }
 
-
     /**
      * Entry point that does <i>not</i> call System.exit.
      *
+     * @param out output stream
+     * @param err error output stream
      * @param args command line arguments
-     * @param out output stream
      * @return an exit code. 0 means success, non-zero means an error occurred.
      */
-    public static int run(String[] args, PrintWriter out) {
+    public static int run(PrintWriter out, PrintWriter err, String... args) {
         JlinkTask t = new JlinkTask();
-        t.setLog(out);
+        t.setLog(out, err);
         return t.run(args);
     }
+
+    public static class JlinkToolProvider implements ToolProvider {
+        public String name() {
+            return "jlink";
+        }
+
+        public int run(PrintWriter out, PrintWriter err, String... args) {
+            return Main.run(out, err, args);
+        }
+    }
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Wed Oct 12 15:41:00 2016 -0700
@@ -32,6 +32,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.io.UncheckedIOException;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleReader;
@@ -136,8 +137,8 @@
     private static final String MODULE_INFO = "module-info.class";
 
     private Options options;
-    private PrintStream out = System.out;
-    void setLog(PrintStream out) {
+    private PrintWriter out = new PrintWriter(System.out, true);
+    void setLog(PrintWriter out, PrintWriter err) {
         this.out = out;
     }
 
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/Main.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/Main.java	Wed Oct 12 15:41:00 2016 -0700
@@ -26,6 +26,7 @@
 package jdk.tools.jmod;
 
 import java.io.*;
+import java.util.spi.ToolProvider;
 
 public class Main {
     public static void main(String... args) throws Exception {
@@ -37,13 +38,24 @@
     /**
      * Entry point that does <i>not</i> call System.exit.
      *
+     * @param out output stream
+     * @param err error output stream
      * @param args command line arguments
-     * @param out output stream
      * @return an exit code. 0 means success, non-zero means an error occurred.
      */
-    public static int run(String[] args, PrintStream out) {
+    public static int run(PrintWriter out, PrintWriter err, String... args) {
         JmodTask t = new JmodTask();
-        t.setLog(out);
+        t.setLog(out, err);
         return t.run(args);
     }
+
+    public static class JmodToolProvider implements ToolProvider {
+        public String name() {
+            return "jmod";
+        }
+
+        public int run(PrintWriter out, PrintWriter err, String... args) {
+            return Main.run(out, err, args);
+        }
+    }
 }
--- a/jdk/src/jdk.jlink/share/classes/module-info.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/src/jdk.jlink/share/classes/module-info.java	Wed Oct 12 15:41:00 2016 -0700
@@ -31,6 +31,9 @@
 
     uses jdk.tools.jlink.plugin.Plugin;
 
+    provides java.util.spi.ToolProvider with jdk.tools.jmod.Main.JmodToolProvider;
+    provides java.util.spi.ToolProvider with jdk.tools.jlink.internal.Main.JlinkToolProvider;
+
     provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.FileCopierPlugin;
     provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.StripDebugPlugin;
     provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.ExcludePlugin;
--- a/jdk/test/java/lang/module/ModuleReader/ModuleReaderTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/java/lang/module/ModuleReader/ModuleReaderTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -25,8 +25,8 @@
  * @test
  * @library /lib/testlibrary
  * @modules java.base/jdk.internal.module
- *          jdk.jlink/jdk.tools.jmod
  *          jdk.compiler
+ *          jdk.jlink
  * @build ModuleReaderTest CompilerUtils JarUtils
  * @run testng ModuleReaderTest
  * @summary Basic tests for java.lang.module.ModuleReader
@@ -48,6 +48,7 @@
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Optional;
+import java.util.spi.ToolProvider;
 
 import jdk.internal.module.ConfigurableModuleFinder;
 import jdk.internal.module.ConfigurableModuleFinder.Phase;
@@ -196,8 +197,11 @@
         String cp = MODS_DIR.resolve(TEST_MODULE).toString();
         String jmod = dir.resolve("m.jmod").toString();
         String[] args = { "create", "--class-path", cp, jmod };
-        jdk.tools.jmod.JmodTask task = new jdk.tools.jmod.JmodTask();
-        assertEquals(task.run(args), 0);
+        ToolProvider jmodTool = ToolProvider.findFirst("jmod")
+            .orElseThrow(() ->
+                new RuntimeException("jmod tool not found")
+            );
+        assertEquals(jmodTool.run(System.out, System.out, args), 0);
 
         test(dir);
     }
--- a/jdk/test/tools/jar/ChangeDir.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/ChangeDir.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,7 +24,7 @@
 /**
  * @test
  * @bug 4806786 8023113
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @summary jar -C doesn't ignore multiple // in path
  */
 
@@ -32,10 +32,15 @@
 import java.nio.file.*;
 import java.util.*;
 import java.util.jar.*;
+import java.util.spi.ToolProvider;
 import java.util.stream.Stream;
-import sun.tools.jar.Main;
 
 public class ChangeDir {
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+
     private final static String jarName = "test.jar";
     private final static String fileName = "hello.txt";
 
@@ -88,8 +93,9 @@
             argList.add(topDir.toString() + sep + "a" + sep + sep + "b"); // Note double 'sep' is intentional
             argList.add(fileName);
 
-            Main jarTool = new Main(System.out, System.err, "jar");
-            if (!jarTool.run(argList.toArray(new String[argList.size()]))) {
+            int rc = JAR_TOOL.run(System.out, System.err,
+                                  argList.toArray(new String[argList.size()]));
+            if (rc != 0) {
                 fail("Could not create jar file.");
             }
 
--- a/jdk/test/tools/jar/InputFilesTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/InputFilesTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -29,7 +29,7 @@
  *          duplicates that sometimes cause exceptions and other times do not,
  *          demonstrating identical behavior to JDK 8 jar tool.
  * @library /lib/testlibrary
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @build jdk.testlibrary.FileUtils
  * @run testng InputFilesTest
  */
@@ -47,12 +47,18 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
+import java.util.spi.ToolProvider;
 import java.util.stream.Stream;
 import java.util.zip.ZipException;
 
 import jdk.testlibrary.FileUtils;
 
 public class InputFilesTest {
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+
     private final String nl = System.lineSeparator();
     private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
     private final PrintStream out = new PrintStream(baos);
@@ -195,9 +201,9 @@
         PrintStream err = new PrintStream(baes);
         PrintStream saveErr = System.err;
         System.setErr(err);
-        boolean ok = new sun.tools.jar.Main(out, err, "jar").run(cmdline.split(" +"));
+        int rc = JAR_TOOL.run(out, err, cmdline.split(" +"));
         System.setErr(saveErr);
-        if (!ok) {
+        if (rc != 0) {
             String s = baes.toString();
             if (s.startsWith("java.util.zip.ZipException: duplicate entry: ")) {
                 throw new ZipException(s);
--- a/jdk/test/tools/jar/JarBackSlash.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/JarBackSlash.java	Wed Oct 12 15:41:00 2016 -0700
@@ -28,7 +28,7 @@
 /*
  * @test
  * @bug 7201156
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @summary jar tool fails to convert file separation characters for list and extract
  * @author Sean Chou
  */
@@ -43,10 +43,13 @@
 import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
-
-import sun.tools.jar.Main;
+import java.util.spi.ToolProvider;
 
 public class JarBackSlash {
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
 
     // used construct an entry JarBackSlash/dir/file.txt
     private static String JARBACKSLASH = "JarBackSlash";
@@ -78,8 +81,8 @@
         PipedInputStream pipedInput = new PipedInputStream(pipedOutput);
         PrintStream out = new PrintStream(pipedOutput);
 
-        Main jarTool = new Main(out, System.err, "jar");
-        if (!jarTool.run(jarArgs)) {
+        int rc = JAR_TOOL.run(out, System.err, jarArgs);
+        if (rc != 0) {
             fail("Could not list jar file.");
         }
 
@@ -101,8 +104,8 @@
         PipedInputStream pipedInput = new PipedInputStream(pipedOutput);
         PrintStream out = new PrintStream(pipedOutput);
 
-        Main jarTool = new Main(out, System.err, "jar");
-        if (!jarTool.run(jarArgs)) {
+        int rc = JAR_TOOL.run(out, System.err, jarArgs);
+        if (rc != 0) {
             fail("Could not list jar file.");
         }
 
--- a/jdk/test/tools/jar/JarEntryTime.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/JarEntryTime.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,7 +24,7 @@
 /**
  * @test
  * @bug 4225317 6969651
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @summary Check extracted files have date as per those in the .jar file
  */
 
@@ -33,9 +33,14 @@
 import java.nio.file.attribute.FileTime;
 import java.util.Date;
 import java.util.TimeZone;
-import sun.tools.jar.Main;
+import java.util.spi.ToolProvider;
 
 public class JarEntryTime {
+    static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+
 
     // ZipEntry's mod date has 2 seconds precision: give extra time to
     // allow for e.g. rounding/truncation and networked/samba drives.
@@ -114,10 +119,8 @@
         check(fileInner.setLastModified(earlier));
 
         // Make a jar file from that directory structure
-        Main jartool = new Main(System.out, System.err, "jar");
-        check(jartool.run(new String[] {
-                "cf",
-                jarFile.getName(), dirOuter.getName() } ));
+        check(JAR_TOOL.run(System.out, System.err,
+                           "cf", jarFile.getName(), dirOuter.getName()) == 0);
         check(jarFile.exists());
 
         check(cleanup(dirInner));
@@ -142,7 +145,6 @@
         final long start = testFile.lastModified();
 
         // Extract and check the last modified values are the current times.
-        // See sun.tools.jar.Main
         extractJar(jarFile, true);
 
         try (PrintWriter pw = new PrintWriter(testFile)) {
--- a/jdk/test/tools/jar/UpdateJar.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/UpdateJar.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,7 +24,7 @@
 /**
  * @test
  * @bug 7175845
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @summary jar -uf should not change file permission
  */
 
@@ -32,9 +32,13 @@
 import java.nio.file.*;
 import java.nio.file.attribute.*;
 import java.util.Set;
-import sun.tools.jar.Main;
+import java.util.spi.ToolProvider;
 
 public class UpdateJar {
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
 
     private static void cleanup(String... fnames) throws Throwable {
         for (String fname : fnames) {
@@ -55,12 +59,12 @@
                     fos1.write(0);
                 }
                 String[] jarArgs = new String[] {"cfM0", jar, e0};
-                if (!new Main(System.out, System.err, "jar").run(jarArgs)) {
+                if (JAR_TOOL.run(System.out, System.err, jarArgs) != 0) {
                     fail("Could not create jar file.");
                 }
                 Set<PosixFilePermission> pm = Files.getPosixFilePermissions(Paths.get(jar));
                 jarArgs = new String[] {"uf", jar, e1};
-                if (!new Main(System.out, System.err, "jar").run(jarArgs)) {
+                if (JAR_TOOL.run(System.out, System.err, jarArgs) != 0) {
                     fail("Could not create jar file.");
                 }
                 equal(pm, Files.getPosixFilePermissions(Paths.get(jar)));
--- a/jdk/test/tools/jar/UpdateManifest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/UpdateManifest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,7 +24,7 @@
 /**
  * @test
  * @bug 6434207 6442687 6984046
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @summary Ensure that jar ufm actually updates the
  * existing jar file's manifest with contents of the
  * manifest file.
@@ -32,14 +32,19 @@
 
 import java.io.*;
 import java.util.logging.*;
+import java.util.spi.ToolProvider;
 import java.util.zip.*;
-import sun.tools.jar.Main;
 
 public class UpdateManifest {
     static PrintStream out = System.out;
     static PrintStream err = System.err;
     static boolean debug = true;
 
+    static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+
     static final Logger JAR_LOGGER = Logger.getLogger("java.util.jar");
 
     public static void realMain(String[] args) throws Throwable {
@@ -64,17 +69,14 @@
         // Create a jar file, specifying a Main-Class
         final String jarFileName = "um-existence.jar";
         new File(jarFileName).delete(); // remove pre-existing first!
-        Main jartool = new Main(out, err, "jar");
-        boolean status = jartool.run(
-            new String[] { "cfe", jarFileName, "Hello", existence.getPath() });
-        check(status);
+        int status = JAR_TOOL.run(out, err, "cfe", jarFileName,
+                                  "Hello", existence.getPath());
+        check(status == 0);
         checkManifest(jarFileName, "Hello");
 
         // Update that jar file by changing the Main-Class
-        jartool = new Main(out, err, "jar");
-        status = jartool.run(
-            new String[] { "ufe", jarFileName, "Bye" });
-        check(status);
+        status = JAR_TOOL.run(out, err, "ufe", jarFileName, "Bye");
+        check(status == 0);
         checkManifest(jarFileName, "Bye");
     }
 
@@ -101,11 +103,9 @@
         // Create a jar file
         final String jarFileName = "um-test.jar";
         new File(jarFileName).delete(); // remove pre-existing first!
-        Main jartool = new Main(out, err, "jar");
-        boolean status = jartool.run(
-                                new String[] {"cfm", jarFileName,
-                                    manifestOrig.getPath(), hello.getPath() });
-        check(status);
+        int status = JAR_TOOL.run(out, err, "cfm", jarFileName,
+                                  manifestOrig.getPath(), hello.getPath());
+        check(status == 0);
 
         // Create a new manifest, to use in updating the jar file.
         File manifestUpdate = File.createTempFile("manifestUpdate", ".txt");
@@ -122,10 +122,9 @@
         pw.close();
 
         // Update jar file with manifest
-        jartool = new Main(out, err, "jar");
-        status = jartool.run(
-            new String[] { "ufm", jarFileName, manifestUpdate.getPath() });
-        check(status);
+        status = JAR_TOOL.run(out, err, "ufm",
+                              jarFileName, manifestUpdate.getPath());
+        check(status == 0);
 
         // Extract jar, and verify contents of manifest file
         File f = new File(jarFileName);
--- a/jdk/test/tools/jar/index/MetaInf.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jar/index/MetaInf.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,17 +24,21 @@
 /*
  * @test
  * @bug 4408526 6854795
- * @modules jdk.jartool/sun.tools.jar
+ * @modules jdk.jartool
  * @summary Index the non-meta files in META-INF, such as META-INF/services.
  */
 
 import java.io.*;
 import java.util.Arrays;
 import java.util.jar.*;
-import sun.tools.jar.Main;
+import java.util.spi.ToolProvider;
 import java.util.zip.ZipFile;
 
 public class MetaInf {
+    static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
 
     static String jarName = "a.jar";
     static String INDEX = "META-INF/INDEX.LIST";
@@ -43,7 +47,7 @@
         System.getProperty("test.src") + File.separatorChar + "jarcontents";
 
     static void run(String ... args) {
-        if (! new Main(System.out, System.err, "jar").run(args))
+        if (JAR_TOOL.run(System.out, System.err, args) != 0)
             throw new Error("jar failed: args=" + Arrays.toString(args));
     }
 
--- a/jdk/test/tools/jlink/JLinkTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jlink/JLinkTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -32,13 +32,13 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.spi.ToolProvider;
 import java.util.stream.Stream;
 
 import jdk.tools.jlink.plugin.Plugin;
 import jdk.tools.jlink.internal.PluginRepository;
 import tests.Helper;
 import tests.JImageGenerator;
-import tests.JImageGenerator.InMemoryFile;
 
 /*
  * @test
@@ -48,13 +48,17 @@
  * @modules java.base/jdk.internal.jimage
  *          jdk.jdeps/com.sun.tools.classfile
  *          jdk.jlink/jdk.tools.jlink.internal
- *          jdk.jlink/jdk.tools.jmod
  *          jdk.jlink/jdk.tools.jimage
  *          jdk.compiler
  * @build tests.*
  * @run main/othervm -Xmx1g JLinkTest
  */
 public class JLinkTest {
+    static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
+        .orElseThrow(() ->
+            new RuntimeException("jlink tool not found")
+        );
+
     // number of built-in plugins from jdk.jlink module
     private static int getNumJlinkPlugins() {
         ModuleDescriptor desc = Plugin.class.getModule().getDescriptor();
@@ -180,7 +184,8 @@
         {
             // Help
             StringWriter writer = new StringWriter();
-            jdk.tools.jlink.internal.Main.run(new String[]{"--help"}, new PrintWriter(writer));
+            PrintWriter pw = new PrintWriter(writer);
+            JLINK_TOOL.run(pw, pw, "--help");
             String output = writer.toString();
             if (output.split("\n").length < 10) {
                 System.err.println(output);
@@ -202,7 +207,9 @@
         {
             // List plugins
             StringWriter writer = new StringWriter();
-            jdk.tools.jlink.internal.Main.run(new String[]{"--list-plugins"}, new PrintWriter(writer));
+            PrintWriter pw = new PrintWriter(writer);
+
+            JLINK_TOOL.run(pw, pw, "--list-plugins");
             String output = writer.toString();
             long number = Stream.of(output.split("\\R"))
                     .filter((s) -> s.matches("Plugin Name:.*"))
--- a/jdk/test/tools/jlink/basic/BasicTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jlink/basic/BasicTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -27,8 +27,7 @@
  * @author Andrei Eremeev
  * @library /lib/testlibrary
  * @modules java.base/jdk.internal.module
- *          jdk.jlink/jdk.tools.jlink.internal
- *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink
  *          jdk.compiler
  * @build jdk.testlibrary.ProcessTools
  *        jdk.testlibrary.OutputAnalyzer
@@ -44,11 +43,21 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.spi.ToolProvider;
 
 import jdk.testlibrary.OutputAnalyzer;
 import jdk.testlibrary.ProcessTools;
 
 public class BasicTest {
+    static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
+        .orElseThrow(() ->
+            new RuntimeException("jmod tool not found")
+        );
+
+    static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
+        .orElseThrow(() ->
+            new RuntimeException("jlink tool not found")
+        );
 
     private final Path jdkHome = Paths.get(System.getProperty("test.jdk"));
     private final Path jdkMods = jdkHome.resolve("jmods");
@@ -110,20 +119,22 @@
                 "--add-modules", modName,
                 "--output", image.toString());
         Collections.addAll(args, options);
-        int rc = jdk.tools.jlink.internal.Main.run(args.toArray(new String[args.size()]), new PrintWriter(System.out));
+
+        PrintWriter pw = new PrintWriter(System.out);
+        int rc = JLINK_TOOL.run(pw, pw, args.toArray(new String[args.size()]));
         if (rc != 0) {
             throw new AssertionError("Jlink failed: rc = " + rc);
         }
     }
 
     private void runJmod(String cp, String modName) {
-        int rc = jdk.tools.jmod.Main.run(new String[] {
+        int rc = JMOD_TOOL.run(System.out, System.out, new String[] {
                 "create",
                 "--class-path", cp,
                 "--module-version", "1.0",
                 "--main-class", "jdk.test.Test",
                 jmods.resolve(modName + ".jmod").toString(),
-        }, System.out);
+        });
         if (rc != 0) {
             throw new AssertionError("Jmod failed: rc = " + rc);
         }
--- a/jdk/test/tools/jmod/JmodNegativeTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jmod/JmodNegativeTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,8 +24,8 @@
 /*
  * @test
  * @library /lib/testlibrary
- * @modules jdk.jlink/jdk.tools.jmod
- *          jdk.compiler
+ * @modules jdk.compiler
+ *          jdk.jlink
  * @build jdk.testlibrary.FileUtils CompilerUtils
  * @run testng JmodNegativeTest
  * @summary Negative tests for jmod
@@ -39,6 +39,7 @@
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
+import java.util.spi.ToolProvider;
 import java.util.zip.ZipOutputStream;
 import jdk.testlibrary.FileUtils;
 import org.testng.annotations.BeforeTest;
@@ -51,6 +52,11 @@
 
 public class JmodNegativeTest {
 
+    static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
+        .orElseThrow(() ->
+            new RuntimeException("jmod tool not found")
+        );
+
     static final String TEST_SRC = System.getProperty("test.src", ".");
     static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
     static final Path EXPLODED_DIR = Paths.get("build");
@@ -515,7 +521,7 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         PrintStream ps = new PrintStream(baos);
         System.out.println("jmod " + Arrays.asList(args));
-        int ec = jdk.tools.jmod.Main.run(args, ps);
+        int ec = JMOD_TOOL.run(ps, ps, args);
         return new JmodResult(ec, new String(baos.toByteArray(), UTF_8));
     }
 
--- a/jdk/test/tools/jmod/JmodTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jmod/JmodTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,8 +24,8 @@
 /*
  * @test
  * @library /lib/testlibrary
- * @modules jdk.jlink/jdk.tools.jmod
- *          jdk.compiler
+ * @modules jdk.compiler
+ *          jdk.jlink
  * @build jdk.testlibrary.FileUtils CompilerUtils
  * @run testng JmodTest
  * @summary Basic test for jmod
@@ -38,6 +38,7 @@
 import java.util.*;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
+import java.util.spi.ToolProvider;
 import java.util.stream.Stream;
 import jdk.testlibrary.FileUtils;
 import org.testng.annotations.BeforeTest;
@@ -51,6 +52,11 @@
 
 public class JmodTest {
 
+    static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
+        .orElseThrow(() ->
+            new RuntimeException("jmod tool not found")
+        );
+
     static final String TEST_SRC = System.getProperty("test.src", ".");
     static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
     static final Path EXPLODED_DIR = Paths.get("build");
@@ -479,7 +485,7 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         PrintStream ps = new PrintStream(baos);
         System.out.println("jmod " + Arrays.asList(args));
-        int ec = jdk.tools.jmod.Main.run(args, ps);
+        int ec = JMOD_TOOL.run(ps, ps, args);
         return new JmodResult(ec, new String(baos.toByteArray(), UTF_8));
     }
 
--- a/jdk/test/tools/jmod/hashes/HashesTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/jmod/hashes/HashesTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -27,8 +27,7 @@
  * @author Andrei Eremeev
  * @library /lib/testlibrary
  * @modules java.base/jdk.internal.module
- *          jdk.jlink/jdk.tools.jlink.internal
- *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink
  *          jdk.compiler
  * @build CompilerUtils
  * @run testng HashesTest
@@ -53,6 +52,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.spi.ToolProvider;
 import java.util.stream.Collectors;
 
 import jdk.internal.module.ConfigurableModuleFinder;
@@ -63,6 +63,10 @@
 import static org.testng.Assert.*;
 
 public class HashesTest {
+    static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
+        .orElseThrow(() ->
+            new RuntimeException("jmod tool not found")
+        );
 
     private final Path testSrc = Paths.get(System.getProperty("test.src"));
     private final Path modSrc = testSrc.resolve("src");
@@ -204,7 +208,7 @@
     }
 
     private void runJmod(List<String> args) {
-        int rc = jdk.tools.jmod.Main.run(args.toArray(new String[args.size()]), System.out);
+        int rc = JMOD_TOOL.run(System.out, System.out, args.toArray(new String[args.size()]));
         System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" ")));
         if (rc != 0) {
             throw new AssertionError("Jmod failed: rc = " + rc);
--- a/jdk/test/tools/launcher/modules/basic/BasicTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/launcher/modules/basic/BasicTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -24,9 +24,9 @@
 /**
  * @test
  * @library /lib/testlibrary
- * @modules jdk.jartool/sun.tools.jar
- *          jdk.jlink/jdk.tools.jmod
- *          jdk.compiler
+ * @modules jdk.compiler
+ *          jdk.jartool
+ *          jdk.jlink
  * @build BasicTest CompilerUtils jdk.testlibrary.*
  * @run testng BasicTest
  * @summary Basic test of starting an application as a module
@@ -36,6 +36,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.spi.ToolProvider;
 
 import jdk.testlibrary.ProcessTools;
 
@@ -46,6 +47,14 @@
 
 @Test
 public class BasicTest {
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+    private static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
+        .orElseThrow(() ->
+            new RuntimeException("jmod tool not found")
+        );
 
     private static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
 
@@ -132,10 +141,8 @@
             "--main-class=" + MAIN_CLASS,
             "-C", classes, "."
         };
-        boolean success
-            = new sun.tools.jar.Main(System.out, System.out, "jar")
-                .run(args);
-        assertTrue(success);
+        int rc = JAR_TOOL.run(System.out, System.out, args);
+        assertTrue(rc == 0);
 
         // java --module-path mlib -module $TESTMODULE
         int exitValue = exec("--module-path", dir.toString(),
@@ -164,8 +171,8 @@
             "--main-class", MAIN_CLASS,
             jmod
         };
-        jdk.tools.jmod.JmodTask task = new jdk.tools.jmod.JmodTask();
-        assertEquals(task.run(args), 0);
+
+        assertEquals(JMOD_TOOL.run(System.out, System.out, args), 0);
 
         // java --module-path mods --module $TESTMODULE
         int exitValue = exec("--module-path", dir.toString(),
@@ -229,10 +236,8 @@
             "--file=" + jar,
             "-C", classes, "."
         };
-        boolean success
-            = new sun.tools.jar.Main(System.out, System.out, "jar")
-                .run(args);
-        assertTrue(success);
+        int rc = JAR_TOOL.run(System.out, System.out, args);
+        assertTrue(rc == 0);
 
         // java --module-path mods -m $TESTMODULE
         int exitValue = exec("--module-path", dir.toString(), "-m", TEST_MODULE);
--- a/jdk/test/tools/launcher/modules/dryrun/DryRunTest.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/launcher/modules/dryrun/DryRunTest.java	Wed Oct 12 15:41:00 2016 -0700
@@ -26,7 +26,7 @@
  * @bug 8159596
  * @library /lib/testlibrary
  * @modules jdk.compiler
- *          jdk.jartool/sun.tools.jar
+ *          jdk.jartool
  * @build DryRunTest CompilerUtils jdk.testlibrary.ProcessTools
  * @run testng DryRunTest
  * @summary Test java --dry-run
@@ -37,6 +37,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.spi.ToolProvider;
 
 import jdk.testlibrary.ProcessTools;
 
@@ -78,8 +79,8 @@
         Files.createDirectories(LIBS_DIR);
 
         // create JAR files with no module-info.class
-        assertTrue(jar(M_MODULE, "p/Lib.class"));
-        assertTrue(jar(TEST_MODULE, "jdk/test/Main.class"));
+        assertTrue(jar(M_MODULE, "p/Lib.class") == 0);
+        assertTrue(jar(TEST_MODULE, "jdk/test/Main.class") == 0);
     }
 
     /**
@@ -197,7 +198,12 @@
         assertTrue(exitValue != 0);
     }
 
-    private static boolean jar(String name, String entries) throws IOException {
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+
+    private static int jar(String name, String entries) throws IOException {
         Path jar = LIBS_DIR.resolve(name + ".jar");
 
         // jar --create ...
@@ -207,8 +213,6 @@
             "--file=" + jar,
             "-C", classes, entries
         };
-        boolean success
-            = new sun.tools.jar.Main(System.out, System.out, "jar").run(args);
-        return success;
+        return JAR_TOOL.run(System.out, System.out, args);
     }
 }
--- a/jdk/test/tools/lib/tests/JImageGenerator.java	Wed Oct 12 14:31:17 2016 -0700
+++ b/jdk/test/tools/lib/tests/JImageGenerator.java	Wed Oct 12 15:41:00 2016 -0700
@@ -338,6 +338,10 @@
     }
 
     public static class JModTask {
+        static final java.util.spi.ToolProvider JMOD_TOOL =
+            java.util.spi.ToolProvider.findFirst("jmod").orElseThrow(() ->
+                new RuntimeException("jmod tool not found")
+            );
 
         private final List<Path> classpath = new ArrayList<>();
         private final List<Path> libs = new ArrayList<>();
@@ -477,7 +481,8 @@
             String[] args = optionsJMod(cmd);
             System.err.println("jmod options: " + optionsPrettyPrint(args));
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            int exitCode = jdk.tools.jmod.Main.run(args, new PrintStream(baos));
+            PrintStream ps = new PrintStream(baos);
+            int exitCode = JMOD_TOOL.run(ps, ps, args);
             String msg = new String(baos.toByteArray());
             return new Result(exitCode, msg, output);
         }
@@ -556,6 +561,10 @@
     }
 
     public static class JLinkTask {
+        static final java.util.spi.ToolProvider JLINK_TOOL =
+            java.util.spi.ToolProvider.findFirst("jlink").orElseThrow(() ->
+                new RuntimeException("jlink tool not found")
+            );
 
         private final List<Path> jars = new ArrayList<>();
         private final List<Path> jmods = new ArrayList<>();
@@ -691,7 +700,8 @@
             String[] args = optionsJLink();
             System.err.println("jlink options: " + optionsPrettyPrint(args));
             StringWriter writer = new StringWriter();
-            int exitCode = jdk.tools.jlink.internal.Main.run(args, new PrintWriter(writer));
+            PrintWriter pw = new PrintWriter(writer);
+            int exitCode = JLINK_TOOL.run(pw, pw, args);
             return new Result(exitCode, writer.toString(), output);
         }
 
@@ -699,7 +709,8 @@
             String[] args = optionsPostProcessJLink();
             System.err.println("jlink options: " + optionsPrettyPrint(args));
             StringWriter writer = new StringWriter();
-            int exitCode = jdk.tools.jlink.internal.Main.run(args, new PrintWriter(writer));
+            PrintWriter pw = new PrintWriter(writer);
+            int exitCode = JLINK_TOOL.run(pw, pw, args);
             return new Result(exitCode, writer.toString(), output);
         }
     }