java plugin: Change the API to use the `DataSet' class.
[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 struct cjni_jvm_env_s /* {{{ */
59 {
60   JNIEnv *jvm_env;
61   int reference_counter;
62 };
63 typedef struct cjni_jvm_env_s cjni_jvm_env_t;
64 /* }}} */
65
66 /*
67  * Global variables
68  */
69 static JavaVM *jvm = NULL;
70 static pthread_key_t jvm_env_key;
71
72 static char **jvm_argv = NULL;
73 static size_t jvm_argc = 0;
74
75 static java_plugin_t *java_plugins     = NULL;
76 static size_t         java_plugins_num = 0;
77
78 /* 
79  * C to Java conversion functions
80  */
81 static int ctoj_string (JNIEnv *jvm_env, /* {{{ */
82     const char *string,
83     jclass class_ptr, jobject object_ptr, const char *method_name)
84 {
85   jmethodID m_set;
86   jstring o_string;
87
88   /* Create a java.lang.String */
89   o_string = (*jvm_env)->NewStringUTF (jvm_env,
90       (string != NULL) ? string : "");
91   if (o_string == NULL)
92   {
93     ERROR ("java plugin: ctoj_string: NewStringUTF failed.");
94     return (-1);
95   }
96
97   /* Search for the `void setFoo (String s)' method. */
98   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
99       method_name, "(Ljava/lang/String;)V");
100   if (m_set == NULL)
101   {
102     ERROR ("java plugin: ctoj_string: Cannot find method `void %s (String)'.",
103         method_name);
104     (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
105     return (-1);
106   }
107
108   /* Call the method. */
109   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, o_string);
110
111   /* Decrease reference counter on the java.lang.String object. */
112   (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
113
114   return (0);
115 } /* }}} int ctoj_string */
116
117 static int ctoj_int (JNIEnv *jvm_env, /* {{{ */
118     jint value,
119     jclass class_ptr, jobject object_ptr, const char *method_name)
120 {
121   jmethodID m_set;
122
123   /* Search for the `void setFoo (int i)' method. */
124   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
125       method_name, "(I)V");
126   if (m_set == NULL)
127   {
128     ERROR ("java plugin: ctoj_int: Cannot find method `void %s (int)'.",
129         method_name);
130     return (-1);
131   }
132
133   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
134
135   return (0);
136 } /* }}} int ctoj_int */
137
138 static int ctoj_long (JNIEnv *jvm_env, /* {{{ */
139     jlong value,
140     jclass class_ptr, jobject object_ptr, const char *method_name)
141 {
142   jmethodID m_set;
143
144   /* Search for the `void setFoo (long l)' method. */
145   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
146       method_name, "(J)V");
147   if (m_set == NULL)
148   {
149     ERROR ("java plugin: ctoj_long: Cannot find method `void %s (long)'.",
150         method_name);
151     return (-1);
152   }
153
154   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, 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   return (0);
178 } /* }}} int ctoj_double */
179
180 /* Convert a jlong to a java.lang.Number */
181 static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */
182 {
183   jclass c_long;
184   jmethodID m_long_constructor;
185
186   /* Look up the java.lang.Long class */
187   c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long");
188   if (c_long == NULL)
189   {
190     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
191         "java.lang.Long class failed.");
192     return (NULL);
193   }
194
195   m_long_constructor = (*jvm_env)->GetMethodID (jvm_env,
196       c_long, "<init>", "(J)V");
197   if (m_long_constructor == NULL)
198   {
199     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
200         "`Long (long)' constructor failed.");
201     return (NULL);
202   }
203
204   return ((*jvm_env)->NewObject (jvm_env,
205         c_long, m_long_constructor, value));
206 } /* }}} jobject ctoj_jlong_to_number */
207
208 /* Convert a jdouble to a java.lang.Number */
209 static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */
210 {
211   jclass c_double;
212   jmethodID m_double_constructor;
213
214   /* Look up the java.lang.Long class */
215   c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double");
216   if (c_double == NULL)
217   {
218     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
219         "java.lang.Double class failed.");
220     return (NULL);
221   }
222
223   m_double_constructor = (*jvm_env)->GetMethodID (jvm_env,
224       c_double, "<init>", "(D)V");
225   if (m_double_constructor == NULL)
226   {
227     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
228         "`Double (double)' constructor failed.");
229     return (NULL);
230   }
231
232   return ((*jvm_env)->NewObject (jvm_env,
233         c_double, m_double_constructor, value));
234 } /* }}} jobject ctoj_jdouble_to_number */
235
236 /* Convert a value_t to a java.lang.Number */
237 static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
238     value_t value, int ds_type)
239 {
240   if (ds_type == DS_TYPE_COUNTER)
241     return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter));
242   else if (ds_type == DS_TYPE_GAUGE)
243     return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge));
244   else
245     return (NULL);
246 } /* }}} jobject ctoj_value_to_number */
247
248 /* Convert a data_source_t to a org.collectd.api.DataSource */
249 static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
250     const data_source_t *dsrc)
251 {
252   jclass c_datasource;
253   jmethodID m_datasource_constructor;
254   jobject o_datasource;
255   int status;
256
257   /* Look up the DataSource class */
258   c_datasource = (*jvm_env)->FindClass (jvm_env,
259       "org.collectd.api.DataSource");
260   if (c_datasource == NULL)
261   {
262     ERROR ("java plugin: ctoj_data_source: "
263         "FindClass (org.collectd.api.DataSource) failed.");
264     return (NULL);
265   }
266
267   /* Lookup the `ValueList ()' constructor. */
268   m_datasource_constructor = (*jvm_env)->GetMethodID (jvm_env, c_datasource,
269       "<init>", "()V");
270   if (m_datasource_constructor == NULL)
271   {
272     ERROR ("java plugin: ctoj_data_source: Cannot find the "
273         "`DataSource ()' constructor.");
274     return (NULL);
275   }
276
277   /* Create a new instance. */
278   o_datasource = (*jvm_env)->NewObject (jvm_env, c_datasource,
279       m_datasource_constructor);
280   if (o_datasource == NULL)
281   {
282     ERROR ("java plugin: ctoj_data_source: "
283         "Creating a new DataSource instance failed.");
284     return (NULL);
285   }
286
287   /* Set name via `void setName (String name)' */
288   status = ctoj_string (jvm_env, dsrc->name,
289       c_datasource, o_datasource, "setName");
290   if (status != 0)
291   {
292     ERROR ("java plugin: ctoj_data_source: "
293         "ctoj_string (setName) failed.");
294     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
295     return (NULL);
296   }
297
298   /* Set type via `void setType (int type)' */
299   status = ctoj_int (jvm_env, dsrc->type,
300       c_datasource, o_datasource, "setType");
301   if (status != 0)
302   {
303     ERROR ("java plugin: ctoj_data_source: "
304         "ctoj_int (setType) failed.");
305     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
306     return (NULL);
307   }
308
309   /* Set min via `void setMin (double min)' */
310   status = ctoj_double (jvm_env, dsrc->min,
311       c_datasource, o_datasource, "setMin");
312   if (status != 0)
313   {
314     ERROR ("java plugin: ctoj_data_source: "
315         "ctoj_double (setMin) failed.");
316     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
317     return (NULL);
318   }
319
320   /* Set max via `void setMax (double max)' */
321   status = ctoj_double (jvm_env, dsrc->max,
322       c_datasource, o_datasource, "setMax");
323   if (status != 0)
324   {
325     ERROR ("java plugin: ctoj_data_source: "
326         "ctoj_double (setMax) failed.");
327     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
328     return (NULL);
329   }
330
331   return (o_datasource);
332 } /* }}} jobject ctoj_data_source */
333
334 /* Convert a oconfig_value_t to a org.collectd.api.OConfigValue */
335 static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
336     oconfig_value_t ocvalue)
337 {
338   jclass c_ocvalue;
339   jmethodID m_ocvalue_constructor;
340   jobject o_argument;
341   jobject o_ocvalue;
342
343   m_ocvalue_constructor = NULL;
344   o_argument = NULL;
345
346   c_ocvalue = (*jvm_env)->FindClass (jvm_env,
347       "org.collectd.api.OConfigValue");
348   if (c_ocvalue == NULL)
349   {
350     ERROR ("java plugin: ctoj_oconfig_value: "
351         "FindClass (org.collectd.api.OConfigValue) failed.");
352     return (NULL);
353   }
354
355   if (ocvalue.type == OCONFIG_TYPE_BOOLEAN)
356   {
357     jboolean tmp_boolean;
358
359     tmp_boolean = (ocvalue.value.boolean == 0) ? JNI_FALSE : JNI_TRUE;
360
361     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
362         "<init>", "(Z)V");
363     if (m_ocvalue_constructor == NULL)
364     {
365       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
366           "`OConfigValue (boolean)' constructor.");
367       return (NULL);
368     }
369
370     return ((*jvm_env)->NewObject (jvm_env,
371           c_ocvalue, m_ocvalue_constructor, tmp_boolean));
372   } /* if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) */
373   else if (ocvalue.type == OCONFIG_TYPE_STRING)
374   {
375     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
376         "<init>", "(Ljava/lang/String;)V");
377     if (m_ocvalue_constructor == NULL)
378     {
379       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
380           "`OConfigValue (String)' constructor.");
381       return (NULL);
382     }
383
384     o_argument = (*jvm_env)->NewStringUTF (jvm_env, ocvalue.value.string);
385     if (o_argument == NULL)
386     {
387       ERROR ("java plugin: ctoj_oconfig_value: "
388           "Creating a String object failed.");
389       return (NULL);
390     }
391   }
392   else if (ocvalue.type == OCONFIG_TYPE_NUMBER)
393   {
394     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
395         "<init>", "(Ljava/lang/Number;)V");
396     if (m_ocvalue_constructor == NULL)
397     {
398       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
399           "`OConfigValue (Number)' constructor.");
400       return (NULL);
401     }
402
403     o_argument = ctoj_jdouble_to_number (jvm_env,
404         (jdouble) ocvalue.value.number);
405     if (o_argument == NULL)
406     {
407       ERROR ("java plugin: ctoj_oconfig_value: "
408           "Creating a Number object failed.");
409       return (NULL);
410     }
411   }
412   else
413   {
414     return (NULL);
415   }
416
417   assert (m_ocvalue_constructor != NULL);
418   assert (o_argument != NULL);
419
420   o_ocvalue = (*jvm_env)->NewObject (jvm_env,
421       c_ocvalue, m_ocvalue_constructor, o_argument);
422   if (o_ocvalue == NULL)
423   {
424     ERROR ("java plugin: ctoj_oconfig_value: "
425         "Creating an OConfigValue object failed.");
426     (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
427     return (NULL);
428   }
429
430   (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
431   return (o_ocvalue);
432 } /* }}} jobject ctoj_oconfig_value */
433
434 /* Convert a oconfig_item_t to a org.collectd.api.OConfigItem */
435 static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
436     const oconfig_item_t *ci)
437 {
438   jclass c_ocitem;
439   jmethodID m_ocitem_constructor;
440   jmethodID m_addvalue;
441   jmethodID m_addchild;
442   jobject o_key;
443   jobject o_ocitem;
444   int i;
445
446   c_ocitem = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.OConfigItem");
447   if (c_ocitem == NULL)
448   {
449     ERROR ("java plugin: ctoj_oconfig_item: "
450         "FindClass (org.collectd.api.OConfigItem) failed.");
451     return (NULL);
452   }
453
454   /* Get the required methods: m_ocitem_constructor, m_addvalue, and m_addchild
455    * {{{ */
456   m_ocitem_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
457       "<init>", "(Ljava/lang/String;)V");
458   if (m_ocitem_constructor == NULL)
459   {
460     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
461         "`OConfigItem (String)' constructor.");
462     return (NULL);
463   }
464
465   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
466       "addValue", "(Lorg/collectd/api/OConfigValue;)V");
467   if (m_addvalue == NULL)
468   {
469     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
470         "`addValue (OConfigValue)' method.");
471     return (NULL);
472   }
473
474   m_addchild = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
475       "addChild", "(Lorg/collectd/api/OConfigItem;)V");
476   if (m_addchild == NULL)
477   {
478     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
479         "`addChild (OConfigItem)' method.");
480     return (NULL);
481   }
482   /* }}} */
483
484   /* Create a String object with the key.
485    * Needed for calling the constructor. */
486   o_key = (*jvm_env)->NewStringUTF (jvm_env, ci->key);
487   if (o_key == NULL)
488   {
489     ERROR ("java plugin: ctoj_oconfig_item: "
490         "Creating String object failed.");
491     return (NULL);
492   }
493
494   /* Create an OConfigItem object */
495   o_ocitem = (*jvm_env)->NewObject (jvm_env,
496       c_ocitem, m_ocitem_constructor, o_key);
497   if (o_ocitem == NULL)
498   {
499     ERROR ("java plugin: ctoj_oconfig_item: "
500         "Creating an OConfigItem object failed.");
501     (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
502     return (NULL);
503   }
504
505   /* We don't need the String object any longer.. */
506   (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
507
508   /* Call OConfigItem.addValue for each value */
509   for (i = 0; i < ci->values_num; i++) /* {{{ */
510   {
511     jobject o_value;
512
513     o_value = ctoj_oconfig_value (jvm_env, ci->values[i]);
514     if (o_value == NULL)
515     {
516       ERROR ("java plugin: ctoj_oconfig_item: "
517           "Creating an OConfigValue object failed.");
518       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
519       return (NULL);
520     }
521
522     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_value);
523     (*jvm_env)->DeleteLocalRef (jvm_env, o_value);
524   } /* }}} for (i = 0; i < ci->values_num; i++) */
525
526   /* Call OConfigItem.addChild for each child */
527   for (i = 0; i < ci->children_num; i++) /* {{{ */
528   {
529     jobject o_child;
530
531     o_child = ctoj_oconfig_item (jvm_env, ci->children + i);
532     if (o_child == NULL)
533     {
534       ERROR ("java plugin: ctoj_oconfig_item: "
535           "Creating an OConfigItem object failed.");
536       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
537       return (NULL);
538     }
539
540     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_child);
541     (*jvm_env)->DeleteLocalRef (jvm_env, o_child);
542   } /* }}} for (i = 0; i < ci->children_num; i++) */
543
544   return (o_ocitem);
545 } /* }}} jobject ctoj_oconfig_item */
546
547 /* Convert a data_set_t to a org.collectd.api.DataSet */
548 static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
549 {
550   jclass c_dataset;
551   jmethodID m_constructor;
552   jmethodID m_add;
553   jobject o_type;
554   jobject o_dataset;
555   int i;
556
557   /* Look up the org.collectd.api.DataSet class */
558   c_dataset = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.DataSet");
559   if (c_dataset == NULL)
560   {
561     ERROR ("java plugin: ctoj_data_set: Looking up the "
562         "org.collectd.api.DataSet class failed.");
563     return (NULL);
564   }
565
566   /* Search for the `DataSet (String type)' constructor. */
567   m_constructor = (*jvm_env)->GetMethodID (jvm_env,
568       c_dataset, "<init>", "(Ljava.lang.String;)V");
569   if (m_constructor == NULL)
570   {
571     ERROR ("java plugin: ctoj_data_set: Looking up the "
572         "`DataSet (String)' constructor failed.");
573     return (NULL);
574   }
575
576   /* Search for the `void addDataSource (DataSource)' method. */
577   m_add = (*jvm_env)->GetMethodID (jvm_env,
578       c_dataset, "addDataSource", "(Lorg.collectd.api.DataSource;)V");
579   if (m_add == NULL)
580   {
581     ERROR ("java plugin: ctoj_data_set: Looking up the "
582         "`addDataSource (DataSource)' method failed.");
583     return (NULL);
584   }
585
586   o_type = (*jvm_env)->NewStringUTF (jvm_env, ds->type);
587   if (o_type == NULL)
588   {
589     ERROR ("java plugin: ctoj_data_set: Creating a String object failed.");
590     return (NULL);
591   }
592
593   o_dataset = (*jvm_env)->NewObject (jvm_env,
594       c_dataset, m_constructor, o_type);
595   if (o_dataset == NULL)
596   {
597     ERROR ("java plugin: ctoj_data_set: Creating a DataSet object failed.");
598     (*jvm_env)->DeleteLocalRef (jvm_env, o_type);
599     return (NULL);
600   }
601
602   /* Decrease reference counter on the java.lang.String object. */
603   (*jvm_env)->DeleteLocalRef (jvm_env, o_type);
604
605   for (i = 0; i < ds->ds_num; i++)
606   {
607     jobject o_datasource;
608
609     o_datasource = ctoj_data_source (jvm_env, ds->ds + i);
610     if (o_datasource == NULL)
611     {
612       ERROR ("java plugin: ctoj_data_set: ctoj_data_source (%s.%s) failed",
613           ds->type, ds->ds[i].name);
614       (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
615       return (NULL);
616     }
617
618     (*jvm_env)->CallVoidMethod (jvm_env, o_dataset, m_add, o_datasource);
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_setdataset;
661   jobject o_dataset;
662
663   /* Look for the `void setDataSource (List<DataSource> ds)' method. */
664   m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
665       "setDataSet", "(Lorg.collectd.api.DataSet;)V");
666   if (m_setdataset == NULL)
667   {
668     ERROR ("java plugin: ctoj_value_list_add_data_set: "
669         "Cannot find the `void setDataSet (DataSet)' method.");
670     return (-1);
671   }
672
673   /* Create a DataSet 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_setdataset, 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.api.ValueList");
705   if (c_valuelist == NULL)
706   {
707     ERROR ("java plugin: ctoj_value_list: "
708         "FindClass (org.collectd.api.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   sstrncpy (buffer, c_str, buffer_size);
834
835   (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str);
836   (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
837
838   return (0);
839 } /* }}} int jtoc_string */
840
841 static int jtoc_long (JNIEnv *jvm_env, /* {{{ */
842     jlong *ret_value,
843     jclass class_ptr, jobject object_ptr, const char *method_name)
844 {
845   jmethodID method_id;
846
847   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
848       method_name, "()J");
849   if (method_id == NULL)
850   {
851     ERROR ("java plugin: jtoc_long: Cannot find method `long %s ()'.",
852         method_name);
853     return (-1);
854   }
855
856   *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id);
857
858   return (0);
859 } /* }}} int jtoc_long */
860
861 static int jtoc_double (JNIEnv *jvm_env, /* {{{ */
862     jdouble *ret_value,
863     jclass class_ptr, jobject object_ptr, const char *method_name)
864 {
865   jmethodID method_id;
866
867   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
868       method_name, "()D");
869   if (method_id == NULL)
870   {
871     ERROR ("java plugin: jtoc_string: Cannot find method `double %s ()'.",
872         method_name);
873     return (-1);
874   }
875
876   *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id);
877
878   return (0);
879 } /* }}} int jtoc_double */
880
881 static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
882     value_t *ret_value, int ds_type, jobject object_ptr)
883 {
884   jclass class_ptr;
885   int status;
886
887   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
888
889   if (ds_type == DS_TYPE_COUNTER)
890   {
891     jlong tmp_long;
892
893     status = jtoc_long (jvm_env, &tmp_long,
894         class_ptr, object_ptr, "longValue");
895     if (status != 0)
896     {
897       ERROR ("java plugin: jtoc_value: "
898           "jtoc_long failed.");
899       return (-1);
900     }
901     (*ret_value).counter = (counter_t) tmp_long;
902   }
903   else
904   {
905     jdouble tmp_double;
906
907     status = jtoc_double (jvm_env, &tmp_double,
908         class_ptr, object_ptr, "doubleValue");
909     if (status != 0)
910     {
911       ERROR ("java plugin: jtoc_value: "
912           "jtoc_double failed.");
913       return (-1);
914     }
915     (*ret_value).gauge = (gauge_t) tmp_double;
916   }
917
918   return (0);
919 } /* }}} int jtoc_value */
920
921 static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */
922     const data_set_t *ds, value_list_t *vl,
923     jclass class_ptr, jobject object_ptr)
924 {
925   jmethodID m_getvalues;
926   jmethodID m_toarray;
927   jobject o_list;
928   jobjectArray o_number_array;
929
930   value_t *values;
931   int values_num;
932   int i;
933
934   values_num = ds->ds_num;
935
936   values = NULL;
937   o_number_array = NULL;
938   o_list = NULL;
939
940 #define BAIL_OUT(status) \
941   free (values); \
942   if (o_number_array != NULL) \
943     (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); \
944   if (o_list != NULL) \
945     (*jvm_env)->DeleteLocalRef (jvm_env, o_list); \
946   return (status);
947
948   /* Call: List<Number> ValueList.getValues () */
949   m_getvalues = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
950       "getValues", "()Ljava/util/List;");
951   if (m_getvalues == NULL)
952   {
953     ERROR ("java plugin: jtoc_values_array: "
954         "Cannot find method `List getValues ()'.");
955     BAIL_OUT (-1);
956   }
957
958   o_list = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, m_getvalues);
959   if (o_list == NULL)
960   {
961     ERROR ("java plugin: jtoc_values_array: "
962         "CallObjectMethod (getValues) failed.");
963     BAIL_OUT (-1);
964   }
965
966   /* Call: Number[] List.toArray () */
967   m_toarray = (*jvm_env)->GetMethodID (jvm_env,
968       (*jvm_env)->GetObjectClass (jvm_env, o_list),
969       "toArray", "()[Ljava/lang/Object;");
970   if (m_toarray == NULL)
971   {
972     ERROR ("java plugin: jtoc_values_array: "
973         "Cannot find method `Object[] toArray ()'.");
974     BAIL_OUT (-1);
975   }
976
977   o_number_array = (*jvm_env)->CallObjectMethod (jvm_env, o_list, m_toarray);
978   if (o_number_array == NULL)
979   {
980     ERROR ("java plugin: jtoc_values_array: "
981         "CallObjectMethod (toArray) failed.");
982     BAIL_OUT (-1);
983   }
984
985   values = calloc (values_num, sizeof (value_t));
986   if (values == NULL)
987   {
988     ERROR ("java plugin: jtoc_values_array: calloc failed.");
989     BAIL_OUT (-1);
990   }
991
992   for (i = 0; i < values_num; i++)
993   {
994     jobject o_number;
995     int status;
996
997     o_number = (*jvm_env)->GetObjectArrayElement (jvm_env,
998         o_number_array, (jsize) i);
999     if (o_number == NULL)
1000     {
1001       ERROR ("java plugin: jtoc_values_array: "
1002           "GetObjectArrayElement (%i) failed.", i);
1003       BAIL_OUT (-1);
1004     }
1005
1006     status = jtoc_value (jvm_env, values + i, ds->ds[i].type, o_number);
1007     if (status != 0)
1008     {
1009       ERROR ("java plugin: jtoc_values_array: "
1010           "jtoc_value (%i) failed.", i);
1011       BAIL_OUT (-1);
1012     }
1013   } /* for (i = 0; i < values_num; i++) */
1014
1015   vl->values = values;
1016   vl->values_len = values_num;
1017
1018 #undef BAIL_OUT
1019   (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array);
1020   (*jvm_env)->DeleteLocalRef (jvm_env, o_list);
1021   return (0);
1022 } /* }}} int jtoc_values_array */
1023
1024 /* Convert a org.collectd.api.ValueList to a value_list_t. */
1025 static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
1026     jobject object_ptr)
1027 {
1028   jclass class_ptr;
1029   int status;
1030   jlong tmp_long;
1031   const data_set_t *ds;
1032
1033   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1034   if (class_ptr == NULL)
1035   {
1036     ERROR ("java plugin: jtoc_value_list: GetObjectClass failed.");
1037     return (-1);
1038   }
1039
1040 #define SET_STRING(buffer,method) do { \
1041   status = jtoc_string (jvm_env, buffer, sizeof (buffer), \
1042       class_ptr, object_ptr, method); \
1043   if (status != 0) { \
1044     ERROR ("java plugin: jtoc_value_list: jtoc_string (%s) failed.", \
1045         method); \
1046     return (-1); \
1047   } } while (0)
1048
1049   SET_STRING(vl->type, "getType");
1050
1051   ds = plugin_get_ds (vl->type);
1052   if (ds == NULL)
1053   {
1054     ERROR ("java plugin: jtoc_value_list: Data-set `%s' is not defined. "
1055         "Please consult the types.db(5) manpage for mor information.",
1056         vl->type);
1057     return (-1);
1058   }
1059
1060   SET_STRING(vl->host, "getHost");
1061   SET_STRING(vl->plugin, "getPlugin");
1062   SET_STRING(vl->plugin_instance, "getPluginInstance");
1063   SET_STRING(vl->type_instance, "getTypeInstance");
1064
1065 #undef SET_STRING
1066
1067   status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime");
1068   if (status != 0)
1069   {
1070     ERROR ("java plugin: jtoc_value_list: jtoc_long (getTime) failed.");
1071     return (-1);
1072   }
1073   /* Java measures time in milliseconds. */
1074   vl->time = (time_t) (tmp_long / ((jlong) 1000));
1075
1076   status = jtoc_long (jvm_env, &tmp_long,
1077       class_ptr, object_ptr, "getInterval");
1078   if (status != 0)
1079   {
1080     ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed.");
1081     return (-1);
1082   }
1083   vl->interval = (int) tmp_long;
1084
1085   status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
1086   if (status != 0)
1087   {
1088     ERROR ("java plugin: jtoc_value_list: jtoc_values_array failed.");
1089     return (-1);
1090   }
1091
1092   return (0);
1093 } /* }}} int jtoc_value_list */
1094
1095 /* 
1096  * Functions accessible from Java
1097  */
1098 static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */
1099     jobject this, jobject java_vl)
1100 {
1101   value_list_t vl = VALUE_LIST_INIT;
1102   int status;
1103
1104   DEBUG ("cjni_api_dispatch_values: java_vl = %p;", (void *) java_vl);
1105
1106   status = jtoc_value_list (jvm_env, &vl, java_vl);
1107   if (status != 0)
1108   {
1109     ERROR ("java plugin: cjni_api_dispatch_values: jtoc_value_list failed.");
1110     return (-1);
1111   }
1112
1113   status = plugin_dispatch_values (&vl);
1114
1115   sfree (vl.values);
1116
1117   return (status);
1118 } /* }}} jint cjni_api_dispatch_values */
1119
1120 static jobject JNICALL cjni_api_get_ds (JNIEnv *jvm_env, /* {{{ */
1121     jobject this, jobject o_string_type)
1122 {
1123   const char *ds_name;
1124   const data_set_t *ds;
1125   jobject o_dataset;
1126
1127   ds_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_string_type, 0);
1128   if (ds_name == NULL)
1129   {
1130     ERROR ("java plugin: cjni_api_get_ds: GetStringUTFChars failed.");
1131     return (NULL);
1132   }
1133
1134   ds = plugin_get_ds (ds_name);
1135   DEBUG ("java plugin: cjni_api_get_ds: "
1136       "plugin_get_ds (%s) = %p;", ds_name, (void *) ds);
1137
1138   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_string_type, ds_name);
1139
1140   if (ds == NULL)
1141     return (NULL);
1142
1143   o_dataset = ctoj_data_set (jvm_env, ds);
1144   return (o_dataset);
1145 } /* }}} jint cjni_api_get_ds */
1146
1147 static JNINativeMethod jni_api_functions[] =
1148 {
1149   { "DispatchValues", "(Lorg/collectd/api/ValueList;)I", cjni_api_dispatch_values },
1150   { "GetDS",          "(Ljava/lang/String;)Lorg/collectd/api/DataSet;", cjni_api_get_ds }
1151 };
1152 static size_t jni_api_functions_num = sizeof (jni_api_functions)
1153   / sizeof (jni_api_functions[0]);
1154
1155 /*
1156  * Functions
1157  */
1158 static JNIEnv *cjni_thread_attach (void) /* {{{ */
1159 {
1160   cjni_jvm_env_t *cjni_env;
1161   JNIEnv *jvm_env;
1162
1163   cjni_env = pthread_getspecific (jvm_env_key);
1164   if (cjni_env == NULL)
1165   {
1166     /* This pointer is free'd in `cjni_jvm_env_destroy'. */
1167     cjni_env = (cjni_jvm_env_t *) malloc (sizeof (*cjni_env));
1168     if (cjni_env == NULL)
1169     {
1170       ERROR ("java plugin: cjni_thread_attach: malloc failed.");
1171       return (NULL);
1172     }
1173     memset (cjni_env, 0, sizeof (*cjni_env));
1174     cjni_env->reference_counter = 0;
1175     cjni_env->jvm_env = NULL;
1176
1177     pthread_setspecific (jvm_env_key, cjni_env);
1178   }
1179
1180   if (cjni_env->reference_counter > 0)
1181   {
1182     cjni_env->reference_counter++;
1183     jvm_env = cjni_env->jvm_env;
1184   }
1185   else
1186   {
1187     int status;
1188     JavaVMAttachArgs args;
1189
1190     assert (cjni_env->jvm_env == NULL);
1191
1192     memset (&args, 0, sizeof (args));
1193     args.version = JNI_VERSION_1_2;
1194
1195     status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args);
1196     if (status != 0)
1197     {
1198       ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed "
1199           "with status %i.", status);
1200       return (NULL);
1201     }
1202
1203     cjni_env->reference_counter = 1;
1204     cjni_env->jvm_env = jvm_env;
1205   }
1206
1207   DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i",
1208       cjni_env->reference_counter);
1209   assert (jvm_env != NULL);
1210   return (jvm_env);
1211 } /* }}} JNIEnv *cjni_thread_attach */
1212
1213 static int cjni_thread_detach (void) /* {{{ */
1214 {
1215   cjni_jvm_env_t *cjni_env;
1216   int status;
1217
1218   cjni_env = pthread_getspecific (jvm_env_key);
1219   if (cjni_env == NULL)
1220   {
1221     ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed.");
1222     return (-1);
1223   }
1224
1225   assert (cjni_env->reference_counter > 0);
1226   assert (cjni_env->jvm_env != NULL);
1227
1228   cjni_env->reference_counter--;
1229   DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i",
1230       cjni_env->reference_counter);
1231
1232   if (cjni_env->reference_counter > 0)
1233     return (0);
1234
1235   status = (*jvm)->DetachCurrentThread (jvm);
1236   if (status != 0)
1237   {
1238     ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed "
1239         "with status %i.", status);
1240   }
1241
1242   cjni_env->reference_counter = 0;
1243   cjni_env->jvm_env = NULL;
1244
1245   return (0);
1246 } /* }}} JNIEnv *cjni_thread_attach */
1247
1248 static void cjni_jvm_env_destroy (void *args) /* {{{ */
1249 {
1250   cjni_jvm_env_t *cjni_env;
1251
1252   if (args == NULL)
1253     return;
1254
1255   cjni_env = (cjni_jvm_env_t *) args;
1256
1257   if (cjni_env->reference_counter > 0)
1258   {
1259     ERROR ("java plugin: cjni_jvm_env_destroy: "
1260         "cjni_env->reference_counter = %i;", cjni_env->reference_counter);
1261   }
1262
1263   if (cjni_env->jvm_env != NULL)
1264   {
1265     ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;",
1266         (void *) cjni_env->jvm_env);
1267   }
1268
1269   /* The pointer is allocated in `cjni_thread_attach' */
1270   free (cjni_env);
1271 } /* }}} void cjni_jvm_env_destroy */
1272
1273 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
1274 {
1275   char **tmp;
1276
1277   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1278   {
1279     WARNING ("java plugin: `JVMArg' needs exactly one string argument.");
1280     return (-1);
1281   }
1282
1283   tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1));
1284   if (tmp == NULL)
1285   {
1286     ERROR ("java plugin: realloc failed.");
1287     return (-1);
1288   }
1289   jvm_argv = tmp;
1290
1291   jvm_argv[jvm_argc] = strdup (ci->values[0].value.string);
1292   if (jvm_argv[jvm_argc] == NULL)
1293   {
1294     ERROR ("java plugin: strdup failed.");
1295     return (-1);
1296   }
1297   jvm_argc++;
1298
1299   return (0);
1300 } /* }}} int cjni_config_add_jvm_arg */
1301
1302 static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
1303 {
1304   java_plugin_t *jp;
1305
1306   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1307   {
1308     WARNING ("java plugin: `LoadPlugin' needs exactly one string argument.");
1309     return (-1);
1310   }
1311
1312   jp = (java_plugin_t *) realloc (java_plugins,
1313       sizeof (*java_plugins) * (java_plugins_num + 1));
1314   if (jp == NULL)
1315   {
1316     ERROR ("java plugin: realloc failed.");
1317     return (-1);
1318   }
1319   java_plugins = jp;
1320   jp = java_plugins + java_plugins_num;
1321
1322   memset (jp, 0, sizeof (*jp));
1323   jp->class_name = strdup (ci->values[0].value.string);
1324   if (jp->class_name == NULL)
1325   {
1326     ERROR ("java plugin: strdup failed.");
1327     return (-1);
1328   }
1329
1330   jp->class_ptr  = NULL;
1331   jp->object_ptr = NULL;
1332   jp->ci         = NULL;
1333   jp->flags      = 0;
1334   jp->m_config   = NULL;
1335   jp->m_init     = NULL;
1336   jp->m_read     = NULL;
1337   jp->m_write    = NULL;
1338   jp->m_shutdown = NULL;
1339
1340   java_plugins_num++;
1341
1342   return (0);
1343 } /* }}} int cjni_config_load_plugin */
1344
1345 static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
1346 {
1347   size_t i;
1348   const char *class_name;
1349
1350   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1351   {
1352     WARNING ("java plugin: `Plugin' blocks "
1353         "need exactly one string argument.");
1354     return (-1);
1355   }
1356
1357   class_name = ci->values[0].value.string;
1358   for (i = 0; i < java_plugins_num; i++)
1359     if (strcmp (java_plugins[i].class_name, class_name) == 0)
1360       break;
1361
1362   if (i >= java_plugins_num)
1363   {
1364     WARNING ("java plugin: Configuration block for the `%s' plugin found, "
1365         "but the plugin has not been loaded. Please note, that the class "
1366         "name is case-sensitive!",
1367         class_name);
1368     return (0);
1369   }
1370
1371   if (java_plugins[i].ci != NULL)
1372   {
1373     WARNING ("java plugin: There are more than one <Plugin> blocks for the "
1374         "`%s' plugin. This is currently not supported - only the first block "
1375         "will be used!",
1376         class_name);
1377     return (0);
1378   }
1379
1380   java_plugins[i].ci = oconfig_clone (ci);
1381   if (java_plugins[i].ci == NULL)
1382   {
1383     ERROR ("java plugin: cjni_config_plugin_block: "
1384         "oconfig_clone failed for `%s'.",
1385         class_name);
1386     return (-1);
1387   }
1388
1389   DEBUG ("java plugin: cjni_config_plugin_block: "
1390       "Successfully copied config for `%s'.",
1391       class_name);
1392
1393   return (0);
1394 } /* }}} int cjni_config_plugin_block */
1395
1396 static int cjni_config (oconfig_item_t *ci) /* {{{ */
1397 {
1398   int success;
1399   int errors;
1400   int status;
1401   int i;
1402
1403   success = 0;
1404   errors = 0;
1405
1406   for (i = 0; i < ci->children_num; i++)
1407   {
1408     oconfig_item_t *child = ci->children + i;
1409
1410     if (strcasecmp ("JVMArg", child->key) == 0)
1411     {
1412       status = cjni_config_add_jvm_arg (child);
1413       if (status == 0)
1414         success++;
1415       else
1416         errors++;
1417     }
1418     else if (strcasecmp ("LoadPlugin", child->key) == 0)
1419     {
1420       status = cjni_config_load_plugin (child);
1421       if (status == 0)
1422         success++;
1423       else
1424         errors++;
1425     }
1426     else if (strcasecmp ("Plugin", child->key) == 0)
1427     {
1428       status = cjni_config_plugin_block (child);
1429       if (status == 0)
1430         success++;
1431       else
1432         errors++;
1433     }
1434     else
1435     {
1436       WARNING ("java plugin: Option `%s' not allowed here.", child->key);
1437       errors++;
1438     }
1439   }
1440
1441   if ((success == 0) && (errors > 0))
1442   {
1443     ERROR ("java plugin: All statements failed.");
1444     return (-1);
1445   }
1446
1447   return (0);
1448 } /* }}} int cjni_config */
1449
1450 static int cjni_write_one_plugin (JNIEnv *jvm_env, /* {{{ */
1451     java_plugin_t *jp, jobject vl_java)
1452 {
1453   int status;
1454
1455   if ((jp == NULL)
1456       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1457       || (jp->m_write == NULL))
1458     return (0);
1459
1460   DEBUG ("java plugin: Calling: %s.Write(ValueList)", jp->class_name);
1461
1462   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1463       jp->m_write, vl_java);
1464   if (status != 0)
1465   {
1466     ERROR ("java plugin: cjni_write_one_plugin: "
1467         "Calling `Write' on an `%s' object failed with status %i.",
1468         jp->class_name, status);
1469     return (-1);
1470   }
1471
1472   return (0);
1473 } /* }}} int cjni_write_one_plugin */
1474
1475 static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
1476     user_data_t __attribute__((unused)) *user_data)
1477 {
1478   JNIEnv *jvm_env;
1479   java_plugin_t *jp;
1480   jobject vl_java;
1481   int status;
1482
1483   if (jvm == NULL)
1484   {
1485     ERROR ("java plugin: cjni_write: jvm == NULL");
1486     return (-1);
1487   }
1488
1489   if ((user_data == NULL) || (user_data->data == NULL))
1490   {
1491     ERROR ("java plugin: cjni_read: Invalid user data.");
1492     return (-1);
1493   }
1494
1495   jvm_env = cjni_thread_attach ();
1496   if (jvm_env == NULL)
1497     return (-1);
1498
1499   vl_java = ctoj_value_list (jvm_env, ds, vl);
1500   if (vl_java == NULL)
1501   {
1502     ERROR ("java plugin: cjni_write_plugins: ctoj_value_list failed.");
1503     return (-1);
1504   }
1505
1506   jp = (java_plugin_t *) user_data->data;
1507
1508   cjni_write_one_plugin (jvm_env, jp, vl_java);
1509
1510   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
1511
1512   status = cjni_thread_detach ();
1513   if (status != 0)
1514     return (-1);
1515
1516   return (0);
1517 } /* }}} int cjni_write */
1518
1519 static int cjni_shutdown_one_plugin (JNIEnv *jvm_env, /* {{{ */
1520     java_plugin_t *jp)
1521 {
1522   int status;
1523
1524   if ((jp == NULL)
1525       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1526       || (jp->m_shutdown == NULL))
1527     return (0);
1528
1529   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1530       jp->m_shutdown);
1531   if (status != 0)
1532   {
1533     ERROR ("cjni_shutdown_one_plugin: Destroying an `%s' object failed "
1534         "with status %i.", jp->class_name, status);
1535     return (-1);
1536   }
1537   jp->flags &= ~CJNI_FLAG_ENABLED;
1538
1539   return (0);
1540 } /* }}} int cjni_shutdown_one_plugin */
1541
1542 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
1543 {
1544   size_t j;
1545
1546   for (j = 0; j < java_plugins_num; j++)
1547     cjni_shutdown_one_plugin (jvm_env, &java_plugins[j]);
1548
1549   return (0);
1550 } /* }}} int cjni_shutdown_plugins */
1551
1552 static int cjni_shutdown (void) /* {{{ */
1553 {
1554   JNIEnv *jvm_env;
1555   JavaVMAttachArgs args;
1556   int status;
1557   size_t i;
1558
1559   if (jvm == NULL)
1560     return (0);
1561
1562   jvm_env = NULL;
1563   memset (&args, 0, sizeof (args));
1564   args.version = JNI_VERSION_1_2;
1565
1566   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1567   if (status != 0)
1568   {
1569     ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
1570         status);
1571     return (-1);
1572   }
1573
1574   cjni_shutdown_plugins (jvm_env);
1575
1576   (*jvm)->DestroyJavaVM (jvm);
1577   jvm = NULL;
1578   jvm_env = NULL;
1579
1580   pthread_key_delete (jvm_env_key);
1581
1582   for (i = 0; i < jvm_argc; i++)
1583   {
1584     sfree (jvm_argv[i]);
1585   }
1586   sfree (jvm_argv);
1587   jvm_argc = 0;
1588
1589   for (i = 0; i < java_plugins_num; i++)
1590   {
1591     sfree (java_plugins[i].class_name);
1592     oconfig_free (java_plugins[i].ci);
1593   }
1594   sfree (java_plugins);
1595   java_plugins_num = 0;
1596
1597   return (0);
1598 } /* }}} int cjni_shutdown */
1599
1600 static int cjni_read_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
1601 {
1602   int status;
1603
1604   if ((jp == NULL)
1605       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1606       || (jp->m_read == NULL))
1607     return (0);
1608
1609   DEBUG ("java plugin: Calling: %s.Read()", jp->class_name);
1610
1611   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1612       jp->m_read);
1613   if (status != 0)
1614   {
1615     ERROR ("java plugin: cjni_read_one_plugin: "
1616         "Calling `Read' on an `%s' object failed with status %i.",
1617         jp->class_name, status);
1618     return (-1);
1619   }
1620
1621   return (0);
1622 } /* }}} int cjni_read_one_plugin */
1623
1624 static int cjni_read (user_data_t *user_data) /* {{{ */
1625 {
1626   JNIEnv *jvm_env;
1627   java_plugin_t *jp;
1628   int status;
1629
1630   if (jvm == NULL)
1631   {
1632     ERROR ("java plugin: cjni_read: jvm == NULL");
1633     return (-1);
1634   }
1635
1636   if ((user_data == NULL) || (user_data->data == NULL))
1637   {
1638     ERROR ("java plugin: cjni_read: Invalid user data.");
1639     return (-1);
1640   }
1641
1642   jvm_env = cjni_thread_attach ();
1643   if (jvm_env == NULL)
1644     return (-1);
1645
1646   jp = (java_plugin_t *) user_data->data;
1647
1648   cjni_read_one_plugin (jvm_env, jp);
1649
1650   status = cjni_thread_detach ();
1651   if (status != 0)
1652     return (-1);
1653
1654   return (0);
1655 } /* }}} int cjni_read */
1656
1657 static int cjni_init_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
1658 {
1659   char plugin_name[128];
1660   jmethodID constructor_id;
1661   int status;
1662
1663   jp->class_ptr = (*jvm_env)->FindClass (jvm_env, jp->class_name);
1664   if (jp->class_ptr == NULL)
1665   {
1666     ERROR ("cjni_init_one_plugin: FindClass (%s) failed.",
1667         jp->class_name);
1668     return (-1);
1669   }
1670
1671   constructor_id = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1672       "<init>", "()V");
1673   if (constructor_id == NULL)
1674   {
1675     ERROR ("cjni_init_one_plugin: Could not find the constructor for `%s'.",
1676         jp->class_name);
1677     return (-1);
1678   }
1679
1680   jp->object_ptr = (*jvm_env)->NewObject (jvm_env, jp->class_ptr,
1681       constructor_id);
1682   if (jp->object_ptr == NULL)
1683   {
1684     ERROR ("cjni_init_one_plugin: Could create a new `%s' object.",
1685         jp->class_name);
1686     return (-1);
1687   }
1688
1689   jp->m_config = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1690       "Config", "(Lorg/collectd/api/OConfigItem;)I");
1691   DEBUG ("java plugin: cjni_init_one_plugin: "
1692       "jp->class_name = %s; jp->m_config = %p;",
1693       jp->class_name, (void *) jp->m_config);
1694
1695   jp->m_init = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1696       "Init", "()I");
1697   DEBUG ("java plugin: cjni_init_one_plugin: "
1698       "jp->class_name = %s; jp->m_init = %p;",
1699       jp->class_name, (void *) jp->m_init);
1700
1701   jp->m_read = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1702       "Read", "()I");
1703   DEBUG ("java plugin: cjni_init_one_plugin: "
1704       "jp->class_name = %s; jp->m_read = %p;",
1705       jp->class_name, (void *) jp->m_read);
1706
1707   jp->m_write = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1708       "Write", "(Lorg/collectd/api/ValueList;)I");
1709   DEBUG ("java plugin: cjni_init_one_plugin: "
1710       "jp->class_name = %s; jp->m_write = %p;",
1711       jp->class_name, (void *) jp->m_write);
1712
1713   jp->m_shutdown = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1714       "Shutdown", "()I");
1715   DEBUG ("java plugin: cjni_init_one_plugin: "
1716       "jp->class_name = %s; jp->m_shutdown = %p;",
1717       jp->class_name, (void *) jp->m_shutdown);
1718
1719   if (jp->ci != NULL)
1720   {
1721     if (jp->m_config == NULL)
1722     {
1723       WARNING ("java plugin: Configuration for the `%s' plugin is present, "
1724           "but plugin doesn't provide a configuration method.",
1725           jp->class_name);
1726     }
1727     else /* if (jp->m_config != NULL) */
1728     {
1729       jobject o_ocitem;
1730
1731       o_ocitem = ctoj_oconfig_item (jvm_env, jp->ci);
1732       if (o_ocitem == NULL)
1733       {
1734         ERROR ("java plugin: Creating an OConfigItem object failed. "
1735             "Can't pass configuration information to the `%s' plugin!",
1736             jp->class_name);
1737       }
1738       else /* if (o_ocitem != NULL) */
1739       {
1740         status = (*jvm_env)->CallIntMethod (jvm_env,
1741             jp->object_ptr, jp->m_config, o_ocitem);
1742         if (status != 0)
1743         {
1744           ERROR ("java plugin: cjni_init_one_plugin: "
1745               "Configuring the `%s' object failed with status %i.",
1746               jp->class_name, status);
1747           (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
1748           return (-1);
1749         }
1750         (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
1751       } /* if (o_ocitem != NULL) */
1752     } /* if (jp->m_config != NULL) */
1753   } /* if (jp->ci != NULL) */
1754
1755   if (jp->m_init != NULL)
1756   {
1757     status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1758         jp->m_init);
1759     if (status != 0)
1760     {
1761       ERROR ("java plugin: cjni_init_one_plugin: "
1762         "Initializing `%s' object failed with status %i.",
1763         jp->class_name, status);
1764       return (-1);
1765     }
1766   }
1767   jp->flags |= CJNI_FLAG_ENABLED;
1768
1769   ssnprintf (plugin_name, sizeof (plugin_name), "java:%s", jp->class_name);
1770   if (jp->m_read != NULL)
1771   {
1772     user_data_t ud;
1773
1774     memset (&ud, 0, sizeof (ud));
1775     ud.data = jp;
1776     ud.free_func = NULL;
1777
1778     plugin_register_complex_read (plugin_name, cjni_read, &ud);
1779   }
1780
1781   if (jp->m_write != NULL)
1782   {
1783     user_data_t ud;
1784
1785     memset (&ud, 0, sizeof (ud));
1786     ud.data = jp;
1787     ud.free_func = NULL;
1788
1789     plugin_register_write (plugin_name, cjni_write, &ud);
1790   }
1791
1792   return (0);
1793 } /* }}} int cjni_init_one_plugin */
1794
1795 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
1796 {
1797   int have_shutdown;
1798   size_t j;
1799
1800   have_shutdown = 0;
1801
1802   for (j = 0; j < java_plugins_num; j++)
1803   {
1804     cjni_init_one_plugin (jvm_env, &java_plugins[j]);
1805
1806     if (java_plugins[j].m_shutdown != NULL)
1807       have_shutdown++;
1808   }
1809
1810   if (have_shutdown > 0)
1811     plugin_register_shutdown ("java", cjni_shutdown);
1812
1813   return (0);
1814 } /* }}} int cjni_init_plugins */
1815
1816 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
1817 {
1818   jclass api_class_ptr;
1819   int status;
1820
1821   api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.CollectdAPI");
1822   if (api_class_ptr == NULL)
1823   {
1824     ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.CollectdAPI'.");
1825     return (-1);
1826   }
1827
1828   status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
1829       jni_api_functions, (jint) jni_api_functions_num);
1830   if (status != 0)
1831   {
1832     ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
1833     return (-1);
1834   }
1835
1836   return (0);
1837 } /* }}} int cjni_init_native */
1838
1839 static int cjni_init (void) /* {{{ */
1840 {
1841   JNIEnv *jvm_env;
1842   JavaVMInitArgs vm_args;
1843   JavaVMOption vm_options[jvm_argc];
1844
1845   int status;
1846   size_t i;
1847
1848   if (jvm != NULL)
1849     return (0);
1850
1851   status = pthread_key_create (&jvm_env_key, cjni_jvm_env_destroy);
1852   if (status != 0)
1853   {
1854     ERROR ("java plugin: cjni_init: pthread_key_create failed "
1855         "with status %i.", status);
1856     return (-1);
1857   }
1858
1859   jvm_env = NULL;
1860
1861   memset (&vm_args, 0, sizeof (vm_args));
1862   vm_args.version = JNI_VERSION_1_2;
1863   vm_args.options = vm_options;
1864   vm_args.nOptions = (jint) jvm_argc;
1865
1866   for (i = 0; i < jvm_argc; i++)
1867   {
1868     DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]);
1869     vm_args.options[i].optionString = jvm_argv[i];
1870   }
1871   /*
1872   vm_args.options[0].optionString = "-verbose:jni";
1873   vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
1874   */
1875
1876   status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
1877   if (status != 0)
1878   {
1879     ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.",
1880         status);
1881     return (-1);
1882   }
1883   assert (jvm != NULL);
1884   assert (jvm_env != NULL);
1885
1886   /* Call RegisterNatives */
1887   status = cjni_init_native (jvm_env);
1888   if (status != 0)
1889   {
1890     ERROR ("cjni_init: cjni_init_native failed.");
1891     return (-1);
1892   }
1893
1894   cjni_init_plugins (jvm_env);
1895
1896   return (0);
1897 } /* }}} int cjni_init */
1898
1899 void module_register (void)
1900 {
1901   plugin_register_complex_config ("java", cjni_config);
1902   plugin_register_init ("java", cjni_init);
1903 } /* void module_register (void) */
1904
1905 /* vim: set sw=2 sts=2 et fdm=marker : */