Hi,

Quite a few command line tools are available through
java.util.spi.ToolProvider. But not so jarsigner and keytool. If that
finds some common interest, I'd be glad to contribute a patch or two.

Advantages of having jarsigner and/or keytool available as a tool
(though that could certainly be achieved in another way as well) would
be
- Explicit Stdin, Stdout, and Stderr as parameters (possible already
now by forking a separate process with the overhead of running the
tools in their own vm)
- Easy prevention of all possible calls to System.exit upon
unfavourable command line paramters (at least with jarsigner, and only
when working with sun.security.tools.jarsigner.Main and not with
jdk.security.jarsigner.JarSigner)
- Easier to detect whether a jarsigner or keytool tool is available on
the class or module path or not.
- Those who are more familiar with the command line options can use
them also programmatically.

An example seems to be JLinkSigningTest.java, [1]. Even though only a
test and in that context not actually severe but still not such a bad
example. I figure the following should not happen in real code:
- The tests catches an Exception which is never thrown (especially not
for the reason in the message, "jarsigner not found", I'd expect at
best a NoClassDefFoundError but which would not be caught there).
- Jarsigner (sun.security.tools.jarsigner.Main) will call System.exit
instead of throwing an exception (8 occurrences of System.exit in [2])
for certain command line args
- If command line arguments would be specified such that jarsigner
would prompt for input, it would be most probably stuck indefinitely.

I tried to search existing resources about the topic but found almost
nothing, potentially related [3], [4], and [5].

I'm not entirely sure, if java.util.spi.ToolProvider is preferred over
javax.tools.ToolProvider (or java.util.ServiceLoader). [7] might be a
hint that java.util.spi.ToolProvider is more current than
javax.tools.ToolProvider.

An additional point for keytool and jarsigner as opposed to jar, javac,
and other tools is that both jarsigner and keytool can use stdin when
prompting for passwords or maybe X.509 details which is not currently
possible through the api of java.util.spi.ToolProvider and would
require an additional parameter for Stdin such as in [6]. I would
propose that when jarsigner or keytool are invoked without a Stdin that
they throw an exception or return a non-zero exit code as their return
value immediately whenever they would have otherwise waited for input.

Some system properties also have influence on the operation of
jarsigner and possibly keytool (for example user.language and
java.security.properties among probably others) which could not
necessarily be set individually for each invocation of a tool gotten by
a ToolProvider but probably that would not be necessary in many cases
anyway.

Just like [3] sounds according to its subject,
jdk.security.jarsigner.JarSigner already has a useful API, so the need
for a ToolProvider is probably not that big. The current
jdk.security.jarsigner.JarSigner API, however, expects a key or
certificate or chain of certificates to be provided through the API and
therefore does not leverage the jarsigner and keytool command line
tools' keystore accessing features and aliases. I also don't see how it
would provide a possibility to verify a signed JAR but that could as
well be achieved in another way than with a ToolProvider.

It might be a bit of a challenge to catch all System.out and System.err
such as in sun.security.tools.KeyStoreUtil.getPassWithModifier or
sun.security.util.Debug or from whatever provider classes involved with
algorithms loaded with "-providerClass" or "-altsigner" arguments. One
conceivable approach might be the one used in the compiler with a
context object holding references to Stdout and Stderr. What I don't
like for now in the attached patch is that wrapping System.out or
System.err PrintStreams into PrintWriters is repeated a few times and I
wonder if that could be refactored or recombined into another
PrintWriter constructor or PrintStream.toPrintWriter or something...

So far I haven't found an overview over all the tools available through
java.util.spi.ToolProvider other than the example in ToolProvider#name
or other documentation that might have to be updated with introducing
jarsigner as a tool.

Also I wonder if Stdin/System.in should be added to the ToolProvider
interface or a run method with only args that takes Stdin, Stdout, and
Stderr from System.in, System.out, and System.err but I guess that can
be discussed independently.

Attached is a patch with JarSignerToolProvider that provides jarsigner
as a tool. It's not yet meant as ready to merge but shows that it is
possible and more or less how big the change is.

I'm not really familiar at all with the processes involved with such a
proposal and would appreciate greatly any kind of guidance. Was
this mailing list the right place to propose it?

Regards,
Philipp


[1] http://hg.openjdk.org/jdk/jdk/file/9c3fe09f69bc/test/jdk/tools/jlin
k/JLinkSigningTest.java#l79
[2] http://hg.openjdk.org/jdk/jdk/file/9c3fe09f69bc/src/jdk.jartool/sha
re/classes/sun/security/tools/jarsigner/Main.java
[3] https://bugs.openjdk.java.net/browse/JDK-8056174
[4] https://bugs.openjdk.java.net/browse/JDK-8058778
[5] http://mail.openjdk.java.net/pipermail/security-dev/2015-March/0118
31.html
[6] http://hg.openjdk.org/jdk/jdk/file/9c3fe09f69bc/src/jdk.compiler/sh
are/classes/com/sun/tools/javac/api/JavacTool.java#l203
[7] http://hg.openjdk.org/jdk/jdk/file/9c3fe09f69bc/src/java.compiler/s
hare/classes/javax/tools/ToolProvider.java#l90
diff -r 072b382347db src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
--- a/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -25,13 +25,12 @@
 
 package sun.security.tools;
 
-
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
-
+import java.io.PrintWriter;
 import java.io.StreamTokenizer;
 import java.io.StringReader;
 import java.net.URL;
