/** * Licensed to JumpMind Inc under one or more contributor * license agreements. See the NOTICE file distributed * with this work for additional information regarding * copyright ownership. JumpMind Inc licenses this file * to you under the GNU General Public License, version 3.0 (GPLv3) * (the "License"); you may not use this file except in compliance * with the License. * * You should have received a copy of the GNU General Public License, * version 3.0 (GPLv3) along with this library; if not, see * <http://www.gnu.org/licenses/>. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jumpmind.symmetric; import static org.apache.commons.lang.StringUtils.isBlank; import java.lang.reflect.Constructor; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.jumpmind.properties.TypedProperties; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.io.data.CsvUtils; public class JmxCommand extends AbstractCommandLauncher { private static final String OPTION_LISTBEANS = "listbeans"; private static final String OPTION_LISTMETHODS = "listmethods"; private static final String OPTION_BEAN = "bean"; private static final String OPTION_METHOD = "method"; private static final String OPTION_ARGS = "args"; private static final String OPTION_ARGS_DELIM = "args-delimiter"; public JmxCommand() { super("jmx", "", "Jmx.Option."); } public static void main(String[] args) { new JmxCommand().execute(args); } @Override protected void printHelp(CommandLine cmd, Options options) { System.out.println(app + " version " + Version.version()); System.out .println("Provides a command line interface to execute JMX methods for a specific engine"); super.printHelp(cmd, options); } @Override protected void buildOptions(Options options) { super.buildOptions(options); addOption(options, null, OPTION_LISTBEANS, false); addOption(options, null, OPTION_LISTMETHODS, false); addOption(options, null, OPTION_BEAN, true); addOption(options, null, OPTION_METHOD, true); addOption(options, null, OPTION_ARGS, true); addOption(options, null, OPTION_ARGS_DELIM, true); } @Override protected boolean printHelpIfNoOptionsAreProvided() { return true; } @Override protected boolean requiresPropertiesFile() { return true; } @Override protected boolean executeWithOptions(final CommandLine line) throws Exception { if (line.hasOption(OPTION_LISTBEANS)) { execute(new IJmxTemplate<Object>() { @Override public Object execute(String engineName, MBeanServerConnection mbeanConn) throws Exception { Set<ObjectName> beanSet = mbeanConn.queryNames(null, null); for (ObjectName objectName : beanSet) { if (objectName.getDomain().startsWith( "org.jumpmind.symmetric." + engineName)) { System.out.println(objectName.toString()); } } return null; } }); } else if (line.hasOption(OPTION_LISTMETHODS) || line.hasOption(OPTION_METHOD)) { if (line.hasOption(OPTION_BEAN)) { execute(new IJmxTemplate<Object>() { @Override public Object execute(String engineName, MBeanServerConnection mbeanConn) throws Exception { String beanName = line.getOptionValue(OPTION_BEAN); MBeanInfo info = mbeanConn.getMBeanInfo(new ObjectName(beanName)); if (info != null) { if (line.hasOption(OPTION_LISTMETHODS)) { MBeanOperationInfo[] operations = info.getOperations(); Map<String, MBeanOperationInfo> orderedMap = new TreeMap<String, MBeanOperationInfo>(); for (MBeanOperationInfo methodInfo : operations) { orderedMap.put(methodInfo.getName(), methodInfo); } for (MBeanOperationInfo methodInfo : orderedMap.values()) { System.out.print(methodInfo.getName() + "(" ); MBeanParameterInfo[] params = methodInfo.getSignature(); int index = 0; for (MBeanParameterInfo p : params) { if (index > 0) { System.out.print(", "); } System.out.print(p.getType() + " " + p.getName()); index++; } System.out.print(")"); if (methodInfo.getReturnType() != null && !methodInfo.getReturnType().equals("void")) { System.out.print(" : " + methodInfo.getReturnType()); } System.out.println(); } } else if (line.hasOption(OPTION_METHOD)) { String argsDelimiter = line.getOptionValue(OPTION_ARGS_DELIM); if (isBlank(argsDelimiter)) { argsDelimiter = ","; } else { argsDelimiter = argsDelimiter.trim(); } String methodName = line.getOptionValue(OPTION_METHOD); String[] args = null; if (line.hasOption(OPTION_ARGS)) { String argLine = line.getOptionValue(OPTION_ARGS); args = argsDelimiter == "," ? CsvUtils.tokenizeCsvData(argLine) : argLine.split(argsDelimiter);; } else { args = new String[0]; } MBeanOperationInfo[] operations = info.getOperations(); for (MBeanOperationInfo methodInfo : operations) { MBeanParameterInfo[] paramInfos = methodInfo.getSignature(); if (methodInfo.getName().equals(methodName) && paramInfos.length == args.length) { String[] signature = new String[args.length]; Object[] objArgs = new Object[args.length]; int index = 0; for (MBeanParameterInfo paramInfo : paramInfos) { signature[index] = paramInfo.getType(); if (!paramInfo.getType().equals(String.class.getName())) { Class<?> clazz = Class.forName(paramInfo.getType()); Constructor<?> constructor = clazz .getConstructor(String.class); objArgs[index] = constructor .newInstance(args[index]); } else { objArgs[index] = args[index]; } index++; } Object returnValue = mbeanConn.invoke(new ObjectName( beanName), methodName, objArgs, signature); if (methodInfo.getReturnType() != null && !methodInfo.getReturnType().equals("void")) { System.out.println(returnValue); } System.exit(0); } } System.out.println("ERROR: Could not locate a JMX method named: " + methodName + " with " + args.length + " arguments on bean: " + beanName); System.exit(1); return null; } } else { System.out .println("ERROR: Could not locate a JMX bean with the name of: " + beanName); System.exit(1); } return null; } }); } else { System.out.println("ERROR: Must specifiy the --bean option."); System.exit(1); } } else { return false; } return true; } protected <T> T execute(IJmxTemplate<T> template) throws Exception { String host = "localhost"; String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + System.getProperty("jmx.agent.port", "31418") + "/jmxrmi"; JMXServiceURL serviceUrl = new JMXServiceURL(url); JMXConnector jmxConnector = JMXConnectorFactory.connect(serviceUrl, null); TypedProperties properties = getTypedProperties(); String engineName = properties.get(ParameterConstants.ENGINE_NAME, "unknown"); try { MBeanServerConnection mbeanConn = jmxConnector.getMBeanServerConnection(); return template.execute(engineName, mbeanConn); } finally { jmxConnector.close(); } } interface IJmxTemplate<T> { public T execute(String engineName, MBeanServerConnection mbeanConn) throws Exception; } }