java plugin: Added the possibility to configure java plugins.
[collectd.git] / src / java.c
1 /**
2  * collectd - src/java.c
3  * Copyright (C) 2009  Florian octo Forster
4  * Copyright (C) 2008  Justo Alonso Achaques
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  *   Justo Alonso Achaques <justo.alonso at gmail.com>
22  **/
23
24 #include "collectd.h"
25 #include "plugin.h"
26 #include "common.h"
27
28 #include <pthread.h>
29 #include <jni.h>
30
31 #if !defined(JNI_VERSION_1_2)
32 # error "Need JNI 1.2 compatible interface!"
33 #endif
34
35 /*
36  * Types
37  */
38 struct java_plugin_s /* {{{ */
39 {
40   char *class_name;
41   jclass class_ptr;
42   jobject object_ptr;
43
44   oconfig_item_t *ci;
45
46 #define CJNI_FLAG_ENABLED 0x0001
47   int flags;
48
49   jmethodID m_config;
50   jmethodID m_init;
51   jmethodID m_read;
52   jmethodID m_write;
53   jmethodID m_shutdown;
54 };
55 typedef struct java_plugin_s java_plugin_t;
56 /* }}} */
57
58 /*
59  * Global variables
60  */
61 static JavaVM *jvm = NULL;
62
63 static char **jvm_argv = NULL;
64 static size_t jvm_argc = 0;
65
66 static java_plugin_t *java_plugins     = NULL;
67 static size_t         java_plugins_num = 0;
68
69 /* 
70  * C to Java conversion functions
71  */
72 static int ctoj_string (JNIEnv *jvm_env, /* {{{ */
73     const char *string,
74     jclass class_ptr, jobject object_ptr, const char *method_name)
75 {
76   jmethodID m_set;
77   jstring o_string;
78
79   /* Create a java.lang.String */
80   o_string = (*jvm_env)->NewStringUTF (jvm_env,
81       (string != NULL) ? string : "");
82   if (o_string == NULL)
83   {
84     ERROR ("java plugin: ctoj_string: NewStringUTF failed.");
85     return (-1);
86   }
87
88   /* Search for the `void setFoo (String s)' method. */
89   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
90       method_name, "(Ljava/lang/String;)V");
91   if (m_set == NULL)
92   {
93     ERROR ("java plugin: ctoj_string: Cannot find method `void %s (String)'.",
94         method_name);
95     (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
96     return (-1);
97   }
98
99   /* Call the method. */
100   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, o_string);
101
102   /* Decrease reference counter on the java.lang.String object. */
103   (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
104
105   DEBUG ("java plugin: ctoj_string: ->%s (%s);",
106       method_name, (string != NULL) ? string : "");
107
108   return (0);
109 } /* }}} int ctoj_string */
110
111 static int ctoj_int (JNIEnv *jvm_env, /* {{{ */
112     jint value,
113     jclass class_ptr, jobject object_ptr, const char *method_name)
114 {
115   jmethodID m_set;
116
117   /* Search for the `void setFoo (int i)' method. */
118   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
119       method_name, "(I)V");
120   if (m_set == NULL)
121   {
122     ERROR ("java plugin: ctoj_int: Cannot find method `void %s (int)'.",
123         method_name);
124     return (-1);
125   }
126
127   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
128
129   DEBUG ("java plugin: ctoj_int: ->%s (%i);",
130       method_name, (int) value);
131
132   return (0);
133 } /* }}} int ctoj_int */
134
135 static int ctoj_long (JNIEnv *jvm_env, /* {{{ */
136     jlong value,
137     jclass class_ptr, jobject object_ptr, const char *method_name)
138 {
139   jmethodID m_set;
140
141   /* Search for the `void setFoo (long l)' method. */
142   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
143       method_name, "(J)V");
144   if (m_set == NULL)
145   {
146     ERROR ("java plugin: ctoj_long: Cannot find method `void %s (long)'.",
147         method_name);
148     return (-1);
149   }
150
151   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
152
153   DEBUG ("java plugin: ctoj_long: ->%s (%"PRIi64");",
154       method_name, (int64_t) value);
155
156   return (0);
157 } /* }}} int ctoj_long */
158
159 static int ctoj_double (JNIEnv *jvm_env, /* {{{ */
160     jdouble value,
161     jclass class_ptr, jobject object_ptr, const char *method_name)
162 {
163   jmethodID m_set;
164
165   /* Search for the `void setFoo (double d)' method. */
166   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
167       method_name, "(D)V");
168   if (m_set == NULL)
169   {
170     ERROR ("java plugin: ctoj_double: Cannot find method `void %s (double)'.",
171         method_name);
172     return (-1);
173   }
174
175   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
176
177   DEBUG ("java plugin: ctoj_double: ->%s (%g);",
178       method_name, (double) value);
179
180   return (0);
181 } /* }}} int ctoj_double */
182
183 /* Convert a jlong to a java.lang.Number */
184 static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */
185 {
186   jclass c_long;
187   jmethodID m_long_constructor;
188
189   /* Look up the java.lang.Long class */
190   c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long");
191   if (c_long == NULL)
192   {
193     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
194         "java.lang.Long class failed.");
195     return (NULL);
196   }
197
198   m_long_constructor = (*jvm_env)->GetMethodID (jvm_env,
199       c_long, "<init>", "(J)V");
200   if (m_long_constructor == NULL)
201   {
202     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
203         "`Long (long)' constructor failed.");
204     return (NULL);
205   }
206
207   return ((*jvm_env)->NewObject (jvm_env,
208         c_long, m_long_constructor, value));
209 } /* }}} jobject ctoj_jlong_to_number */
210
211 /* Convert a jdouble to a java.lang.Number */
212 static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */
213 {
214   jclass c_double;
215   jmethodID m_double_constructor;
216
217   /* Look up the java.lang.Long class */
218   c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double");
219   if (c_double == NULL)
220   {
221     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
222         "java.lang.Double class failed.");
223     return (NULL);
224   }
225
226   m_double_constructor = (*jvm_env)->GetMethodID (jvm_env,
227       c_double, "<init>", "(D)V");
228   if (m_double_constructor == NULL)
229   {
230     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
231         "`Double (double)' constructor failed.");
232     return (NULL);
233   }
234
235   return ((*jvm_env)->NewObject (jvm_env,
236         c_double, m_double_constructor, value));
237 } /* }}} jobject ctoj_jdouble_to_number */
238
239 /* Convert a value_t to a java.lang.Number */
240 static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
241     value_t value, int ds_type)
242 {
243   if (ds_type == DS_TYPE_COUNTER)
244     return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter));
245   else if (ds_type == DS_TYPE_GAUGE)
246     return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge));
247   else
248     return (NULL);
249 } /* }}} jobject ctoj_value_to_number */
250
251 /* Convert a data_source_t to a org.collectd.protocol.DataSource */
252 static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
253     const data_source_t *dsrc)
254 {
255   jclass c_datasource;
256   jmethodID m_datasource_constructor;
257   jobject o_datasource;
258   int status;
259
260   /* Look up the DataSource class */
261   c_datasource = (*jvm_env)->FindClass (jvm_env,
262       "org.collectd.protocol.DataSource");
263   if (c_datasource == NULL)
264   {
265     ERROR ("java plugin: ctoj_data_source: "
266         "FindClass (org.collectd.protocol.DataSource) failed.");
267     return (NULL);
268   }
269
270   /* Lookup the `ValueList ()' constructor. */
271   m_datasource_constructor = (*jvm_env)->GetMethodID (jvm_env, c_datasource,
272       "<init>", "()V");
273   if (m_datasource_constructor == NULL)
274   {
275     ERROR ("java plugin: ctoj_data_source: Cannot find the "
276         "`DataSource ()' constructor.");
277     return (NULL);
278   }
279
280   /* Create a new instance. */
281   o_datasource = (*jvm_env)->NewObject (jvm_env, c_datasource,
282       m_datasource_constructor);
283   if (o_datasource == NULL)
284   {
285     ERROR ("java plugin: ctoj_data_source: "
286         "Creating a new DataSource instance failed.");
287     return (NULL);
288   }
289
290   /* Set name via `void setName (String name)' */
291   status = ctoj_string (jvm_env, dsrc->name,
292       c_datasource, o_datasource, "setName");
293   if (status != 0)
294   {
295     ERROR ("java plugin: ctoj_data_source: "
296         "ctoj_string (setName) failed.");
297     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
298     return (NULL);
299   }
300
301   /* Set type via `void setType (int type)' */
302   status = ctoj_int (jvm_env, dsrc->type,
303       c_datasource, o_datasource, "setType");
304   if (status != 0)
305   {
306     ERROR ("java plugin: ctoj_data_source: "
307         "ctoj_int (setType) failed.");
308     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
309     return (NULL);
310   }
311
312   /* Set min via `void setMin (double min)' */
313   status = ctoj_double (jvm_env, dsrc->min,
314       c_datasource, o_datasource, "setMin");
315   if (status != 0)
316   {
317     ERROR ("java plugin: ctoj_data_source: "
318         "ctoj_double (setMin) failed.");
319     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
320     return (NULL);
321   }
322
323   /* Set max via `void setMax (double max)' */
324   status = ctoj_double (jvm_env, dsrc->max,
325       c_datasource, o_datasource, "setMax");
326   if (status != 0)
327   {
328     ERROR ("java plugin: ctoj_data_source: "
329         "ctoj_double (setMax) failed.");
330     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
331     return (NULL);
332   }
333
334   return (o_datasource);
335 } /* }}} jobject ctoj_data_source */
336
337 /* Convert a oconfig_value_t to a org.collectd.api.OConfigValue */
338 static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
339     oconfig_value_t ocvalue)
340 {
341   jclass c_ocvalue;
342   jmethodID m_ocvalue_constructor;
343   jobject o_argument;
344   jobject o_ocvalue;
345
346   m_ocvalue_constructor = NULL;
347   o_argument = NULL;
348
349   c_ocvalue = (*jvm_env)->FindClass (jvm_env,
350       "org.collectd.api.OConfigValue");
351   if (c_ocvalue == NULL)
352   {
353     ERROR ("java plugin: ctoj_oconfig_value: "
354         "FindClass (org.collectd.api.OConfigValue) failed.");
355     return (NULL);
356   }
357
358   if (ocvalue.type == OCONFIG_TYPE_BOOLEAN)
359   {
360     jboolean tmp_boolean;
361
362     tmp_boolean = (ocvalue.value.boolean == 0) ? JNI_FALSE : JNI_TRUE;
363
364     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
365         "<init>", "(Z)V");
366     if (m_ocvalue_constructor == NULL)
367     {
368       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
369           "`OConfigValue (boolean)' constructor.");
370       return (NULL);
371     }
372
373     return ((*jvm_env)->NewObject (jvm_env,
374           c_ocvalue, m_ocvalue_constructor, tmp_boolean));
375   } /* if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) */
376   else if (ocvalue.type == OCONFIG_TYPE_STRING)
377   {
378     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
379         "<init>", "(Ljava/lang/String;)V");
380     if (m_ocvalue_constructor == NULL)
381     {
382       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
383           "`OConfigValue (String)' constructor.");
384       return (NULL);
385     }
386
387     o_argument = (*jvm_env)->NewStringUTF (jvm_env, ocvalue.value.string);
388     if (o_argument == NULL)
389     {
390       ERROR ("java plugin: ctoj_oconfig_value: "
391           "Creating a String object failed.");
392       return (NULL);
393     }
394   }
395   else if (ocvalue.type == OCONFIG_TYPE_NUMBER)
396   {
397     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
398         "<init>", "(Ljava/lang/Number;)V");
399     if (m_ocvalue_constructor == NULL)
400     {
401       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
402           "`OConfigValue (Number)' constructor.");
403       return (NULL);
404     }
405
406     o_argument = ctoj_jdouble_to_number (jvm_env,
407         (jdouble) ocvalue.value.number);
408     if (o_argument == NULL)
409     {
410       ERROR ("java plugin: ctoj_oconfig_value: "
411           "Creating a Number object failed.");
412       return (NULL);
413     }
414   }
415   else
416   {
417     return (NULL);
418   }
419
420   assert (m_ocvalue_constructor != NULL);
421   assert (o_argument != NULL);
422
423   o_ocvalue = (*jvm_env)->NewObject (jvm_env,
424       c_ocvalue, m_ocvalue_constructor, o_argument);
425   if (o_ocvalue == NULL)
426   {
427     ERROR ("java plugin: ctoj_oconfig_value: "
428         "Creating an OConfigValue object failed.");
429     (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
430     return (NULL);
431   }
432
433   (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
434   return (o_ocvalue);
435 } /* }}} jobject ctoj_oconfig_value */
436
437 /* Convert a oconfig_item_t to a org.collectd.api.OConfigItem */
438 static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
439     const oconfig_item_t *ci)
440 {
441   jclass c_ocitem;
442   jmethodID m_ocitem_constructor;
443   jmethodID m_addvalue;
444   jmethodID m_addchild;
445   jobject o_key;
446   jobject o_ocitem;
447   int i;
448
449   c_ocitem = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.OConfigItem");
450   if (c_ocitem == NULL)
451   {
452     ERROR ("java plugin: ctoj_oconfig_item: "
453         "FindClass (org.collectd.api.OConfigItem) failed.");
454     return (NULL);
455   }
456
457   /* Get the required methods: m_ocitem_constructor, m_addvalue, and m_addchild
458    * {{{ */
459   m_ocitem_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
460       "<init>", "(Ljava/lang/String;)V");
461   if (m_ocitem_constructor == NULL)
462   {
463     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
464         "`OConfigItem (String)' constructor.");
465     return (NULL);
466   }
467
468   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
469       "addValue", "(Lorg/collectd/api/OConfigValue;)V");
470   if (m_addvalue == NULL)
471   {
472     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
473         "`addValue (OConfigValue)' method.");
474     return (NULL);
475   }
476
477   m_addchild = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
478       "addChild", "(Lorg/collectd/api/OConfigItem;)V");
479   if (m_addchild == NULL)
480   {
481     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
482         "`addChild (OConfigItem)' method.");
483     return (NULL);
484   }
485   /* }}} */
486
487   /* Create a String object with the key.
488    * Needed for calling the constructor. */
489   o_key = (*jvm_env)->NewStringUTF (jvm_env, ci->key);
490   if (o_key == NULL)
491   {
492     ERROR ("java plugin: ctoj_oconfig_item: "
493         "Creating String object failed.");
494     return (NULL);
495   }
496
497   /* Create an OConfigItem object */
498   o_ocitem = (*jvm_env)->NewObject (jvm_env,
499       c_ocitem, m_ocitem_constructor, o_key);
500   if (o_ocitem == NULL)
501   {
502     ERROR ("java plugin: ctoj_oconfig_item: "
503         "Creating an OConfigItem object failed.");
504     (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
505     return (NULL);
506   }
507
508   /* We don't need the String object any longer.. */
509   (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
510
511   /* Call OConfigItem.addValue for each value */
512   for (i = 0; i < ci->values_num; i++) /* {{{ */
513   {
514     jobject o_value;
515
516     o_value = ctoj_oconfig_value (jvm_env, ci->values[i]);
517     if (o_value == NULL)
518     {
519       ERROR ("java plugin: ctoj_oconfig_item: "
520           "Creating an OConfigValue object failed.");
521       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
522       return (NULL);
523     }
524
525     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_value);
526     (*jvm_env)->DeleteLocalRef (jvm_env, o_value);
527   } /* }}} for (i = 0; i < ci->values_num; i++) */
528
529   /* Call OConfigItem.addChild for each child */
530   for (i = 0; i < ci->children_num; i++) /* {{{ */
531   {
532     jobject o_child;
533
534     o_child = ctoj_oconfig_item (jvm_env, ci->children + i);
535     if (o_child == NULL)
536     {
537       ERROR ("java plugin: ctoj_oconfig_item: "
538           "Creating an OConfigItem object failed.");
539       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
540       return (NULL);
541     }
542
543     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_child);
544     (*jvm_env)->DeleteLocalRef (jvm_env, o_child);
545   } /* }}} for (i = 0; i < ci->children_num; i++) */
546
547   return (o_ocitem);
548 } /* }}} jobject ctoj_oconfig_item */
549
550 /* Convert a data_set_t to a java.util.List<DataSource> */
551 static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
552 {
553   jclass c_arraylist;
554   jmethodID m_constructor;
555   jmethodID m_add;
556   jobject o_dataset;
557   int i;
558
559   /* Look up the java.util.ArrayList class */
560   c_arraylist = (*jvm_env)->FindClass (jvm_env, "java.util.ArrayList");
561   if (c_arraylist == NULL)
562   {
563     ERROR ("java plugin: ctoj_data_set: Looking up the "
564         "java.util.ArrayList class failed.");
565     return (NULL);
566   }
567
568   /* Search for the `ArrayList (int capacity)' constructor. */
569   m_constructor = (*jvm_env)->GetMethodID (jvm_env,
570       c_arraylist, "<init>", "()V");
571   if (m_constructor == NULL)
572   {
573     ERROR ("java plugin: ctoj_data_set: Looking up the "
574         "`ArrayList (void)' constructor failed.");
575     return (NULL);
576   }
577
578   /* Search for the `boolean add  (Object element)' method. */
579   m_add = (*jvm_env)->GetMethodID (jvm_env,
580       c_arraylist, "add", "(Ljava/lang/Object;)Z");
581   if (m_add == NULL)
582   {
583     ERROR ("java plugin: ctoj_data_set: Looking up the "
584         "`add (Object)' method failed.");
585     return (NULL);
586   }
587
588   o_dataset = (*jvm_env)->NewObject (jvm_env, c_arraylist, m_constructor);
589   if (o_dataset == NULL)
590   {
591     ERROR ("java plugin: ctoj_data_set: "
592         "Creating an ArrayList object failed.");
593     return (NULL);
594   }
595
596   for (i = 0; i < ds->ds_num; i++)
597   {
598     jobject o_datasource;
599     jboolean status;
600
601     o_datasource = ctoj_data_source (jvm_env, ds->ds + i);
602     if (o_datasource == NULL)
603     {
604       ERROR ("java plugin: ctoj_data_set: ctoj_data_source (%s.%s) failed",
605           ds->type, ds->ds[i].name);
606       (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
607       return (NULL);
608     }
609
610     status = (*jvm_env)->CallBooleanMethod (jvm_env,
611         o_dataset, m_add, o_datasource);
612     if (!status)
613     {
614       ERROR ("java plugin: ctoj_data_set: ArrayList.add returned FALSE.");
615       (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
616       (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
617       return (NULL);
618     }
619
620     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
621   } /* for (i = 0; i < ds->ds_num; i++) */
622
623   return (o_dataset);
624 } /* }}} jobject ctoj_data_set */
625
626 static int ctoj_value_list_add_value (JNIEnv *jvm_env, /* {{{ */
627     value_t value, int ds_type,
628     jclass class_ptr, jobject object_ptr)
629 {
630   jmethodID m_addvalue;
631   jobject o_number;
632
633   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
634       "addValue", "(Ljava/lang/Number;)V");
635   if (m_addvalue == NULL)
636   {
637     ERROR ("java plugin: ctoj_value_list_add_value: "
638         "Cannot find method `void addValue (Number)'.");
639     return (-1);
640   }
641
642   o_number = ctoj_value_to_number (jvm_env, value, ds_type);
643   if (o_number == NULL)
644   {
645     ERROR ("java plugin: ctoj_value_list_add_value: "
646         "ctoj_value_to_number failed.");
647     return (-1);
648   }
649
650   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_addvalue, o_number);
651
652   (*jvm_env)->DeleteLocalRef (jvm_env, o_number);
653
654   return (0);
655 } /* }}} int ctoj_value_list_add_value */
656
657 static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */
658     jclass c_valuelist, jobject o_valuelist, const data_set_t *ds)
659 {
660   jmethodID m_setdatasource;
661   jobject o_dataset;
662
663   /* Look for the `void setDataSource (List<DataSource> ds)' method. */
664   m_setdatasource = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
665       "setDataSource", "(Ljava/util/List;)V");
666   if (m_setdatasource == NULL)
667   {
668     ERROR ("java plugin: ctoj_value_list_add_data_set: "
669         "Cannot find the `void setDataSource (List<DataSource> ds)' method.");
670     return (-1);
671   }
672
673   /* Create a List<DataSource> object. */
674   o_dataset = ctoj_data_set (jvm_env, ds);
675   if (o_dataset == NULL)
676   {
677     ERROR ("java plugin: ctoj_value_list_add_data_set: "
678         "ctoj_data_set (%s) failed.", ds->type);
679     return (-1);
680   }
681
682   /* Actually call the method. */
683   (*jvm_env)->CallVoidMethod (jvm_env,
684       o_valuelist, m_setdatasource, o_dataset);
685
686   /* Decrease reference counter on the List<DataSource> object. */
687   (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
688
689   return (0);
690 } /* }}} int ctoj_value_list_add_data_set */
691
692 static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
693     const data_set_t *ds, const value_list_t *vl)
694 {
695   jclass c_valuelist;
696   jmethodID m_valuelist_constructor;
697   jobject o_valuelist;
698   int status;
699   int i;
700
701   /* First, create a new ValueList instance..
702    * Look up the class.. */
703   c_valuelist = (*jvm_env)->FindClass (jvm_env,
704       "org.collectd.protocol.ValueList");
705   if (c_valuelist == NULL)
706   {
707     ERROR ("java plugin: ctoj_value_list: "
708         "FindClass (org.collectd.protocol.ValueList) failed.");
709     return (NULL);
710   }
711
712   /* Lookup the `ValueList ()' constructor. */
713   m_valuelist_constructor = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
714       "<init>", "()V");
715   if (m_valuelist_constructor == NULL)
716   {
717     ERROR ("java plugin: ctoj_value_list: Cannot find the "
718         "`ValueList ()' constructor.");
719     return (NULL);
720   }
721
722   /* Create a new instance. */
723   o_valuelist = (*jvm_env)->NewObject (jvm_env, c_valuelist,
724       m_valuelist_constructor);
725   if (o_valuelist == NULL)
726   {
727     ERROR ("java plugin: ctoj_value_list: Creating a new ValueList instance "
728         "failed.");
729     return (NULL);
730   }
731
732   status = ctoj_value_list_add_data_set (jvm_env,
733       c_valuelist, o_valuelist, ds);
734   if (status != 0)
735   {
736     ERROR ("java plugin: ctoj_value_list: "
737         "ctoj_value_list_add_data_set failed.");
738     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
739     return (NULL);
740   }
741
742   /* Set the strings.. */
743 #define SET_STRING(str,method_name) do { \
744   status = ctoj_string (jvm_env, str, \
745       c_valuelist, o_valuelist, method_name); \
746   if (status != 0) { \
747     ERROR ("java plugin: ctoj_value_list: jtoc_string (%s) failed.", \
748         method_name); \
749     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); \
750     return (NULL); \
751   } } while (0)
752
753   SET_STRING (vl->host,            "setHost");
754   SET_STRING (vl->plugin,          "setPlugin");
755   SET_STRING (vl->plugin_instance, "setPluginInstance");
756   SET_STRING (vl->type,            "setType");
757   SET_STRING (vl->type_instance,   "setTypeInstance");
758
759 #undef SET_STRING
760
761   /* Set the `time' member. Java stores time in milliseconds. */
762   status = ctoj_long (jvm_env, ((jlong) vl->time) * ((jlong) 1000),
763       c_valuelist, o_valuelist, "setTime");
764   if (status != 0)
765   {
766     ERROR ("java plugin: ctoj_value_list: ctoj_long (setTime) failed.");
767     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
768     return (NULL);
769   }
770
771   /* Set the `interval' member.. */
772   status = ctoj_long (jvm_env, (jlong) vl->interval,
773       c_valuelist, o_valuelist, "setInterval");
774   if (status != 0)
775   {
776     ERROR ("java plugin: ctoj_value_list: ctoj_long (setInterval) failed.");
777     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
778     return (NULL);
779   }
780
781   for (i = 0; i < vl->values_len; i++)
782   {
783     status = ctoj_value_list_add_value (jvm_env, vl->values[i], ds->ds[i].type,
784         c_valuelist, o_valuelist);
785     if (status != 0)
786     {
787       ERROR ("java plugin: ctoj_value_list: "
788           "ctoj_value_list_add_value failed.");
789       (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
790       return (NULL);
791     }
792   }
793
794   return (o_valuelist);
795 } /* }}} int ctoj_value_list */
796
797 /*
798  * Java to C conversion functions
799  */
800 static int jtoc_string (JNIEnv *jvm_env, /* {{{ */
801     char *buffer, size_t buffer_size,
802     jclass class_ptr, jobject object_ptr, const char *method_name)
803 {
804   jmethodID method_id;
805   jobject string_obj;
806   const char *c_str;
807
808   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
809       method_name, "()Ljava/lang/String;");
810   if (method_id == NULL)
811   {
812     ERROR ("java plugin: jtoc_string: Cannot find method `String %s ()'.",
813         method_name);
814     return (-1);
815   }
816
817   string_obj = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, method_id);
818   if (string_obj == NULL)
819   {
820     ERROR ("java plugin: jtoc_string: CallObjectMethod (%s) failed.",
821         method_name);
822     return (-1);
823   }
824
825   c_str = (*jvm_env)->GetStringUTFChars (jvm_env, string_obj, 0);
826   if (c_str == NULL)
827   {
828     ERROR ("java plugin: jtoc_string: GetStringUTFChars failed.");
829     (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
830     return (-1);
831   }
832
833   DEBUG ("java plugin: jtoc_string: ->%s() = %s", method_name, c_str);
834
835   sstrncpy (buffer, c_str, buffer_size);
836
837   (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str);
838   (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
839
840   return (0);
841 } /* }}} int jtoc_string */
842
843 static int jtoc_long (JNIEnv *jvm_env, /* {{{ */
844     jlong *ret_value,
845     jclass class_ptr, jobject object_ptr, const char *method_name)
846 {
847   jmethodID method_id;
848
849   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
850       method_name, "()J");
851   if (method_id == NULL)
852   {
853     ERROR ("java plugin: jtoc_long: Cannot find method `long %s ()'.",
854         method_name);
855     return (-1);
856   }
857
858   *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id);
859
860   DEBUG ("java plugin: jtoc_long: ->%s() = %li",
861       method_name, (long int) *ret_value);
862
863   return (0);
864 } /* }}} int jtoc_long */
865
866 static int jtoc_double (JNIEnv *jvm_env, /* {{{ */
867     jdouble *ret_value,
868     jclass class_ptr, jobject object_ptr, const char *method_name)
869 {
870   jmethodID method_id;
871
872   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
873       method_name, "()D");
874   if (method_id == NULL)
875   {
876     ERROR ("java plugin: jtoc_string: Cannot find method `double %s ()'.",
877         method_name);
878     return (-1);
879   }
880
881   *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id);
882
883   DEBUG ("java plugin: jtoc_double: ->%s() = %g",
884       method_name, (double) *ret_value);
885
886   return (0);
887 } /* }}} int jtoc_double */
888
889 static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
890     value_t *ret_value, int ds_type, jobject object_ptr)
891 {
892   jclass class_ptr;
893   int status;
894
895   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
896
897   if (ds_type == DS_TYPE_COUNTER)
898   {
899     jlong tmp_long;
900
901     status = jtoc_long (jvm_env, &tmp_long,
902         class_ptr, object_ptr, "longValue");
903     if (status != 0)
904     {
905       ERROR ("java plugin: jtoc_value: "
906           "jtoc_long failed.");
907       return (-1);
908     }
909     (*ret_value).counter = (counter_t) tmp_long;
910   }
911   else
912   {
913     jdouble tmp_double;
914
915     status = jtoc_double (jvm_env, &tmp_double,
916         class_ptr, object_ptr, "doubleValue");
917     if (status != 0)
918     {
919       ERROR ("java plugin: jtoc_value: "
920           "jtoc_double failed.");
921       return (-1);
922     }
923     (*ret_value).gauge = (gauge_t) tmp_double;
924   }
925
926   return (0);
927 } /* }}} int jtoc_value */
928
929 static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */
930     const data_set_t *ds, value_list_t *vl,
931     jclass class_ptr, jobject object_ptr)
932 {
933   jmethodID m_getvalues;
934   jmethodID m_toarray;
935   jobject o_list;
936   jobjectArray o_number_array;
937
938   value_t *values;
939   int values_num;
940   int i;
941
942   values_num = ds->ds_num;
943
944   values = NULL;
945   o_number_array = NULL;
946   o_list = NULL;
947
948 #define BAIL_OUT(status) \
949   free (values); \
950   if (o_number_array != NULL) \
951     (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); \
952   if (o_list != NULL) \
953     (*jvm_env)->DeleteLocalRef (jvm_env, o_list); \
954   return (status);
955
956   /* Call: List<Number> ValueList.getValues () */
957   m_getvalues = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
958       "getValues", "()Ljava/util/List;");
959   if (m_getvalues == NULL)
960   {
961     ERROR ("java plugin: jtoc_values_array: "
962         "Cannot find method `List getValues ()'.");
963     BAIL_OUT (-1);
964   }
965
966   o_list = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, m_getvalues);
967   if (o_list == NULL)
968   {
969     ERROR ("java plugin: jtoc_values_array: "
970         "CallObjectMethod (getValues) failed.");
971     BAIL_OUT (-1);
972   }
973
974   /* Call: Number[] List.toArray () */
975   m_toarray = (*jvm_env)->GetMethodID (jvm_env,
976       (*jvm_env)->GetObjectClass (jvm_env, o_list),
977       "toArray", "()[Ljava/lang/Object;");
978   if (m_toarray == NULL)
979   {
980     ERROR ("java plugin: jtoc_values_array: "
981         "Cannot find method `Object[] toArray ()'.");
982     BAIL_OUT (-1);
983   }
984
985   o_number_array = (*jvm_env)->CallObjectMethod (jvm_env, o_list, m_toarray);
986   if (o_number_array == NULL)
987   {
988     ERROR ("java plugin: jtoc_values_array: "
989         "CallObjectMethod (toArray) failed.");
990     BAIL_OUT (-1);
991   }
992
993   values = calloc (values_num, sizeof (value_t));
994   if (values == NULL)
995   {
996     ERROR ("java plugin: jtoc_values_array: calloc failed.");
997     BAIL_OUT (-1);
998   }
999
1000   for (i = 0; i < values_num; i++)
1001   {
1002     jobject o_number;
1003     int status;
1004
1005     o_number = (*jvm_env)->GetObjectArrayElement (jvm_env,
1006         o_number_array, (jsize) i);
1007     if (o_number == NULL)
1008     {
1009       ERROR ("java plugin: jtoc_values_array: "
1010           "GetObjectArrayElement (%i) failed.", i);
1011       BAIL_OUT (-1);
1012     }
1013
1014     status = jtoc_value (jvm_env, values + i, ds->ds[i].type, o_number);
1015     if (status != 0)
1016     {
1017       ERROR ("java plugin: jtoc_values_array: "
1018           "jtoc_value (%i) failed.", i);
1019       BAIL_OUT (-1);
1020     }
1021   } /* for (i = 0; i < values_num; i++) */
1022
1023   vl->values = values;
1024   vl->values_len = values_num;
1025
1026 #undef BAIL_OUT
1027   (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array);
1028   (*jvm_env)->DeleteLocalRef (jvm_env, o_list);
1029   return (0);
1030 } /* }}} int jtoc_values_array */
1031
1032 /* Convert a org.collectd.protocol.ValueList to a value_list_t. */
1033 static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
1034     jobject object_ptr)
1035 {
1036   jclass class_ptr;
1037   int status;
1038   jlong tmp_long;
1039   const data_set_t *ds;
1040
1041   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1042   if (class_ptr == NULL)
1043   {
1044     ERROR ("java plugin: jtoc_value_list: GetObjectClass failed.");
1045     return (-1);
1046   }
1047
1048 #define SET_STRING(buffer,method) do { \
1049   status = jtoc_string (jvm_env, buffer, sizeof (buffer), \
1050       class_ptr, object_ptr, method); \
1051   if (status != 0) { \
1052     ERROR ("java plugin: jtoc_value_list: jtoc_string (%s) failed.", \
1053         method); \
1054     return (-1); \
1055   } } while (0)
1056
1057   SET_STRING(vl->type, "getType");
1058
1059   ds = plugin_get_ds (vl->type);
1060   if (ds == NULL)
1061   {
1062     ERROR ("java plugin: jtoc_value_list: Data-set `%s' is not defined. "
1063         "Please consult the types.db(5) manpage for mor information.",
1064         vl->type);
1065     return (-1);
1066   }
1067
1068   SET_STRING(vl->host, "getHost");
1069   SET_STRING(vl->plugin, "getPlugin");
1070   SET_STRING(vl->plugin_instance, "getPluginInstance");
1071   SET_STRING(vl->type_instance, "getTypeInstance");
1072
1073 #undef SET_STRING
1074
1075   status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime");
1076   if (status != 0)
1077   {
1078     ERROR ("java plugin: jtoc_value_list: jtoc_long (getTime) failed.");
1079     return (-1);
1080   }
1081   vl->time = (time_t) tmp_long;
1082
1083   status = jtoc_long (jvm_env, &tmp_long,
1084       class_ptr, object_ptr, "getInterval");
1085   if (status != 0)
1086   {
1087     ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed.");
1088     return (-1);
1089   }
1090   vl->interval = (int) tmp_long;
1091
1092   status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
1093   if (status != 0)
1094   {
1095     ERROR ("java plugin: jtoc_value_list: jtoc_values_array failed.");
1096     return (-1);
1097   }
1098
1099   return (0);
1100 } /* }}} int jtoc_value_list */
1101
1102 /* 
1103  * Functions accessible from Java
1104  */
1105 static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */
1106     jobject this, jobject java_vl)
1107 {
1108   value_list_t vl = VALUE_LIST_INIT;
1109   int status;
1110
1111   DEBUG ("cjni_api_dispatch_values: java_vl = %p;", (void *) java_vl);
1112
1113   status = jtoc_value_list (jvm_env, &vl, java_vl);
1114   if (status != 0)
1115   {
1116     ERROR ("java plugin: cjni_api_dispatch_values: jtoc_value_list failed.");
1117     return (-1);
1118   }
1119
1120   plugin_dispatch_values (&vl);
1121
1122   sfree (vl.values);
1123
1124   return (0);
1125 } /* }}} jint cjni_api_dispatch_values */
1126
1127 static JNINativeMethod jni_api_functions[] =
1128 {
1129   { "DispatchValues", "(Lorg/collectd/protocol/ValueList;)I", cjni_api_dispatch_values }
1130 };
1131 static size_t jni_api_functions_num = sizeof (jni_api_functions)
1132   / sizeof (jni_api_functions[0]);
1133
1134 /*
1135  * Functions
1136  */
1137 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
1138 {
1139   char **tmp;
1140
1141   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1142   {
1143     WARNING ("java plugin: `JVMArg' needs exactly one string argument.");
1144     return (-1);
1145   }
1146
1147   tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1));
1148   if (tmp == NULL)
1149   {
1150     ERROR ("java plugin: realloc failed.");
1151     return (-1);
1152   }
1153   jvm_argv = tmp;
1154
1155   jvm_argv[jvm_argc] = strdup (ci->values[0].value.string);
1156   if (jvm_argv[jvm_argc] == NULL)
1157   {
1158     ERROR ("java plugin: strdup failed.");
1159     return (-1);
1160   }
1161   jvm_argc++;
1162
1163   return (0);
1164 } /* }}} int cjni_config_add_jvm_arg */
1165
1166 static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
1167 {
1168   java_plugin_t *jp;
1169
1170   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1171   {
1172     WARNING ("java plugin: `LoadPlugin' needs exactly one string argument.");
1173     return (-1);
1174   }
1175
1176   jp = (java_plugin_t *) realloc (java_plugins,
1177       sizeof (*java_plugins) * (java_plugins_num + 1));
1178   if (jp == NULL)
1179   {
1180     ERROR ("java plugin: realloc failed.");
1181     return (-1);
1182   }
1183   java_plugins = jp;
1184   jp = java_plugins + java_plugins_num;
1185
1186   memset (jp, 0, sizeof (*jp));
1187   jp->class_name = strdup (ci->values[0].value.string);
1188   if (jp->class_name == NULL)
1189   {
1190     ERROR ("java plugin: strdup failed.");
1191     return (-1);
1192   }
1193
1194   jp->class_ptr  = NULL;
1195   jp->object_ptr = NULL;
1196   jp->ci         = NULL;
1197   jp->flags      = 0;
1198   jp->m_config   = NULL;
1199   jp->m_init     = NULL;
1200   jp->m_read     = NULL;
1201   jp->m_write    = NULL;
1202   jp->m_shutdown = NULL;
1203
1204   java_plugins_num++;
1205
1206   return (0);
1207 } /* }}} int cjni_config_load_plugin */
1208
1209 static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
1210 {
1211   size_t i;
1212   const char *class_name;
1213
1214   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1215   {
1216     WARNING ("java plugin: `Plugin' blocks "
1217         "need exactly one string argument.");
1218     return (-1);
1219   }
1220
1221   class_name = ci->values[0].value.string;
1222   for (i = 0; i < java_plugins_num; i++)
1223     if (strcmp (java_plugins[i].class_name, class_name) == 0)
1224       break;
1225
1226   if (i >= java_plugins_num)
1227   {
1228     WARNING ("java plugin: Configuration block for the `%s' plugin found, "
1229         "but the plugin has not been loaded. Please note, that the class "
1230         "name is case-sensitive!",
1231         class_name);
1232     return (0);
1233   }
1234
1235   if (java_plugins[i].ci != NULL)
1236   {
1237     WARNING ("java plugin: There are more than one <Plugin> blocks for the "
1238         "`%s' plugin. This is currently not supported - only the first block "
1239         "will be used!",
1240         class_name);
1241     return (0);
1242   }
1243
1244   java_plugins[i].ci = oconfig_clone (ci);
1245   if (java_plugins[i].ci == NULL)
1246   {
1247     ERROR ("java plugin: cjni_config_plugin_block: "
1248         "oconfig_clone failed for `%s'.",
1249         class_name);
1250     return (-1);
1251   }
1252
1253   DEBUG ("java plugin: cjni_config_plugin_block: "
1254       "Successfully copied config for `%s'.",
1255       class_name);
1256
1257   return (0);
1258 } /* }}} int cjni_config_plugin_block */
1259
1260 static int cjni_config (oconfig_item_t *ci) /* {{{ */
1261 {
1262   int success;
1263   int errors;
1264   int status;
1265   int i;
1266
1267   success = 0;
1268   errors = 0;
1269
1270   for (i = 0; i < ci->children_num; i++)
1271   {
1272     oconfig_item_t *child = ci->children + i;
1273
1274     if (strcasecmp ("JVMArg", child->key) == 0)
1275     {
1276       status = cjni_config_add_jvm_arg (child);
1277       if (status == 0)
1278         success++;
1279       else
1280         errors++;
1281     }
1282     else if (strcasecmp ("LoadPlugin", child->key) == 0)
1283     {
1284       status = cjni_config_load_plugin (child);
1285       if (status == 0)
1286         success++;
1287       else
1288         errors++;
1289     }
1290     else if (strcasecmp ("Plugin", child->key) == 0)
1291     {
1292       status = cjni_config_plugin_block (child);
1293       if (status == 0)
1294         success++;
1295       else
1296         errors++;
1297     }
1298     else
1299     {
1300       WARNING ("java plugin: Option `%s' not allowed here.", child->key);
1301       errors++;
1302     }
1303   }
1304
1305   if ((success == 0) && (errors > 0))
1306   {
1307     ERROR ("java plugin: All statements failed.");
1308     return (-1);
1309   }
1310
1311   return (0);
1312 } /* }}} int cjni_config */
1313
1314 static int cjni_init_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
1315 {
1316   jmethodID constructor_id;
1317   int status;
1318
1319   jp->class_ptr = (*jvm_env)->FindClass (jvm_env, jp->class_name);
1320   if (jp->class_ptr == NULL)
1321   {
1322     ERROR ("cjni_init_one_plugin: FindClass (%s) failed.",
1323         jp->class_name);
1324     return (-1);
1325   }
1326
1327   constructor_id = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1328       "<init>", "()V");
1329   if (constructor_id == NULL)
1330   {
1331     ERROR ("cjni_init_one_plugin: Could not find the constructor for `%s'.",
1332         jp->class_name);
1333     return (-1);
1334   }
1335
1336   jp->object_ptr = (*jvm_env)->NewObject (jvm_env, jp->class_ptr,
1337       constructor_id);
1338   if (jp->object_ptr == NULL)
1339   {
1340     ERROR ("cjni_init_one_plugin: Could create a new `%s' object.",
1341         jp->class_name);
1342     return (-1);
1343   }
1344
1345   jp->m_config = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1346       "Config", "(Lorg/collectd/api/OConfigItem;)I");
1347   DEBUG ("java plugin: cjni_init_one_plugin: "
1348       "jp->class_name = %s; jp->m_config = %p;",
1349       jp->class_name, (void *) jp->m_config);
1350
1351   jp->m_init = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1352       "Init", "()I");
1353   DEBUG ("java plugin: cjni_init_one_plugin: "
1354       "jp->class_name = %s; jp->m_init = %p;",
1355       jp->class_name, (void *) jp->m_init);
1356
1357   jp->m_read = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1358       "Read", "()I");
1359   DEBUG ("java plugin: cjni_init_one_plugin: "
1360       "jp->class_name = %s; jp->m_read = %p;",
1361       jp->class_name, (void *) jp->m_read);
1362
1363   jp->m_write = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1364       "Write", "(Lorg/collectd/protocol/ValueList;)I");
1365   DEBUG ("java plugin: cjni_init_one_plugin: "
1366       "jp->class_name = %s; jp->m_write = %p;",
1367       jp->class_name, (void *) jp->m_write);
1368
1369   jp->m_shutdown = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1370       "Shutdown", "()I");
1371   DEBUG ("java plugin: cjni_init_one_plugin: "
1372       "jp->class_name = %s; jp->m_shutdown = %p;",
1373       jp->class_name, (void *) jp->m_shutdown);
1374
1375   if (jp->ci != NULL)
1376   {
1377     if (jp->m_config == NULL)
1378     {
1379       WARNING ("java plugin: Configuration for the `%s' plugin is present, "
1380           "but plugin doesn't provide a configuration method.",
1381           jp->class_name);
1382     }
1383     else
1384     {
1385       jobject o_ocitem;
1386
1387       o_ocitem = ctoj_oconfig_item (jvm_env, jp->ci);
1388       if (o_ocitem == NULL)
1389       {
1390         ERROR ("java plugin: Creating an OConfigItem object failed. "
1391             "Can't pass configuration information to the `%s' plugin!",
1392             jp->class_name);
1393       }
1394       else
1395       {
1396         status = (*jvm_env)->CallIntMethod (jvm_env,
1397             jp->object_ptr, jp->m_config, o_ocitem);
1398         if (status != 0)
1399         {
1400           ERROR ("java plugin: cjni_init_one_plugin: "
1401               "Configuring the `%s' object failed with status %i.",
1402               jp->class_name, status);
1403         }
1404         (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
1405       }
1406     }
1407   } /* if (jp->ci != NULL) */
1408
1409   if (jp->m_init != NULL)
1410   {
1411     status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1412         jp->m_init);
1413     if (status != 0)
1414     {
1415       ERROR ("java plugin: cjni_init_one_plugin: "
1416         "Initializing `%s' object failed with status %i.",
1417         jp->class_name, status);
1418       return (-1);
1419     }
1420   }
1421   jp->flags |= CJNI_FLAG_ENABLED;
1422
1423   return (0);
1424 } /* }}} int cjni_init_one_plugin */
1425
1426 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
1427 {
1428   size_t j;
1429
1430   for (j = 0; j < java_plugins_num; j++)
1431     cjni_init_one_plugin (jvm_env, &java_plugins[j]);
1432
1433   return (0);
1434 } /* }}} int cjni_init_plugins */
1435
1436 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
1437 {
1438   jclass api_class_ptr;
1439   int status;
1440
1441   api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.CollectdAPI");
1442   if (api_class_ptr == NULL)
1443   {
1444     ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.CollectdAPI'.");
1445     return (-1);
1446   }
1447
1448   status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
1449       jni_api_functions, (jint) jni_api_functions_num);
1450   if (status != 0)
1451   {
1452     ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
1453     return (-1);
1454   }
1455
1456   return (0);
1457 } /* }}} int cjni_init_native */
1458
1459 static int cjni_init (void) /* {{{ */
1460 {
1461   JNIEnv *jvm_env;
1462   JavaVMInitArgs vm_args;
1463   JavaVMOption vm_options[jvm_argc];
1464
1465   int status;
1466   size_t i;
1467
1468   if (jvm != NULL)
1469     return (0);
1470
1471   jvm_env = NULL;
1472
1473   memset (&vm_args, 0, sizeof (vm_args));
1474   vm_args.version = JNI_VERSION_1_2;
1475   vm_args.options = vm_options;
1476   vm_args.nOptions = (jint) jvm_argc;
1477
1478   for (i = 0; i < jvm_argc; i++)
1479   {
1480     DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]);
1481     vm_args.options[i].optionString = jvm_argv[i];
1482   }
1483   /*
1484   vm_args.options[0].optionString = "-verbose:jni";
1485   vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
1486   */
1487
1488   status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
1489   if (status != 0)
1490   {
1491     ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.",
1492         status);
1493     return (-1);
1494   }
1495   assert (jvm != NULL);
1496   assert (jvm_env != NULL);
1497
1498   /* Call RegisterNatives */
1499   status = cjni_init_native (jvm_env);
1500   if (status != 0)
1501   {
1502     ERROR ("cjni_init: cjni_init_native failed.");
1503     return (-1);
1504   }
1505
1506   cjni_init_plugins (jvm_env);
1507
1508   return (0);
1509 } /* }}} int cjni_init */
1510
1511 static int cjni_read_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
1512 {
1513   int status;
1514
1515   if ((jp == NULL)
1516       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1517       || (jp->m_read == NULL))
1518     return (0);
1519
1520   DEBUG ("java plugin: Calling: %s.Read()", jp->class_name);
1521
1522   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1523       jp->m_read);
1524   if (status != 0)
1525   {
1526     ERROR ("java plugin: cjni_read_one_plugin: "
1527         "Calling `Read' on an `%s' object failed with status %i.",
1528         jp->class_name, status);
1529     return (-1);
1530   }
1531
1532   return (0);
1533 } /* }}} int cjni_read_one_plugin */
1534
1535 static int cjni_read_plugins (JNIEnv *jvm_env) /* {{{ */
1536 {
1537   size_t j;
1538
1539   for (j = 0; j < java_plugins_num; j++)
1540     cjni_read_one_plugin (jvm_env, &java_plugins[j]);
1541
1542   return (0);
1543 } /* }}} int cjni_read_plugins */
1544
1545 static int cjni_read (void) /* {{{ */
1546 {
1547   JNIEnv *jvm_env;
1548   JavaVMAttachArgs args;
1549   int status;
1550
1551   if (jvm == NULL)
1552   {
1553     ERROR ("java plugin: cjni_read: jvm == NULL");
1554     return (-1);
1555   }
1556
1557   jvm_env = NULL;
1558   memset (&args, 0, sizeof (args));
1559   args.version = JNI_VERSION_1_2;
1560
1561   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1562   if (status != 0)
1563   {
1564     ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
1565         status);
1566     return (-1);
1567   }
1568
1569   cjni_read_plugins (jvm_env);
1570
1571   status = (*jvm)->DetachCurrentThread (jvm);
1572   if (status != 0)
1573   {
1574     ERROR ("java plugin: cjni_read: DetachCurrentThread failed with status %i.",
1575         status);
1576     return (-1);
1577   }
1578
1579   return (0);
1580 } /* }}} int cjni_read */
1581
1582 static int cjni_write_one_plugin (JNIEnv *jvm_env, /* {{{ */
1583     java_plugin_t *jp, jobject vl_java)
1584 {
1585   int status;
1586
1587   if ((jp == NULL)
1588       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1589       || (jp->m_write == NULL))
1590     return (0);
1591
1592   DEBUG ("java plugin: Calling: %s.Write(ValueList)", jp->class_name);
1593
1594   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1595       jp->m_write, vl_java);
1596   if (status != 0)
1597   {
1598     ERROR ("java plugin: cjni_write_one_plugin: "
1599         "Calling `Write' on an `%s' object failed with status %i.",
1600         jp->class_name, status);
1601     return (-1);
1602   }
1603
1604   return (0);
1605 } /* }}} int cjni_write_one_plugin */
1606
1607 static int cjni_write_plugins (JNIEnv *jvm_env, /* {{{ */
1608     const data_set_t *ds, const value_list_t *vl)
1609 {
1610   size_t j;
1611
1612   jobject vl_java;
1613
1614   vl_java = ctoj_value_list (jvm_env, ds, vl);
1615   if (vl_java == NULL)
1616   {
1617     ERROR ("java plugin: cjni_write_plugins: ctoj_value_list failed.");
1618     return (-1);
1619   }
1620
1621   for (j = 0; j < java_plugins_num; j++)
1622     cjni_write_one_plugin (jvm_env, &java_plugins[j], vl_java);
1623
1624   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
1625
1626   return (0);
1627 } /* }}} int cjni_write_plugins */
1628
1629 static int cjni_write (const data_set_t *ds, const value_list_t *vl) /* {{{ */
1630 {
1631   JNIEnv *jvm_env;
1632   JavaVMAttachArgs args;
1633   int status;
1634
1635   if (jvm == NULL)
1636   {
1637     ERROR ("java plugin: cjni_write: jvm == NULL");
1638     return (-1);
1639   }
1640
1641   jvm_env = NULL;
1642   memset (&args, 0, sizeof (args));
1643   args.version = JNI_VERSION_1_2;
1644
1645   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1646   if (status != 0)
1647   {
1648     ERROR ("java plugin: cjni_write: AttachCurrentThread failed with status %i.",
1649         status);
1650     return (-1);
1651   }
1652
1653   cjni_write_plugins (jvm_env, ds, vl);
1654
1655   status = (*jvm)->DetachCurrentThread (jvm);
1656   if (status != 0)
1657   {
1658     ERROR ("java plugin: cjni_write: DetachCurrentThread failed with status %i.",
1659         status);
1660     return (-1);
1661   }
1662
1663   return (0);
1664 } /* }}} int cjni_write */
1665
1666 static int cjni_shutdown_one_plugin (JNIEnv *jvm_env, /* {{{ */
1667     java_plugin_t *jp)
1668 {
1669   int status;
1670
1671   if ((jp == NULL)
1672       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1673       || (jp->m_shutdown == NULL))
1674     return (0);
1675
1676   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1677       jp->m_shutdown);
1678   if (status != 0)
1679   {
1680     ERROR ("cjni_shutdown_one_plugin: Destroying an `%s' object failed "
1681         "with status %i.", jp->class_name, status);
1682     return (-1);
1683   }
1684   jp->flags &= ~CJNI_FLAG_ENABLED;
1685
1686   return (0);
1687 } /* }}} int cjni_shutdown_one_plugin */
1688
1689 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
1690 {
1691   size_t j;
1692
1693   for (j = 0; j < java_plugins_num; j++)
1694     cjni_shutdown_one_plugin (jvm_env, &java_plugins[j]);
1695
1696   return (0);
1697 } /* }}} int cjni_shutdown_plugins */
1698
1699 static int cjni_shutdown (void) /* {{{ */
1700 {
1701   JNIEnv *jvm_env;
1702   JavaVMAttachArgs args;
1703   int status;
1704   size_t i;
1705
1706   if (jvm == NULL)
1707     return (0);
1708
1709   jvm_env = NULL;
1710   memset (&args, 0, sizeof (args));
1711   args.version = JNI_VERSION_1_2;
1712
1713   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1714   if (status != 0)
1715   {
1716     ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
1717         status);
1718     return (-1);
1719   }
1720
1721   cjni_shutdown_plugins (jvm_env);
1722
1723   (*jvm)->DestroyJavaVM (jvm);
1724   jvm = NULL;
1725   jvm_env = NULL;
1726
1727   for (i = 0; i < jvm_argc; i++)
1728   {
1729     sfree (jvm_argv[i]);
1730   }
1731   sfree (jvm_argv);
1732   jvm_argc = 0;
1733
1734   for (i = 0; i < java_plugins_num; i++)
1735   {
1736     sfree (java_plugins[i].class_name);
1737     oconfig_free (java_plugins[i].ci);
1738   }
1739   sfree (java_plugins);
1740   java_plugins_num = 0;
1741
1742   return (0);
1743 } /* }}} int cjni_shutdown */
1744
1745 void module_register (void)
1746 {
1747   plugin_register_complex_config ("java", cjni_config);
1748   plugin_register_init ("java", cjni_init);
1749   plugin_register_read ("java", cjni_read);
1750   plugin_register_write ("java", cjni_write);
1751   plugin_register_shutdown ("java", cjni_shutdown);
1752 } /* void module_register (void) */
1753
1754 /* vim: set sw=2 sts=2 et fdm=marker : */