package org.radargun.service;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import org.radargun.config.Property;
import org.radargun.logging.Log;
import org.radargun.logging.LogFactory;
import org.radargun.traits.JmxConnectionProvider;
import org.radargun.traits.ProvidesTrait;
/**
* @author Radim Vansa <rvansa@redhat.com>
*/
public class JavaProcessService extends ProcessService {
private final Log log = LogFactory.getLog(getClass());
private static final String CONNECTOR_ADDRESS =
"com.sun.management.jmxremote.localConnectorAddress";
protected static final String JAVA_HOME = "JAVA_HOME";
protected static final String JAVA_OPTS = "JAVA_OPTS";
@Property(doc = "Java binary used to start the server. Default is ${env.JAVA_HOME}.")
protected String java = System.getenv(JAVA_HOME);
@Property(doc = "Extra Java options used. Default is none.")
protected String javaOpts;
@Property(doc = "Connect to the process and retrieve JMX connection. Default is true.")
protected boolean jmxConnectionEnabled = true;
@ProvidesTrait
public JmxConnectionProvider createConnectionProvider() {
try {
Class.forName("com.sun.tools.attach.AttachNotSupportedException");
} catch (ClassNotFoundException e) {
log.warn("Cannot load JDK classes from JAVA_HOME/lib/tools.jar. " +
"Please make sure that tools.jar is on the classpath by " +
"adding the following entry to plugin.properties:\n\n" +
"classpath ${env.JAVA_HOME}/lib/tools.jar\n");
String classPath = System.getProperty("java.class.path");
if (classPath.contains("tools.jar")) {
Optional<String> tools = Arrays.asList(classPath.split(File.pathSeparator)).stream().filter(s -> s.contains("tools.jar")).findFirst();
if (tools.isPresent()) {
if (new File(tools.get()).exists()) {
log.warnf("Classpath contains tools.jar (%s), that file exists but the classes are not there.", tools.get());
} else {
log.warnf("Classpath contains tools.jar (%s) but the file does not exist", tools.get());
}
} else {
log.warn("Classpath is " + classPath);
}
}
return null;
}
if (!jmxConnectionEnabled) {
return null;
}
return new JmxConnectionProvider() {
@Override
public JMXConnector getConnector() {
String pid = getJavaPIDs();
if (pid == null) return null;
try {
VirtualMachine vm;
try {
vm = VirtualMachine.attach(pid);
} catch (AttachNotSupportedException e) {
log.error("Cannot attach to machine " + pid, e);
return null;
}
String connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
if (connectorAddress == null) {
String agent = vm.getSystemProperties().getProperty("java.home") +
File.separator + "lib" + File.separator +
"management-agent.jar";
try {
vm.loadAgent(agent);
connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
} catch (Exception e) {
log.error("Cannot load management agent into target VM.");
}
}
if (connectorAddress == null) {
log.error("Failed to retrieve connector address.");
return null;
}
return JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress));
} catch (NumberFormatException e) {
log.error("Failed to parse service JVM PID");
return null;
} catch (IOException e) {
log.error("Failed to connect JVM", e);
return null;
}
}
};
}
public String getJavaPIDs() {
if (lifecycle.getPid() != null) {
ProcessBuilder pb = new ProcessBuilder()
.command(Arrays.asList(getCommandPrefix() + "jvms" + getCommandSuffix(), lifecycle.getPid()));
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
try {
Process process = pb.start();
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null)
sb.append(line);
}
return sb.toString().split(" ")[0];
} catch (IOException e) {
log.error("Failed to read JVM PIDs", e);
return null;
}
}
return null;
}
@Override
public Map<String, String> getEnvironment() {
Map<String, String> envs = new HashMap(super.getEnvironment());
if (java != null) {
if (envs.containsKey(JAVA_HOME)) {
log.warn("Overwriting " + JAVA_HOME + ": " + envs.get(JAVA_HOME) + " with " + java);
}
envs.put(JAVA_HOME, java);
}
if (javaOpts != null) {
if (envs.containsKey(JAVA_OPTS)) {
log.warn("Overwriting " + JAVA_OPTS + ": " + envs.get(JAVA_OPTS) + " with " + javaOpts);
}
envs.put(JAVA_OPTS, javaOpts);
}
return envs;
}
}