8159855: Create an SPI for tools
Reviewed-by: mchung
This commit is contained in:
parent
2d52d60a1e
commit
0f9a011475
165
jdk/src/java.base/share/classes/java/util/spi/ToolProvider.java
Normal file
165
jdk/src/java.base/share/classes/java/util/spi/ToolProvider.java
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 java.util.spi;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* An interface for command-line tools to provide a way to
|
||||
* be invoked without necessarily starting a new VM.
|
||||
*
|
||||
* <p>Tool providers are normally located using the service-provider
|
||||
* loading facility defined by {@link ServiceLoader}.
|
||||
* Each provider must provide a name, and a method to run
|
||||
* an instance of the corresponding tool. When a tool is run,
|
||||
* it will be provided with an array of string arguments, and a
|
||||
* pair of streams: one for normal (or expected) output and the other
|
||||
* for reporting any errors that may occur.
|
||||
* The interpretation of the string arguments will normally be defined by
|
||||
* each individual tool provider, but will generally correspond to the
|
||||
* arguments that could be provided to the tool when invoking the tool
|
||||
* from the command line.
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public interface ToolProvider {
|
||||
/**
|
||||
* 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".
|
||||
*
|
||||
* @return the name of this tool provider
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param out a stream to which "expected" output should be written
|
||||
*
|
||||
* @param err a stream to which any error messages should be written
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @throws NullPointerException if any of the arguments are {@code null},
|
||||
* or if there are any {@code null} values in the {@code args} array
|
||||
*/
|
||||
int run(PrintWriter out, PrintWriter err, String... args);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @implNote This implementation wraps the {@code out} and {@code err}
|
||||
* streams within {@link PrintWriter}s, and then calls
|
||||
* {@link run(PrintWriter, PrintWriter, String[])}.
|
||||
*
|
||||
* @param out a stream to which "expected" output should be written
|
||||
*
|
||||
* @param err a stream to which any error messages should be written
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @throws NullPointerException if any of the arguments are {@code null},
|
||||
* 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);
|
||||
Objects.requireNonNull(err);
|
||||
for (String arg : args) {
|
||||
Objects.requireNonNull(args);
|
||||
}
|
||||
|
||||
PrintWriter outWriter = new PrintWriter(out);
|
||||
PrintWriter errWriter = new PrintWriter(err);
|
||||
try {
|
||||
try {
|
||||
return run(outWriter, errWriter, args);
|
||||
} finally {
|
||||
outWriter.flush();
|
||||
}
|
||||
} finally {
|
||||
errWriter.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first instance of a {@code ToolProvider} with the given name,
|
||||
* as loaded by {@link ServiceLoader} using the system class loader.
|
||||
*
|
||||
* @param name the name of the desired tool provider
|
||||
*
|
||||
* @return an {@code Optional<ToolProvider>} of the first instance found
|
||||
*
|
||||
* @throws NullPointerException if {@code name} is {@code null}
|
||||
*/
|
||||
static Optional<ToolProvider> findFirst(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
|
||||
return AccessController.doPrivileged(
|
||||
(PrivilegedAction<Optional<ToolProvider>>) () -> {
|
||||
ServiceLoader<ToolProvider> sl =
|
||||
ServiceLoader.load(ToolProvider.class, systemClassLoader);
|
||||
return StreamSupport.stream(sl.spliterator(), false)
|
||||
.filter(p -> p.name().equals(name))
|
||||
.findFirst();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,7 @@ module java.base {
|
||||
uses java.util.spi.ResourceBundleControlProvider;
|
||||
uses java.util.spi.ResourceBundleProvider;
|
||||
uses java.util.spi.TimeZoneNameProvider;
|
||||
uses java.util.spi.ToolProvider;
|
||||
uses javax.security.auth.spi.LoginModule;
|
||||
|
||||
|
||||
|
105
jdk/test/java/util/spi/ToolProviderTest.java
Normal file
105
jdk/test/java/util/spi/ToolProviderTest.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8159855
|
||||
* @summary test ToolProvider SPI
|
||||
* @run main/othervm ToolProviderTest
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.spi.ToolProvider;
|
||||
|
||||
public class ToolProviderTest {
|
||||
public static void main(String... args) throws Exception {
|
||||
ToolProviderTest t = new ToolProviderTest();
|
||||
t.run();
|
||||
}
|
||||
|
||||
void run() throws Exception {
|
||||
initServices();
|
||||
|
||||
System.out.println("test without security manager present:");
|
||||
test();
|
||||
|
||||
System.setSecurityManager(new SecurityManager());
|
||||
|
||||
System.out.println("test with security manager present:");
|
||||
test();
|
||||
}
|
||||
|
||||
private void test() throws Exception {
|
||||
ToolProvider testProvider = ToolProvider.findFirst("test").get();
|
||||
int rc = testProvider.run(System.out, System.err, "hello test");
|
||||
if (rc != 0) {
|
||||
throw new Exception("unexpected exit code: " + rc);
|
||||
}
|
||||
}
|
||||
|
||||
private void initServices() throws IOException {
|
||||
Path testClasses = Paths.get(System.getProperty("test.classes"));
|
||||
Path services = testClasses.resolve(Paths.get("META-INF", "services"));
|
||||
Files.createDirectories(services);
|
||||
Files.write(services.resolve(ToolProvider.class.getName()),
|
||||
Arrays.asList(TestProvider.class.getName()));
|
||||
}
|
||||
|
||||
public static class TestProvider implements ToolProvider {
|
||||
public TestProvider() {
|
||||
checkPrivileges();
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
public int run(PrintWriter out, PrintWriter err, String... args) {
|
||||
out.println("Test: " + Arrays.toString(args));
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void checkPrivileges() {
|
||||
boolean haveSecurityManager = (System.getSecurityManager() != null);
|
||||
try {
|
||||
// validate appropriate privileges by checking access to a
|
||||
// system property
|
||||
System.getProperty("java.home");
|
||||
if (haveSecurityManager) {
|
||||
throw new Error("exception exception not thrown");
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
if (!haveSecurityManager) {
|
||||
throw new Error("unexpected exception: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user