@@ -134,12 +133,22 @@
 
     public static char[] getPassWithModifier(String modifier, String arg,
                                              java.util.ResourceBundle rb) {
+        PrintWriter errWriter = new PrintWriter(System.err);
+        try {
+            return getPassWithModifier(modifier, arg, rb, errWriter);
+        } finally {
+            errWriter.flush();
+        }
+    }
+
+    public static char[] getPassWithModifier(String modifier, String arg,
+            java.util.ResourceBundle rb, PrintWriter err) {
         if (modifier == null) {
             return arg.toCharArray();
         } else if (collator.compare(modifier, "env") == 0) {
             String value = System.getenv(arg);
             if (value == null) {
-                System.err.println(rb.getString(
+                err.println(rb.getString(
                         "Cannot.find.environment.variable.") + arg);
                 return null;
             } else {
@@ -155,8 +164,7 @@
                     if (f.exists()) {
                         url = f.toURI().toURL();
                     } else {
-                        System.err.println(rb.getString(
-                                "Cannot.find.file.") + arg);
+                        err.println(rb.getString("Cannot.find.file.") + arg);
                         return null;
                     }
                 }
@@ -173,12 +181,11 @@
                     return value.toCharArray();
                 }
             } catch (IOException ioe) {
-                System.err.println(ioe);
+                err.println(ioe);
                 return null;
             }
         } else {
-            System.err.println(rb.getString("Unknown.password.type.") +
-                    modifier);
+            err.println(rb.getString("Unknown.password.type.") + modifier);
             return null;
         }
     }
diff -r 072b382347db src/jdk.jartool/share/classes/module-info.java
--- a/src/jdk.jartool/share/classes/module-info.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/src/jdk.jartool/share/classes/module-info.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -30,11 +30,12 @@
  * This module also defines APIs for signing JAR files.
  *
  * <p> This module provides the equivalent of command-line access to
- * <em>jar</em> via the {@link java.util.spi.ToolProvider ToolProvider} SPI.
+ * <em>jar</em> and <em>jarsigner</em> tools via the
+ * {@link java.util.spi.ToolProvider ToolProvider} SPI.
  * Instances of the tool can be obtained by calling
  * {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst}
  * or the {@linkplain java.util.ServiceLoader service loader} with the name
- * {@code "jar"}.
+ * {@code "jar"} or {@code "jarsigner"}.
  *
  * <dl style="font-family:'DejaVu Sans', Arial, Helvetica, sans serif">
  * <dt class="simpleTagLabel">Tool Guides:
@@ -50,5 +51,6 @@
     exports jdk.security.jarsigner;
 
     provides java.util.spi.ToolProvider with
-        sun.tools.jar.JarToolProvider;
+        sun.tools.jar.JarToolProvider,
+        sun.security.tools.jarsigner.JarSignerToolProvider;
 }
diff -r 072b382347db src/jdk.jartool/share/classes/sun/security/tools/jarsigner/JarSignerToolProvider.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/JarSignerToolProvider.java	Wed Feb 27 08:14:10 2019 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019, 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.security.tools.jarsigner;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.spi.ToolProvider;
+
+public class JarSignerToolProvider implements ToolProvider {
+    public String name() {
+        return "jarsigner";
+    }
+
+    public int run(PrintWriter out, PrintWriter err, String... args) {
+        Objects.requireNonNull(out);
+        Objects.requireNonNull(err);
+        Objects.requireNonNull(args);
+        for (String arg : args) {
+            Objects.requireNonNull(arg);
+        }
+
+        return new Main(out, err).run(args);
+    }
+}
diff -r 072b382347db src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
--- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -111,11 +111,32 @@
     // signer is not in alias list
     static final int SIGNED_BY_ALIAS = 0x08;    // signer is in alias list
 
-    // Attention:
-    // This is the entry that get launched by the security tool jarsigner.
+    final InputStream in;
+    final PrintWriter out, err;
+
+    public Main(InputStream in, PrintWriter out, PrintWriter err) {
+        this.in = in;
+        this.out = out;
+        this.err = err;
+    }
+
+    // This is used by the jarsigner ToolProvider.
+    public Main(PrintWriter out, PrintWriter err) {
+        this(null, out, err);
+    }
+
+    // This is the entry that gets launched by the jarsigner command line tool.
     public static void main(String args[]) throws Exception {
-        Main js = new Main();
-        js.run(args);
+        int exitCode = 0;
+        PrintWriter outWriter = new PrintWriter(System.out);
+        PrintWriter errWriter = new PrintWriter(System.err);
+        try {
+            exitCode = new Main(System.in, outWriter, errWriter).run(args);
+        } finally {
+            outWriter.flush();
+            errWriter.flush();
+        }
+        if (exitCode != 0) System.exit(exitCode);
     }
 
     X509Certificate[] certChain;    // signer's cert chain (when composing)
@@ -204,7 +225,9 @@
     PKIXBuilderParameters pkixParameters;
     Set<X509Certificate> trustedCerts = new HashSet<>();
 
