| /* |
| * Copyright (c) 2015, 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. |
| */ |
| |
| package jdk.test.lib.dcmd; |
| |
| import jdk.test.lib.process.OutputAnalyzer; |
| |
| import javax.management.*; |
| import javax.management.remote.JMXConnector; |
| import javax.management.remote.JMXConnectorFactory; |
| import javax.management.remote.JMXServiceURL; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| |
| import java.lang.management.ManagementFactory; |
| |
| import java.util.HashMap; |
| |
| /** |
| * Executes Diagnostic Commands on the target VM (specified by a host/port combination or a full JMX Service URL) using |
| * the JMX interface. If the target is not the current VM, the JMX Remote interface must be enabled beforehand. |
| */ |
| public class JMXExecutor extends CommandExecutor { |
| |
| private final MBeanServerConnection mbs; |
| |
| /** |
| * Instantiates a new JMXExecutor targeting the current VM |
| */ |
| public JMXExecutor() { |
| super(); |
| mbs = ManagementFactory.getPlatformMBeanServer(); |
| } |
| |
| /** |
| * Instantiates a new JMXExecutor targeting the VM indicated by the given host/port combination or a full JMX |
| * Service URL |
| * |
| * @param target a host/port combination on the format "host:port" or a full JMX Service URL of the target VM |
| */ |
| public JMXExecutor(String target) { |
| String urlStr; |
| |
| if (target.matches("^\\w[\\w\\-]*(\\.[\\w\\-]+)*:\\d+$")) { |
| /* Matches "hostname:port" */ |
| urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s/jmxrmi", target); |
| } else if (target.startsWith("service:")) { |
| urlStr = target; |
| } else { |
| throw new IllegalArgumentException("Could not recognize target string: " + target); |
| } |
| |
| try { |
| JMXServiceURL url = new JMXServiceURL(urlStr); |
| JMXConnector c = JMXConnectorFactory.connect(url, new HashMap<>()); |
| mbs = c.getMBeanServerConnection(); |
| } catch (IOException e) { |
| throw new CommandExecutorException("Could not initiate connection to target: " + target, e); |
| } |
| } |
| |
| protected OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException { |
| String stdout = ""; |
| String stderr = ""; |
| |
| String[] cmdParts = cmd.split(" ", 2); |
| String operation = commandToMethodName(cmdParts[0]); |
| Object[] dcmdArgs = produceArguments(cmdParts); |
| String[] signature = {String[].class.getName()}; |
| |
| ObjectName beanName = getMBeanName(); |
| |
| try { |
| stdout = (String) mbs.invoke(beanName, operation, dcmdArgs, signature); |
| } |
| |
| /* Failures on the "local" side, the one invoking the command. */ |
| catch (ReflectionException e) { |
| Throwable cause = e.getCause(); |
| if (cause instanceof NoSuchMethodException) { |
| /* We want JMXExecutor to match the behavior of the other CommandExecutors */ |
| String message = "Unknown diagnostic command: " + operation; |
| stderr = exceptionTraceAsString(new IllegalArgumentException(message, e)); |
| } else { |
| rethrowExecutorException(operation, dcmdArgs, e); |
| } |
| } |
| |
| /* Failures on the "local" side, the one invoking the command. */ |
| catch (InstanceNotFoundException | IOException e) { |
| rethrowExecutorException(operation, dcmdArgs, e); |
| } |
| |
| /* Failures on the remote side, the one executing the invoked command. */ |
| catch (MBeanException e) { |
| stdout = exceptionTraceAsString(e); |
| } |
| |
| return new OutputAnalyzer(stdout, stderr); |
| } |
| |
| private void rethrowExecutorException(String operation, Object[] dcmdArgs, |
| Exception e) throws CommandExecutorException { |
| String message = String.format("Could not invoke: %s %s", operation, |
| String.join(" ", (String[]) dcmdArgs[0])); |
| throw new CommandExecutorException(message, e); |
| } |
| |
| private ObjectName getMBeanName() throws CommandExecutorException { |
| String MBeanName = "com.sun.management:type=DiagnosticCommand"; |
| |
| try { |
| return new ObjectName(MBeanName); |
| } catch (MalformedObjectNameException e) { |
| String message = "MBean not found: " + MBeanName; |
| throw new CommandExecutorException(message, e); |
| } |
| } |
| |
| private Object[] produceArguments(String[] cmdParts) { |
| Object[] dcmdArgs = {new String[0]}; /* Default: No arguments */ |
| |
| if (cmdParts.length == 2) { |
| dcmdArgs[0] = cmdParts[1].split(" "); |
| } |
| return dcmdArgs; |
| } |
| |
| /** |
| * Convert from diagnostic command to MBean method name |
| * |
| * Examples: |
| * help --> help |
| * VM.version --> vmVersion |
| * VM.command_line --> vmCommandLine |
| */ |
| private static String commandToMethodName(String cmd) { |
| String operation = ""; |
| boolean up = false; /* First letter is to be lower case */ |
| |
| /* |
| * If a '.' or '_' is encountered it is not copied, |
| * instead the next character will be converted to upper case |
| */ |
| for (char c : cmd.toCharArray()) { |
| if (('.' == c) || ('_' == c)) { |
| up = true; |
| } else if (up) { |
| operation = operation.concat(Character.toString(c).toUpperCase()); |
| up = false; |
| } else { |
| operation = operation.concat(Character.toString(c).toLowerCase()); |
| } |
| } |
| |
| return operation; |
| } |
| |
| private static String exceptionTraceAsString(Throwable cause) { |
| StringWriter sw = new StringWriter(); |
| cause.printStackTrace(new PrintWriter(sw)); |
| return sw.toString(); |
| } |
| |
| } |