java bindings: GenericJMX: This first prototype version seems to do something.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 2 Aug 2009 14:50:22 +0000 (16:50 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 2 Aug 2009 14:50:22 +0000 (16:50 +0200)
Well, at least it's not throwing exceptions like mad..

bindings/java/org/collectd/java/GenericJMX.java
bindings/java/org/collectd/java/GenericJMXConfMBean.java
bindings/java/org/collectd/java/GenericJMXConfValue.java

index 978e989..319615c 100644 (file)
@@ -105,7 +105,17 @@ public class GenericJMX implements CollectdConfigInterface,
   public int read () /* {{{ */
   {
     for (int i = 0; i < this._connections.size (); i++)
-      this._connections.get (i).query ();
+    {
+      try
+      {
+        this._connections.get (i).query ();
+      }
+      catch (Exception e)
+      {
+        Collectd.logError ("GenericJMX: Caught unexpected exception: " + e);
+        e.printStackTrace ();
+      }
+    }
 
     return (0);
   } /* }}} int read */
index eea2d8a..1b4d9cc 100644 (file)
 
 package org.collectd.java;
 
-import java.util.List;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 import java.util.ArrayList;
 
 import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
 import javax.management.MalformedObjectNameException;
 
-
 import org.collectd.api.Collectd;
 import org.collectd.api.PluginData;
 import org.collectd.api.OConfigValue;
@@ -139,17 +139,49 @@ class GenericJMXConfMBean
 
   } /* }}} GenericJMXConfMBean (OConfigItem ci) */
 
-  public String getName ()
+  public String getName () /* {{{ */
   {
     return (this._name);
-  }
+  } /* }}} */
 
   public void query (MBeanServerConnection conn, PluginData pd) /* {{{ */
   {
-    pd.setPluginInstance ((this._instance != null) ? this._instance : "");
+    Set<ObjectName> names;
+    Iterator<ObjectName> iter;
+
+    try
+    {
+      names = conn.queryNames (this._obj_name, /* query = */ null);
+    }
+    catch (Exception e)
+    {
+      Collectd.logError ("GenericJMXConfMBean: queryNames failed: " + e);
+      return;
+    }
+
+    if (names.size () == 0)
+    {
+      Collectd.logWarning ("GenericJMXConfMBean: No MBean matched "
+          + "the ObjectName " + this._obj_name);
+    }
+
+    iter = names.iterator ();
+    while (iter.hasNext ())
+    {
+      ObjectName objName;
+      PluginData pd_tmp;
 
-    for (int i = 0; i < this._values.size (); i++)
-      this._values.get (i).query (conn, this._obj_name, pd);
+      objName = iter.next ();
+      pd_tmp = new PluginData (pd);
+
+      Collectd.logDebug ("GenericJMXConfMBean: objName = "
+          + objName.toString ());
+
+      pd_tmp.setPluginInstance ((this._instance != null) ? this._instance : "");
+
+      for (int i = 0; i < this._values.size (); i++)
+        this._values.get (i).query (conn, objName, pd_tmp);
+    }
   } /* }}} void query */
 }
 
index dcbe648..0fcf31a 100644 (file)
 
 package org.collectd.java;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 import java.util.Iterator;
 import java.util.ArrayList;
 
 import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.InvalidKeyException;
 
 import org.collectd.api.Collectd;
 import org.collectd.api.DataSet;
@@ -36,12 +40,25 @@ import org.collectd.api.PluginData;
 import org.collectd.api.OConfigValue;
 import org.collectd.api.OConfigItem;
 
