GenericJMX plugin: Implement the "PluginName" config option.
[collectd.git] / bindings / java / org / collectd / java / GenericJMXConfValue.java
index cdca02f..802622c 100644 (file)
@@ -1,28 +1,34 @@
-/*
- * collectd/java - org/collectd/java/GenericJMXConfValue.java
- * Copyright (C) 2009  Florian octo Forster
+/**
+ * collectd - bindings/java/org/collectd/java/GenericJMXConfValue.java
+ * Copyright (C) 2009       Florian octo Forster
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * This program 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
- * General Public License for more details.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  */
 
 package org.collectd.java;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Collection;
 import java.util.Set;
 import java.util.Iterator;
 import java.util.ArrayList;
@@ -34,6 +40,7 @@ import javax.management.MBeanServerConnection;
 import javax.management.ObjectName;
 import javax.management.openmbean.OpenType;
 import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
 import javax.management.openmbean.InvalidKeyException;
 
 import org.collectd.api.Collectd;
@@ -62,6 +69,8 @@ class GenericJMXConfValue
   private DataSet _ds;
   private List<String> _attributes;
   private String _instance_prefix;
+  private List<String> _instance_from;
+  private String _plugin_name;
   private boolean _is_table;
 
   /**
@@ -123,6 +132,11 @@ class GenericJMXConfValue
     return (null);
   } /* }}} Number genericObjectToNumber */
 
+  /**
+   * Converts a generic list to a list of numbers.
+   *
+   * Returns null if one or more objects could not be converted.
+   */
   private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
   {
     List<Number> ret = new ArrayList<Number> ();
@@ -143,6 +157,14 @@ class GenericJMXConfValue
     return (ret);
   } /* }}} List<Number> genericListToNumber */
 
+  /**
+   * Converts a list of CompositeData to a list of numbers.
+   *
+   * From each <em>CompositeData </em> the key <em>key</em> is received and all
+   * those values are converted to a number. If one of the
+   * <em>CompositeData</em> doesn't have the specified key or one returned
+   * object cannot converted to a number then the function will return null.
+   */
   private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
       String key)
   {
@@ -168,7 +190,8 @@ class GenericJMXConfValue
     return (genericListToNumber (objects));
   } /* }}} List<Number> genericCompositeToNumber */
 
-  private void submitTable (List<Object> objects, ValueList vl) /* {{{ */
+  private void submitTable (List<Object> objects, ValueList vl, /* {{{ */
+      String instancePrefix)
   {
     List<CompositeData> cdlist;
     Set<String> keySet = null;
@@ -218,17 +241,18 @@ class GenericJMXConfValue
         continue;
       }
 
-      if (this._instance_prefix == null)
+      if (instancePrefix == null)
         vl.setTypeInstance (key);
       else
-        vl.setTypeInstance (this._instance_prefix + key);
+        vl.setTypeInstance (instancePrefix + key);
       vl.setValues (values);
 
       Collectd.dispatchValues (vl);
     }
   } /* }}} void submitTable */
 
-  private void submitScalar (List<Object> objects, ValueList vl) /* {{{ */
+  private void submitScalar (List<Object> objects, ValueList vl, /* {{{ */
+      String instancePrefix)
   {
     List<Number> values;
 
@@ -240,10 +264,10 @@ class GenericJMXConfValue
       return;
     }
 
-    if (this._instance_prefix == null)
+    if (instancePrefix == null)
       vl.setTypeInstance ("");
     else
-      vl.setTypeInstance (this._instance_prefix);
+      vl.setTypeInstance (instancePrefix);
     vl.setValues (values);
 
     Collectd.dispatchValues (vl);
