org.wildfly.plugin.tools.cli.ForkedCLIUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wildfly-plugin-tools Show documentation
Show all versions of wildfly-plugin-tools Show documentation
A group of tools for interacting/managing with a WildFly container
The newest version!
/*
* Copyright The WildFly Authors
* SPDX-License-Identifier: Apache-2.0
*/
package org.wildfly.plugin.tools.cli;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jboss.logging.Logger;
/**
* A utility for forking a CLI process.
*
* @author jdenise
* @author James R. Perkins
*/
public class ForkedCLIUtil {
private static final Logger LOGGER = Logger.getLogger(ForkedCLIUtil.class);
private static final Path JAVA_HOME;
private static final String JAVA_CMD;
static {
final String javaHome = System.getProperty("java.home");
JAVA_HOME = Path.of(javaHome);
final Path cmd = JAVA_HOME.resolve("bin").resolve("java");
if (Files.notExists(cmd)) {
JAVA_CMD = "java";
} else {
JAVA_CMD = cmd.toAbsolutePath().toString();
}
}
/**
* Forks a CLI process.
*
* @param artifacts the artifacts to add to the class path
* @param clazz the class to invoke
* @param home the home directory, this is always the first argument
* @param output the path to the output file for the process
* @param args any additional arguments to send add to the call
*
* @throws IOException if an error occurs create the process
*/
public static void fork(final String[] artifacts, final Class> clazz, final Path home,
final Path output, final String... args) throws IOException {
fork(List.of(artifacts), clazz, home, output, args);
}
/**
* Forks a CLI process.
*
* @param artifacts the artifacts to add to the class path
* @param clazz the class to invoke
* @param home the home directory, this is always the first argument
* @param output the path to the output file for the process
* @param args any additional arguments to send add to the call
*
* @throws IOException if an error occurs create the process
*/
public static void fork(final Collection artifacts, final Class> clazz, final Path home,
final Path output, final String... args) throws IOException {
// prepare the classpath
final StringBuilder cp = new StringBuilder();
for (String loc : artifacts) {
cp.append(loc).append(File.pathSeparator);
}
final StringBuilder contextCP = new StringBuilder();
collectCpUrls(Thread.currentThread().getContextClassLoader(), contextCP);
// This happens when running tests, use the process classpath to retrieve the CLIForkedExecutor main class
if (contextCP.length() == 0) {
LOGGER.debug("Re-using process classpath to retrieve Maven plugin classes to fork CLI process.");
cp.append(System.getProperty("java.class.path"));
} else {
cp.append(contextCP);
}
final Path properties = storeSystemProps();
// Create the command
final List argsList = new ArrayList<>();
argsList.add(JAVA_CMD);
argsList.add("-server");
argsList.add("-cp");
argsList.add(cp.toString());
argsList.add(clazz.getName());
argsList.add(home.toString());
argsList.add(output.toString());
argsList.add(properties.toString());
argsList.addAll(List.of(args));
LOGGER.debugf("CLI process command line %s", argsList);
try {
final Process p = new ProcessBuilder(argsList).redirectErrorStream(true).start();
final StringBuilder traces = new StringBuilder();
try (
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
String line = reader.readLine();
while (line != null) {
traces.append(line).append(System.lineSeparator());
line = reader.readLine();
}
if (p.isAlive()) {
try {
p.waitFor();
} catch (InterruptedException e) {
LOGGER.errorf(e, "Interrupted while waiting for forked process %d to terminate.", p.pid());
}
}
}
int exitCode = p.exitValue();
if (exitCode != 0) {
LOGGER.errorf("Error executing CLI: %s", traces);
throw new RuntimeException("CLI execution failed:" + traces);
}
} finally {
Files.deleteIfExists(properties);
}
}
private static Path storeSystemProps() throws IOException {
final Path props;
props = Files.createTempFile("wfbootablejar", "sysprops");
try (BufferedWriter writer = Files.newBufferedWriter(props)) {
System.getProperties().store(writer, "");
}
return props;
}
private static void collectCpUrls(final ClassLoader cl, final StringBuilder buf) {
final ClassLoader parentCl = cl.getParent();
if (parentCl != null) {
collectCpUrls(cl.getParent(), buf);
}
if (cl instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) cl).getURLs()) {
final String file;
try {
file = new File(url.toURI()).getAbsolutePath();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
if (file.startsWith(JAVA_HOME.toString())) {
continue;
}
if (buf.length() > 0) {
buf.append(File.pathSeparatorChar);
}
buf.append(file);
}
}
}
}