+/**
+ * Representation of a &lt;value&nbsp;/&gt; block and query functionality.
+ *
+ * This class represents a &lt;value&nbsp;/&gt; block in the configuration. As
+ * such, the constructor takes an {@link org.collectd.api.OConfigValue} to
+ * construct an object of this class.
+ *
+ * The object can then be asked to query data from JMX and dispatch it to
+ * collectd.
+ *
+ * @see GenericJMXConfMBean
+ */
 class GenericJMXConfValue
 {
   private String ds_name;
-  private DataSet ds;
+  private DataSet _ds;
   private List<String> _attributes;
-  private String instance_prefix;
+  private String _instance_prefix;
+  private boolean _is_table;
 
   private Number genericObjectToNumber (Object obj, int ds_type) /* {{{ */
   {
@@ -77,15 +94,180 @@ class GenericJMXConfValue
     return (null);
   } /* }}} Number genericObjectToNumber */
 
-  private Number queryAttribute (MBeanServerConnection conn, /* {{{ */
-      ObjectName objName, String attrName,
-      DataSource dsrc)
+  private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
+  {
+    List<Number> ret = new ArrayList<Number> ();
+    List<DataSource> dsrc = this._ds.getDataSources ();
+
+    assert (objects.size () == dsrc.size ());
+
+    for (int i = 0; i < objects.size (); i++)
+    {
+      Number n;
+
+      n = genericObjectToNumber (objects.get (i), dsrc.get (i).getType ());
+      if (n == null)
+        return (null);
+      ret.add (n);
+    }
+
+    return (ret);
+  } /* }}} List<Number> genericListToNumber */
+
+  private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
+      String key)
+  {
+    List<Object> objects = new ArrayList<Object> ();
+
+    for (int i = 0; i < cdlist.size (); i++)
+    {
+      CompositeData cd;
+      Object value;
+
+      cd = cdlist.get (i);
+      try
+      {
+        value = cd.get (key);
+      }
+      catch (InvalidKeyException e)
+      {
+        return (null);
+      }
+      objects.add (value);
+    }
+
+    return (genericListToNumber (objects));
+  } /* }}} List<Number> genericCompositeToNumber */
+
+  private void submitTable (List<Object> objects, ValueList vl) /* {{{ */
+  {
+    List<CompositeData> cdlist;
+    Set<String> keySet = null;
+    Iterator<String> keyIter;
+
+    cdlist = new ArrayList<CompositeData> ();
+    for (int i = 0; i < objects.size (); i++)
+    {
+      Object obj;
+
+      obj = objects.get (i);
+      if (obj instanceof CompositeData)
+      {
+        CompositeData cd;
+
+        cd = (CompositeData) obj;
+
+        if (i == 0)
+          keySet = cd.getCompositeType ().keySet ();
+
+        cdlist.add (cd);
+      }
+      else
+      {
+        Collectd.logError ("GenericJMXConfValue: At least one of the "
+            + "attributes was not of type `CompositeData', as required "
+            + "when table is set to `true'.");
+        return;
+      }
+    }
+
+    assert (keySet != null);
+
+    keyIter = keySet.iterator ();
+    while (keyIter.hasNext ())
+    {
+      String key;
+      List<Number> values;
+
+      key = keyIter.next ();
+      values = genericCompositeToNumber (cdlist, key);
+      if (values == null)
+      {
+        Collectd.logError ("GenericJMXConfValue: Cannot build a list of "
+            + "numbers for key " + key + ". Most likely not all attributes "
+            + "have this key.");
+        continue;
+      }
+
+      if (this._instance_prefix == null)
+        vl.setTypeInstance (key);
+      else
+        vl.setTypeInstance (this._instance_prefix + key);
+      vl.setValues (values);
+
+      Collectd.dispatchValues (vl);
+    }
+  } /* }}} void submitTable */
+
+  private void submitScalar (List<Object> objects, ValueList vl) /* {{{ */
+  {
+    List<Number> values;
+
+    values = genericListToNumber (objects);
+    if (values == null)
+    {
+      Collectd.logError ("GenericJMXConfValue: Cannot convert list of "
+          + "objects to numbers.");
+      return;
+    }
+
+    if (this._instance_prefix == null)
+      vl.setTypeInstance ("");
+    else
+      vl.setTypeInstance (this._instance_prefix);
+    vl.setValues (values);
+
+    Collectd.dispatchValues (vl);
+  } /* }}} void submitScalar */
+
+  private Object queryAttributeRecursive (CompositeData parent, /* {{{ */
+      List<String> attrName)
+  {
+    String key;
+    Object value;
+
+    key = attrName.remove (0);
+
+    try
+    {
+      value = parent.get (key);
+    }
+    catch (InvalidKeyException e)
+    {
+      return (null);
+    }
+
+    if (attrName.size () == 0)
+    {
+      return (value);
+    }
+    else
+    {
+      if (value instanceof CompositeData)
+        return (queryAttributeRecursive ((CompositeData) value, attrName));
+      else
+        return (null);
+    }
+  } /* }}} queryAttributeRecursive */
+
+  private Object queryAttribute (MBeanServerConnection conn, /* {{{ */
+      ObjectName objName, String attrName)
   {
-    Object attrObj;
+    List<String> attrNameList;
+    String key;
+    Object value;
+    String[] attrNameArray;
+
+    attrNameList = new ArrayList<String> ();
+
+    attrNameArray = attrName.split ("\\.");
+    key = attrNameArray[0];
+    for (int i = 1; i < attrNameArray.length; i++)
+      attrNameList.add (attrNameArray[i]);
 
     try
     {
-      attrObj = conn.getAttribute (objName, attrName);
+      value = conn.getAttribute (objName, key);
     }
     catch (Exception e)
     {
@@ -94,8 +276,18 @@ class GenericJMXConfValue
       return (null);
     }
 
-    return (genericObjectToNumber (attrObj, dsrc.getType ()));
-  } /* }}} int queryAttribute */
+    if (attrNameList.size () == 0)
+    {
+      return (value);
+    }
+    else
+    {
+      if (value instanceof CompositeData)
+        return (queryAttributeRecursive ((CompositeData) value, attrNameList));
+      else
+        return (null);
+    }
+  } /* }}} Object queryAttribute */
 
   private String getConfigString (OConfigItem ci) /* {{{ */
   {
@@ -121,14 +313,34 @@ class GenericJMXConfValue
     return (v.getString ());
   } /* }}} String getConfigString */
 
-/*
- *    <Value>
- *      Type "memory"
- *      Attribute "HeapMemoryUsage"
- *      # Type instance:
- *      InstancePrefix "heap-"
- *    </Value>
- */
+  private Boolean getConfigBoolean (OConfigItem ci) /* {{{ */
+  {
+    List<OConfigValue> values;
+    OConfigValue v;
+    Boolean b;
+
+    values = ci.getValues ();
+    if (values.size () != 1)
+    {
+      Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
+          + " configuration option needs exactly one boolean argument.");
+      return (null);
+    }
+
+    v = values.get (0);
+    if (v.getType () != OConfigValue.OCONFIG_TYPE_BOOLEAN)
+    {
+      Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
+          + " configuration option needs exactly one boolean argument.");
+      return (null);
+    }
+
+    return (new Boolean (v.getBoolean ()));
+  } /* }}} String getConfigBoolean */
+
+  /**
+   * Constructs a new value with the configured properties.
+   */
   public GenericJMXConfValue (OConfigItem ci) /* {{{ */
     throws IllegalArgumentException
   {
@@ -136,11 +348,22 @@ class GenericJMXConfValue
     Iterator<OConfigItem> iter;
 
     this.ds_name = null;
-    this.ds = null;
+    this._ds = null;
     this._attributes = new ArrayList<String> ();
-    this.instance_prefix = null;
-
+    this._instance_prefix = null;
+    this._is_table = false;
 
+    /*
+     * <Value>
+     *   Type "memory"
+     *   Table true|false
+     *   Attribute "HeapMemoryUsage"
+     *   Attribute "..."
+     *   :
+     *   # Type instance:
+     *   InstancePrefix "heap-"
+     * </Value>
+     */
     children = ci.getChildren ();
     iter = children.iterator ();
     while (iter.hasNext ())
@@ -153,6 +376,12 @@ class GenericJMXConfValue
         if (tmp != null)
           this.ds_name = tmp;
       }