@@ -274,6 +298,46 @@ class GenericJMXConfValue
     {
       if (value instanceof CompositeData)
         return (queryAttributeRecursive ((CompositeData) value, attrName));
+      else if (value instanceof TabularData)
+        return (queryAttributeRecursive ((TabularData) value, attrName));
+      else
+        return (null);
+    }
+  } /* }}} queryAttributeRecursive */
+
+  private Object queryAttributeRecursive (TabularData parent, /* {{{ */
+      List<String> attrName)
+  {
+    String key;
+    Object value = null;
+
+    key = attrName.remove (0);
+
+    TabularData tabularData = (TabularData) parent;
+    Collection<CompositeData> table =
+        (Collection<CompositeData>)tabularData.values();
+    for (CompositeData compositeData : table)
+    {
+      if (key.equals(compositeData.get("key")))
+      {
+        value = compositeData.get("value");
+      }
+    }
+    if (null == value)
+    {
+      return (null);
+    }
+
+    if (attrName.size () == 0)
+    {
+      return (value);
+    }
+    else
+    {
+      if (value instanceof CompositeData)
+        return (queryAttributeRecursive ((CompositeData) value, attrName));
+      else if (value instanceof TabularData)
+        return (queryAttributeRecursive ((TabularData) value, attrName));
       else
         return (null);
     }
@@ -296,7 +360,14 @@ class GenericJMXConfValue
 
     try
     {
-      value = conn.getAttribute (objName, key);
+      try
+      {
+        value = conn.getAttribute (objName, key);
+      }
+      catch (javax.management.AttributeNotFoundException e)
+      {
+        value = conn.invoke (objName, key, /* args = */ null, /* types = */ null);
+      }
     }
     catch (Exception e)
     {
@@ -313,6 +384,8 @@ class GenericJMXConfValue
     {
       if (value instanceof CompositeData)
         return (queryAttributeRecursive((CompositeData) value, attrNameList));
+      else if (value instanceof TabularData)
+        return (queryAttributeRecursive((TabularData) value, attrNameList));
       else if (value instanceof OpenType)
       {
         OpenType ot = (OpenType) value;
@@ -323,12 +396,28 @@ class GenericJMXConfValue
       else
       {
         Collectd.logError ("GenericJMXConfValue: Received object of "
-            + "unknown class.");
+            + "unknown class. " + attrName + " " + ((value == null)?"null":value.getClass().getName()));
         return (null);
       }
     }
   } /* }}} Object queryAttribute */
 
+  private String join (String separator, List<String> list) /* {{{ */
+  {
+    StringBuffer sb;
+
+    sb = new StringBuffer ();
+
+    for (int i = 0; i < list.size (); i++)
+    {
+      if (i > 0)
+        sb.append ("-");
+      sb.append (list.get (i));
+    }
+
+    return (sb.toString ());
+  } /* }}} String join */
+
   private String getConfigString (OConfigItem ci) /* {{{ */
   {
     List<OConfigValue> values;
@@ -391,6 +480,8 @@ class GenericJMXConfValue
     this._ds = null;
     this._attributes = new ArrayList<String> ();
     this._instance_prefix = null;
+    this._instance_from = new ArrayList<String> ();
+    this._plugin_name = null;
     this._is_table = false;
 
     /*
@@ -434,6 +525,18 @@ class GenericJMXConfValue
         if (tmp != null)
           this._instance_prefix = tmp;
       }
+      else if (child.getKey ().equalsIgnoreCase ("InstanceFrom"))
+      {
+        String tmp = getConfigString (child);
+        if (tmp != null)
+          this._instance_from.add (tmp);
+        else if (child.getKey ().equalsIgnoreCase ("PluginName"))
+        {
+          String tmp = getConfigString (child);
+          if (tmp != null)
+            this._plugin_name = tmp;
+        }
+      }
       else
         throw (new IllegalArgumentException ("Unknown option: "
               + child.getKey ()));
@@ -449,7 +552,7 @@ class GenericJMXConfValue
    * Query values via JMX according to the object's configuration and dispatch
    * them to collectd.
    *
-   * @param conn Connection to the MBeanServer.
+   * @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.
@@ -460,6 +563,8 @@ class GenericJMXConfValue
     ValueList vl;
     List<DataSource> dsrc;
     List<Object> values;
+    List<String> instanceList;
+    String instancePrefix;
 
     if (this._ds == null)
     {
@@ -485,10 +590,45 @@ class GenericJMXConfValue
 
     vl = new ValueList (pd);
     vl.setType (this._ds_name);
-    vl.setTypeInstance (this._instance_prefix);
+    if (this._plugin_name != null)
+    {
+      vl.setPlugin (this._plugin_name);
+    }
 
-    values = new ArrayList<Object> ();
+    /*
+     * Build the instnace prefix from the fixed string prefix and the
+     * properties of the objName.
+     */
+    instanceList = new ArrayList<String> ();
+    for (int i = 0; i < this._instance_from.size (); i++)
+    {
+      String propertyName;
+      String propertyValue;
 
+      propertyName = this._instance_from.get (i);
+      propertyValue = objName.getKeyProperty (propertyName);
+      if (propertyValue == null)
+      {
+        Collectd.logError ("GenericJMXConfMBean: "
+            + "No such property in object name: " + propertyName);
+      }
+      else
+      {
+        instanceList.add (propertyValue);
+      }
+    }
+
+    if (this._instance_prefix != null)
+      instancePrefix = new String (this._instance_prefix
+          + join ("-", instanceList));
+    else
+      instancePrefix = join ("-", instanceList);
+
+    /*
+     * Build a list of `Object's which is then passed to `submitTable' and
+     * `submitScalar'.
+     */
+    values = new ArrayList<Object> ();
     assert (dsrc.size () == this._attributes.size ());
     for (int i = 0; i < this._attributes.size (); i++)
     {
@@ -506,10 +646,10 @@ class GenericJMXConfValue
     }
 
     if (this._is_table)
-      submitTable (values, vl);
+      submitTable (values, vl, instancePrefix);
     else
-      submitScalar (values, vl);
+      submitScalar (values, vl, instancePrefix);
   } /* }}} void query */
-}
+} /* class GenericJMXConfValue */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */