java bindings: GenericJMX: Fix a couple of error messages.
[collectd.git] / bindings / java / org / collectd / java / GenericJMXConfValue.java
1 /*
2  * collectd/java - org/collectd/java/GenericJMXConfValue.java
3  * Copyright (C) 2009  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  */
21
22 package org.collectd.java;
23
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.Iterator;
28 import java.util.ArrayList;
29
30 import javax.management.MBeanServerConnection;
31 import javax.management.ObjectName;
32 import javax.management.openmbean.OpenType;
33 import javax.management.openmbean.CompositeData;
34 import javax.management.openmbean.InvalidKeyException;
35
36 import org.collectd.api.Collectd;
37 import org.collectd.api.DataSet;
38 import org.collectd.api.DataSource;
39 import org.collectd.api.ValueList;
40 import org.collectd.api.PluginData;
41 import org.collectd.api.OConfigValue;
42 import org.collectd.api.OConfigItem;
43
44 /**
45  * Representation of a &lt;value&nbsp;/&gt; block and query functionality.
46  *
47  * This class represents a &lt;value&nbsp;/&gt; block in the configuration. As
48  * such, the constructor takes an {@link org.collectd.api.OConfigValue} to
49  * construct an object of this class.
50  *
51  * The object can then be asked to query data from JMX and dispatch it to
52  * collectd.
53  *
54  * @see GenericJMXConfMBean
55  */
56 class GenericJMXConfValue
57 {
58   private String _ds_name;
59   private DataSet _ds;
60   private List<String> _attributes;
61   private String _instance_prefix;
62   private boolean _is_table;
63
64   private Number genericObjectToNumber (Object obj, int ds_type) /* {{{ */
65   {
66     if (obj instanceof String)
67     {
68       String str = (String) obj;
69       
70       try
71       {
72         if (ds_type == DataSource.TYPE_GAUGE)
73           return (new Double (str));
74         else
75           return (new Long (str));
76       }
77       catch (NumberFormatException e)
78       {
79         return (null);
80       }
81     }
82     else if (obj instanceof Integer)
83     {
84       return (new Integer ((Integer) obj));
85     }
86     else if (obj instanceof Long)
87     {
88       return (new Long ((Long) obj));
89     }
90     else if (obj instanceof Double)
91     {
92       return (new Double ((Double) obj));
93     }
94
95     return (null);
96   } /* }}} Number genericObjectToNumber */
97
98   private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
99   {
100     List<Number> ret = new ArrayList<Number> ();
101     List<DataSource> dsrc = this._ds.getDataSources ();
102
103     assert (objects.size () == dsrc.size ());
104
105     for (int i = 0; i < objects.size (); i++)
106     {
107       Number n;
108
109       n = genericObjectToNumber (objects.get (i), dsrc.get (i).getType ());
110       if (n == null)
111         return (null);
112       ret.add (n);
113     }
114
115     return (ret);
116   } /* }}} List<Number> genericListToNumber */
117
118   private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
119       String key)
120   {
121     List<Object> objects = new ArrayList<Object> ();
122
123     for (int i = 0; i < cdlist.size (); i++)
124     {
125       CompositeData cd;
126       Object value;
127
128       cd = cdlist.get (i);
129       try
130       {
131         value = cd.get (key);
132       }
133       catch (InvalidKeyException e)
134       {
135         return (null);
136       }
137       objects.add (value);
138     }
139
140     return (genericListToNumber (objects));
141   } /* }}} List<Number> genericCompositeToNumber */
142
143   private void submitTable (List<Object> objects, ValueList vl) /* {{{ */
144   {
145     List<CompositeData> cdlist;
146     Set<String> keySet = null;
147     Iterator<String> keyIter;
148
149     cdlist = new ArrayList<CompositeData> ();
150     for (int i = 0; i < objects.size (); i++)
151     {
152       Object obj;
153
154       obj = objects.get (i);
155       if (obj instanceof CompositeData)
156       {
157         CompositeData cd;
158
159         cd = (CompositeData) obj;
160
161         if (i == 0)
162           keySet = cd.getCompositeType ().keySet ();
163
164         cdlist.add (cd);
165       }
166       else
167       {
168         Collectd.logError ("GenericJMXConfValue: At least one of the "
169             + "attributes was not of type `CompositeData', as required "
170             + "when table is set to `true'.");
171         return;
172       }
173     }
174
175     assert (keySet != null);
176
177     keyIter = keySet.iterator ();
178     while (keyIter.hasNext ())
179     {
180       String key;
181       List<Number> values;
182
183       key = keyIter.next ();
184       values = genericCompositeToNumber (cdlist, key);
185       if (values == null)
186       {
187         Collectd.logError ("GenericJMXConfValue: Cannot build a list of "
188             + "numbers for key " + key + ". Most likely not all attributes "
189             + "have this key.");
190         continue;
191       }
192
193       if (this._instance_prefix == null)
194         vl.setTypeInstance (key);
195       else
196         vl.setTypeInstance (this._instance_prefix + key);
197       vl.setValues (values);
198
199       Collectd.dispatchValues (vl);
200     }
201   } /* }}} void submitTable */
202
203   private void submitScalar (List<Object> objects, ValueList vl) /* {{{ */
204   {
205     List<Number> values;
206
207     values = genericListToNumber (objects);
208     if (values == null)
209     {
210       Collectd.logError ("GenericJMXConfValue: Cannot convert list of "
211           + "objects to numbers.");
212       return;
213     }
214
215     if (this._instance_prefix == null)
216       vl.setTypeInstance ("");
217     else
218       vl.setTypeInstance (this._instance_prefix);
219     vl.setValues (values);
220
221     Collectd.dispatchValues (vl);
222   } /* }}} void submitScalar */
223
224   private Object queryAttributeRecursive (CompositeData parent, /* {{{ */
225       List<String> attrName)
226   {
227     String key;
228     Object value;
229
230     key = attrName.remove (0);
231
232     try
233     {
234       value = parent.get (key);
235     }
236     catch (InvalidKeyException e)
237     {
238       return (null);
239     }
240
241     if (attrName.size () == 0)
242     {
243       return (value);
244     }
245     else
246     {
247       if (value instanceof CompositeData)
248         return (queryAttributeRecursive ((CompositeData) value, attrName));
249       else
250         return (null);
251     }
252   } /* }}} queryAttributeRecursive */
253
254   private Object queryAttribute (MBeanServerConnection conn, /* {{{ */
255       ObjectName objName, String attrName)
256   {
257     List<String> attrNameList;
258     String key;
259     Object value;
260     String[] attrNameArray;
261
262     attrNameList = new ArrayList<String> ();
263
264     attrNameArray = attrName.split ("\\.");
265     key = attrNameArray[0];
266     for (int i = 1; i < attrNameArray.length; i++)
267       attrNameList.add (attrNameArray[i]);
268
269     try
270     {
271       value = conn.getAttribute (objName, key);
272     }
273     catch (Exception e)
274     {
275       Collectd.logError ("GenericJMXConfValue.query: getAttribute failed: "
276           + e);
277       return (null);
278     }
279
280     if (attrNameList.size () == 0)
281     {
282       return (value);
283     }
284     else
285     {
286       if (value instanceof CompositeData)
287         return (queryAttributeRecursive((CompositeData) value, attrNameList));
288       else if (value instanceof OpenType)
289       {
290         OpenType ot = (OpenType) value;
291         Collectd.logNotice ("GenericJMXConfValue: Handling of OpenType \""
292             + ot.getTypeName () + "\" is not yet implemented.");
293         return (null);
294       }
295       else
296       {
297         Collectd.logError ("GenericJMXConfValue: Received object of "
298             + "unknown class.");
299         return (null);
300       }
301     }
302   } /* }}} Object queryAttribute */
303
304   private String getConfigString (OConfigItem ci) /* {{{ */
305   {
306     List<OConfigValue> values;
307     OConfigValue v;
308
309     values = ci.getValues ();
310     if (values.size () != 1)
311     {
312       Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
313           + " configuration option needs exactly one string argument.");
314       return (null);
315     }
316
317     v = values.get (0);
318     if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
319     {
320       Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
321           + " configuration option needs exactly one string argument.");
322       return (null);
323     }
324
325     return (v.getString ());
326   } /* }}} String getConfigString */
327
328   private Boolean getConfigBoolean (OConfigItem ci) /* {{{ */
329   {
330     List<OConfigValue> values;
331     OConfigValue v;
332     Boolean b;
333
334     values = ci.getValues ();
335     if (values.size () != 1)
336     {
337       Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
338           + " configuration option needs exactly one boolean argument.");
339       return (null);
340     }
341
342     v = values.get (0);
343     if (v.getType () != OConfigValue.OCONFIG_TYPE_BOOLEAN)
344     {
345       Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
346           + " configuration option needs exactly one boolean argument.");
347       return (null);
348     }
349
350     return (new Boolean (v.getBoolean ()));
351   } /* }}} String getConfigBoolean */
352
353   /**
354    * Constructs a new value with the configured properties.
355    */
356   public GenericJMXConfValue (OConfigItem ci) /* {{{ */
357     throws IllegalArgumentException
358   {
359     List<OConfigItem> children;
360     Iterator<OConfigItem> iter;
361
362     this._ds_name = null;
363     this._ds = null;
364     this._attributes = new ArrayList<String> ();
365     this._instance_prefix = null;
366     this._is_table = false;
367
368     /*
369      * <Value>
370      *   Type "memory"
371      *   Table true|false
372      *   Attribute "HeapMemoryUsage"
373      *   Attribute "..."
374      *   :
375      *   # Type instance:
376      *   InstancePrefix "heap-"
377      * </Value>
378      */
379     children = ci.getChildren ();
380     iter = children.iterator ();
381     while (iter.hasNext ())
382     {
383       OConfigItem child = iter.next ();
384
385       if (child.getKey ().equalsIgnoreCase ("Type"))
386       {
387         String tmp = getConfigString (child);
388         if (tmp != null)
389           this._ds_name = tmp;
390       }
391       else if (child.getKey ().equalsIgnoreCase ("Table"))
392       {
393         Boolean tmp = getConfigBoolean (child);
394         if (tmp != null)
395           this._is_table = tmp.booleanValue ();
396       }
397       else if (child.getKey ().equalsIgnoreCase ("Attribute"))
398       {
399         String tmp = getConfigString (child);
400         if (tmp != null)
401           this._attributes.add (tmp);
402       }
403       else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
404       {
405         String tmp = getConfigString (child);
406         if (tmp != null)
407           this._instance_prefix = tmp;
408       }
409       else
410         throw (new IllegalArgumentException ("Unknown option: "
411               + child.getKey ()));
412     }
413
414     if (this._ds_name == null)
415       throw (new IllegalArgumentException ("No data set was defined."));
416     else if (this._attributes.size () == 0)
417       throw (new IllegalArgumentException ("No attribute was defined."));
418   } /* }}} GenericJMXConfValue (OConfigItem ci) */
419
420   /**
421    * Query values via JMX according to the object's configuration and dispatch
422    * them to collectd.
423    *
424    * @param conn Connection to the MBeanServer.
425    * @param objName Object name of the MBean to query.
426    * @param pd      Preset naming components. The members host, plugin and
427    *                plugin instance will be used.
428    */
429   public void query (MBeanServerConnection conn, ObjectName objName, /* {{{ */
430       PluginData pd)
431   {
432     ValueList vl;
433     List<DataSource> dsrc;
434     List<Object> values;
435
436     if (this._ds == null)
437     {
438       this._ds = Collectd.getDS (this._ds_name);
439       if (this._ds == null)
440       {
441         Collectd.logError ("GenericJMXConfValue: Unknown type: "
442             + this._ds_name);
443         return;
444       }
445     }
446
447     dsrc = this._ds.getDataSources ();
448     if (dsrc.size () != this._attributes.size ())
449     {
450       Collectd.logError ("GenericJMXConfValue.query: The data set "
451           + this._ds_name + " has " + this._ds.getDataSources ().size ()
452           + " data sources, but there were " + this._attributes.size ()
453           + " attributes configured. This doesn't match!");
454       this._ds = null;
455       return;
456     }
457
458     vl = new ValueList (pd);
459     vl.setType (this._ds_name);
460     vl.setTypeInstance (this._instance_prefix);
461
462     values = new ArrayList<Object> ();
463
464     assert (dsrc.size () == this._attributes.size ());
465     for (int i = 0; i < this._attributes.size (); i++)
466     {
467       Object v;
468
469       v = queryAttribute (conn, objName, this._attributes.get (i));
470       if (v == null)
471       {
472         Collectd.logError ("GenericJMXConfValue.query: "
473             + "Querying attribute " + this._attributes.get (i) + " failed.");
474         return;
475       }
476
477       values.add (v);
478     }
479
480     if (this._is_table)
481       submitTable (values, vl);
482     else
483       submitScalar (values, vl);
484   } /* }}} void query */
485 }
486
487 /* vim: set sw=2 sts=2 et fdm=marker : */