diff --git a/java/README.md b/java/README.md
index af193ac..b26edeb 100644
--- a/java/README.md
+++ b/java/README.md
@@ -10,7 +10,7 @@ Usage
------
```
-jmxquery [-url] [-username,u] [-password,p] [-query,q] [-incjvm] [-json] [-help]
+jmxquery [-url] [-username,u] [-password,p] [-query,q] [-call,c] [-incjvm] [-json] [-help]
```
options are:
@@ -33,6 +33,12 @@ options are:
{attributeKey} is optional and only used for Composite metric types.
Use semi-colon to separate metrics.
+-call, c
+ Call a method using a base64 encoded JSON as parameter
+ JSON format:
+ {"objectName":"{mBeanName}","name":"{methodName}","params":[{"value":"{value}","type":"{type}"},...]}
+ Supported types: char, short, int, long, boolean, double, float, String, Float, Double, Short, Int, Long
+
-incjvm
Will add all standard JVM metrics to the -metrics query if used under java.lang domain
Useful utility function to add JVM metrics quickly and also for testing connections if
@@ -79,6 +85,12 @@ You can get multiple values by joining the mbeans together with semi colons.
java -jar JMXQuery.jar -url service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi -q "java.lang:type=ClassLoading/LoadedClassCount;java.lang:type=ClassLoading/UnloadedClassCount"
```
+```
+Invoke the add method: {"objectName":"com.outlyer.jmx.jmxquery.app.tests:type=Test","name":"add","params":[{"value":"Test ","type":"String"},{"value":"test","type":"String"}]}
+
+java -jar JMXQuery.jar -url service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi -c eyJvYmplY3ROYW1lIjoiY29tLm91dGx5ZXIuam14LmpteHF1ZXJ5LmFwcC50ZXN0czp0eXBlPVRlc3QiLCJuYW1lIjoiYWRkIiwicGFyYW1zIjpbeyJ2YWx1ZSI6IlRlc3QgIiwidHlwZSI6IlN0cmluZyJ9LHsidmFsdWUiOiJ0ZXN0IiwidHlwZSI6IlN0cmluZyJ9XX0=
+
+```
Building the Jar
----------------
diff --git a/java/pom.xml b/java/pom.xml
index 3674428..d64000f 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -31,6 +31,11 @@
system
${toolsjar}
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.9.8
+
diff --git a/java/src/main/java/com/outlyer/jmx/jmxquery/JMXConnector.java b/java/src/main/java/com/outlyer/jmx/jmxquery/JMXConnector.java
index 23842a5..8691aa8 100644
--- a/java/src/main/java/com/outlyer/jmx/jmxquery/JMXConnector.java
+++ b/java/src/main/java/com/outlyer/jmx/jmxquery/JMXConnector.java
@@ -3,14 +3,17 @@
import com.outlyer.jmx.jmxquery.tools.JMXTools;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
@@ -108,6 +111,74 @@ public void disconnect() throws IOException {
}
}
+ /**
+ * invokes a method on the server using JMX
+ *
+ * @param name
+ * - The object name of the MBean on which the method is to be
+ * invoked.
+ * @param method
+ * - The name of the method to be invoked.
+ * @param args
+ * - An array containing the arguments of the method to be set
+ * when the operation is invoked
+ * @param signatures
+ * - An array containing the signatures of the arguments, an
+ * array of class names in the format returned by
+ * Class.getName().
+ * @return result of invoking the operation on the MBean specified
+ * @throws java.io.IOException
+ * @throws javax.management.MalformedObjectNameException
+ * @throws javax.management.InstanceNotFoundException
+ * @throws javax.management.IntrospectionException
+ * @throws javax.management.ReflectionException
+ * @throws MBeanException
+ */
+ public Object invoke(String objectName, String method, Object[] args, String[] signatures)
+ throws IOException, MalformedObjectNameException, InstanceNotFoundException, IntrospectionException,
+ ReflectionException, MBeanException {
+ return connection.invoke(new ObjectName(objectName), method, args, signatures);
+ }
+
+ /**
+ * set an attribute on the server using JMX
+ *
+ * @param name
+ * - The object name of the MBean on which the method is to be
+ * invoked.
+ * @param field
+ * - The name of the field to be invoked.
+ * @param value
+ * - A value of type parameter
+ * @return void
+ * @throws java.io.IOException
+ * @throws javax.management.MalformedObjectNameException
+ * @throws javax.management.InstanceNotFoundException
+ * @throws javax.management.IntrospectionException
+ * @throws javax.management.ReflectionException
+ * @throws MBeanException
+ * @throws InvalidAttributeValueException
+ * @throws AttributeNotFoundException
+ */
+ public void set(String objectName, String field, Object value)
+ throws IOException, MalformedObjectNameException, InstanceNotFoundException, IntrospectionException,
+ ReflectionException, MBeanException, AttributeNotFoundException, InvalidAttributeValueException {
+ connection.setAttribute(new ObjectName(objectName), new Attribute(field,value));
+ }
+
+ /**
+ * query the actual name of the MBean
+ *
+ * @param name
+ * - The object search string.
+ * @return actual name of the MBean
+ * @throws java.io.IOException
+ * @throws javax.management.MalformedObjectNameException
+ */
+ public ObjectName queryName(String name) throws MalformedObjectNameException, IOException {
+ return connection.queryNames(new ObjectName(name), null).iterator().next();
+ }
+
/**
* Fetches a list of metrics and their values in one go
*
diff --git a/java/src/main/java/com/outlyer/jmx/jmxquery/JMXMetric.java b/java/src/main/java/com/outlyer/jmx/jmxquery/JMXMetric.java
index c6859c2..aac0ab3 100644
--- a/java/src/main/java/com/outlyer/jmx/jmxquery/JMXMetric.java
+++ b/java/src/main/java/com/outlyer/jmx/jmxquery/JMXMetric.java
@@ -289,11 +289,42 @@ public String toString() {
s += " (" + attributeType + ")";
}
if (value != null) {
- s += " = " + value.toString();
+ if (value instanceof String) {
+ s += " = " + value.toString();
+ } else {
+ s += " = " + valueToString(value);
+ }
}
return s;
}
+
+ private static String valueToString(Object value) {
+ if (value == null) {
+ return "null";
+ }
+ if (value.getClass().isArray()) {
+ return arrayToString((Object[]) value);
+ }
+ if ((value instanceof Integer) || (value instanceof Long) || (value instanceof Double)
+ || (value instanceof Boolean)) {
+ return value.toString();
+ }
+ return new StringBuilder().append("\"").append(value).append("\"").toString();
+ }
+
+ private static String arrayToString(Object[] array) {
+ String s = "[";
+ boolean separatorRequired = false;
+ for (Object v : array) {
+ if (separatorRequired)
+ s += ",";
+ separatorRequired = true;
+ s += valueToString(v);
+ }
+ s += "]";
+ return s;
+ }
/**
* Returns JSON representation of metric
@@ -326,14 +357,7 @@ public String toJSON() {
json += ", \"attributeType\" : \"" + this.attributeType + "\"";
}
if (this.value != null) {
- if ((this.value instanceof Integer) ||
- (this.value instanceof Long) ||
- (this.value instanceof Double) ||
- (this.value instanceof Boolean)) {
- json += ", \"value\" : " + this.value.toString();
- } else {
- json += ", \"value\" : \"" + this.value.toString() + "\"";
- }
+ json += ", \"value\" : " + valueToString(value);
}
json += "}";
diff --git a/java/src/main/java/com/outlyer/jmx/jmxquery/JMXQuery.java b/java/src/main/java/com/outlyer/jmx/jmxquery/JMXQuery.java
index 351f652..92a9169 100644
--- a/java/src/main/java/com/outlyer/jmx/jmxquery/JMXQuery.java
+++ b/java/src/main/java/com/outlyer/jmx/jmxquery/JMXQuery.java
@@ -5,11 +5,16 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
-
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Base64;
+
import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.outlyer.jmx.jmxquery.object.JMXAttribute;
+import com.outlyer.jmx.jmxquery.object.JMXMethod;
/**
*
* JMXQuery is used for local or remote request of JMX attributes
@@ -28,6 +33,8 @@ public class JMXQuery {
String password = null;
boolean outputJSON = false;
+ JMXMethod methodInvoke = null;
+ JMXAttribute setterInvoke = null;
/**
* @param args
*/
@@ -52,6 +59,33 @@ public static void main(String[] args) throws Exception {
// Process Query
try {
+ // Set attribute
+ if (query.setterInvoke != null) {
+ ObjectName objName = query.connector.queryName(query.setterInvoke.getObjectName());
+
+ query.connector.set(objName.getCanonicalName(), query.setterInvoke.getName(),
+ query.setterInvoke.getTypedValue());
+ query.connector.disconnect();
+ return;
+ }
+ // Method invoke
+ if (query.methodInvoke != null) {
+ ObjectName objName = query.connector.queryName(query.methodInvoke.getObjectName());
+
+ Object ret = query.connector.invoke(objName.getCanonicalName(), query.methodInvoke.getName(),
+ query.methodInvoke.getValues(), query.methodInvoke.getTypes());
+ if (ret != null) {
+ if (query.outputJSON) {
+ System.out.println("{ \"result\": \"" + toString(ret) + "\"}");
+ } else {
+ System.out.println(toString(ret));
+ }
+ }
+ query.connector.disconnect();
+ return;
+ }
+
+ // Read attributes
ArrayList outputMetrics = query.connector.getMetrics(query.metrics);
if (query.outputJSON) {
System.out.println("[");
@@ -105,6 +139,18 @@ public static void main(String[] args) throws Exception {
query.connector.disconnect();
}
+ private static final String toString(Object obj) {
+ if (obj instanceof Object[]) {
+ StringBuilder strBuild = new StringBuilder();
+ for (Object el : (Object[]) obj) {
+ strBuild.append(el.toString() + "\n");
+ }
+ return strBuild.toString();
+
+ }
+ return obj.toString();
+ }
+
/**
* Get key JVM stats. Utility method for quickly grabbing key java metrics
* and also for testing
@@ -181,15 +227,23 @@ private void parse(String[] args) throws ParseError {
username = args[++i];
} else if (option.equals("-password") || option.equals("-p")) {
password = args[++i];
- } else if (option.equals("-query") || option.equals("-q")) {
-
+ } else if (option.equals("-query") || option.equals("-q")) {
// Parse query string to break up string in format:
// {mbean}/{attribute}/{key};
String[] query = args[++i].split(";");
for (String metricQuery : query) {
metrics.add(new JMXMetric(metricQuery));
}
-
+ } else if (option.equals("-call") || option.equals("-c")) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ byte[] decodedBytes = Base64.getDecoder().decode(args[++i]);
+ String decodedString = new String(decodedBytes);
+ methodInvoke = objectMapper.readValue(decodedString, JMXMethod.class);
+ } else if (option.equals("-set") || option.equals("-s")) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ byte[] decodedBytes = Base64.getDecoder().decode(args[++i]);
+ String decodedString = new String(decodedBytes);
+ setterInvoke = objectMapper.readValue(decodedString, JMXAttribute.class);
} else if (option.equals("-json")) {
outputJSON = true;
} else if (option.equals("-incjvm")) {
diff --git a/java/src/main/java/com/outlyer/jmx/jmxquery/object/JMXAttribute.java b/java/src/main/java/com/outlyer/jmx/jmxquery/object/JMXAttribute.java
new file mode 100644
index 0000000..336b2d2
--- /dev/null
+++ b/java/src/main/java/com/outlyer/jmx/jmxquery/object/JMXAttribute.java
@@ -0,0 +1,44 @@
+package com.outlyer.jmx.jmxquery.object;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ *
+ * JMXAttribute is used for set the JMX attributes
+ *
+ * @author Tibor Kiss
+ *
+ */
+public class JMXAttribute extends JMXObject {
+
+ private JMXParam value = null;
+
+ /**
+ * get the JMX MBean value
+ */
+ public JMXParam getValue() {
+ return value;
+ }
+
+ /**
+ * return the actual value
+ */
+ @JsonIgnore
+ public Object getTypedValue() {
+ if(value == null)
+ return null;
+ return JMXObjectTypeConverter.toObject(value.getType(), (String)value.getValue());
+ }
+
+ /**
+ * set the JMX MBean value
+ *
+ * @param value
+ * JMX MBean new value
+ */
+ public void setValue(JMXParam value) {
+ this.value = value;
+ }
+
+
+}
diff --git a/java/src/main/java/com/outlyer/jmx/jmxquery/object/JMXMethod.java b/java/src/main/java/com/outlyer/jmx/jmxquery/object/JMXMethod.java
new file mode 100644
index 0000000..3ce3df6
--- /dev/null
+++ b/java/src/main/java/com/outlyer/jmx/jmxquery/object/JMXMethod.java
@@ -0,0 +1,67 @@
+package com.outlyer.jmx.jmxquery.object;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ *
+ * JMXMethod is used for local or remote invoke of JMX methods
+ *
+ * @author Tibor Kiss
+ *
+ */
+public class JMXMethod extends JMXObject {
+
+ private final List params = new ArrayList();
+
+
+ /**
+ * return the list of parameters
+ *
+ * @return list of parameters
+ */
+ public List getParams() {
+ return params;
+ }
+
+ /**
+ * This method can be used to add a parameter to a method
+ *
+ * @param value
+ * @param type
+ */
+ public void addParam(Object value, String type) {
+ params.add(new JMXParam(value, type));
+ }
+
+ /**
+ * This method returns the values as an array
+ *
+ * @return array of value
+ */
+ @JsonIgnore
+ public Object[] getValues() {
+ List