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 60 * used on 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 * 71 * Two streams should be provided, for "expected" output, and for any 72 * error messages. If it is not necessary to distinguish the output, 73 * the same stream may be used for both. 74 * 75 * @apiNote The interpretation of the arguments will be specific to 76 * each tool. 77 * 78 * @param out a stream to which "expected" output should be written 79 * 80 * @param err a stream to which any error messages should be written 81 * 82 * @param args the command-line arguments for the tool 83 * 84 * @return the result of executing the tool. 85 * A return value of 0 means the tool did not encounter any errors; 86 * any other value indicates that at least one error occurred 87 * during execution. 88 * 89 * @throws NullPointerException if any of the arguments are {@code null}, 90 * or if there are any {@code null} values in the {@code args} 91 * array 92 */ 93 int run(PrintWriter out, PrintWriter err, String... args); 94 95 /** 96 * Runs an instance of the tool, returning zero for a successful run. 97 * Any non-zero return value indicates a tool-specific error during the 98 * execution. 99 * 100 * Two streams should be provided, for "expected" output, and for any 101 * error messages. If it is not necessary to distinguish the output, 102 * the same stream may be used for both. 103 * 104 * @apiNote The interpretation of the arguments will be specific to 105 * each tool. 106 * 107 * @implNote This implementation wraps the {@code out} and {@code err} 108 * streams within {@link PrintWriter}s, and then calls 109 * {@link #run(PrintWriter, PrintWriter, String[])}. 110 * 111 * @param out a stream to which "expected" output should be written 112 * 113 * @param err a stream to which any error messages should be written 114 * 115 * @param args the command-line arguments for the tool 116 * 117 * @return the result of executing the tool. 118 * A return value of 0 means the tool did not encounter any errors; 119 * any other value indicates that at least one error occurred 120 * during execution. 121 * 122 * @throws NullPointerException if any of the arguments are {@code null}, 123 * or if there are any {@code null} values in the {@code args} 124 * array 125 */ 126 default int run(PrintStream out, PrintStream err, String... args) { 127 Objects.requireNonNull(out); 128 Objects.requireNonNull(err); 129 for (String arg : args) { 130 Objects.requireNonNull(args); 131 } 132 133 PrintWriter outWriter = new PrintWriter(out); 134 PrintWriter errWriter = new PrintWriter(err); 135 try { 136 try { 137 return run(outWriter, errWriter, args); 138 } finally { 139 outWriter.flush(); 140 } 141 } finally { 142 errWriter.flush(); 143 } 144 } 145 146 /** 147 * Returns the first instance of a {@code ToolProvider} with the given name, 148 * as loaded by {@link ServiceLoader} using the system class loader. 149 * 150 * @param name the name of the desired tool provider 151 * 152 * @return an {@code Optional<ToolProvider>} of the first instance found 153 * 154 * @throws NullPointerException if {@code name} is {@code null} 155 */ 156 static Optional<ToolProvider> findFirst(String name) { 157 Objects.requireNonNull(name); 158 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 159 return AccessController.doPrivileged( 160 (PrivilegedAction<Optional<ToolProvider>>) () -> { 161 ServiceLoader<ToolProvider> sl = 162 ServiceLoader.load(ToolProvider.class, systemClassLoader); 163 return StreamSupport.stream(sl.spliterator(), false) 164 .filter(p -> p.name().equals(name)) 165 .findFirst(); 166 }); 167 } 168 } 169