package org.radargun.utils;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.radargun.Directories;
import org.radargun.Slave;
import org.radargun.config.Evaluator;
import org.radargun.config.VmArgs;
import org.radargun.logging.LogFactory;
/**
* This utility will start another process after certain lock (args[0]) is released.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
public final class RestartHelper {
private RestartHelper() {}
public static void init() {
if (ArgsHolder.getTempConfigDir() != null) {
Utils.deleteOnExitRecursive(new File(ArgsHolder.getTempConfigDir()));
}
}
public static void spawnSlave(int slaveIndex, UUID nextUuid, String plugin, VmArgs vmArgs, HashMap<String, String> envs) throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.inheritIO();
StringBuilder classpathBuilder = new StringBuilder();
// plugin-specific stuff should be prepended
ArgsHolder.PluginParam pluginParam = ArgsHolder.getPluginParams().get(plugin);
Path tempConfigDir = null;
if (pluginParam != null) {
if (pluginParam.getPath() != null) {
File extDir = new File(pluginParam.getPath());
if (extDir.exists() && extDir.isDirectory()) {
addConfAndLib(classpathBuilder, extDir);
}
}
if (!pluginParam.getConfigFiles().isEmpty()) {
tempConfigDir = Files.createTempDirectory("radargun-" + plugin + "-" + slaveIndex);
classpathBuilder.append(File.pathSeparatorChar).append(tempConfigDir.toAbsolutePath());
for (String configFile : pluginParam.getConfigFiles()) {
File file = new File(configFile);
Files.copy(file.toPath(), tempConfigDir.resolve(file.getName()));
}
}
}
File pluginDir = new File(Directories.PLUGINS_DIR, plugin);
if (pluginDir.exists() && pluginDir.isDirectory()) {
addConfAndLib(classpathBuilder, pluginDir);
}
// if plugin requires something specific on classpath, retrieve that
String extraClassPath = Utils.getPluginProperty(plugin, "classpath");
classpathBuilder.append(File.pathSeparatorChar).append(Evaluator.parseString(extraClassPath));
addConfAndLib(classpathBuilder, Directories.ROOT_DIR);
ListBuilder<String> command = new ListBuilder<>(new ArrayList<String>());
String javaExecutable = Paths.get(System.getProperty("java.home"), "bin", "java").toString();
// we need to run intermediate process that waits until this process ends
command.add(javaExecutable)
.add("-cp").add(Directories.LIB_DIR.toString() + "/*").add(RestartHelper.class.getName())
.add(createLockedTempFile(slaveIndex));
command.add(javaExecutable);
List<String> defaultVmArgs = ArgsHolder.getDefaultVmArgs();
command.addAll(vmArgs.getVmArgs(defaultVmArgs));
// we have to specify log4j configuration explicitly because plugin can define its default config
if (!defaultVmArgs.stream().anyMatch(s -> s.startsWith("-Dlog4j.configuration"))) {
command.add("-Dlog4j.configuration=file://" + Directories.ROOT_DIR + "/conf/log4j.xml");
}
if (!defaultVmArgs.stream().anyMatch(s -> s.startsWith("-Dlog4j.configurationFile"))) {
command.add("-Dlog4j.configurationFile=file://" + Directories.ROOT_DIR + "/conf/log4j2.xml");
}
command.add("-cp").add(classpathBuilder.toString());
command.add(Slave.class.getName());
command.add(ArgsHolder.MASTER).add(ArgsHolder.getMasterHost() + ":" + ArgsHolder.getMasterPort());
command.add(ArgsHolder.SLAVE_INDEX).add(String.valueOf(slaveIndex));
command.add(ArgsHolder.UUID).add(nextUuid.toString());
command.add(ArgsHolder.CURRENT_PLUGIN).add(plugin);
if (tempConfigDir != null) {
command.add(ArgsHolder.TEMP_CONFIG_DIR).add(tempConfigDir.toString());
}
// we have to repeat the args for future generations
for (Map.Entry<String, ArgsHolder.PluginParam> entry : ArgsHolder.getPluginParams().entrySet()) {
ArgsHolder.PluginParam pp = entry.getValue();
if (pp.getPath() != null) {
command.add(ArgsHolder.ADD_PLUGIN).add(pp.getPath());
}
for (String configFile : pp.getConfigFiles()) {
command.add(ArgsHolder.ADD_CONFIG).add(entry.getKey() + ":" + configFile);
}
}
for (String vmArg : defaultVmArgs) {
command.add(ArgsHolder.DEFAULT_VM_ARG).add(vmArg);
}
LogFactory.getLog(RestartHelper.class).info("VM start command = " + command.build().toString());
processBuilder.command(command.build());
processBuilder.environment().putAll(envs);
processBuilder.start();
}
private static String createLockedTempFile(int slaveIndex) throws IOException {
File tempFile = File.createTempFile("restart-" + slaveIndex, ".tmp");
new RandomAccessFile(tempFile, "rw").getChannel().lock();
return tempFile.getAbsolutePath();
}
private static void addConfAndLib(StringBuilder classpathBuilder, File parentDir) {
if (classpathBuilder.length() > 0) {
classpathBuilder.append(File.pathSeparatorChar);
}
classpathBuilder.append(parentDir).append(File.separatorChar).append("conf/");
for (File f : new File(parentDir, "lib").listFiles()) {
classpathBuilder.append(File.pathSeparatorChar);
classpathBuilder.append(f.getAbsolutePath());
}
}
public static void main(String[] args) throws IOException {
File file = new File(args[0]);
System.out.printf("%s: Waiting for lock on file %s%n", timestamp(), args[0]);
FileLock lock = new RandomAccessFile(file, "rw").getChannel().lock();
System.out.printf("%s: Lock acquired%n", timestamp());
lock.release();
file.delete();
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.inheritIO();
ArrayList<String> command = new ArrayList<>();
for (int i = 1; i < args.length; ++i) command.add(args[i]);
processBuilder.command(command);
processBuilder.start();
System.exit(0);
}
private static String timestamp() {
return new SimpleDateFormat("HH:mm:ss,S").format(new Date());
}
}