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