From 6c327649908e6d127340076732d7c800f68cee83 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 18 May 2012 16:46:31 +0200 Subject: [PATCH 1/2] Embed the source of jmxclient into akka-kernel. See #2079 * Found source code here https://archive-crawler.svn.sourceforge.net * Included org.archive.jmx.Client in akka-kernel * Modified akka-cluster script * Removed dependency to jmxclient from build * Tested from dist with kernel app with cluster settings --- akka-kernel/src/main/dist/bin/akka-cluster | 18 +- .../src/main/java/org/archive/jmx/Client.java | 781 ++++++++++++++++++ project/AkkaBuild.scala | 3 +- 3 files changed, 792 insertions(+), 10 deletions(-) create mode 100644 akka-kernel/src/main/java/org/archive/jmx/Client.java diff --git a/akka-kernel/src/main/dist/bin/akka-cluster b/akka-kernel/src/main/dist/bin/akka-cluster index ecca52fa9b..7bb3a670da 100755 --- a/akka-kernel/src/main/dist/bin/akka-cluster +++ b/akka-kernel/src/main/dist/bin/akka-cluster @@ -3,7 +3,6 @@ # ============== Akka Cluster Administration Tool ============== # # This script is meant to be used from within the Akka distribution. -# Requires setting $AKKA_HOME to the root of the distribution. # # Add these options to the sbt or startup script: # java \ @@ -15,9 +14,12 @@ # FIXME support authentication? if so add: -Dcom.sun.management.jmxremote.password.file= AND tweak this script to support it (arg need 'user:passwd' instead of '-') -# NOTE: The 'cmdline-jmxclient' JAR is available as part of the Akka distribution. -# Provided by Typesafe Maven Repository: http://repo.typesafe.com/typesafe/releases/cmdline-jmxclient. -JMX_CLIENT="java -jar $AKKA_HOME/lib/akka/cmdline-jmxclient-0.10.3.jar -" +declare AKKA_HOME="$(cd "$(cd "$(dirname "$0")"; pwd -P)"/..; pwd)" + +[ -n "$JMX_CLIENT_CLASSPATH" ] || JMX_CLIENT_CLASSPATH="$AKKA_HOME/lib/akka/akka-kernel-*" + +# NOTE: The 'cmdline-jmxclient' is available as part of the Akka distribution. +JMX_CLIENT="java -cp $JMX_CLIENT_CLASSPATH org.archive.jmx.Client -" SELF=`basename $0` # script name HOST=$1 # cluster node:port to talk to through JMX @@ -168,7 +170,7 @@ case "$2" in ;; *) - printf "Usage: $SELF ...\n" + printf "Usage: bin/$SELF ...\n" printf "\n" printf "Supported commands are:\n" printf "%26s - %s\n" "join " "Sends request a JOIN node with the specified URL" @@ -183,9 +185,9 @@ case "$2" in printf "%26s - %s\n" has-convergence "Checks if there is a cluster convergence" printf "Where the should be on the format of 'akka://actor-system-name@hostname:port'\n" printf "\n" - printf "Examples: $SELF localhost:9999 is-available\n" - printf " $SELF localhost:9999 join akka://MySystem@darkstar:2552\n" - printf " $SELF localhost:9999 cluster-status\n" + printf "Examples: bin/$SELF localhost:9999 is-available\n" + printf " bin/$SELF localhost:9999 join akka://MySystem@darkstar:2552\n" + printf " bin/$SELF localhost:9999 cluster-status\n" exit 1 ;; esac diff --git a/akka-kernel/src/main/java/org/archive/jmx/Client.java b/akka-kernel/src/main/java/org/archive/jmx/Client.java new file mode 100644 index 0000000000..136de87ec3 --- /dev/null +++ b/akka-kernel/src/main/java/org/archive/jmx/Client.java @@ -0,0 +1,781 @@ +/* + * Client + * + * $Id$ + * + * Created on Nov 12, 2004 + * + * Copyright (C) 2004 Internet Archive. + * + * This file is part of the Heritrix web crawler (crawler.archive.org). + * + * Heritrix is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * any later version. + * + * Heritrix 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with Heritrix; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package org.archive.jmx; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.FieldPosition; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + + +/** + * A Simple Command-Line JMX Client. + * Tested against the JDK 1.5.0 JMX Agent. + * See Monitoring + * and Management Using JMX. + *