-    public void run(String args[]) {
+    // This is the entry that get launched by the jarsigner ToolProvider tool
+    // and indirectly through main by the command line jarsigner tool.
+    public int run(String args[]) {
         try {
             args = parseArgs(args);
 
@@ -215,7 +238,7 @@
                         KeyStoreUtil.loadProviderByName(provName,
                                 providerArgs.get(provName));
                         if (debug) {
-                            System.out.println("loadProviderByName: " + provName);
+                            out.println("loadProviderByName: " + provName);
                         }
                     } catch (IllegalArgumentException e) {
                         throw new Exception(String.format(rb.getString(
@@ -231,7 +254,7 @@
                         KeyStoreUtil.loadProviderByClass(provClass,
                                 providerArgs.get(provClass), cl);
                         if (debug) {
-                            System.out.println("loadProviderByClass: " + provClass);
+                            out.println("loadProviderByClass: " + provClass);
                         }
                     } catch (ClassCastException cce) {
                         throw new Exception(String.format(rb.getString(
@@ -248,12 +271,7 @@
                     loadKeyStore(keystore, false);
                 } catch (Exception e) {
                     if ((keystore != null) || (storepass != null)) {
-                        System.out.println(rb.getString("jarsigner.error.") +
-                                        e.getMessage());
-                        if (debug) {
-                            e.printStackTrace();
-                        }
-                        System.exit(1);
+                        throw e;
                     }
                 }
                 /*              if (debug) {
@@ -261,19 +279,32 @@
                     ManifestEntryVerifier.setDebug(true);
                 }
                 */
-                verifyJar(jarfile);
+                int exitCode = verifyJar(jarfile);
+                if (exitCode != 0) {
+                    return exitCode;
+                }
             } else {
                 loadKeyStore(keystore, true);
                 getAliasInfo(alias);
 
                 signJar(jarfile, alias);
             }
+        } catch (FullUsageException e) {
+            return e.fullusage();
+        } catch (InvalidUsageException e) {
+            return e.usage();
+        } catch (JarSignerErrorException e) {
+            out.println(rb.getString("jarsigner.") + e.getMessage());
+            if (debug && e.getCause() != null) {
+                e.getCause().printStackTrace(err);
+            }
+            return 1;
         } catch (Exception e) {
-            System.out.println(rb.getString("jarsigner.error.") + e);
+            out.println(rb.getString("jarsigner.error.") + e);
             if (debug) {
-                e.printStackTrace();
+                e.printStackTrace(err);
             }
-            System.exit(1);
+            return 1;
         } finally {
             // zero-out private key password
             if (keypass != null) {
@@ -287,8 +318,8 @@
             }
         }
 
+        int exitCode = 0;
         if (strict) {
-            int exitCode = 0;
             if (weakAlg != 0 || chainNotValidated || hasExpiredCert
                     || hasExpiredTsaCert || notYetValidCert || signerSelfSigned) {
                 exitCode |= 4;
@@ -305,10 +336,8 @@
             if (tsaChainNotValidated) {
                 exitCode |= 64;
             }
-            if (exitCode != 0) {
-                System.exit(exitCode);
-            }
         }
+        return exitCode;
     }
 
     /*
@@ -318,7 +347,7 @@
         /* parse flags */
         int n = 0;
 
-        if (args.length == 0) fullusage();
+        if (args.length == 0) throw new FullUsageException();
 
         String confFile = null;
         String command = "-sign";
@@ -343,8 +372,7 @@
 
         if (debug) {
             // No need to localize debug output
-            System.out.println("Command line args: " +
-                    Arrays.toString(args));
+            out.println("Command line args: " + Arrays.toString(args));
         }
 
         for (n=0; n < args.length; n++) {
@@ -443,14 +471,12 @@
             } else if (collator.compare(flags, "-altsigner") ==0) {
                 if (++n == args.length) usageNoArg();
                 altSignerClass = args[n];
-                System.err.println(
-                        rb.getString("This.option.is.deprecated") +
+                err.println(rb.getString("This.option.is.deprecated") +
                                 "-altsigner");
             } else if (collator.compare(flags, "-altsignerpath") ==0) {
                 if (++n == args.length) usageNoArg();
                 altSignerClasspath = args[n];
-                System.err.println(
-                        rb.getString("This.option.is.deprecated") +
+                err.println(rb.getString("This.option.is.deprecated") +
                                 "-altsignerpath");
             } else if (collator.compare(flags, "-sectionsonly") ==0) {
                 signManifest = false;
@@ -475,11 +501,10 @@
                        collator.compare(flags, "--help") == 0 ||
                        // -help: legacy.
                        collator.compare(flags, "-help") == 0) {
-                fullusage();
+                throw new FullUsageException();
             } else {
-                System.err.println(
-                        rb.getString("Illegal.option.") + flags);
-                usage();
+                err.println(rb.getString("Illegal.option.") + flags);
+                throw new InvalidUsageException();
             }
         }
 
@@ -487,16 +512,16 @@
         if (verbose == null) showcerts = false;
 
         if (jarfile == null) {
-            System.err.println(rb.getString("Please.specify.jarfile.name"));
-            usage();
+            err.println(rb.getString("Please.specify.jarfile.name"));
+            throw new InvalidUsageException();
         }
         if (!verify && alias == null) {
-            System.err.println(rb.getString("Please.specify.alias.name"));
-            usage();
+            err.println(rb.getString("Please.specify.alias.name"));
+            throw new InvalidUsageException();
         }
         if (!verify && ckaliases.size() > 1) {
-            System.err.println(rb.getString("Only.one.alias.can.be.specified"));
-            usage();
+            err.println(rb.getString("Only.one.alias.can.be.specified"));
+            throw new InvalidUsageException();
         }
 
         if (storetype == null) {
@@ -527,161 +552,172 @@
         }
 
         if (token && !nullStream) {
-            System.err.println(MessageFormat.format(rb.getString
+            err.println(MessageFormat.format(rb.getString
                 (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype));
-            usage();
+            throw new InvalidUsageException();
         }
 
         if (token && keypass != null) {
-            System.err.println(MessageFormat.format(rb.getString
+            err.println(MessageFormat.format(rb.getString
                 (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype));
-            usage();
+            throw new InvalidUsageException();
         }
 
         if (protectedPath) {
             if (storepass != null || keypass != null) {
-                System.err.println(rb.getString
+                err.println(rb.getString
                         ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified"));
-                usage();
+                throw new InvalidUsageException();
             }
         }
         if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
             if (storepass != null || keypass != null) {
-                System.err.println(rb.getString
+                err.println(rb.getString
                         ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified"));
-                usage();
+                throw new InvalidUsageException();
             }
         }
         return args;
     }
 
-    static char[] getPass(String modifier, String arg) {
-        char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb);
+    char[] getPass(String modifier, String arg) throws InvalidUsageException {
+        char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb, err);
         if (output != null) return output;
-        usage();
-        return null;    // Useless, usage() already exit
-    }
-
-    static void usageNoArg() {
-        System.out.println(rb.getString("Option.lacks.argument"));
-        usage();
-    }
-
-    static void usage() {
-        System.out.println();
-        System.out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
-        System.exit(1);
+        throw new InvalidUsageException();
     }
 
-    static void fullusage() {
-        System.out.println(rb.getString
-                ("Usage.jarsigner.options.jar.file.alias"));
-        System.out.println(rb.getString
-                (".jarsigner.verify.options.jar.file.alias."));
-        System.out.println();
-        System.out.println(rb.getString
-                (".keystore.url.keystore.location"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".storepass.password.password.for.keystore.integrity"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".storetype.type.keystore.type"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".keypass.password.password.for.private.key.if.different."));
-        System.out.println();
-        System.out.println(rb.getString
-                (".certchain.file.name.of.alternative.certchain.file"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".sigfile.file.name.of.SF.DSA.file"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".signedjar.file.name.of.signed.JAR.file"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".digestalg.algorithm.name.of.digest.algorithm"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".sigalg.algorithm.name.of.signature.algorithm"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".verify.verify.a.signed.JAR.file"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".verbose.suboptions.verbose.output.when.signing.verifying."));
-        System.out.println(rb.getString
-                (".suboptions.can.be.all.grouped.or.summary"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".certs.display.certificates.when.verbose.and.verifying"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".tsa.url.location.of.the.Timestamping.Authority"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".internalsf.include.the.SF.file.inside.the.signature.block"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".sectionsonly.don.t.compute.hash.of.entire.manifest"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".protected.keystore.has.protected.authentication.path"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".providerName.name.provider.name"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".add.provider.option"));
-        System.out.println(rb.getString
-                (".providerArg.option.1"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".providerClass.option"));
-        System.out.println(rb.getString
-                (".providerArg.option.2"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".strict.treat.warnings.as.errors"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".conf.url.specify.a.pre.configured.options.file"));
-        System.out.println();
-        System.out.println(rb.getString
-                (".print.this.help.message"));
-        System.out.println();
-
-        System.exit(0);
+    void usageNoArg() throws InvalidUsageException {
+        out.println(rb.getString("Option.lacks.argument"));
+        throw new InvalidUsageException();
     }
 
-    void verifyJar(String jarName)
-        throws Exception
-    {
+    /**
+     * Indicates that {@code jarsigner} should print the usage and then exit
+     * and is used to jump to the {@link #main main method}'s catch clause from
+     * where it will return with an exit code.
+     */
+    private class InvalidUsageException extends Exception {
+        static final long serialVersionUID = 7112441295109570400L;
+
+        int usage() {
+            out.println();
+            out.println(rb.getString("Please.type.jarsigner.help.for.usage"));
+            return 1;
+        }
+    }
+
+    /**
+     * Indicates that {@code jarsigner} should print the full usage and then
+     * exit.
+     */
+    private class FullUsageException extends Exception {
+        static final long serialVersionUID = 8965543537622373650L;
+
+        int fullusage() {
+            out.println(rb.getString
+                ("Usage.jarsigner.options.jar.file.alias"));
+            out.println(rb.getString
+                (".jarsigner.verify.options.jar.file.alias."));
+            out.println();
+            out.println(rb.getString
+                (".keystore.url.keystore.location"));
+            out.println();
+            out.println(rb.getString
+                (".storepass.password.password.for.keystore.integrity"));
+            out.println();
+            out.println(rb.getString
+                (".storetype.type.keystore.type"));
+            out.println();
+            out.println(rb.getString
+                (".keypass.password.password.for.private.key.if.different."));
+            out.println();
+            out.println(rb.getString
+                (".certchain.file.name.of.alternative.certchain.file"));
+            out.println();
+            out.println(rb.getString
+                (".sigfile.file.name.of.SF.DSA.file"));
+            out.println();
+            out.println(rb.getString
+                (".signedjar.file.name.of.signed.JAR.file"));
+            out.println();
+            out.println(rb.getString
+                (".digestalg.algorithm.name.of.digest.algorithm"));
+            out.println();
+            out.println(rb.getString
+                (".sigalg.algorithm.name.of.signature.algorithm"));
+            out.println();
+            out.println(rb.getString
+                (".verify.verify.a.signed.JAR.file"));
+            out.println();
+            out.println(rb.getString
+                (".verbose.suboptions.verbose.output.when.signing.verifying."));
+            out.println(rb.getString
+                (".suboptions.can.be.all.grouped.or.summary"));
+            out.println();
+            out.println(rb.getString
+                (".certs.display.certificates.when.verbose.and.verifying"));
+            out.println();
+            out.println(rb.getString
+                (".tsa.url.location.of.the.Timestamping.Authority"));
+            out.println();
+            out.println(rb.getString
+                (".tsacert.alias.public.key.certificate.for.Timestamping.Authority"));
+            out.println();
+            out.println(rb.getString
+                (".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));
+            out.println();
+            out.println(rb.getString
+                (".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));
+            out.println();
+            out.println(rb.getString
+                (".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
+            out.println();
+            out.println(rb.getString
+                (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism"));
+            out.println();
+            out.println(rb.getString
+                (".internalsf.include.the.SF.file.inside.the.signature.block"));
+            out.println();
+            out.println(rb.getString
+                (".sectionsonly.don.t.compute.hash.of.entire.manifest"));
+            out.println();
+            out.println(rb.getString
+                (".protected.keystore.has.protected.authentication.path"));
+            out.println();
+            out.println(rb.getString
+                (".providerName.name.provider.name"));
+            out.println();
+            out.println(rb.getString
+                (".add.provider.option"));
+            out.println(rb.getString
+                (".providerArg.option.1"));
+            out.println();
+            out.println(rb.getString
+                (".providerClass.option"));
+            out.println(rb.getString
+                (".providerArg.option.2"));
+            out.println();
+            out.println(rb.getString
+                (".strict.treat.warnings.as.errors"));
+            out.println();
+            out.println(rb.getString
+                (".conf.url.specify.a.pre.configured.options.file"));
+            out.println();
+            out.println(rb.getString
+                (".print.this.help.message"));
+            out.println();
+            return 0;
+        }
+    }
+
+    int verifyJar(String jarName) throws Exception {
         boolean anySigned = false;  // if there exists entry inside jar signed
-        JarFile jf = null;
         Map<String,String> digestMap = new HashMap<>();
         Map<String,PKCS7> sigMap = new HashMap<>();
         Map<String,String> sigNameMap = new HashMap<>();
         Map<String,String> unparsableSignatures = new HashMap<>();
 
-        try {
-            jf = new JarFile(jarName, true);
+        try (JarFile jf = new JarFile(jarName, true)) {
             Vector<JarEntry> entriesVec = new Vector<>();
             byte[] buffer = new byte[8192];
 
@@ -740,7 +776,7 @@
             Map<String,List<String>> output = new LinkedHashMap<>();
 
             if (man != null) {
-                if (verbose != null) System.out.println();
+                if (verbose != null) out.println();
                 Enumeration<JarEntry> e = entriesVec.elements();
 
                 String tab = rb.getString("6SPACE");
@@ -852,42 +888,42 @@
                     int pipe = key.indexOf('|');
                     if (verbose.equals("all")) {
                         for (String f: files) {
-                            System.out.println(key.substring(0, pipe) + f);
-                            System.out.printf(key.substring(pipe+1));
+                            out.println(key.substring(0, pipe) + f);
+                            out.printf(key.substring(pipe + 1));
                         }
                     } else {
                         if (verbose.equals("grouped")) {
                             for (String f: files) {
-                                System.out.println(key.substring(0, pipe) + f);
+                                out.println(key.substring(0, pipe) + f);
                             }
                         } else if (verbose.equals("summary")) {
-                            System.out.print(key.substring(0, pipe));
+                            out.print(key.substring(0, pipe));
                             if (files.size() > 1) {
-                                System.out.println(files.get(0) + " " +
+                                out.println(files.get(0) + " " +
                                         String.format(rb.getString(
                                         ".and.d.more."), files.size()-1));
                             } else {
-                                System.out.println(files.get(0));
+                                out.println(files.get(0));
                             }
                         }
-                        System.out.printf(key.substring(pipe+1));
+                        out.printf(key.substring(pipe + 1));
                     }
                 }
-                System.out.println();
-                System.out.println(rb.getString(
+                out.println();
+                out.println(rb.getString(
                     ".s.signature.was.verified."));
-                System.out.println(rb.getString(
+                out.println(rb.getString(
                     ".m.entry.is.listed.in.manifest"));
-                System.out.println(rb.getString(
+                out.println(rb.getString(
                     ".k.at.least.one.certificate.was.found.in.keystore"));
                 if (ckaliases.size() > 0) {
-                    System.out.println(rb.getString(
+                    out.println(rb.getString(
                         ".X.not.signed.by.specified.alias.es."));
                 }
             }
             if (man == null) {
-                System.out.println();
-                System.out.println(rb.getString("no.manifest."));
+                out.println();
+                out.println(rb.getString("no.manifest."));
             }
 
             // If signer is a trusted cert or private entry in user's own
@@ -903,7 +939,7 @@
                     || !sigMap.isEmpty()
                     || !unparsableSignatures.isEmpty()) {
                 if (verbose != null) {
-                    System.out.println();
+                    out.println();
                 }
                 for (String s : sigMap.keySet()) {
                     if (!digestMap.containsKey(s)) {
@@ -966,7 +1002,7 @@
                                     sigNameMap.get(s));
                         }
                         if (verbose != null) {
-                            System.out.println(history);
+                            out.println(history);
                         }
                     } else {
                         unparsableSignatures.putIfAbsent(s, String.format(
@@ -975,45 +1011,37 @@
                 }
                 if (verbose != null) {
                     for (String s : unparsableSignatures.keySet()) {
-                        System.out.println(unparsableSignatures.get(s));
+                        out.println(unparsableSignatures.get(s));
                     }
                 }
             }
-            System.out.println();
+            out.println();
             if (!anySigned) {
                 if (seeWeak) {
                     if (verbose != null) {
-                        System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
-                        System.out.println("\n  " +
+                        out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
+                        out.println("\n  " +
                                 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS +
                                 "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS));
                     } else {
-                        System.out.println(rb.getString("jar.treated.unsigned.see.weak"));
+                        out.println(rb.getString("jar.treated.unsigned.see.weak"));
                     }
                 } else if (hasSignature) {
-                    System.out.println(rb.getString("jar.treated.unsigned"));
+                    out.println(rb.getString("jar.treated.unsigned"));
                 } else {
-                    System.out.println(rb.getString("jar.is.unsigned"));
+                    out.println(rb.getString("jar.is.unsigned"));
                 }
             } else {
                 displayMessagesAndResult(false);
             }
-            return;
+            return 0;
         } catch (Exception e) {
-            System.out.println(rb.getString("jarsigner.") + e);
-            if (debug) {
-                e.printStackTrace();
-            }
-        } finally { // close the resource
-            if (jf != null) {
-                jf.close();
-            }
+            throw error(rb.getString("jarsigner.") + e, e);
         }
-
-        System.exit(1);
     }
 
-    private void displayMessagesAndResult(boolean isSigning) {
+    private void displayMessagesAndResult(boolean isSigning)
+            throws JarSignerErrorException {
         String result;
         List<String> errors = new ArrayList<>();
         List<String> warnings = new ArrayList<>();
@@ -1181,30 +1209,30 @@
             }
         }
 
-        System.out.println(result);
+        out.println(result);
         if (strict) {
             if (!errors.isEmpty()) {
-                System.out.println();
-                System.out.println(rb.getString("Error."));
-                errors.forEach(System.out::println);
+                out.println();
+                out.println(rb.getString("Error."));
+                errors.forEach(out::println);
             }
             if (!warnings.isEmpty()) {
-                System.out.println();
-                System.out.println(rb.getString("Warning."));
-                warnings.forEach(System.out::println);
+                out.println();
+                out.println(rb.getString("Warning."));
+                warnings.forEach(out::println);
             }
         } else {
             if (!errors.isEmpty() || !warnings.isEmpty()) {
-                System.out.println();
-                System.out.println(rb.getString("Warning."));
-                errors.forEach(System.out::println);
-                warnings.forEach(System.out::println);
+                out.println();
+                out.println(rb.getString("Warning."));
+                errors.forEach(out::println);
+                warnings.forEach(out::println);
             }
         }
         if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {
             if (! (verbose != null && showcerts)) {
-                System.out.println();
-                System.out.println(rb.getString(
+                out.println();
+                out.println(rb.getString(
                         "Re.run.with.the.verbose.and.certs.options.for.more.details."));
             }
         }
@@ -1232,8 +1260,8 @@
         }
 
         if (!info.isEmpty()) {
-            System.out.println();
-            info.forEach(System.out::println);
+            out.println();
+            info.forEach(out::println);
         }
     }
 
@@ -1553,13 +1581,13 @@
             builder.eventHandler((action, file) -> {
                 switch (action) {
                     case "signing":
-                        System.out.println(rb.getString(".signing.") + file);
+                        out.println(rb.getString(".signing.") + file);
                         break;
                     case "adding":
-                        System.out.println(rb.getString(".adding.") + file);
+                        out.println(rb.getString(".adding.") + file);
                         break;
                     case "updating":
-                        System.out.println(rb.getString(".updating.") + file);
+                        out.println(rb.getString(".updating.") + file);
                         break;
                     default:
                         throw new IllegalArgumentException("unknown action: "
@@ -1586,12 +1614,11 @@
 
         if (tsaURI != null) {
             if (verbose != null) {
-                System.out.println(
-                        rb.getString("requesting.a.signature.timestamp"));
+                out.println(rb.getString("requesting.a.signature.timestamp"));
                 if (tsaUrl != null) {
-                    System.out.println(rb.getString("TSA.location.") + tsaUrl);
+                    out.println(rb.getString("TSA.location.") + tsaUrl);
                 } else if (tsaCert != null) {
-                    System.out.println(rb.getString("TSA.certificate.") +
+                    out.println(rb.getString("TSA.certificate.") +
                             printCert(true, "", tsaCert, null, false));
                 }
             }
@@ -1608,8 +1635,8 @@
         if (altSignerClass != null) {
             builder.setProperty("altSigner", altSignerClass);
             if (verbose != null) {
-                System.out.println(
-                        rb.getString("using.an.alternative.signing.mechanism"));
+                out.println(rb.getString
+                        ("using.an.alternative.signing.mechanism"));
             }
         }
 
@@ -1675,7 +1702,7 @@
         }
 
         if (verbose != null) {
-            System.out.println();
+            out.println();
         }
 
         // The JarSigner API always accepts the timestamp received.
@@ -1698,11 +1725,11 @@
             // Spaces before the ">>> Signer" and other lines are different
             String result = certsAndTSInfo("", "    ", Arrays.asList(certChain), ts);
             if (verbose != null) {
-                System.out.println(result);
+                out.println(result);
             }
         } catch (Exception e) {
             if (debug) {
-                e.printStackTrace();
+                e.printStackTrace(err);
             }
         }
 
@@ -1836,7 +1863,8 @@
         return sb.toString();
     }
 
-    void loadKeyStore(String keyStoreName, boolean prompt) {
+    void loadKeyStore(String keyStoreName, boolean prompt)
+            throws JarSignerErrorException {
 
         if (!nullStream && keyStoreName == null) {
             keyStoreName = System.getProperty("user.home") + File.separator
@@ -1947,7 +1975,7 @@
         }
     }
 
-    X509Certificate getTsaCert(String alias) {
+    X509Certificate getTsaCert(String alias) throws JarSignerErrorException {
 
         java.security.cert.Certificate cs = null;
 
@@ -2118,18 +2146,31 @@
         }
     }
 
-    void error(String message) {
-        System.out.println(rb.getString("jarsigner.")+message);
-        System.exit(1);
+    /**
+     * Indicates that {@code jarsigner} cannot continue and terminate normally
+     * and is used to jump to the {@link #main main method}'s catch clause from
+     * where it will return with an exit code.
+     */
+    static class JarSignerErrorException extends Exception {
+        static final long serialVersionUID = -6575209030723808865L;
+
+        JarSignerErrorException(String message) {
+            super(message);
+        }
+
+        JarSignerErrorException(String message, Throwable cause) {
+            super(message, cause);
+        }
     }
 
+    JarSignerErrorException error(String message)
+            throws JarSignerErrorException {
+        throw new JarSignerErrorException(message);
+    }
 
-    void error(String message, Throwable e) {
-        System.out.println(rb.getString("jarsigner.")+message);
-        if (debug) {
-            e.printStackTrace();
-        }
-        System.exit(1);
+    JarSignerErrorException error(String message, Throwable e)
+            throws JarSignerErrorException {
+        throw new JarSignerErrorException(message, e);
     }
 
     /**
@@ -2148,7 +2189,7 @@
                             null, parameter);
         } catch (Exception e) {
             if (debug) {
-                e.printStackTrace();
+                e.printStackTrace(err);
             }
 
             // Exception might be dismissed if another warning flag
@@ -2198,21 +2239,24 @@
         }
     }
 
-    char[] getPass(String prompt) {
-        System.err.print(prompt);
-        System.err.flush();
+    char[] getPass(String prompt) throws JarSignerErrorException {
+        if (in == null) {
+            throw error(rb.getString("unable.to.read.password."));
+        }
+
+        err.print(prompt);
+        err.flush();
         try {
-            char[] pass = Password.readPassword(System.in);
+            char[] pass = Password.readPassword(in);
 
             if (pass == null) {
-                error(rb.getString("you.must.enter.key.password"));
+                throw error(rb.getString("you.must.enter.key.password"));
             } else {
                 return pass;
             }
         } catch (IOException ioe) {
-            error(rb.getString("unable.to.read.password.")+ioe.getMessage());
+            throw error(rb.getString("unable.to.read.password.")
+                    + ioe.getMessage());
         }
-        // this shouldn't happen
-        return null;
     }
 }
diff -r 072b382347db test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java
--- a/test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/test/jdk/java/util/jar/JarInputStream/ExtraFileInMetaInf.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -30,6 +30,7 @@
  */
 
 import java.util.jar.*;
+import java.util.spi.ToolProvider;
 import java.io.*;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
@@ -51,9 +52,10 @@
         new File("ks").delete();
         sun.security.tools.keytool.Main.main(
                 ("-keystore ks -storepass changeit -keypass changeit " +
-                        "-keyalg rsa -alias a -dname CN=A -genkeypair").split(" "));
-        sun.security.tools.jarsigner.Main.main(
-                "-keystore ks -storepass changeit x.jar a".split(" "));
+                 "-keyalg rsa -alias a -dname CN=A -genkeypair").split(" "));
+        ToolProvider jarsignertool = ToolProvider.findFirst("jarsigner").get();
+        jarsignertool.run(System.out, System.err,
+        		"-keystore ks -storepass changeit x.jar a".split(" "));
 
         // Check if the entries are signed
         try (JarInputStream jis =
diff -r 072b382347db test/jdk/sun/security/tools/jarsigner/EntriesOrder.java
--- a/test/jdk/sun/security/tools/jarsigner/EntriesOrder.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/test/jdk/sun/security/tools/jarsigner/EntriesOrder.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -40,6 +40,7 @@
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.JarInputStream;
+import java.util.spi.ToolProvider;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
@@ -69,18 +70,18 @@
         Files.write(Paths.get("META-INF/inf"), "inf".getBytes());
 
         // Pack, sign, and extract to get all files
-        sun.tools.jar.Main m =
-                new sun.tools.jar.Main(System.out, System.err, "jar");
-        if (!m.run("cvf a.jar a META-INF/inf".split(" "))) {
+        ToolProvider jartool = ToolProvider.findFirst("jar").get();
+        if (jartool.run(System.out, System.err,
+        		"cvf a.jar a META-INF/inf".split(" ")) != 0) {
             throw new Exception("jar creation failed");
         }
         sun.security.tools.keytool.Main.main(
                 ("-keystore jks -storepass changeit -keypass changeit -dname" +
                         " CN=A -alias a -genkeypair -keyalg rsa").split(" "));
-        sun.security.tools.jarsigner.Main.main(
-                "-keystore jks -storepass changeit a.jar a".split(" "));
-        m = new sun.tools.jar.Main(System.out, System.err, "jar");
-        if (!m.run("xvf a.jar".split(" "))) {
+        ToolProvider jarsignertool = ToolProvider.findFirst("jarsigner").get();
+        jarsignertool.run(System.out, System.err,
+        		"-keystore jks -storepass changeit a.jar a".split(" "));
+        if (jartool.run(System.out, System.err, "xvf a.jar".split(" ")) != 0) {
             throw new Exception("jar extraction failed");
         }
 
diff -r 072b382347db test/jdk/sun/security/tools/jarsigner/JarSigningNonAscii.java
--- a/test/jdk/sun/security/tools/jarsigner/JarSigningNonAscii.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/test/jdk/sun/security/tools/jarsigner/JarSigningNonAscii.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -32,6 +32,7 @@
 import java.io.*;
 import java.util.*;
 import java.util.jar.*;
+import java.util.spi.ToolProvider;
 import java.security.cert.Certificate;
 
 public class JarSigningNonAscii {
@@ -63,7 +64,8 @@
                         "-signedJar", signedJar,
                         unsignedJar, "b"
                         };
-        sun.security.tools.jarsigner.Main.main(jsArgs);
+        ToolProvider jarsignertool = ToolProvider.findFirst("jarsigner").get();
+        jarsignertool.run(System.out, System.err, jsArgs);
 
         //  verify the signed jar file
 
diff -r 072b382347db test/jdk/sun/security/tools/jarsigner/LargeJarEntry.java
--- a/test/jdk/sun/security/tools/jarsigner/LargeJarEntry.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/test/jdk/sun/security/tools/jarsigner/LargeJarEntry.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2019, 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
@@ -35,6 +35,7 @@
 import java.io.FileOutputStream;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
+import java.util.spi.ToolProvider;
 import java.util.zip.CRC32;
 
 public class LargeJarEntry {
@@ -73,7 +74,8 @@
                 jarName, "b" };
         // now, try to sign it
         try {
-            sun.security.tools.jarsigner.Main.main(jsArgs);
+            ToolProvider jarsignertool = ToolProvider.findFirst("jarsigner").get();
+            jarsignertool.run(System.out, System.err, jsArgs);
         } catch (OutOfMemoryError err) {
             throw new Exception("Test failed with OutOfMemoryError", err);
         } finally {
diff -r 072b382347db test/jdk/sun/security/tools/jarsigner/Options.java
--- a/test/jdk/sun/security/tools/jarsigner/Options.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/test/jdk/sun/security/tools/jarsigner/Options.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -52,6 +52,7 @@
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
+import java.util.spi.ToolProvider;
 
 public class Options {
 
@@ -69,7 +70,8 @@
                         " CN=A -alias a -genkeypair -keyalg rsa").split(" "));
 
         // -altsign
-        sun.security.tools.jarsigner.Main.main(
+        ToolProvider jarsignertool = ToolProvider.findFirst("jarsigner").get();
+        jarsignertool.run(System.out, System.err,
                 ("-debug -signedjar altsign.jar -keystore jks -storepass changeit" +
                         " -altsigner Options$X a.jar a").split(" "));
 
@@ -83,7 +85,7 @@
         }
 
         // -sigfile, -digestalg, -sigalg, -internalsf, -sectionsonly
-        sun.security.tools.jarsigner.Main.main(
+        jarsignertool.run(System.out, System.err,
                 ("-debug -signedjar new.jar -keystore jks -storepass changeit" +
                 " -sigfile olala -digestalg SHA1 -sigalg SHA224withRSA" +
                 " -internalsf -sectionsonly a.jar a").split(" "));
diff -r 072b382347db test/jdk/tools/jlink/JLinkSigningTest.java
--- a/test/jdk/tools/jlink/JLinkSigningTest.java	Sun Feb 24 16:10:52 2019 -0500
+++ b/test/jdk/tools/jlink/JLinkSigningTest.java	Wed Feb 27 08:14:10 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -46,6 +46,8 @@
 public class JLinkSigningTest {
     private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
             .orElseThrow(() -> new RuntimeException("jar tool not found"));
+    private static final ToolProvider JARSIGNER_TOOL = ToolProvider.findFirst("jarsigner")
+            .orElseThrow(() -> new RuntimeException("jarsigner tool not found"));
     private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac")
             .orElseThrow(() -> new RuntimeException("javac tool not found"));
     private static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
@@ -76,10 +78,8 @@
     static void jarsigner(String[] args) {
         report("jarsigner", args);
 
-        try {
-            sun.security.tools.jarsigner.Main.main(args);
-        } catch (Exception ex) {
-            throw new RuntimeException("jarsigner not found");
+        if (JARSIGNER_TOOL.run(System.out, System.err, args) != 0) {
+            throw new RuntimeException("jarsigner failed");
         }
     }
 

Reply via email to