1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util.spi; 27 28 import java.io.PrintStream; 29 import java.io.PrintWriter; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.Objects; 33 import java.util.Optional; 34 import java.util.ServiceLoader; 35 import java.util.stream.StreamSupport; 36 37 /** 38 * An interface for command-line tools to provide a way to 39 * be invoked without necessarily starting a new VM. 40 * 41 * <p>Tool providers are normally located using the service-provider 42 * loading facility defined by {@link ServiceLoader}. 43 * Each provider must provide a name, and a method to run 44 * an instance of the corresponding tool. When a tool is run, 45 * it will be provided with an array of string arguments, and a 46 * pair of streams: one for normal (or expected) output and the other 47 * for reporting any errors that may occur. 48 * The interpretation of the string arguments will normally be defined by 49 * each individual tool provider, but will generally correspond to the 50 * arguments that could be provided to the tool when invoking the tool 51 * from the command line. 52 * 53 * @since 9 54 */ 55 public interface ToolProvider { 56 /** 57 * Returns the name of this tool provider. 58 * 59 * @apiNote It is recommended that the name be the same as would be used on 60 * the command line: for example, "javac", "jar", "jlink". 61 * 62 * @return the name of this tool provider 63 */ 64 String name(); 65 66 /** 67 * Runs an instance of the tool, returning zero for a successful run. 68 * Any non-zero return value indicates a tool-specific error during the 69 * execution. 70 * Two streams should be provided, for "expected" output, and for any 71 * error messages. If it is not necessary to distinguish the output, 72 * the same stream may be used for both. 73 * 74 * @apiNote The interpretation of the arguments will be specific to 75 * each tool. 76 * 77 * @param out a stream to which "expected" output should be written 78 * 79 * @param err a stream to which any error messages should be written 80 * 81 * @param args the command-line arguments for the tool 82 * 83 * @return the result of executing the tool. 84 * A return value of 0 means the tool did not encounter any errors; 85 * any other value indicates that at least one error occurred during 86 * execution. 87 * 88 * @throws NullPointerException if any of the arguments are {@code null}, 89 * or if there are any {@code null} values in the {@code args} array 90 */ 91 int run(PrintWriter out, PrintWriter err, String... args); 92 93 /** 94 * Runs an instance of the tool, returning zero for a successful run. 95 * Any non-zero return value indicates a tool-specific error during the 96 * execution. 97 * Two streams should be provided, for "expected" output, and for any 98 * error messages. If it is not necessary to distinguish the output, 99 * the same stream may be used for both. 100 * 101 * @apiNote The interpretation of the arguments will be specific to 102 * each tool. 103 * 104 * @implNote This implementation wraps the {@code out} and {@code err} 105 * streams within {@link PrintWriter}s, and then calls 106 * {@link run(PrintWriter, PrintWriter, String[])}. 107 * 108 * @param out a stream to which "expected" output should be written 109 * 110 * @param err a stream to which any error messages should be written 111 * 112 * @param args the command-line arguments for the tool 113 * 114 * @return the result of executing the tool. 115 * A return value of 0 means the tool did not encounter any errors; 116 * any other value indicates that at least one error occurred during 117 * execution. 118 * 119 * @throws NullPointerException if any of the arguments are {@code null}, 120 * or if there are any {@code null} values in the {@code args} array 121 */ 122 default int run(PrintStream out, PrintStream err, String... args) { 123 Objects.requireNonNull(out); 124 Objects.requireNonNull(err); 125 for (String arg : args) { 126 Objects.requireNonNull(args); 127 } 128 129 PrintWriter outWriter = new PrintWriter(out); 130 PrintWriter errWriter = new PrintWriter(err); 131 try { 132 try { 133 return run(outWriter, errWriter, args); 134 } finally { 135 outWriter.flush(); 136 } 137 } finally { 138 errWriter.flush(); 139 } 140 } 141 142 /** 143 * Returns the first instance of a {@code ToolProvider} with the given name, 144 * as loaded by {@link ServiceLoader} using the system class loader. 145 * 146 * @param name the name of the desired tool provider 147 * 148 * @return an {@code Optional<ToolProvider>} of the first instance found 149 * 150 * @throws NullPointerException if {@code name} is {@code null} 151 */ 152 static Optional<ToolProvider> findFirst(String name) { 153 Objects.requireNonNull(name); 154 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 155 return AccessController.doPrivileged( 156 (PrivilegedAction<Optional<ToolProvider>>) () -> { 157 ServiceLoader<ToolProvider> sl = 158 ServiceLoader.load(ToolProvider.class, systemClassLoader); 159 return StreamSupport.stream(sl.spliterator(), false) 160 .filter(p -> p.name().equals(name)) 161 .findFirst(); 162 }); 163 } 164 } 165