+      else if (child.getKey ().equalsIgnoreCase ("Table"))
+      {
+        Boolean tmp = getConfigBoolean (child);
+        if (tmp != null)
+          this._is_table = tmp.booleanValue ();
+      }
       else if (child.getKey ().equalsIgnoreCase ("Attribute"))
       {
         String tmp = getConfigString (child);
@@ -163,7 +392,7 @@ class GenericJMXConfValue
       {
         String tmp = getConfigString (child);
         if (tmp != null)
-          this.instance_prefix = tmp;
+          this._instance_prefix = tmp;
       }
       else
         throw (new IllegalArgumentException ("Unknown option: "
@@ -176,16 +405,26 @@ class GenericJMXConfValue
       throw (new IllegalArgumentException ("No attribute was defined."));
   } /* }}} GenericJMXConfValue (OConfigItem ci) */
 
+  /**
+   * Query values via JMX according to the object's configuration and dispatch
+   * them to collectd.
+   *
+   * @param conn Connection to the MBeanServer.
+   * @param objName Object name of the MBean to query.
+   * @param pd      Preset naming components. The members host, plugin and
+   *                plugin instance will be used.
+   */
   public void query (MBeanServerConnection conn, ObjectName objName, /* {{{ */
       PluginData pd)
   {
     ValueList vl;
     List<DataSource> dsrc;
+    List<Object> values;
 
-    if (this.ds == null)
+    if (this._ds == null)
     {
-      this.ds = Collectd.getDS (this.ds_name);
-      if (ds == null)
+      this._ds = Collectd.getDS (this.ds_name);
+      if (this._ds == null)
       {
         Collectd.logError ("GenericJMXConfValue: Unknown type: "
             + this.ds_name);
@@ -193,39 +432,43 @@ class GenericJMXConfValue
       }
     }
 
-    dsrc = this.ds.getDataSources ();
+    dsrc = this._ds.getDataSources ();
     if (dsrc.size () != this._attributes.size ())
     {
       Collectd.logError ("GenericJMXConfValue.query: The data set "
-          + ds_name + " has " + this.ds.getDataSources ().size ()
+          + ds_name + " has " + this._ds.getDataSources ().size ()
           + " data sources, but there were " + this._attributes.size ()
           + " attributes configured. This doesn't match!");
-      this.ds = null;
+      this._ds = null;
       return;
     }
 
     vl = new ValueList (pd);
     vl.setType (this.ds_name);
-    vl.setTypeInstance (this.instance_prefix);
+    vl.setTypeInstance (this._instance_prefix);
+
+    values = new ArrayList<Object> ();
 
     assert (dsrc.size () == this._attributes.size ());
     for (int i = 0; i < this._attributes.size (); i++)
     {
-      Number v;
+      Object v;
 
-      v = queryAttribute (conn, objName, this._attributes.get (i),
-          dsrc.get (i));
+      v = queryAttribute (conn, objName, this._attributes.get (i));
       if (v == null)
       {
         Collectd.logError ("GenericJMXConfValue.query: "
             + "Querying attribute " + this._attributes.get (i) + " failed.");
         return;
       }
-      Collectd.logDebug ("GenericJMXConfValue.query: dsrc[" + i + "]: v = " + v);
-      vl.addValue (v);
+
+      values.add (v);
     }
 
-    Collectd.dispatchValues (vl);
+    if (this._is_table)
+      submitTable (values, vl);
+    else
+      submitScalar (values, vl);
   } /* }}} void query */
 }