Can supply credentials and do primitive string representation of tabular + * and composite openmbeans. + * @author stack + */ +public class Client { + private static final Logger logger = + Logger.getLogger(Client.class.getName()); + + /** + * Usage string. + */ + private static final String USAGE = "Usage: java -jar" + + " cmdline-jmxclient.jar USER:PASS HOST:PORT [BEAN] [COMMAND]\n" + + "Options:\n" + + " USER:PASS Username and password. Required. If none, pass '-'.\n" + + " E.g. 'controlRole:secret'\n" + + " HOST:PORT Hostname and port to connect to. Required." + + " E.g. localhost:8081.\n" + + " Lists registered beans if only USER:PASS and this" + + " argument.\n" + + " BEAN Optional target bean name. If present we list" + + " available operations\n" + + " and attributes.\n" + + " COMMAND Optional operation to run or attribute to fetch. If" + + " none supplied,\n" + + " all operations and attributes are listed. Attributes" + + " begin with a\n" + + " capital letter: e.g. 'Status' or 'Started'." + + " Operations do not.\n" + + " Operations can take arguments by adding an '=' " + + "followed by\n" + + " comma-delimited params. Pass multiple " + + "attributes/operations to run\n" + + " more than one per invocation. Use commands 'create' and " + + "'destroy'\n" + + " to instantiate and unregister beans ('create' takes name " + + "of class).\n" + + " Pass 'Attributes' to get listing of all attributes and " + + "and their\n" + + " values.\n" + + "Requirements:\n" + + " JDK1.5.0. If connecting to a SUN 1.5.0 JDK JMX Agent, remote side" + + " must be\n" + + " started with system properties such as the following:\n" + + " -Dcom.sun.management.jmxremote.port=PORT\n" + + " -Dcom.sun.management.jmxremote.authenticate=false\n" + + " -Dcom.sun.management.jmxremote.ssl=false\n" + + " The above will start the remote server with no password. See\n" + + " http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html" + + " for more on\n" + + " 'Monitoring and Management via JMX'.\n" + + "Client Use Examples:\n" + + " To list MBeans on a non-password protected remote agent:\n" + + " % java -jar cmdline-jmxclient-X.X.jar - localhost:8081 \\\n" + + " org.archive.crawler:name=Heritrix,type=Service\n" + + " To list attributes and attributes of the Heritrix MBean:\n" + + " % java -jar cmdline-jmxclient-X.X.jar - localhost:8081 \\\n" + + " org.archive.crawler:name=Heritrix,type=Service \\\n" + + " schedule=http://www.archive.org\n" + + " To set set logging level to FINE on a password protected JVM:\n" + + " % java -jar cmdline-jmxclient-X.X.jar controlRole:secret" + + " localhost:8081 \\\n" + + " java.util.logging:type=Logging \\\n" + + " setLoggerLevel=org.archive.crawler.Heritrix,FINE"; + + /** + * Pattern that matches a command name followed by + * an optional equals and optional comma-delimited list + * of arguments. + */ + protected static final Pattern CMD_LINE_ARGS_PATTERN = + Pattern.compile("^([^=]+)(?:(?:\\=)(.+))?$"); + + private static final String CREATE_CMD_PREFIX = "create="; + + public static void main(String[] args) throws Exception { + Client client = new Client(); + // Set the logger to use our all-on-one-line formatter. + Logger l = Logger.getLogger(""); + Handler [] hs = l.getHandlers(); + for (int i = 0; i < hs.length; i++) { + Handler h = hs[0]; + if (h instanceof ConsoleHandler) { + h.setFormatter(client.new OneLineSimpleLogger()); + } + } + client.execute(args); + } + + protected static void usage() { + usage(0, null); + } + + protected static void usage(int exitCode, String message) { + if (message != null && message.length() > 0) { + System.out.println(message); + } + System.out.println(USAGE); + System.exit(exitCode); + } + + /** + * Constructor. + */ + public Client() { + super(); + } + + /** + * Parse a 'login:password' string. Assumption is that no + * colon in the login name. + * @param userpass + * @return Array of strings with login in first position. + */ + protected String [] parseUserpass(final String userpass) { + if (userpass == null || userpass.equals("-")) { + return null; + } + int index = userpass.indexOf(':'); + if (index <= 0) { + throw new RuntimeException("Unable to parse: " +userpass); + } + return new String [] {userpass.substring(0, index), + userpass.substring(index + 1)}; + } + + /** + * @param login + * @param password + * @return Credentials as map for RMI. + */ + protected Map formatCredentials(final String login, + final String password) { + Map env = null; + String[] creds = new String[] {login, password}; + env = new HashMap(1); + env.put(JMXConnector.CREDENTIALS, creds); + return env; + } + + protected JMXConnector getJMXConnector(final String hostport, + final String login, final String password) + throws IOException { + // Make up the jmx rmi URL and get a connector. + JMXServiceURL rmiurl = new JMXServiceURL("service:jmx:rmi://" + + hostport + "/jndi/rmi://" + hostport + "/jmxrmi"); + return JMXConnectorFactory.connect(rmiurl, + formatCredentials(login, password)); + } + + protected ObjectName getObjectName(final String beanname) + throws MalformedObjectNameException, NullPointerException { + return notEmpty(beanname)? new ObjectName(beanname): null; + } + + /** + * Version of execute called from the cmdline. + * Prints out result of execution on stdout. + * Parses cmdline args. Then calls {@link #execute(String, String, + * String, String, String[], boolean)}. + * @param args Cmdline args. + * @throws Exception + */ + protected void execute(final String [] args) + throws Exception { + // Process command-line. + if (args.length == 0 || args.length == 1) { + usage(); + } + String userpass = args[0]; + String hostport = args[1]; + String beanname = null; + String [] command = null; + if (args.length > 2) { + beanname = args[2]; + } + if (args.length > 3) { + command = new String [args.length - 3]; + for (int i = 3; i < args.length; i++) { + command[i - 3] = args[i]; + } + } + String [] loginPassword = parseUserpass(userpass); + Object [] result = execute(hostport, + ((loginPassword == null)? null: loginPassword[0]), + ((loginPassword == null)? null: loginPassword[1]), beanname, + command); + // Print out results on stdout. Only log if a result. + if (result != null) { + for (int i = 0; i < result.length; i++) { + if (result[i] != null && result[i].toString().length() > 0) { + if (command != null) { + logger.info(command[i] + ": " + result[i]); + } else { + logger.info("\n" + result[i].toString()); + } + } + } + } + } + + protected Object [] execute(final String hostport, final String login, + final String password, final String beanname, + final String [] command) + throws Exception { + return execute(hostport, login, password, beanname, command, false); + } + + public Object [] executeOneCmd(final String hostport, final String login, + final String password, final String beanname, + final String command) + throws Exception { + return execute(hostport, login, password, beanname, + new String[] {command}, true); + } + + /** + * Execute command against remote JMX agent. + * @param hostport 'host:port' combination. + * @param login RMI login to use. + * @param password RMI password to use. + * @param beanname Name of remote bean to run command against. + * @param command Array of commands to run. + * @param oneBeanOnly Set true if passed beanname is + * an exact name and the query for a bean is only supposed to return + * one bean instance. If not, we raise an exception (Otherwise, if false, + * then we deal with possibility of multiple bean instances coming back + * from query). Set to true when want to get an attribute or run an + * operation. + * @return Array of results -- one per command. + * @throws Exception + */ + protected Object [] execute(final String hostport, final String login, + final String password, final String beanname, + final String [] command, final boolean oneBeanOnly) + throws Exception { + JMXConnector jmxc = getJMXConnector(hostport, login, password); + Object [] result = null; + try { + result = doBeans(jmxc.getMBeanServerConnection(), + getObjectName(beanname), command, oneBeanOnly); + } finally { + jmxc.close(); + } + return result; + } + + protected boolean notEmpty(String s) { + return s != null && s.length() > 0; + } + + protected Object [] doBeans(final MBeanServerConnection mbsc, + final ObjectName objName, final String[] command, + final boolean oneBeanOnly) + throws Exception { + Object [] result = null; + Set beans = mbsc.queryMBeans(objName, null); + if (beans.size() == 0) { + // No bean found. Check if we are to create a bean? + if (command.length == 1 && notEmpty(command[0]) + && command[0].startsWith(CREATE_CMD_PREFIX)) { + String className = + command[0].substring(CREATE_CMD_PREFIX.length()); + mbsc.createMBean(className, objName); + } else { + // TODO: Is there a better JMX exception that RE for this + // scenario? + throw new RuntimeException(objName.getCanonicalName() + + " not registered."); + } + } else if (beans.size() == 1) { + result = doBean(mbsc, (ObjectInstance) beans.iterator().next(), + command); + } else { + if (oneBeanOnly) { + throw new RuntimeException("Only supposed to be one bean " + + "query result"); + } + // This is case of multiple beans in query results. + // Print name of each into a StringBuffer. Return as one + // result. + StringBuffer buffer = new StringBuffer(); + for (Iterator i = beans.iterator(); i.hasNext();) { + Object obj = i.next(); + if (obj instanceof ObjectName) { + buffer.append((((ObjectName) obj).getCanonicalName())); + } else if (obj instanceof ObjectInstance) { + buffer.append((((ObjectInstance) obj).getObjectName() + .getCanonicalName())); + } else { + throw new RuntimeException("Unexpected object type: " + obj); + } + buffer.append("\n"); + } + result = new String [] {buffer.toString()}; + } + return result; + } + + /** + * Get attribute or run operation against passed bean instance. + * + * @param mbsc Server connection. + * @param instance Bean instance we're to get attributes from or run + * operation against. + * @param command Command to run (May be null). + * @return Result. If multiple commands, multiple results. + * @throws Exception + */ + protected Object [] doBean(MBeanServerConnection mbsc, + ObjectInstance instance, String [] command) + throws Exception { + // If no command, then print out list of attributes and operations. + if (command == null || command.length <= 0) { + return new String [] {listOptions(mbsc, instance)}; + } + + // Maybe multiple attributes/operations listed on one command line. + Object [] result = new Object[command.length]; + for (int i = 0; i < command.length; i++) { + result[i] = doSubCommand(mbsc, instance, command[i]); + } + return result; + } + + public Object doSubCommand(MBeanServerConnection mbsc, + ObjectInstance instance, String subCommand) + throws Exception { + // First, handle special case of our being asked to destroy a bean. + if (subCommand.equals("destroy")) { + mbsc.unregisterMBean(instance.getObjectName()); + return null; + } else if (subCommand.startsWith(CREATE_CMD_PREFIX)) { + throw new IllegalArgumentException("You cannot call create " + + "on an already existing bean."); + } + + // Get attribute and operation info. + MBeanAttributeInfo [] attributeInfo = + mbsc.getMBeanInfo(instance.getObjectName()).getAttributes(); + MBeanOperationInfo [] operationInfo = + mbsc.getMBeanInfo(instance.getObjectName()).getOperations(); + // Now, bdbje JMX bean doesn't follow the convention of attributes + // having uppercase first letter and operations having lowercase + // first letter. But most beans do. Be prepared to handle the bdbje + // case. + Object result = null; + if (Character.isUpperCase(subCommand.charAt(0))) { + // Probably an attribute. + if (!isFeatureInfo(attributeInfo, subCommand) && + isFeatureInfo(operationInfo, subCommand)) { + // Its not an attribute name. Looks like its name of an + // operation. Try it. + result = + doBeanOperation(mbsc, instance, subCommand, operationInfo); + } else { + // Then it is an attribute OR its not an attribute name nor + // operation name and the below invocation will throw a + // AttributeNotFoundException. + result = doAttributeOperation(mbsc, instance, subCommand, + attributeInfo); + } + } else { + // Must be an operation. + if (!isFeatureInfo(operationInfo, subCommand) && + isFeatureInfo(attributeInfo, subCommand)) { + // Its not an operation name but looks like it could be an + // attribute name. Try it. + result = doAttributeOperation(mbsc, instance, subCommand, + attributeInfo); + } else { + // Its an operation name OR its neither operation nor attribute + // name and the below will throw a NoSuchMethodException. + result = + doBeanOperation(mbsc, instance, subCommand, operationInfo); + } + } + + // Look at the result. Is it of composite or tabular type? + // If so, convert to a String representation. + if (result instanceof CompositeData) { + result = recurseCompositeData(new StringBuffer("\n"), "", "", + (CompositeData)result); + } else if (result instanceof TabularData) { + result = recurseTabularData(new StringBuffer("\n"), "", "", + (TabularData)result); + } else if (result instanceof String []) { + String [] strs = (String [])result; + StringBuffer buffer = new StringBuffer("\n"); + for (int i = 0; i < strs.length; i++) { + buffer.append(strs[i]); + buffer.append("\n"); + } + result = buffer; + } else if (result instanceof AttributeList) { + AttributeList list = (AttributeList)result; + if (list.size() <= 0) { + result = null; + } else { + StringBuffer buffer = new StringBuffer("\n"); + for (Iterator ii = list.iterator(); ii.hasNext();) { + Attribute a = (Attribute)ii.next(); + buffer.append(a.getName()); + buffer.append(": "); + buffer.append(a.getValue()); + buffer.append("\n"); + } + result = buffer; + } + } + return result; + } + + protected boolean isFeatureInfo(MBeanFeatureInfo [] infos, String cmd) { + return getFeatureInfo(infos, cmd) != null; + } + + protected MBeanFeatureInfo getFeatureInfo(MBeanFeatureInfo [] infos, + String cmd) { + // Cmd may be carrying arguments. Don't count them in the compare. + int index = cmd.indexOf('='); + String name = (index > 0)? cmd.substring(0, index): cmd; + for (int i = 0; i < infos.length; i++) { + if (infos[i].getName().equals(name)) { + return infos[i]; + } + } + return null; + } + + protected StringBuffer recurseTabularData(StringBuffer buffer, + String indent, String name, TabularData data) { + addNameToBuffer(buffer, indent, name); + java.util.Collection c = data.values(); + for (Iterator i = c.iterator(); i.hasNext();) { + Object obj = i.next(); + if (obj instanceof CompositeData) { + recurseCompositeData(buffer, indent + " ", "", + (CompositeData)obj); + } else if (obj instanceof TabularData) { + recurseTabularData(buffer, indent, "", + (TabularData)obj); + } else { + buffer.append(obj); + } + } + return buffer; + } + + protected StringBuffer recurseCompositeData(StringBuffer buffer, + String indent, String name, CompositeData data) { + indent = addNameToBuffer(buffer, indent, name); + for (Iterator i = data.getCompositeType().keySet().iterator(); + i.hasNext();) { + String key = (String)i.next(); + Object o = data.get(key); + if (o instanceof CompositeData) { + recurseCompositeData(buffer, indent + " ", key, + (CompositeData)o); + } else if (o instanceof TabularData) { + recurseTabularData(buffer, indent, key, (TabularData)o); + } else { + buffer.append(indent); + buffer.append(key); + buffer.append(": "); + buffer.append(o); + buffer.append("\n"); + } + } + return buffer; + } + + protected String addNameToBuffer(StringBuffer buffer, String indent, + String name) { + if (name == null || name.length() == 0) { + return indent; + } + buffer.append(indent); + buffer.append(name); + buffer.append(":\n"); + // Move all that comes under this 'name' over by one space. + return indent + " "; + } + + /** + * Class that parses commandline arguments. + * Expected format is 'operationName=arg0,arg1,arg2...'. We are assuming no + * spaces nor comma's in argument values. + */ + protected class CommandParse { + private String cmd; + private String [] args; + + protected CommandParse(String command) throws ParseException { + parse(command); + } + + private void parse(String command) throws ParseException { + Matcher m = CMD_LINE_ARGS_PATTERN.matcher(command); + if (m == null || !m.matches()) { + throw new ParseException("Failed parse of " + command, 0); + } + + this.cmd = m.group(1); + if (m.group(2) != null && m.group(2).length() > 0) { + this.args = m.group(2).split(","); + } else { + this.args = null; + } + } + + protected String getCmd() { + return this.cmd; + } + + protected String [] getArgs() { + return this.args; + } + } + + protected Object doAttributeOperation(MBeanServerConnection mbsc, + ObjectInstance instance, String command, MBeanAttributeInfo [] infos) + throws Exception { + // Usually we get attributes. If an argument, then we're being asked + // to set attribute. + CommandParse parse = new CommandParse(command); + if (parse.getArgs() == null || parse.getArgs().length == 0) { + // Special-casing. If the subCommand is 'Attributes', then return + // list of all attributes. + if (command.equals("Attributes")) { + String [] names = new String[infos.length]; + for (int i = 0; i < infos.length; i++) { + names[i] = infos[i].getName(); + } + return mbsc.getAttributes(instance.getObjectName(), names); + } + return mbsc.getAttribute(instance.getObjectName(), parse.getCmd()); + } + if (parse.getArgs().length != 1) { + throw new IllegalArgumentException("One only argument setting " + + "attribute values: " + parse.getArgs()); + } + // Get first attribute of name 'cmd'. Assumption is no method + // overrides. Then, look at the attribute and use its type. + MBeanAttributeInfo info = + (MBeanAttributeInfo)getFeatureInfo(infos, parse.getCmd()); + java.lang.reflect.Constructor c = Class.forName( + info.getType()).getConstructor(new Class[] {String.class}); + Attribute a = new Attribute(parse.getCmd(), + c.newInstance(new Object[] {parse.getArgs()[0]})); + mbsc.setAttribute(instance.getObjectName(), a); + return null; + } + + protected Object doBeanOperation(MBeanServerConnection mbsc, + ObjectInstance instance, String command, MBeanOperationInfo [] infos) + throws Exception { + // Parse command line. + CommandParse parse = new CommandParse(command); + + // Get first method of name 'cmd'. Assumption is no method + // overrides. Then, look at the method and use its signature + // to make sure client sends over parameters of the correct type. + MBeanOperationInfo op = + (MBeanOperationInfo)getFeatureInfo(infos, parse.getCmd()); + Object result = null; + if (op == null) { + result = "Operation " + parse.getCmd() + " not found."; + } else { + MBeanParameterInfo [] paraminfos = op.getSignature(); + int paraminfosLength = (paraminfos == null)? 0: paraminfos.length; + int objsLength = (parse.getArgs() == null)? + 0: parse.getArgs().length; + if (paraminfosLength != objsLength) { + result = "Passed param count does not match signature count"; + } else { + String [] signature = new String[paraminfosLength]; + Object [] params = (paraminfosLength == 0)? null + : new Object[paraminfosLength]; + for (int i = 0; i < paraminfosLength; i++) { + MBeanParameterInfo paraminfo = paraminfos[i]; + java.lang.reflect.Constructor c = Class.forName( + paraminfo.getType()).getConstructor( + new Class[] {String.class}); + params[i] = + c.newInstance(new Object[] {parse.getArgs()[i]}); + signature[i] = paraminfo.getType(); + } + result = mbsc.invoke(instance.getObjectName(), parse.getCmd(), + params, signature); + } + } + return result; + } + + protected String listOptions(MBeanServerConnection mbsc, + ObjectInstance instance) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException, IOException { + StringBuffer result = new StringBuffer(); + MBeanInfo info = mbsc.getMBeanInfo(instance.getObjectName()); + MBeanAttributeInfo [] attributes = info.getAttributes(); + if (attributes.length > 0) { + result.append("Attributes:"); + result.append("\n"); + for (int i = 0; i < attributes.length; i++) { + result.append(' ' + attributes[i].getName() + + ": " + attributes[i].getDescription() + + " (type=" + attributes[i].getType() + + ")"); + result.append("\n"); + } + } + MBeanOperationInfo [] operations = info.getOperations(); + if (operations.length > 0) { + result.append("Operations:"); + result.append("\n"); + for (int i = 0; i < operations.length; i++) { + MBeanParameterInfo [] params = operations[i].getSignature(); + StringBuffer paramsStrBuffer = new StringBuffer(); + if (params != null) { + for (int j = 0; j < params.length; j++) { + paramsStrBuffer.append("\n name="); + paramsStrBuffer.append(params[j].getName()); + paramsStrBuffer.append(" type="); + paramsStrBuffer.append(params[j].getType()); + paramsStrBuffer.append(" "); + paramsStrBuffer.append(params[j].getDescription()); + } + } + result.append(' ' + operations[i].getName() + + ": " + operations[i].getDescription() + + "\n Parameters " + params.length + + ", return type=" + operations[i].getReturnType() + + paramsStrBuffer.toString()); + result.append("\n"); + } + } + return result.toString(); + } + + /** + * Logger that writes entry on one line with less verbose date. + * Modelled on the OneLineSimpleLogger from Heritrix. + * + * @author stack + * @version $Revision$, $Date$ + */ + private class OneLineSimpleLogger extends SimpleFormatter { + /** + * Date instance. + * + * Keep around instance of date. + */ + private Date date = new Date(); + + /** + * Field position instance. + * + * Keep around this instance. + */ + private FieldPosition position = new FieldPosition(0); + + /** + * MessageFormatter for date. + */ + private SimpleDateFormat formatter = + new SimpleDateFormat("MM/dd/yyyy HH:mm:ss Z"); + + /** + * Persistent buffer in which we conjure the log. + */ + private StringBuffer buffer = new StringBuffer(); + + + public OneLineSimpleLogger() { + super(); + } + + public synchronized String format(LogRecord record) { + this.buffer.setLength(0); + this.date.setTime(record.getMillis()); + this.position.setBeginIndex(0); + this.formatter.format(this.date, this.buffer, this.position); + this.buffer.append(' '); + if (record.getSourceClassName() != null) { + this.buffer.append(record.getSourceClassName()); + } else { + this.buffer.append(record.getLoggerName()); + } + this.buffer.append(' '); + this.buffer.append(formatMessage(record)); + this.buffer.append(System.getProperty("line.separator")); + if (record.getThrown() != null) { + try { + StringWriter writer = new StringWriter(); + PrintWriter printer = new PrintWriter(writer); + record.getThrown().printStackTrace(printer); + writer.close(); + this.buffer.append(writer.toString()); + } catch (Exception e) { + this.buffer.append("Failed to get stack trace: " + + e.getMessage()); + } + } + return this.buffer.toString(); + } + } +} \ No newline at end of file diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 4804c0f796..3b1b84ef77 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -380,7 +380,7 @@ object Dependencies { val fileMailbox = Seq(Test.commonsIo, Test.scalatest, Test.junit) - val kernel = Seq(jmxClient, Test.scalatest, Test.junit) + val kernel = Seq(Test.scalatest, Test.junit) val camel = Seq(camelCore, Test.scalatest, Test.junit, Test.mockito) @@ -408,7 +408,6 @@ object Dependency { // Compile val camelCore = "org.apache.camel" % "camel-core" % V.Camel // ApacheV2 - val jmxClient = "cmdline-jmxclient" % "cmdline-jmxclient" % "0.10.3" // LGPL val netty = "io.netty" % "netty" % V.Netty // ApacheV2 val protobuf = "com.google.protobuf" % "protobuf-java" % V.Protobuf // New BSD val scalaStm = "org.scala-tools" % "scala-stm_2.9.1" % V.ScalaStm // Modified BSD (Scala) From 4e2c4955b34095781729b733cf3db296eb41643e Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Sun, 20 May 2012 20:16:06 +0200 Subject: [PATCH 2/2] Changed package to akka.jmx, for OSGi purposes. See #2079 --- akka-kernel/src/main/dist/bin/akka-cluster | 2 +- akka-kernel/src/main/java/{org/archive => akka}/jmx/Client.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename akka-kernel/src/main/java/{org/archive => akka}/jmx/Client.java (99%) diff --git a/akka-kernel/src/main/dist/bin/akka-cluster b/akka-kernel/src/main/dist/bin/akka-cluster index 7bb3a670da..3e76cdbb11 100755 --- a/akka-kernel/src/main/dist/bin/akka-cluster +++ b/akka-kernel/src/main/dist/bin/akka-cluster @@ -19,7 +19,7 @@ declare AKKA_HOME="$(cd "$(cd "$(dirname "$0")"; pwd -P)"/..; pwd)" [ -n "$JMX_CLIENT_CLASSPATH" ] || JMX_CLIENT_CLASSPATH="$AKKA_HOME/lib/akka/akka-kernel-*" # NOTE: The 'cmdline-jmxclient' is available as part of the Akka distribution. -JMX_CLIENT="java -cp $JMX_CLIENT_CLASSPATH org.archive.jmx.Client -" +JMX_CLIENT="java -cp $JMX_CLIENT_CLASSPATH akka.jmx.Client -" SELF=`basename $0` # script name HOST=$1 # cluster node:port to talk to through JMX diff --git a/akka-kernel/src/main/java/org/archive/jmx/Client.java b/akka-kernel/src/main/java/akka/jmx/Client.java similarity index 99% rename from akka-kernel/src/main/java/org/archive/jmx/Client.java rename to akka-kernel/src/main/java/akka/jmx/Client.java index 136de87ec3..9ebf63e9eb 100644 --- a/akka-kernel/src/main/java/org/archive/jmx/Client.java +++ b/akka-kernel/src/main/java/akka/jmx/Client.java @@ -23,7 +23,7 @@ * along with Heritrix; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -package org.archive.jmx; +package akka.jmx; import java.io.IOException; import java.io.PrintWriter;