JMXExecutor.java revision 2224:2a8815d86b93
151829Smdodd/* 251829Smdodd * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 351829Smdodd * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 451829Smdodd * 551829Smdodd * This code is free software; you can redistribute it and/or modify it 651829Smdodd * under the terms of the GNU General Public License version 2 only, as 751829Smdodd * published by the Free Software Foundation. 851829Smdodd * 951829Smdodd * This code is distributed in the hope that it will be useful, but WITHOUT 1051829Smdodd * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1151829Smdodd * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1251829Smdodd * version 2 for more details (a copy is included in the LICENSE file that 1351829Smdodd * accompanied this code). 1451829Smdodd * 1551829Smdodd * You should have received a copy of the GNU General Public License version 1651829Smdodd * 2 along with this work; if not, write to the Free Software Foundation, 1751829Smdodd * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1851829Smdodd * 1951829Smdodd * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2051829Smdodd * or visit www.oracle.com if you need additional information or have any 2151829Smdodd * questions. 2251829Smdodd */ 2351829Smdodd 2451829Smdoddpackage jdk.test.lib.dcmd; 2551829Smdodd 2651829Smdoddimport jdk.test.lib.process.OutputAnalyzer; 2751829Smdodd 2851829Smdoddimport javax.management.*; 29119418Sobrienimport javax.management.remote.JMXConnector; 30119418Sobrienimport javax.management.remote.JMXConnectorFactory; 31119418Sobrienimport javax.management.remote.JMXServiceURL; 3251829Smdodd 3351829Smdoddimport java.io.IOException; 34241611Spluknetimport java.io.PrintWriter; 3551829Smdoddimport java.io.StringWriter; 36117126Sscottl 37117126Sscottlimport java.lang.management.ManagementFactory; 3851829Smdodd 3951829Smdoddimport java.util.HashMap; 4051829Smdodd 4151829Smdodd/** 4251829Smdodd * Executes Diagnostic Commands on the target VM (specified by a host/port combination or a full JMX Service URL) using 4351829Smdodd * the JMX interface. If the target is not the current VM, the JMX Remote interface must be enabled beforehand. 4451829Smdodd */ 45135260Sphkpublic class JMXExecutor extends CommandExecutor { 4651829Smdodd 4751829Smdodd private final MBeanServerConnection mbs; 4851829Smdodd 4951829Smdodd /** 5051829Smdodd * Instantiates a new JMXExecutor targeting the current VM 5151829Smdodd */ 5251829Smdodd public JMXExecutor() { 5351829Smdodd super(); 5451829Smdodd mbs = ManagementFactory.getPlatformMBeanServer(); 5551829Smdodd } 5651829Smdodd 5751829Smdodd /** 5851829Smdodd * Instantiates a new JMXExecutor targeting the VM indicated by the given host/port combination or a full JMX 5951829Smdodd * Service URL 6051829Smdodd * 6151829Smdodd * @param target a host/port combination on the format "host:port" or a full JMX Service URL of the target VM 62122340Simp */ 6351829Smdodd public JMXExecutor(String target) { 64122340Simp String urlStr; 6551829Smdodd 6651829Smdodd if (target.matches("^\\w[\\w\\-]*(\\.[\\w\\-]+)*:\\d+$")) { 6751829Smdodd /* Matches "hostname:port" */ 6851829Smdodd urlStr = String.format("service:jmx:rmi:///jndi/rmi://%s/jmxrmi", target); 6951829Smdodd } else if (target.startsWith("service:")) { 7051829Smdodd urlStr = target; 7151829Smdodd } else { 7251829Smdodd throw new IllegalArgumentException("Could not recognize target string: " + target); 7351829Smdodd } 7451829Smdodd 7551829Smdodd try { 7651829Smdodd JMXServiceURL url = new JMXServiceURL(urlStr); 7751829Smdodd JMXConnector c = JMXConnectorFactory.connect(url, new HashMap<>()); 7851829Smdodd mbs = c.getMBeanServerConnection(); 7951829Smdodd } catch (IOException e) { 8051829Smdodd throw new CommandExecutorException("Could not initiate connection to target: " + target, e); 8151829Smdodd } 8251829Smdodd } 8351829Smdodd 8451829Smdodd protected OutputAnalyzer executeImpl(String cmd) throws CommandExecutorException { 8551829Smdodd String stdout = ""; 8651829Smdodd String stderr = ""; 8751829Smdodd 8851829Smdodd String[] cmdParts = cmd.split(" ", 2); 89122340Simp String operation = commandToMethodName(cmdParts[0]); 90122340Simp Object[] dcmdArgs = produceArguments(cmdParts); 91140467Simp String[] signature = {String[].class.getName()}; 92140467Simp 93140467Simp ObjectName beanName = getMBeanName(); 9451829Smdodd 9551829Smdodd try { 9651829Smdodd stdout = (String) mbs.invoke(beanName, operation, dcmdArgs, signature); 9751829Smdodd } 9851829Smdodd 9951829Smdodd /* Failures on the "local" side, the one invoking the command. */ 10051829Smdodd catch (ReflectionException e) { 10151829Smdodd Throwable cause = e.getCause(); 10251829Smdodd if (cause instanceof NoSuchMethodException) { 10351829Smdodd /* We want JMXExecutor to match the behavior of the other CommandExecutors */ 10451829Smdodd String message = "Unknown diagnostic command: " + operation; 10551829Smdodd stderr = exceptionTraceAsString(new IllegalArgumentException(message, e)); 10651829Smdodd } else { 10751829Smdodd rethrowExecutorException(operation, dcmdArgs, e); 10851829Smdodd } 10951829Smdodd } 11051829Smdodd 11151829Smdodd /* Failures on the "local" side, the one invoking the command. */ 11251829Smdodd catch (InstanceNotFoundException | IOException e) { 11351829Smdodd rethrowExecutorException(operation, dcmdArgs, e); 11451829Smdodd } 11551829Smdodd 11651829Smdodd /* Failures on the remote side, the one executing the invoked command. */ 11751829Smdodd catch (MBeanException e) { 11851829Smdodd stdout = exceptionTraceAsString(e); 11951829Smdodd } 120122361Simp 121140467Simp return new OutputAnalyzer(stdout, stderr); 12251829Smdodd } 123140467Simp 124140467Simp private void rethrowExecutorException(String operation, Object[] dcmdArgs, 125140467Simp Exception e) throws CommandExecutorException { 126140467Simp String message = String.format("Could not invoke: %s %s", operation, 12751829Smdodd String.join(" ", (String[]) dcmdArgs[0])); 12851829Smdodd throw new CommandExecutorException(message, e); 12951829Smdodd } 13051829Smdodd 131140467Simp private ObjectName getMBeanName() throws CommandExecutorException { 132140467Simp String MBeanName = "com.sun.management:type=DiagnosticCommand"; 133140467Simp 134140467Simp try { 13551829Smdodd return new ObjectName(MBeanName); 13651829Smdodd } catch (MalformedObjectNameException e) { 13751829Smdodd String message = "MBean not found: " + MBeanName; 13851829Smdodd throw new CommandExecutorException(message, e); 139140467Simp } 140140467Simp } 141140467Simp 142140467Simp private Object[] produceArguments(String[] cmdParts) { 14351829Smdodd Object[] dcmdArgs = {new String[0]}; /* Default: No arguments */ 14451829Smdodd 14551829Smdodd if (cmdParts.length == 2) { 14651829Smdodd dcmdArgs[0] = cmdParts[1].split(" "); 147241589Sjhb } 14851829Smdodd return dcmdArgs; 14951829Smdodd } 15051829Smdodd 15151829Smdodd /** 15251829Smdodd * Convert from diagnostic command to MBean method name 15351829Smdodd * 15451829Smdodd * Examples: 15551829Smdodd * help --> help 15651829Smdodd * VM.version --> vmVersion 15751829Smdodd * VM.command_line --> vmCommandLine 15851829Smdodd */ 15951829Smdodd private static String commandToMethodName(String cmd) { 160140467Simp String operation = ""; 16151829Smdodd boolean up = false; /* First letter is to be lower case */ 162112782Smdodd 163183678Simp /* 164112782Smdodd * If a '.' or '_' is encountered it is not copied, 165112782Smdodd * instead the next character will be converted to upper case 166112782Smdodd */ 167112782Smdodd for (char c : cmd.toCharArray()) { 168112782Smdodd if (('.' == c) || ('_' == c)) { 169112782Smdodd up = true; 170112782Smdodd } else if (up) { 171112782Smdodd operation = operation.concat(Character.toString(c).toUpperCase()); 172112782Smdodd up = false; 173112782Smdodd } else { 174241589Sjhb operation = operation.concat(Character.toString(c).toLowerCase()); 175241589Sjhb } 176112782Smdodd } 17751829Smdodd 17851829Smdodd return operation; 17951829Smdodd } 18051829Smdodd 18151829Smdodd private static String exceptionTraceAsString(Throwable cause) { 18251829Smdodd StringWriter sw = new StringWriter(); 18351829Smdodd cause.printStackTrace(new PrintWriter(sw)); 18451829Smdodd return sw.toString(); 18551829Smdodd } 18651829Smdodd 18751829Smdodd} 18851829Smdodd