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