Merge pull request #1816 from octo/grpc-free-iter
[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 collectd.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 #include "filter_chain.h"
28
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 cjni_jvm_env_s /* {{{ */
39 {
40   JNIEnv *jvm_env;
41   int reference_counter;
42 };
43 typedef struct cjni_jvm_env_s cjni_jvm_env_t;
44 /* }}} */
45
46 struct java_plugin_class_s /* {{{ */
47 {
48   char     *name;
49   jclass    class;
50   jobject   object;
51 };
52 typedef struct java_plugin_class_s java_plugin_class_t;
53 /* }}} */
54
55 #define CB_TYPE_CONFIG       1
56 #define CB_TYPE_INIT         2
57 #define CB_TYPE_READ         3
58 #define CB_TYPE_WRITE        4
59 #define CB_TYPE_FLUSH        5
60 #define CB_TYPE_SHUTDOWN     6
61 #define CB_TYPE_LOG          7
62 #define CB_TYPE_NOTIFICATION 8
63 #define CB_TYPE_MATCH        9
64 #define CB_TYPE_TARGET      10
65 struct cjni_callback_info_s /* {{{ */
66 {
67   char     *name;
68   int       type;
69   jclass    class;
70   jobject   object;
71   jmethodID method;
72 };
73 typedef struct cjni_callback_info_s cjni_callback_info_t;
74 /* }}} */
75
76 /*
77  * Global variables
78  */
79 static JavaVM *jvm = NULL;
80 static pthread_key_t jvm_env_key;
81
82 /* Configuration options for the JVM. */
83 static char **jvm_argv = NULL;
84 static size_t jvm_argc = 0;
85
86 /* List of class names to load */
87 static java_plugin_class_t  *java_classes_list = NULL;
88 static size_t                java_classes_list_len;
89
90 /* List of config, init, and shutdown callbacks. */
91 static cjni_callback_info_t *java_callbacks      = NULL;
92 static size_t                java_callbacks_num  = 0;
93 static pthread_mutex_t       java_callbacks_lock = PTHREAD_MUTEX_INITIALIZER;
94
95 static oconfig_item_t       *config_block = NULL;
96
97 /*
98  * Prototypes
99  *
100  * Mostly functions that are needed by the Java interface (``native'')
101  * functions.
102  */
103 static void cjni_callback_info_destroy (void *arg);
104 static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env,
105     jobject o_name, jobject o_callback, int type);
106 static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name,
107     jobject o_callback, int type);
108 static int cjni_read (user_data_t *user_data);
109 static int cjni_write (const data_set_t *ds, const value_list_t *vl,
110     user_data_t *ud);
111 static int cjni_flush (cdtime_t timeout, const char *identifier, user_data_t *ud);
112 static void cjni_log (int severity, const char *message, user_data_t *ud);
113 static int cjni_notification (const notification_t *n, user_data_t *ud);
114
115 /* Create, destroy, and match/invoke functions, used by both, matches AND
116  * targets. */
117 static int cjni_match_target_create (const oconfig_item_t *ci, void **user_data);
118 static int cjni_match_target_destroy (void **user_data);
119 static int cjni_match_target_invoke (const data_set_t *ds, value_list_t *vl,
120     notification_meta_t **meta, void **user_data);
121
122 /*
123  * C to Java conversion functions
124  */
125 static int ctoj_string (JNIEnv *jvm_env, /* {{{ */
126     const char *string,
127     jclass class_ptr, jobject object_ptr, const char *method_name)
128 {
129   jmethodID m_set;
130   jstring o_string;
131
132   /* Create a java.lang.String */
133   o_string = (*jvm_env)->NewStringUTF (jvm_env,
134       (string != NULL) ? string : "");
135   if (o_string == NULL)
136   {
137     ERROR ("java plugin: ctoj_string: NewStringUTF failed.");
138     return (-1);
139   }
140
141   /* Search for the `void setFoo (String s)' method. */
142   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
143       method_name, "(Ljava/lang/String;)V");
144   if (m_set == NULL)
145   {
146     ERROR ("java plugin: ctoj_string: Cannot find method `void %s (String)'.",
147         method_name);
148     (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
149     return (-1);
150   }
151
152   /* Call the method. */
153   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, o_string);
154
155   /* Decrease reference counter on the java.lang.String object. */
156   (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
157
158   return (0);
159 } /* }}} int ctoj_string */
160
161 static jstring ctoj_output_string (JNIEnv *jvm_env, /* {{{ */
162     const char *string)
163 {
164   jstring o_string;
165
166   /* Create a java.lang.String */
167   o_string = (*jvm_env)->NewStringUTF (jvm_env,
168       (string != NULL) ? string : "");
169   if (o_string == NULL)
170   {
171     ERROR ("java plugin: ctoj_output_string: NewStringUTF failed.");
172     return NULL;
173   }
174
175   return (o_string);
176 } /* }}} int ctoj_output_string */
177
178 static int ctoj_int (JNIEnv *jvm_env, /* {{{ */
179     jint value,
180     jclass class_ptr, jobject object_ptr, const char *method_name)
181 {
182   jmethodID m_set;
183
184   /* Search for the `void setFoo (int i)' method. */
185   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
186       method_name, "(I)V");
187   if (m_set == NULL)
188   {
189     ERROR ("java plugin: ctoj_int: Cannot find method `void %s (int)'.",
190         method_name);
191     return (-1);
192   }
193
194   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
195
196   return (0);
197 } /* }}} int ctoj_int */
198
199 static int ctoj_long (JNIEnv *jvm_env, /* {{{ */
200     jlong value,
201     jclass class_ptr, jobject object_ptr, const char *method_name)
202 {
203   jmethodID m_set;
204
205   /* Search for the `void setFoo (long l)' method. */
206   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
207       method_name, "(J)V");
208   if (m_set == NULL)
209   {
210     ERROR ("java plugin: ctoj_long: Cannot find method `void %s (long)'.",
211         method_name);
212     return (-1);
213   }
214
215   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
216
217   return (0);
218 } /* }}} int ctoj_long */
219
220 static int ctoj_double (JNIEnv *jvm_env, /* {{{ */
221     jdouble value,
222     jclass class_ptr, jobject object_ptr, const char *method_name)
223 {
224   jmethodID m_set;
225
226   /* Search for the `void setFoo (double d)' method. */
227   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
228       method_name, "(D)V");
229   if (m_set == NULL)
230   {
231     ERROR ("java plugin: ctoj_double: Cannot find method `void %s (double)'.",
232         method_name);
233     return (-1);
234   }
235
236   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
237
238   return (0);
239 } /* }}} int ctoj_double */
240
241 /* Convert a jlong to a java.lang.Number */
242 static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */
243 {
244   jclass c_long;
245   jmethodID m_long_constructor;
246
247   /* Look up the java.lang.Long class */
248   c_long = (*jvm_env)->FindClass (jvm_env, "java/lang/Long");
249   if (c_long == NULL)
250   {
251     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
252         "java.lang.Long class failed.");
253     return (NULL);
254   }
255
256   m_long_constructor = (*jvm_env)->GetMethodID (jvm_env,
257       c_long, "<init>", "(J)V");
258   if (m_long_constructor == NULL)
259   {
260     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
261         "`Long (long)' constructor failed.");
262     return (NULL);
263   }
264
265   return ((*jvm_env)->NewObject (jvm_env,
266         c_long, m_long_constructor, value));
267 } /* }}} jobject ctoj_jlong_to_number */
268
269 /* Convert a jdouble to a java.lang.Number */
270 static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */
271 {
272   jclass c_double;
273   jmethodID m_double_constructor;
274
275   /* Look up the java.lang.Long class */
276   c_double = (*jvm_env)->FindClass (jvm_env, "java/lang/Double");
277   if (c_double == NULL)
278   {
279     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
280         "java.lang.Double class failed.");
281     return (NULL);
282   }
283
284   m_double_constructor = (*jvm_env)->GetMethodID (jvm_env,
285       c_double, "<init>", "(D)V");
286   if (m_double_constructor == NULL)
287   {
288     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
289         "`Double (double)' constructor failed.");
290     return (NULL);
291   }
292
293   return ((*jvm_env)->NewObject (jvm_env,
294         c_double, m_double_constructor, value));
295 } /* }}} jobject ctoj_jdouble_to_number */
296
297 /* Convert a value_t to a java.lang.Number */
298 static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
299     value_t value, int ds_type)
300 {
301   if (ds_type == DS_TYPE_COUNTER)
302     return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter));
303   else if (ds_type == DS_TYPE_GAUGE)
304     return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge));
305   if (ds_type == DS_TYPE_DERIVE)
306     return (ctoj_jlong_to_number (jvm_env, (jlong) value.derive));
307   if (ds_type == DS_TYPE_ABSOLUTE)
308     return (ctoj_jlong_to_number (jvm_env, (jlong) value.absolute));
309   else
310     return (NULL);
311 } /* }}} jobject ctoj_value_to_number */
312
313 /* Convert a data_source_t to a org/collectd/api/DataSource */
314 static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
315     const data_source_t *dsrc)
316 {
317   jclass c_datasource;
318   jmethodID m_datasource_constructor;
319   jobject o_datasource;
320   int status;
321
322   /* Look up the DataSource class */
323   c_datasource = (*jvm_env)->FindClass (jvm_env,
324       "org/collectd/api/DataSource");
325   if (c_datasource == NULL)
326   {
327     ERROR ("java plugin: ctoj_data_source: "
328         "FindClass (org/collectd/api/DataSource) failed.");
329     return (NULL);
330   }
331
332   /* Lookup the `ValueList ()' constructor. */
333   m_datasource_constructor = (*jvm_env)->GetMethodID (jvm_env, c_datasource,
334       "<init>", "()V");
335   if (m_datasource_constructor == NULL)
336   {
337     ERROR ("java plugin: ctoj_data_source: Cannot find the "
338         "`DataSource ()' constructor.");
339     return (NULL);
340   }
341
342   /* Create a new instance. */
343   o_datasource = (*jvm_env)->NewObject (jvm_env, c_datasource,
344       m_datasource_constructor);
345   if (o_datasource == NULL)
346   {
347     ERROR ("java plugin: ctoj_data_source: "
348         "Creating a new DataSource instance failed.");
349     return (NULL);
350   }
351
352   /* Set name via `void setName (String name)' */
353   status = ctoj_string (jvm_env, dsrc->name,
354       c_datasource, o_datasource, "setName");
355   if (status != 0)
356   {
357     ERROR ("java plugin: ctoj_data_source: "
358         "ctoj_string (setName) failed.");
359     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
360     return (NULL);
361   }
362
363   /* Set type via `void setType (int type)' */
364   status = ctoj_int (jvm_env, dsrc->type,
365       c_datasource, o_datasource, "setType");
366   if (status != 0)
367   {
368     ERROR ("java plugin: ctoj_data_source: "
369         "ctoj_int (setType) failed.");
370     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
371     return (NULL);
372   }
373
374   /* Set min via `void setMin (double min)' */
375   status = ctoj_double (jvm_env, dsrc->min,
376       c_datasource, o_datasource, "setMin");
377   if (status != 0)
378   {
379     ERROR ("java plugin: ctoj_data_source: "
380         "ctoj_double (setMin) failed.");
381     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
382     return (NULL);
383   }
384
385   /* Set max via `void setMax (double max)' */
386   status = ctoj_double (jvm_env, dsrc->max,
387       c_datasource, o_datasource, "setMax");
388   if (status != 0)
389   {
390     ERROR ("java plugin: ctoj_data_source: "
391         "ctoj_double (setMax) failed.");
392     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
393     return (NULL);
394   }
395
396   return (o_datasource);
397 } /* }}} jobject ctoj_data_source */
398
399 /* Convert a oconfig_value_t to a org/collectd/api/OConfigValue */
400 static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
401     oconfig_value_t ocvalue)
402 {
403   jclass c_ocvalue;
404   jmethodID m_ocvalue_constructor;
405   jobject o_argument;
406   jobject o_ocvalue;
407
408   m_ocvalue_constructor = NULL;
409   o_argument = NULL;
410
411   c_ocvalue = (*jvm_env)->FindClass (jvm_env,
412       "org/collectd/api/OConfigValue");
413   if (c_ocvalue == NULL)
414   {
415     ERROR ("java plugin: ctoj_oconfig_value: "
416         "FindClass (org/collectd/api/OConfigValue) failed.");
417     return (NULL);
418   }
419
420   if (ocvalue.type == OCONFIG_TYPE_BOOLEAN)
421   {
422     jboolean tmp_boolean;
423
424     tmp_boolean = (ocvalue.value.boolean == 0) ? JNI_FALSE : JNI_TRUE;
425
426     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
427         "<init>", "(Z)V");
428     if (m_ocvalue_constructor == NULL)
429     {
430       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
431           "`OConfigValue (boolean)' constructor.");
432       return (NULL);
433     }
434
435     return ((*jvm_env)->NewObject (jvm_env,
436           c_ocvalue, m_ocvalue_constructor, tmp_boolean));
437   } /* if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) */
438   else if (ocvalue.type == OCONFIG_TYPE_STRING)
439   {
440     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
441         "<init>", "(Ljava/lang/String;)V");
442     if (m_ocvalue_constructor == NULL)
443     {
444       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
445           "`OConfigValue (String)' constructor.");
446       return (NULL);
447     }
448
449     o_argument = (*jvm_env)->NewStringUTF (jvm_env, ocvalue.value.string);
450     if (o_argument == NULL)
451     {
452       ERROR ("java plugin: ctoj_oconfig_value: "
453           "Creating a String object failed.");
454       return (NULL);
455     }
456   }
457   else if (ocvalue.type == OCONFIG_TYPE_NUMBER)
458   {
459     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
460         "<init>", "(Ljava/lang/Number;)V");
461     if (m_ocvalue_constructor == NULL)
462     {
463       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
464           "`OConfigValue (Number)' constructor.");
465       return (NULL);
466     }
467
468     o_argument = ctoj_jdouble_to_number (jvm_env,
469         (jdouble) ocvalue.value.number);
470     if (o_argument == NULL)
471     {
472       ERROR ("java plugin: ctoj_oconfig_value: "
473           "Creating a Number object failed.");
474       return (NULL);
475     }
476   }
477   else
478   {
479     return (NULL);
480   }
481
482   assert (m_ocvalue_constructor != NULL);
483   assert (o_argument != NULL);
484
485   o_ocvalue = (*jvm_env)->NewObject (jvm_env,
486       c_ocvalue, m_ocvalue_constructor, o_argument);
487   if (o_ocvalue == NULL)
488   {
489     ERROR ("java plugin: ctoj_oconfig_value: "
490         "Creating an OConfigValue object failed.");
491     (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
492     return (NULL);
493   }
494
495   (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
496   return (o_ocvalue);
497 } /* }}} jobject ctoj_oconfig_value */
498
499 /* Convert a oconfig_item_t to a org/collectd/api/OConfigItem */
500 static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
501     const oconfig_item_t *ci)
502 {
503   jclass c_ocitem;
504   jmethodID m_ocitem_constructor;
505   jmethodID m_addvalue;
506   jmethodID m_addchild;
507   jobject o_key;
508   jobject o_ocitem;
509   int i;
510
511   c_ocitem = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/OConfigItem");
512   if (c_ocitem == NULL)
513   {
514     ERROR ("java plugin: ctoj_oconfig_item: "
515         "FindClass (org/collectd/api/OConfigItem) failed.");
516     return (NULL);
517   }
518
519   /* Get the required methods: m_ocitem_constructor, m_addvalue, and m_addchild
520    * {{{ */
521   m_ocitem_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
522       "<init>", "(Ljava/lang/String;)V");
523   if (m_ocitem_constructor == NULL)
524   {
525     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
526         "`OConfigItem (String)' constructor.");
527     return (NULL);
528   }
529
530   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
531       "addValue", "(Lorg/collectd/api/OConfigValue;)V");
532   if (m_addvalue == NULL)
533   {
534     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
535         "`addValue (OConfigValue)' method.");
536     return (NULL);
537   }
538
539   m_addchild = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
540       "addChild", "(Lorg/collectd/api/OConfigItem;)V");
541   if (m_addchild == NULL)
542   {
543     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
544         "`addChild (OConfigItem)' method.");
545     return (NULL);
546   }
547   /* }}} */
548
549   /* Create a String object with the key.
550    * Needed for calling the constructor. */
551   o_key = (*jvm_env)->NewStringUTF (jvm_env, ci->key);
552   if (o_key == NULL)
553   {
554     ERROR ("java plugin: ctoj_oconfig_item: "
555         "Creating String object failed.");
556     return (NULL);
557   }
558
559   /* Create an OConfigItem object */
560   o_ocitem = (*jvm_env)->NewObject (jvm_env,
561       c_ocitem, m_ocitem_constructor, o_key);
562   if (o_ocitem == NULL)
563   {
564     ERROR ("java plugin: ctoj_oconfig_item: "
565         "Creating an OConfigItem object failed.");
566     (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
567     return (NULL);
568   }
569
570   /* We don't need the String object any longer.. */
571   (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
572
573   /* Call OConfigItem.addValue for each value */
574   for (i = 0; i < ci->values_num; i++) /* {{{ */
575   {
576     jobject o_value;
577
578     o_value = ctoj_oconfig_value (jvm_env, ci->values[i]);
579     if (o_value == NULL)
580     {
581       ERROR ("java plugin: ctoj_oconfig_item: "
582           "Creating an OConfigValue object failed.");
583       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
584       return (NULL);
585     }
586
587     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_value);
588     (*jvm_env)->DeleteLocalRef (jvm_env, o_value);
589   } /* }}} for (i = 0; i < ci->values_num; i++) */
590
591   /* Call OConfigItem.addChild for each child */
592   for (i = 0; i < ci->children_num; i++) /* {{{ */
593   {
594     jobject o_child;
595
596     o_child = ctoj_oconfig_item (jvm_env, ci->children + i);
597     if (o_child == NULL)
598     {
599       ERROR ("java plugin: ctoj_oconfig_item: "
600           "Creating an OConfigItem object failed.");
601       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
602       return (NULL);
603     }
604
605     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addchild, o_child);
606     (*jvm_env)->DeleteLocalRef (jvm_env, o_child);
607   } /* }}} for (i = 0; i < ci->children_num; i++) */
608
609   return (o_ocitem);
610 } /* }}} jobject ctoj_oconfig_item */
611
612 /* Convert a data_set_t to a org/collectd/api/DataSet */
613 static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
614 {
615   jclass c_dataset;
616   jmethodID m_constructor;
617   jmethodID m_add;
618   jobject o_type;
619   jobject o_dataset;
620   size_t i;
621
622   /* Look up the org/collectd/api/DataSet class */
623   c_dataset = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/DataSet");
624   if (c_dataset == NULL)
625   {
626     ERROR ("java plugin: ctoj_data_set: Looking up the "
627         "org/collectd/api/DataSet class failed.");
628     return (NULL);
629   }
630
631   /* Search for the `DataSet (String type)' constructor. */
632   m_constructor = (*jvm_env)->GetMethodID (jvm_env,
633       c_dataset, "<init>", "(Ljava/lang/String;)V");
634   if (m_constructor == NULL)
635   {
636     ERROR ("java plugin: ctoj_data_set: Looking up the "
637         "`DataSet (String)' constructor failed.");
638     return (NULL);
639   }
640
641   /* Search for the `void addDataSource (DataSource)' method. */
642   m_add = (*jvm_env)->GetMethodID (jvm_env,
643       c_dataset, "addDataSource", "(Lorg/collectd/api/DataSource;)V");
644   if (m_add == NULL)
645   {
646     ERROR ("java plugin: ctoj_data_set: Looking up the "
647         "`addDataSource (DataSource)' method failed.");
648     return (NULL);
649   }
650
651   o_type = (*jvm_env)->NewStringUTF (jvm_env, ds->type);
652   if (o_type == NULL)
653   {
654     ERROR ("java plugin: ctoj_data_set: Creating a String object failed.");
655     return (NULL);
656   }
657
658   o_dataset = (*jvm_env)->NewObject (jvm_env,
659       c_dataset, m_constructor, o_type);
660   if (o_dataset == NULL)
661   {
662     ERROR ("java plugin: ctoj_data_set: Creating a DataSet object failed.");
663     (*jvm_env)->DeleteLocalRef (jvm_env, o_type);
664     return (NULL);
665   }
666
667   /* Decrease reference counter on the java.lang.String object. */
668   (*jvm_env)->DeleteLocalRef (jvm_env, o_type);
669
670   for (i = 0; i < ds->ds_num; i++)
671   {
672     jobject o_datasource;
673
674     o_datasource = ctoj_data_source (jvm_env, ds->ds + i);
675     if (o_datasource == NULL)
676     {
677       ERROR ("java plugin: ctoj_data_set: ctoj_data_source (%s.%s) failed",
678           ds->type, ds->ds[i].name);
679       (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
680       return (NULL);
681     }
682
683     (*jvm_env)->CallVoidMethod (jvm_env, o_dataset, m_add, o_datasource);
684
685     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
686   } /* for (i = 0; i < ds->ds_num; i++) */
687
688   return (o_dataset);
689 } /* }}} jobject ctoj_data_set */
690
691 static int ctoj_value_list_add_value (JNIEnv *jvm_env, /* {{{ */
692     value_t value, int ds_type,
693     jclass class_ptr, jobject object_ptr)
694 {
695   jmethodID m_addvalue;
696   jobject o_number;
697
698   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
699       "addValue", "(Ljava/lang/Number;)V");
700   if (m_addvalue == NULL)
701   {
702     ERROR ("java plugin: ctoj_value_list_add_value: "
703         "Cannot find method `void addValue (Number)'.");
704     return (-1);
705   }
706
707   o_number = ctoj_value_to_number (jvm_env, value, ds_type);
708   if (o_number == NULL)
709   {
710     ERROR ("java plugin: ctoj_value_list_add_value: "
711         "ctoj_value_to_number failed.");
712     return (-1);
713   }
714
715   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_addvalue, o_number);
716
717   (*jvm_env)->DeleteLocalRef (jvm_env, o_number);
718
719   return (0);
720 } /* }}} int ctoj_value_list_add_value */
721
722 static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */
723     jclass c_valuelist, jobject o_valuelist, const data_set_t *ds)
724 {
725   jmethodID m_setdataset;
726   jobject o_dataset;
727
728   /* Look for the `void setDataSource (List<DataSource> ds)' method. */
729   m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
730       "setDataSet", "(Lorg/collectd/api/DataSet;)V");
731   if (m_setdataset == NULL)
732   {
733     ERROR ("java plugin: ctoj_value_list_add_data_set: "
734         "Cannot find the `void setDataSet (DataSet)' method.");
735     return (-1);
736   }
737
738   /* Create a DataSet object. */
739   o_dataset = ctoj_data_set (jvm_env, ds);
740   if (o_dataset == NULL)
741   {
742     ERROR ("java plugin: ctoj_value_list_add_data_set: "
743         "ctoj_data_set (%s) failed.", ds->type);
744     return (-1);
745   }
746
747   /* Actually call the method. */
748   (*jvm_env)->CallVoidMethod (jvm_env,
749       o_valuelist, m_setdataset, o_dataset);
750
751   /* Decrease reference counter on the List<DataSource> object. */
752   (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
753
754   return (0);
755 } /* }}} int ctoj_value_list_add_data_set */
756
757 /* Convert a value_list_t (and data_set_t) to a org/collectd/api/ValueList */
758 static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
759     const data_set_t *ds, const value_list_t *vl)
760 {
761   jclass c_valuelist;
762   jmethodID m_valuelist_constructor;
763   jobject o_valuelist;
764   int status;
765   size_t i;
766
767   /* First, create a new ValueList instance..
768    * Look up the class.. */
769   c_valuelist = (*jvm_env)->FindClass (jvm_env,
770       "org/collectd/api/ValueList");
771   if (c_valuelist == NULL)
772   {
773     ERROR ("java plugin: ctoj_value_list: "
774         "FindClass (org/collectd/api/ValueList) failed.");
775     return (NULL);
776   }
777
778   /* Lookup the `ValueList ()' constructor. */
779   m_valuelist_constructor = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
780       "<init>", "()V");
781   if (m_valuelist_constructor == NULL)
782   {
783     ERROR ("java plugin: ctoj_value_list: Cannot find the "
784         "`ValueList ()' constructor.");
785     return (NULL);
786   }
787
788   /* Create a new instance. */
789   o_valuelist = (*jvm_env)->NewObject (jvm_env, c_valuelist,
790       m_valuelist_constructor);
791   if (o_valuelist == NULL)
792   {
793     ERROR ("java plugin: ctoj_value_list: Creating a new ValueList instance "
794         "failed.");
795     return (NULL);
796   }
797
798   status = ctoj_value_list_add_data_set (jvm_env,
799       c_valuelist, o_valuelist, ds);
800   if (status != 0)
801   {
802     ERROR ("java plugin: ctoj_value_list: "
803         "ctoj_value_list_add_data_set failed.");
804     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
805     return (NULL);
806   }
807
808   /* Set the strings.. */
809 #define SET_STRING(str,method_name) do { \
810   status = ctoj_string (jvm_env, str, \
811       c_valuelist, o_valuelist, method_name); \
812   if (status != 0) { \
813     ERROR ("java plugin: ctoj_value_list: ctoj_string (%s) failed.", \
814         method_name); \
815     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); \
816     return (NULL); \
817   } } while (0)
818
819   SET_STRING (vl->host,            "setHost");
820   SET_STRING (vl->plugin,          "setPlugin");
821   SET_STRING (vl->plugin_instance, "setPluginInstance");
822   SET_STRING (vl->type,            "setType");
823   SET_STRING (vl->type_instance,   "setTypeInstance");
824
825 #undef SET_STRING
826
827   /* Set the `time' member. Java stores time in milliseconds. */
828   status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (vl->time),
829       c_valuelist, o_valuelist, "setTime");
830   if (status != 0)
831   {
832     ERROR ("java plugin: ctoj_value_list: ctoj_long (setTime) failed.");
833     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
834     return (NULL);
835   }
836
837   /* Set the `interval' member.. */
838   status = ctoj_long (jvm_env,
839       (jlong) CDTIME_T_TO_MS (vl->interval),
840       c_valuelist, o_valuelist, "setInterval");
841   if (status != 0)
842   {
843     ERROR ("java plugin: ctoj_value_list: ctoj_long (setInterval) failed.");
844     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
845     return (NULL);
846   }
847
848   for (i = 0; i < vl->values_len; i++)
849   {
850     status = ctoj_value_list_add_value (jvm_env, vl->values[i], ds->ds[i].type,
851         c_valuelist, o_valuelist);
852     if (status != 0)
853     {
854       ERROR ("java plugin: ctoj_value_list: "
855           "ctoj_value_list_add_value failed.");
856       (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
857       return (NULL);
858     }
859   }
860
861   return (o_valuelist);
862 } /* }}} jobject ctoj_value_list */
863
864 /* Convert a notification_t to a org/collectd/api/Notification */
865 static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
866     const notification_t *n)
867 {
868   jclass c_notification;
869   jmethodID m_constructor;
870   jobject o_notification;
871   int status;
872
873   /* First, create a new Notification instance..
874    * Look up the class.. */
875   c_notification = (*jvm_env)->FindClass (jvm_env,
876       "org/collectd/api/Notification");
877   if (c_notification == NULL)
878   {
879     ERROR ("java plugin: ctoj_notification: "
880         "FindClass (org/collectd/api/Notification) failed.");
881     return (NULL);
882   }
883
884   /* Lookup the `Notification ()' constructor. */
885   m_constructor = (*jvm_env)->GetMethodID (jvm_env, c_notification,
886       "<init>", "()V");
887   if (m_constructor == NULL)
888   {
889     ERROR ("java plugin: ctoj_notification: Cannot find the "
890         "`Notification ()' constructor.");
891     return (NULL);
892   }
893
894   /* Create a new instance. */
895   o_notification = (*jvm_env)->NewObject (jvm_env, c_notification,
896       m_constructor);
897   if (o_notification == NULL)
898   {
899     ERROR ("java plugin: ctoj_notification: Creating a new Notification "
900         "instance failed.");
901     return (NULL);
902   }
903
904   /* Set the strings.. */
905 #define SET_STRING(str,method_name) do { \
906   status = ctoj_string (jvm_env, str, \
907       c_notification, o_notification, method_name); \
908   if (status != 0) { \
909     ERROR ("java plugin: ctoj_notification: ctoj_string (%s) failed.", \
910         method_name); \
911     (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); \
912     return (NULL); \
913   } } while (0)
914
915   SET_STRING (n->host,            "setHost");
916   SET_STRING (n->plugin,          "setPlugin");
917   SET_STRING (n->plugin_instance, "setPluginInstance");
918   SET_STRING (n->type,            "setType");
919   SET_STRING (n->type_instance,   "setTypeInstance");
920   SET_STRING (n->message,         "setMessage");
921
922 #undef SET_STRING
923
924   /* Set the `time' member. Java stores time in milliseconds. */
925   status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (n->time),
926       c_notification, o_notification, "setTime");
927   if (status != 0)
928   {
929     ERROR ("java plugin: ctoj_notification: ctoj_long (setTime) failed.");
930     (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
931     return (NULL);
932   }
933
934   /* Set the `severity' member.. */
935   status = ctoj_int (jvm_env, (jint) n->severity,
936       c_notification, o_notification, "setSeverity");
937   if (status != 0)
938   {
939     ERROR ("java plugin: ctoj_notification: ctoj_int (setSeverity) failed.");
940     (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
941     return (NULL);
942   }
943
944   return (o_notification);
945 } /* }}} jobject ctoj_notification */
946
947 /*
948  * Java to C conversion functions
949  */
950 /* Call a `String <method> ()' method. */
951 static int jtoc_string (JNIEnv *jvm_env, /* {{{ */
952     char *buffer, size_t buffer_size, int empty_okay,
953     jclass class_ptr, jobject object_ptr, const char *method_name)
954 {
955   jmethodID method_id;
956   jobject string_obj;
957   const char *c_str;
958
959   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
960       method_name, "()Ljava/lang/String;");
961   if (method_id == NULL)
962   {
963     ERROR ("java plugin: jtoc_string: Cannot find method `String %s ()'.",
964         method_name);
965     return (-1);
966   }
967
968   string_obj = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, method_id);
969   if ((string_obj == NULL) && (empty_okay == 0))
970   {
971     ERROR ("java plugin: jtoc_string: CallObjectMethod (%s) failed.",
972         method_name);
973     return (-1);
974   }
975   else if ((string_obj == NULL) && (empty_okay != 0))
976   {
977     memset (buffer, 0, buffer_size);
978     return (0);
979   }
980
981   c_str = (*jvm_env)->GetStringUTFChars (jvm_env, string_obj, 0);
982   if (c_str == NULL)
983   {
984     ERROR ("java plugin: jtoc_string: GetStringUTFChars failed.");
985     (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
986     return (-1);
987   }
988
989   sstrncpy (buffer, c_str, buffer_size);
990
991   (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str);
992   (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
993
994   return (0);
995 } /* }}} int jtoc_string */
996
997 /* Call an `int <method> ()' method. */
998 static int jtoc_int (JNIEnv *jvm_env, /* {{{ */
999     jint *ret_value,
1000     jclass class_ptr, jobject object_ptr, const char *method_name)
1001 {
1002   jmethodID method_id;
1003
1004   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
1005       method_name, "()I");
1006   if (method_id == NULL)
1007   {
1008     ERROR ("java plugin: jtoc_int: Cannot find method `int %s ()'.",
1009         method_name);
1010     return (-1);
1011   }
1012
1013   *ret_value = (*jvm_env)->CallIntMethod (jvm_env, object_ptr, method_id);
1014
1015   return (0);
1016 } /* }}} int jtoc_int */
1017
1018 /* Call a `long <method> ()' method. */
1019 static int jtoc_long (JNIEnv *jvm_env, /* {{{ */
1020     jlong *ret_value,
1021     jclass class_ptr, jobject object_ptr, const char *method_name)
1022 {
1023   jmethodID method_id;
1024
1025   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
1026       method_name, "()J");
1027   if (method_id == NULL)
1028   {
1029     ERROR ("java plugin: jtoc_long: Cannot find method `long %s ()'.",
1030         method_name);
1031     return (-1);
1032   }
1033
1034   *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id);
1035
1036   return (0);
1037 } /* }}} int jtoc_long */
1038
1039 /* Call a `double <method> ()' method. */
1040 static int jtoc_double (JNIEnv *jvm_env, /* {{{ */
1041     jdouble *ret_value,
1042     jclass class_ptr, jobject object_ptr, const char *method_name)
1043 {
1044   jmethodID method_id;
1045
1046   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
1047       method_name, "()D");
1048   if (method_id == NULL)
1049   {
1050     ERROR ("java plugin: jtoc_double: Cannot find method `double %s ()'.",
1051         method_name);
1052     return (-1);
1053   }
1054
1055   *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id);
1056
1057   return (0);
1058 } /* }}} int jtoc_double */
1059
1060 static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
1061     value_t *ret_value, int ds_type, jobject object_ptr)
1062 {
1063   jclass class_ptr;
1064   int status;
1065
1066   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1067
1068   if (ds_type == DS_TYPE_GAUGE)
1069   {
1070     jdouble tmp_double;
1071
1072     status = jtoc_double (jvm_env, &tmp_double,
1073         class_ptr, object_ptr, "doubleValue");
1074     if (status != 0)
1075     {
1076       ERROR ("java plugin: jtoc_value: "
1077           "jtoc_double failed.");
1078       return (-1);
1079     }
1080     (*ret_value).gauge = (gauge_t) tmp_double;
1081   }
1082   else
1083   {
1084     jlong tmp_long;
1085
1086     status = jtoc_long (jvm_env, &tmp_long,
1087         class_ptr, object_ptr, "longValue");
1088     if (status != 0)
1089     {
1090       ERROR ("java plugin: jtoc_value: "
1091           "jtoc_long failed.");
1092       return (-1);
1093     }
1094
1095     if (ds_type == DS_TYPE_DERIVE)
1096       (*ret_value).derive = (derive_t) tmp_long;
1097     else if (ds_type == DS_TYPE_ABSOLUTE)
1098       (*ret_value).absolute = (absolute_t) tmp_long;
1099     else
1100       (*ret_value).counter = (counter_t) tmp_long;
1101   }
1102
1103   return (0);
1104 } /* }}} int jtoc_value */
1105
1106 /* Read a List<Number>, convert it to `value_t' and add it to the given
1107  * `value_list_t'. */
1108 static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */
1109     const data_set_t *ds, value_list_t *vl,
1110     jclass class_ptr, jobject object_ptr)
1111 {
1112   jmethodID m_getvalues;
1113   jmethodID m_toarray;
1114   jobject o_list;
1115   jobjectArray o_number_array;
1116
1117   value_t *values;
1118   int values_num;
1119   int i;
1120
1121   values_num = ds->ds_num;
1122
1123   values = NULL;
1124   o_number_array = NULL;
1125   o_list = NULL;
1126
1127 #define BAIL_OUT(status) \
1128   free (values); \
1129   if (o_number_array != NULL) \
1130     (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); \
1131   if (o_list != NULL) \
1132     (*jvm_env)->DeleteLocalRef (jvm_env, o_list); \
1133   return (status);
1134
1135   /* Call: List<Number> ValueList.getValues () */
1136   m_getvalues = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
1137       "getValues", "()Ljava/util/List;");
1138   if (m_getvalues == NULL)
1139   {
1140     ERROR ("java plugin: jtoc_values_array: "
1141         "Cannot find method `List getValues ()'.");
1142     BAIL_OUT (-1);
1143   }
1144
1145   o_list = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, m_getvalues);
1146   if (o_list == NULL)
1147   {
1148     ERROR ("java plugin: jtoc_values_array: "
1149         "CallObjectMethod (getValues) failed.");
1150     BAIL_OUT (-1);
1151   }
1152
1153   /* Call: Number[] List.toArray () */
1154   m_toarray = (*jvm_env)->GetMethodID (jvm_env,
1155       (*jvm_env)->GetObjectClass (jvm_env, o_list),
1156       "toArray", "()[Ljava/lang/Object;");
1157   if (m_toarray == NULL)
1158   {
1159     ERROR ("java plugin: jtoc_values_array: "
1160         "Cannot find method `Object[] toArray ()'.");
1161     BAIL_OUT (-1);
1162   }
1163
1164   o_number_array = (*jvm_env)->CallObjectMethod (jvm_env, o_list, m_toarray);
1165   if (o_number_array == NULL)
1166   {
1167     ERROR ("java plugin: jtoc_values_array: "
1168         "CallObjectMethod (toArray) failed.");
1169     BAIL_OUT (-1);
1170   }
1171
1172   values = (value_t *) calloc (values_num, sizeof (value_t));
1173   if (values == NULL)
1174   {
1175     ERROR ("java plugin: jtoc_values_array: calloc failed.");
1176     BAIL_OUT (-1);
1177   }
1178
1179   for (i = 0; i < values_num; i++)
1180   {
1181     jobject o_number;
1182     int status;
1183
1184     o_number = (*jvm_env)->GetObjectArrayElement (jvm_env,
1185         o_number_array, (jsize) i);
1186     if (o_number == NULL)
1187     {
1188       ERROR ("java plugin: jtoc_values_array: "
1189           "GetObjectArrayElement (%i) failed.", i);
1190       BAIL_OUT (-1);
1191     }
1192
1193     status = jtoc_value (jvm_env, values + i, ds->ds[i].type, o_number);
1194     if (status != 0)
1195     {
1196       ERROR ("java plugin: jtoc_values_array: "
1197           "jtoc_value (%i) failed.", i);
1198       BAIL_OUT (-1);
1199     }
1200   } /* for (i = 0; i < values_num; i++) */
1201
1202   vl->values = values;
1203   vl->values_len = values_num;
1204
1205 #undef BAIL_OUT
1206   (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array);
1207   (*jvm_env)->DeleteLocalRef (jvm_env, o_list);
1208   return (0);
1209 } /* }}} int jtoc_values_array */
1210
1211 /* Convert a org/collectd/api/ValueList to a value_list_t. */
1212 static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
1213     jobject object_ptr)
1214 {
1215   jclass class_ptr;
1216   int status;
1217   jlong tmp_long;
1218   const data_set_t *ds;
1219
1220   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1221   if (class_ptr == NULL)
1222   {
1223     ERROR ("java plugin: jtoc_value_list: GetObjectClass failed.");
1224     return (-1);
1225   }
1226
1227   /* eo == empty okay */
1228 #define SET_STRING(buffer,method, eo) do { \
1229   status = jtoc_string (jvm_env, buffer, sizeof (buffer), eo, \
1230       class_ptr, object_ptr, method); \
1231   if (status != 0) { \
1232     ERROR ("java plugin: jtoc_value_list: jtoc_string (%s) failed.", \
1233         method); \
1234     return (-1); \
1235   } } while (0)
1236
1237   SET_STRING(vl->type, "getType", /* empty = */ 0);
1238
1239   ds = plugin_get_ds (vl->type);
1240   if (ds == NULL)
1241   {
1242     ERROR ("java plugin: jtoc_value_list: Data-set `%s' is not defined. "
1243         "Please consult the types.db(5) manpage for mor information.",
1244         vl->type);
1245     return (-1);
1246   }
1247
1248   SET_STRING(vl->host,            "getHost",           /* empty = */ 0);
1249   SET_STRING(vl->plugin,          "getPlugin",         /* empty = */ 0);
1250   SET_STRING(vl->plugin_instance, "getPluginInstance", /* empty = */ 1);
1251   SET_STRING(vl->type_instance,   "getTypeInstance",   /* empty = */ 1);
1252
1253 #undef SET_STRING
1254
1255   status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime");
1256   if (status != 0)
1257   {
1258     ERROR ("java plugin: jtoc_value_list: jtoc_long (getTime) failed.");
1259     return (-1);
1260   }
1261   /* Java measures time in milliseconds. */
1262   vl->time = MS_TO_CDTIME_T (tmp_long);
1263
1264   status = jtoc_long (jvm_env, &tmp_long,
1265       class_ptr, object_ptr, "getInterval");
1266   if (status != 0)
1267   {
1268     ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed.");
1269     return (-1);
1270   }
1271   vl->interval = MS_TO_CDTIME_T (tmp_long);
1272
1273   status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
1274   if (status != 0)
1275   {
1276     ERROR ("java plugin: jtoc_value_list: jtoc_values_array failed.");
1277     return (-1);
1278   }
1279
1280   return (0);
1281 } /* }}} int jtoc_value_list */
1282
1283 /* Convert a org/collectd/api/Notification to a notification_t. */
1284 static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */
1285     jobject object_ptr)
1286 {
1287   jclass class_ptr;
1288   int status;
1289   jlong tmp_long;
1290   jint tmp_int;
1291
1292   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1293   if (class_ptr == NULL)
1294   {
1295     ERROR ("java plugin: jtoc_notification: GetObjectClass failed.");
1296     return (-1);
1297   }
1298
1299   /* eo == empty okay */
1300 #define SET_STRING(buffer,method, eo) do { \
1301   status = jtoc_string (jvm_env, buffer, sizeof (buffer), eo, \
1302       class_ptr, object_ptr, method); \
1303   if (status != 0) { \
1304     ERROR ("java plugin: jtoc_notification: jtoc_string (%s) failed.", \
1305         method); \
1306     return (-1); \
1307   } } while (0)
1308
1309   SET_STRING (n->host,            "getHost",           /* empty = */ 1);
1310   SET_STRING (n->plugin,          "getPlugin",         /* empty = */ 1);
1311   SET_STRING (n->plugin_instance, "getPluginInstance", /* empty = */ 1);
1312   SET_STRING (n->type,            "getType",           /* empty = */ 1);
1313   SET_STRING (n->type_instance,   "getTypeInstance",   /* empty = */ 1);
1314   SET_STRING (n->message,         "getMessage",        /* empty = */ 0);
1315
1316 #undef SET_STRING
1317
1318   status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime");
1319   if (status != 0)
1320   {
1321     ERROR ("java plugin: jtoc_notification: jtoc_long (getTime) failed.");
1322     return (-1);
1323   }
1324   /* Java measures time in milliseconds. */
1325   n->time = MS_TO_CDTIME_T(tmp_long);
1326
1327   status = jtoc_int (jvm_env, &tmp_int,
1328       class_ptr, object_ptr, "getSeverity");
1329   if (status != 0)
1330   {
1331     ERROR ("java plugin: jtoc_notification: jtoc_int (getSeverity) failed.");
1332     return (-1);
1333   }
1334   n->severity = (int) tmp_int;
1335
1336   return (0);
1337 } /* }}} int jtoc_notification */
1338 /*
1339  * Functions accessible from Java
1340  */
1341 static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */
1342     jobject this, jobject java_vl)
1343 {
1344   value_list_t vl = VALUE_LIST_INIT;
1345   int status;
1346
1347   DEBUG ("cjni_api_dispatch_values: java_vl = %p;", (void *) java_vl);
1348
1349   status = jtoc_value_list (jvm_env, &vl, java_vl);
1350   if (status != 0)
1351   {
1352     ERROR ("java plugin: cjni_api_dispatch_values: jtoc_value_list failed.");
1353     return (-1);
1354   }
1355
1356   status = plugin_dispatch_values (&vl);
1357
1358   sfree (vl.values);
1359
1360   return (status);
1361 } /* }}} jint cjni_api_dispatch_values */
1362
1363 static jint JNICALL cjni_api_dispatch_notification (JNIEnv *jvm_env, /* {{{ */
1364     jobject this, jobject o_notification)
1365 {
1366   notification_t n;
1367   int status;
1368
1369   memset (&n, 0, sizeof (n));
1370   n.meta = NULL;
1371
1372   status = jtoc_notification (jvm_env, &n, o_notification);
1373   if (status != 0)
1374   {
1375     ERROR ("java plugin: cjni_api_dispatch_notification: jtoc_notification failed.");
1376     return (-1);
1377   }
1378
1379   status = plugin_dispatch_notification (&n);
1380
1381   return (status);
1382 } /* }}} jint cjni_api_dispatch_notification */
1383
1384 static jobject JNICALL cjni_api_get_ds (JNIEnv *jvm_env, /* {{{ */
1385     jobject this, jobject o_string_type)
1386 {
1387   const char *ds_name;
1388   const data_set_t *ds;
1389   jobject o_dataset;
1390
1391   ds_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_string_type, 0);
1392   if (ds_name == NULL)
1393   {
1394     ERROR ("java plugin: cjni_api_get_ds: GetStringUTFChars failed.");
1395     return (NULL);
1396   }
1397
1398   ds = plugin_get_ds (ds_name);
1399   DEBUG ("java plugin: cjni_api_get_ds: "
1400       "plugin_get_ds (%s) = %p;", ds_name, (void *) ds);
1401
1402   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_string_type, ds_name);
1403
1404   if (ds == NULL)
1405     return (NULL);
1406
1407   o_dataset = ctoj_data_set (jvm_env, ds);
1408   return (o_dataset);
1409 } /* }}} jint cjni_api_get_ds */
1410
1411 static jint JNICALL cjni_api_register_config (JNIEnv *jvm_env, /* {{{ */
1412     jobject this, jobject o_name, jobject o_config)
1413 {
1414   return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_CONFIG));
1415 } /* }}} jint cjni_api_register_config */
1416
1417 static jint JNICALL cjni_api_register_init (JNIEnv *jvm_env, /* {{{ */
1418     jobject this, jobject o_name, jobject o_config)
1419 {
1420   return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_INIT));
1421 } /* }}} jint cjni_api_register_init */
1422
1423 static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */
1424     jobject this, jobject o_name, jobject o_read)
1425 {
1426   user_data_t ud;
1427   cjni_callback_info_t *cbi;
1428
1429   cbi = cjni_callback_info_create (jvm_env, o_name, o_read, CB_TYPE_READ);
1430   if (cbi == NULL)
1431     return (-1);
1432
1433   DEBUG ("java plugin: Registering new read callback: %s", cbi->name);
1434
1435   memset (&ud, 0, sizeof (ud));
1436   ud.data = (void *) cbi;
1437   ud.free_func = cjni_callback_info_destroy;
1438
1439   plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
1440       /* interval = */ 0, &ud);
1441
1442   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
1443
1444   return (0);
1445 } /* }}} jint cjni_api_register_read */
1446
1447 static jint JNICALL cjni_api_register_write (JNIEnv *jvm_env, /* {{{ */
1448     jobject this, jobject o_name, jobject o_write)
1449 {
1450   user_data_t ud;
1451   cjni_callback_info_t *cbi;
1452
1453   cbi = cjni_callback_info_create (jvm_env, o_name, o_write, CB_TYPE_WRITE);
1454   if (cbi == NULL)
1455     return (-1);
1456
1457   DEBUG ("java plugin: Registering new write callback: %s", cbi->name);
1458
1459   memset (&ud, 0, sizeof (ud));
1460   ud.data = (void *) cbi;
1461   ud.free_func = cjni_callback_info_destroy;
1462
1463   plugin_register_write (cbi->name, cjni_write, &ud);
1464
1465   (*jvm_env)->DeleteLocalRef (jvm_env, o_write);
1466
1467   return (0);
1468 } /* }}} jint cjni_api_register_write */
1469
1470 static jint JNICALL cjni_api_register_flush (JNIEnv *jvm_env, /* {{{ */
1471     jobject this, jobject o_name, jobject o_flush)
1472 {
1473   user_data_t ud;
1474   cjni_callback_info_t *cbi;
1475
1476   cbi = cjni_callback_info_create (jvm_env, o_name, o_flush, CB_TYPE_FLUSH);
1477   if (cbi == NULL)
1478     return (-1);
1479
1480   DEBUG ("java plugin: Registering new flush callback: %s", cbi->name);
1481
1482   memset (&ud, 0, sizeof (ud));
1483   ud.data = (void *) cbi;
1484   ud.free_func = cjni_callback_info_destroy;
1485
1486   plugin_register_flush (cbi->name, cjni_flush, &ud);
1487
1488   (*jvm_env)->DeleteLocalRef (jvm_env, o_flush);
1489
1490   return (0);
1491 } /* }}} jint cjni_api_register_flush */
1492
1493 static jint JNICALL cjni_api_register_shutdown (JNIEnv *jvm_env, /* {{{ */
1494     jobject this, jobject o_name, jobject o_shutdown)
1495 {
1496   return (cjni_callback_register (jvm_env, o_name, o_shutdown,
1497         CB_TYPE_SHUTDOWN));
1498 } /* }}} jint cjni_api_register_shutdown */
1499
1500 static jint JNICALL cjni_api_register_log (JNIEnv *jvm_env, /* {{{ */
1501     jobject this, jobject o_name, jobject o_log)
1502 {
1503   user_data_t ud;
1504   cjni_callback_info_t *cbi;
1505
1506   cbi = cjni_callback_info_create (jvm_env, o_name, o_log, CB_TYPE_LOG);
1507   if (cbi == NULL)
1508     return (-1);
1509
1510   DEBUG ("java plugin: Registering new log callback: %s", cbi->name);
1511
1512   memset (&ud, 0, sizeof (ud));
1513   ud.data = (void *) cbi;
1514   ud.free_func = cjni_callback_info_destroy;
1515
1516   plugin_register_log (cbi->name, cjni_log, &ud);
1517
1518   (*jvm_env)->DeleteLocalRef (jvm_env, o_log);
1519
1520   return (0);
1521 } /* }}} jint cjni_api_register_log */
1522
1523 static jint JNICALL cjni_api_register_notification (JNIEnv *jvm_env, /* {{{ */
1524     jobject this, jobject o_name, jobject o_notification)
1525 {
1526   user_data_t ud;
1527   cjni_callback_info_t *cbi;
1528
1529   cbi = cjni_callback_info_create (jvm_env, o_name, o_notification,
1530       CB_TYPE_NOTIFICATION);
1531   if (cbi == NULL)
1532     return (-1);
1533
1534   DEBUG ("java plugin: Registering new notification callback: %s", cbi->name);
1535
1536   memset (&ud, 0, sizeof (ud));
1537   ud.data = (void *) cbi;
1538   ud.free_func = cjni_callback_info_destroy;
1539
1540   plugin_register_notification (cbi->name, cjni_notification, &ud);
1541
1542   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
1543
1544   return (0);
1545 } /* }}} jint cjni_api_register_notification */
1546
1547 static jint JNICALL cjni_api_register_match_target (JNIEnv *jvm_env, /* {{{ */
1548     jobject this, jobject o_name, jobject o_match, int type)
1549 {
1550   int status;
1551   const char *c_name;
1552
1553   c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0);
1554   if (c_name == NULL)
1555   {
1556     ERROR ("java plugin: cjni_api_register_match_target: "
1557         "GetStringUTFChars failed.");
1558     return (-1);
1559   }
1560
1561   status = cjni_callback_register (jvm_env, o_name, o_match, type);
1562   if (status != 0)
1563   {
1564     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1565     return (-1);
1566   }
1567
1568   if (type == CB_TYPE_MATCH)
1569   {
1570     match_proc_t m_proc;
1571
1572     memset (&m_proc, 0, sizeof (m_proc));
1573     m_proc.create  = cjni_match_target_create;
1574     m_proc.destroy = cjni_match_target_destroy;
1575     m_proc.match   = (void *) cjni_match_target_invoke;
1576
1577     status = fc_register_match (c_name, m_proc);
1578   }
1579   else if (type == CB_TYPE_TARGET)
1580   {
1581     target_proc_t t_proc;
1582
1583     memset (&t_proc, 0, sizeof (t_proc));
1584     t_proc.create  = cjni_match_target_create;
1585     t_proc.destroy = cjni_match_target_destroy;
1586     t_proc.invoke  = cjni_match_target_invoke;
1587
1588     status = fc_register_target (c_name, t_proc);
1589   }
1590   else
1591   {
1592     ERROR ("java plugin: cjni_api_register_match_target: "
1593         "Don't know whether to create a match or a target.");
1594     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1595     return (-1);
1596   }
1597
1598   if (status != 0)
1599   {
1600     ERROR ("java plugin: cjni_api_register_match_target: "
1601         "%s failed.",
1602         (type == CB_TYPE_MATCH) ? "fc_register_match" : "fc_register_target");
1603     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1604     return (-1);
1605   }
1606
1607   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1608
1609   return (0);
1610 } /* }}} jint cjni_api_register_match_target */
1611
1612 static jint JNICALL cjni_api_register_match (JNIEnv *jvm_env, /* {{{ */
1613     jobject this, jobject o_name, jobject o_match)
1614 {
1615   return (cjni_api_register_match_target (jvm_env, this, o_name, o_match,
1616         CB_TYPE_MATCH));
1617 } /* }}} jint cjni_api_register_match */
1618
1619 static jint JNICALL cjni_api_register_target (JNIEnv *jvm_env, /* {{{ */
1620     jobject this, jobject o_name, jobject o_target)
1621 {
1622   return (cjni_api_register_match_target (jvm_env, this, o_name, o_target,
1623         CB_TYPE_TARGET));
1624 } /* }}} jint cjni_api_register_target */
1625
1626 static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */
1627     jobject this, jint severity, jobject o_message)
1628 {
1629   const char *c_str;
1630
1631   c_str = (*jvm_env)->GetStringUTFChars (jvm_env, o_message, 0);
1632   if (c_str == NULL)
1633   {
1634     ERROR ("java plugin: cjni_api_log: GetStringUTFChars failed.");
1635     return;
1636   }
1637
1638   if (severity < LOG_ERR)
1639     severity = LOG_ERR;
1640   if (severity > LOG_DEBUG)
1641     severity = LOG_DEBUG;
1642
1643   plugin_log (severity, "%s", c_str);
1644
1645   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_message, c_str);
1646 } /* }}} void cjni_api_log */
1647
1648 static jstring JNICALL cjni_api_get_hostname (JNIEnv *jvm_env, jobject this)
1649 {
1650     return ctoj_output_string(jvm_env, hostname_g);
1651 }
1652
1653 /* List of ``native'' functions, i. e. C-functions that can be called from
1654  * Java. */
1655 static JNINativeMethod jni_api_functions[] = /* {{{ */
1656 {
1657   { "dispatchValues",
1658     "(Lorg/collectd/api/ValueList;)I",
1659     cjni_api_dispatch_values },
1660
1661   { "dispatchNotification",
1662     "(Lorg/collectd/api/Notification;)I",
1663     cjni_api_dispatch_notification },
1664
1665   { "getDS",
1666     "(Ljava/lang/String;)Lorg/collectd/api/DataSet;",
1667     cjni_api_get_ds },
1668
1669   { "registerConfig",
1670     "(Ljava/lang/String;Lorg/collectd/api/CollectdConfigInterface;)I",
1671     cjni_api_register_config },
1672
1673   { "registerInit",
1674     "(Ljava/lang/String;Lorg/collectd/api/CollectdInitInterface;)I",
1675     cjni_api_register_init },
1676
1677   { "registerRead",
1678     "(Ljava/lang/String;Lorg/collectd/api/CollectdReadInterface;)I",
1679     cjni_api_register_read },
1680
1681   { "registerWrite",
1682     "(Ljava/lang/String;Lorg/collectd/api/CollectdWriteInterface;)I",
1683     cjni_api_register_write },
1684
1685   { "registerFlush",
1686     "(Ljava/lang/String;Lorg/collectd/api/CollectdFlushInterface;)I",
1687     cjni_api_register_flush },
1688
1689   { "registerShutdown",
1690     "(Ljava/lang/String;Lorg/collectd/api/CollectdShutdownInterface;)I",
1691     cjni_api_register_shutdown },
1692
1693   { "registerLog",
1694     "(Ljava/lang/String;Lorg/collectd/api/CollectdLogInterface;)I",
1695     cjni_api_register_log },
1696
1697   { "registerNotification",
1698     "(Ljava/lang/String;Lorg/collectd/api/CollectdNotificationInterface;)I",
1699     cjni_api_register_notification },
1700
1701   { "registerMatch",
1702     "(Ljava/lang/String;Lorg/collectd/api/CollectdMatchFactoryInterface;)I",
1703     cjni_api_register_match },
1704
1705   { "registerTarget",
1706     "(Ljava/lang/String;Lorg/collectd/api/CollectdTargetFactoryInterface;)I",
1707     cjni_api_register_target },
1708
1709   { "log",
1710     "(ILjava/lang/String;)V",
1711     cjni_api_log },
1712
1713   { "getHostname",
1714     "()Ljava/lang/String;",
1715     cjni_api_get_hostname },
1716
1717 };
1718 static size_t jni_api_functions_num = sizeof (jni_api_functions)
1719   / sizeof (jni_api_functions[0]);
1720 /* }}} */
1721
1722 /*
1723  * Functions
1724  */
1725 /* Allocate a `cjni_callback_info_t' given the type and objects necessary for
1726  * all registration functions. */
1727 static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{ */
1728     jobject o_name, jobject o_callback, int type)
1729 {
1730   const char *c_name;
1731   cjni_callback_info_t *cbi;
1732   const char *method_name;
1733   const char *method_signature;
1734
1735   switch (type)
1736   {
1737     case CB_TYPE_CONFIG:
1738       method_name = "config";
1739       method_signature = "(Lorg/collectd/api/OConfigItem;)I";
1740       break;
1741
1742     case CB_TYPE_INIT:
1743       method_name = "init";
1744       method_signature = "()I";
1745       break;
1746
1747     case CB_TYPE_READ:
1748       method_name = "read";
1749       method_signature = "()I";
1750       break;
1751
1752     case CB_TYPE_WRITE:
1753       method_name = "write";
1754       method_signature = "(Lorg/collectd/api/ValueList;)I";
1755       break;
1756
1757     case CB_TYPE_FLUSH:
1758       method_name = "flush";
1759       method_signature = "(Ljava/lang/Number;Ljava/lang/String;)I";
1760       break;
1761
1762     case CB_TYPE_SHUTDOWN:
1763       method_name = "shutdown";
1764       method_signature = "()I";
1765       break;
1766
1767     case CB_TYPE_LOG:
1768       method_name = "log";
1769       method_signature = "(ILjava/lang/String;)V";
1770       break;
1771
1772     case CB_TYPE_NOTIFICATION:
1773       method_name = "notification";
1774       method_signature = "(Lorg/collectd/api/Notification;)I";
1775       break;
1776
1777     case CB_TYPE_MATCH:
1778       method_name = "createMatch";
1779       method_signature = "(Lorg/collectd/api/OConfigItem;)"
1780         "Lorg/collectd/api/CollectdMatchInterface;";
1781       break;
1782
1783     case CB_TYPE_TARGET:
1784       method_name = "createTarget";
1785       method_signature = "(Lorg/collectd/api/OConfigItem;)"
1786         "Lorg/collectd/api/CollectdTargetInterface;";
1787       break;
1788
1789     default:
1790       ERROR ("java plugin: cjni_callback_info_create: Unknown type: %#x",
1791           type);
1792       return (NULL);
1793   }
1794
1795   c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0);
1796   if (c_name == NULL)
1797   {
1798     ERROR ("java plugin: cjni_callback_info_create: "
1799         "GetStringUTFChars failed.");
1800     return (NULL);
1801   }
1802
1803   cbi = calloc (1, sizeof (*cbi));
1804   if (cbi == NULL)
1805   {
1806     ERROR ("java plugin: cjni_callback_info_create: calloc failed.");
1807     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1808     return (NULL);
1809   }
1810   cbi->type = type;
1811
1812   cbi->name = strdup (c_name);
1813   if (cbi->name == NULL)
1814   {
1815     pthread_mutex_unlock (&java_callbacks_lock);
1816     ERROR ("java plugin: cjni_callback_info_create: strdup failed.");
1817     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1818     sfree (cbi);
1819     return (NULL);
1820   }
1821
1822   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1823
1824   cbi->object = (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
1825   if (cbi->object == NULL)
1826   {
1827     ERROR ("java plugin: cjni_callback_info_create: NewGlobalRef failed.");
1828     sfree (cbi->name);
1829     sfree (cbi);
1830     return (NULL);
1831   }
1832
1833   cbi->class  = (*jvm_env)->GetObjectClass (jvm_env, cbi->object);
1834   if (cbi->class == NULL)
1835   {
1836     ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed.");
1837     (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
1838     sfree (cbi->name);
1839     sfree (cbi);
1840     return (NULL);
1841   }
1842
1843   cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class,
1844       method_name, method_signature);
1845   if (cbi->method == NULL)
1846   {
1847     ERROR ("java plugin: cjni_callback_info_create: "
1848         "Cannot find the `%s' method with signature `%s'.",
1849         method_name, method_signature);
1850     (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
1851     sfree (cbi->name);
1852     sfree (cbi);
1853     return (NULL);
1854   }
1855
1856   return (cbi);
1857 } /* }}} cjni_callback_info_t cjni_callback_info_create */
1858
1859 /* Allocate a `cjni_callback_info_t' via `cjni_callback_info_create' and add it
1860  * to the global `java_callbacks' variable. This is used for `config', `init',
1861  * and `shutdown' callbacks. */
1862 static int cjni_callback_register (JNIEnv *jvm_env, /* {{{ */
1863     jobject o_name, jobject o_callback, int type)
1864 {
1865   cjni_callback_info_t *cbi;
1866   cjni_callback_info_t *tmp;
1867 #if COLLECT_DEBUG
1868   const char *type_str;
1869 #endif
1870
1871   cbi = cjni_callback_info_create (jvm_env, o_name, o_callback, type);
1872   if (cbi == NULL)
1873     return (-1);
1874
1875 #if COLLECT_DEBUG
1876   switch (type)
1877   {
1878     case CB_TYPE_CONFIG:
1879       type_str = "config";
1880       break;
1881
1882     case CB_TYPE_INIT:
1883       type_str = "init";
1884       break;
1885
1886     case CB_TYPE_SHUTDOWN:
1887       type_str = "shutdown";
1888       break;
1889
1890     case CB_TYPE_MATCH:
1891       type_str = "match";
1892       break;
1893
1894     case CB_TYPE_TARGET:
1895       type_str = "target";
1896       break;
1897
1898     default:
1899       type_str = "<unknown>";
1900   }
1901   DEBUG ("java plugin: Registering new %s callback: %s",
1902       type_str, cbi->name);
1903 #endif
1904
1905   pthread_mutex_lock (&java_callbacks_lock);
1906
1907   tmp = realloc (java_callbacks,
1908       (java_callbacks_num + 1) * sizeof (*java_callbacks));
1909   if (tmp == NULL)
1910   {
1911     pthread_mutex_unlock (&java_callbacks_lock);
1912     ERROR ("java plugin: cjni_callback_register: realloc failed.");
1913
1914     (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
1915     free (cbi);
1916
1917     return (-1);
1918   }
1919   java_callbacks = tmp;
1920   java_callbacks[java_callbacks_num] = *cbi;
1921   java_callbacks_num++;
1922
1923   pthread_mutex_unlock (&java_callbacks_lock);
1924
1925   free (cbi);
1926   return (0);
1927 } /* }}} int cjni_callback_register */
1928
1929 /* Callback for `pthread_key_create'. It frees the data contained in
1930  * `jvm_env_key' and prints a warning if the reference counter is not zero. */
1931 static void cjni_jvm_env_destroy (void *args) /* {{{ */
1932 {
1933   cjni_jvm_env_t *cjni_env;
1934
1935   if (args == NULL)
1936     return;
1937
1938   cjni_env = (cjni_jvm_env_t *) args;
1939
1940   if (cjni_env->reference_counter > 0)
1941   {
1942     ERROR ("java plugin: cjni_jvm_env_destroy: "
1943         "cjni_env->reference_counter = %i;", cjni_env->reference_counter);
1944   }
1945
1946   if (cjni_env->jvm_env != NULL)
1947   {
1948     ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;",
1949         (void *) cjni_env->jvm_env);
1950   }
1951
1952   /* The pointer is allocated in `cjni_thread_attach' */
1953   free (cjni_env);
1954 } /* }}} void cjni_jvm_env_destroy */
1955
1956 /* Register ``native'' functions with the JVM. Native functions are C-functions
1957  * that can be called by Java code. */
1958 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
1959 {
1960   jclass api_class_ptr;
1961   int status;
1962
1963   api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/Collectd");
1964   if (api_class_ptr == NULL)
1965   {
1966     ERROR ("cjni_init_native: Cannot find the API class \"org.collectd.api"
1967         ".Collectd\". Please set the correct class path "
1968         "using 'JVMArg \"-Djava.class.path=...\"'.");
1969     return (-1);
1970   }
1971
1972   status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
1973       jni_api_functions, (jint) jni_api_functions_num);
1974   if (status != 0)
1975   {
1976     ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
1977     return (-1);
1978   }
1979
1980   return (0);
1981 } /* }}} int cjni_init_native */
1982
1983 /* Create the JVM. This is called when the first thread tries to access the JVM
1984  * via cjni_thread_attach. */
1985 static int cjni_create_jvm (void) /* {{{ */
1986 {
1987   JNIEnv *jvm_env;
1988   JavaVMInitArgs vm_args;
1989   JavaVMOption vm_options[jvm_argc];
1990
1991   int status;
1992   size_t i;
1993
1994   if (jvm != NULL)
1995     return (0);
1996
1997   status = pthread_key_create (&jvm_env_key, cjni_jvm_env_destroy);
1998   if (status != 0)
1999   {
2000     ERROR ("java plugin: cjni_create_jvm: pthread_key_create failed "
2001         "with status %i.", status);
2002     return (-1);
2003   }
2004
2005   jvm_env = NULL;
2006
2007   memset (&vm_args, 0, sizeof (vm_args));
2008   vm_args.version = JNI_VERSION_1_2;
2009   vm_args.options = vm_options;
2010   vm_args.nOptions = (jint) jvm_argc;
2011
2012   for (i = 0; i < jvm_argc; i++)
2013   {
2014     DEBUG ("java plugin: cjni_create_jvm: jvm_argv[%zu] = %s",
2015         i, jvm_argv[i]);
2016     vm_args.options[i].optionString = jvm_argv[i];
2017   }
2018
2019   status = JNI_CreateJavaVM (&jvm, (void *) &jvm_env, (void *) &vm_args);
2020   if (status != 0)
2021   {
2022     ERROR ("java plugin: cjni_create_jvm: "
2023         "JNI_CreateJavaVM failed with status %i.",
2024         status);
2025     return (-1);
2026   }
2027   assert (jvm != NULL);
2028   assert (jvm_env != NULL);
2029
2030   /* Call RegisterNatives */
2031   status = cjni_init_native (jvm_env);
2032   if (status != 0)
2033   {
2034     ERROR ("java plugin: cjni_create_jvm: cjni_init_native failed.");
2035     return (-1);
2036   }
2037
2038   DEBUG ("java plugin: The JVM has been created.");
2039   return (0);
2040 } /* }}} int cjni_create_jvm */
2041
2042 /* Increase the reference counter to the JVM for this thread. If it was zero,
2043  * attach the JVM first. */
2044 static JNIEnv *cjni_thread_attach (void) /* {{{ */
2045 {
2046   cjni_jvm_env_t *cjni_env;
2047   JNIEnv *jvm_env;
2048
2049   /* If we're the first thread to access the JVM, we'll have to create it
2050    * first.. */
2051   if (jvm == NULL)
2052   {
2053     int status;
2054
2055     status = cjni_create_jvm ();
2056     if (status != 0)
2057     {
2058       ERROR ("java plugin: cjni_thread_attach: cjni_create_jvm failed.");
2059       return (NULL);
2060     }
2061   }
2062   assert (jvm != NULL);
2063
2064   cjni_env = pthread_getspecific (jvm_env_key);
2065   if (cjni_env == NULL)
2066   {
2067     /* This pointer is free'd in `cjni_jvm_env_destroy'. */
2068     cjni_env = calloc (1, sizeof (*cjni_env));
2069     if (cjni_env == NULL)
2070     {
2071       ERROR ("java plugin: cjni_thread_attach: calloc failed.");
2072       return (NULL);
2073     }
2074     cjni_env->reference_counter = 0;
2075     cjni_env->jvm_env = NULL;
2076
2077     pthread_setspecific (jvm_env_key, cjni_env);
2078   }
2079
2080   if (cjni_env->reference_counter > 0)
2081   {
2082     cjni_env->reference_counter++;
2083     jvm_env = cjni_env->jvm_env;
2084   }
2085   else
2086   {
2087     int status;
2088     JavaVMAttachArgs args;
2089
2090     assert (cjni_env->jvm_env == NULL);
2091
2092     memset (&args, 0, sizeof (args));
2093     args.version = JNI_VERSION_1_2;
2094
2095     status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args);
2096     if (status != 0)
2097     {
2098       ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed "
2099           "with status %i.", status);
2100       return (NULL);
2101     }
2102
2103     cjni_env->reference_counter = 1;
2104     cjni_env->jvm_env = jvm_env;
2105   }
2106
2107   DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i",
2108       cjni_env->reference_counter);
2109   assert (jvm_env != NULL);
2110   return (jvm_env);
2111 } /* }}} JNIEnv *cjni_thread_attach */
2112
2113 /* Decrease the reference counter of this thread. If it reaches zero, detach
2114  * from the JVM. */
2115 static int cjni_thread_detach (void) /* {{{ */
2116 {
2117   cjni_jvm_env_t *cjni_env;
2118   int status;
2119
2120   cjni_env = pthread_getspecific (jvm_env_key);
2121   if (cjni_env == NULL)
2122   {
2123     ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed.");
2124     return (-1);
2125   }
2126
2127   assert (cjni_env->reference_counter > 0);
2128   assert (cjni_env->jvm_env != NULL);
2129
2130   cjni_env->reference_counter--;
2131   DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i",
2132       cjni_env->reference_counter);
2133
2134   if (cjni_env->reference_counter > 0)
2135     return (0);
2136
2137   status = (*jvm)->DetachCurrentThread (jvm);
2138   if (status != 0)
2139   {
2140     ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed "
2141         "with status %i.", status);
2142   }
2143
2144   cjni_env->reference_counter = 0;
2145   cjni_env->jvm_env = NULL;
2146
2147   return (0);
2148 } /* }}} int cjni_thread_detach */
2149
2150 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
2151 {
2152   char **tmp;
2153
2154   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
2155   {
2156     WARNING ("java plugin: `JVMArg' needs exactly one string argument.");
2157     return (-1);
2158   }
2159
2160   if (jvm != NULL)
2161   {
2162     ERROR ("java plugin: All `JVMArg' options MUST appear before all "
2163         "`LoadPlugin' options! The JVM is already started and I have to "
2164         "ignore this argument: %s",
2165         ci->values[0].value.string);
2166     return (-1);
2167   }
2168
2169   tmp = realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1));
2170   if (tmp == NULL)
2171   {
2172     ERROR ("java plugin: realloc failed.");
2173     return (-1);
2174   }
2175   jvm_argv = tmp;
2176
2177   jvm_argv[jvm_argc] = strdup (ci->values[0].value.string);
2178   if (jvm_argv[jvm_argc] == NULL)
2179   {
2180     ERROR ("java plugin: strdup failed.");
2181     return (-1);
2182   }
2183   jvm_argc++;
2184
2185   return (0);
2186 } /* }}} int cjni_config_add_jvm_arg */
2187
2188 static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
2189 {
2190   JNIEnv *jvm_env;
2191   java_plugin_class_t *class;
2192   jmethodID constructor_id;
2193   jobject tmp_object;
2194
2195   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
2196   {
2197     WARNING ("java plugin: `LoadPlugin' needs exactly one string argument.");
2198     return (-1);
2199   }
2200
2201   jvm_env = cjni_thread_attach ();
2202   if (jvm_env == NULL)
2203     return (-1);
2204
2205   class = realloc (java_classes_list,
2206       (java_classes_list_len + 1) * sizeof (*java_classes_list));
2207   if (class == NULL)
2208   {
2209     ERROR ("java plugin: realloc failed.");
2210     cjni_thread_detach ();
2211     return (-1);
2212   }
2213   java_classes_list = class;
2214   class = java_classes_list + java_classes_list_len;
2215
2216   memset (class, 0, sizeof (*class));
2217   class->name = strdup (ci->values[0].value.string);
2218   if (class->name == NULL)
2219   {
2220     ERROR ("java plugin: strdup failed.");
2221     cjni_thread_detach ();
2222     return (-1);
2223   }
2224   class->class = NULL;
2225   class->object = NULL;
2226
2227   { /* Replace all dots ('.') with slashes ('/'). Dots are usually used
2228        thorough the Java community, but (Sun's) `FindClass' and friends need
2229        slashes. */
2230     size_t i;
2231     for (i = 0; class->name[i] != 0; i++)
2232       if (class->name[i] == '.')
2233         class->name[i] = '/';
2234   }
2235
2236   DEBUG ("java plugin: Loading class %s", class->name);
2237
2238   class->class = (*jvm_env)->FindClass (jvm_env, class->name);
2239   if (class->class == NULL)
2240   {
2241     ERROR ("java plugin: cjni_config_load_plugin: FindClass (%s) failed.",
2242         class->name);
2243     cjni_thread_detach ();
2244     free (class->name);
2245     return (-1);
2246   }
2247
2248   constructor_id = (*jvm_env)->GetMethodID (jvm_env, class->class,
2249       "<init>", "()V");
2250   if (constructor_id == NULL)
2251   {
2252     ERROR ("java plugin: cjni_config_load_plugin: "
2253         "Could not find the constructor for `%s'.",
2254         class->name);
2255     cjni_thread_detach ();
2256     free (class->name);
2257     return (-1);
2258   }
2259
2260   tmp_object = (*jvm_env)->NewObject (jvm_env, class->class,
2261       constructor_id);
2262   if (tmp_object != NULL)
2263     class->object = (*jvm_env)->NewGlobalRef (jvm_env, tmp_object);
2264   else
2265     class->object = NULL;
2266   if (class->object == NULL)
2267   {
2268     ERROR ("java plugin: cjni_config_load_plugin: "
2269         "Could create a new `%s' object.",
2270         class->name);
2271     cjni_thread_detach ();
2272     free (class->name);
2273     return (-1);
2274   }
2275
2276   cjni_thread_detach ();
2277
2278   java_classes_list_len++;
2279
2280   return (0);
2281 } /* }}} int cjni_config_load_plugin */
2282
2283 static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
2284 {
2285   JNIEnv *jvm_env;
2286   cjni_callback_info_t *cbi;
2287   jobject o_ocitem;
2288   const char *name;
2289   size_t i;
2290
2291   jclass class;
2292   jmethodID method;
2293
2294   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
2295   {
2296     WARNING ("java plugin: `Plugin' blocks "
2297         "need exactly one string argument.");
2298     return (-1);
2299   }
2300
2301   name = ci->values[0].value.string;
2302
2303   cbi = NULL;
2304   for (i = 0; i < java_callbacks_num; i++)
2305   {
2306     if (java_callbacks[i].type != CB_TYPE_CONFIG)
2307       continue;
2308
2309     if (strcmp (name, java_callbacks[i].name) != 0)
2310       continue;
2311
2312     cbi = java_callbacks + i;
2313     break;
2314   }
2315
2316   if (cbi == NULL)
2317   {
2318     NOTICE ("java plugin: Configuration block for `%s' found, but no such "
2319         "configuration callback has been registered. Please make sure, the "
2320         "`LoadPlugin' lines precede the `Plugin' blocks.",
2321         name);
2322     return (0);
2323   }
2324
2325   DEBUG ("java plugin: Configuring %s", name);
2326
2327   jvm_env = cjni_thread_attach ();
2328   if (jvm_env == NULL)
2329     return (-1);
2330
2331   o_ocitem = ctoj_oconfig_item (jvm_env, ci);
2332   if (o_ocitem == NULL)
2333   {
2334     ERROR ("java plugin: cjni_config_plugin_block: ctoj_oconfig_item failed.");
2335     cjni_thread_detach ();
2336     return (-1);
2337   }
2338
2339   class = (*jvm_env)->GetObjectClass (jvm_env, cbi->object);
2340   method = (*jvm_env)->GetMethodID (jvm_env, class,
2341       "config", "(Lorg/collectd/api/OConfigItem;)I");
2342
2343   (*jvm_env)->CallIntMethod (jvm_env,
2344       cbi->object, method, o_ocitem);
2345
2346   (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
2347   cjni_thread_detach ();
2348   return (0);
2349 } /* }}} int cjni_config_plugin_block */
2350
2351 static int cjni_config_perform (oconfig_item_t *ci) /* {{{ */
2352 {
2353   int success;
2354   int errors;
2355   int status;
2356   int i;
2357
2358   success = 0;
2359   errors = 0;
2360
2361   for (i = 0; i < ci->children_num; i++)
2362   {
2363     oconfig_item_t *child = ci->children + i;
2364
2365     if (strcasecmp ("JVMArg", child->key) == 0)
2366     {
2367       status = cjni_config_add_jvm_arg (child);
2368       if (status == 0)
2369         success++;
2370       else
2371         errors++;
2372     }
2373     else if (strcasecmp ("LoadPlugin", child->key) == 0)
2374     {
2375       status = cjni_config_load_plugin (child);
2376       if (status == 0)
2377         success++;
2378       else
2379         errors++;
2380     }
2381     else if (strcasecmp ("Plugin", child->key) == 0)
2382     {
2383       status = cjni_config_plugin_block (child);
2384       if (status == 0)
2385         success++;
2386       else
2387         errors++;
2388     }
2389     else
2390     {
2391       WARNING ("java plugin: Option `%s' not allowed here.", child->key);
2392       errors++;
2393     }
2394   }
2395
2396   DEBUG ("java plugin: jvm_argc = %zu;", jvm_argc);
2397   DEBUG ("java plugin: java_classes_list_len = %zu;", java_classes_list_len);
2398
2399   if ((success == 0) && (errors > 0))
2400   {
2401     ERROR ("java plugin: All statements failed.");
2402     return (-1);
2403   }
2404
2405   return (0);
2406 } /* }}} int cjni_config_perform */
2407
2408 /* Copy the children of `ci' to the global `config_block' variable. */
2409 static int cjni_config_callback (oconfig_item_t *ci) /* {{{ */
2410 {
2411   oconfig_item_t *ci_copy;
2412   oconfig_item_t *tmp;
2413
2414   assert (ci != NULL);
2415   if (ci->children_num == 0)
2416     return (0); /* nothing to do */
2417
2418   ci_copy = oconfig_clone (ci);
2419   if (ci_copy == NULL)
2420   {
2421     ERROR ("java plugin: oconfig_clone failed.");
2422     return (-1);
2423   }
2424
2425   if (config_block == NULL)
2426   {
2427     config_block = ci_copy;
2428     return (0);
2429   }
2430
2431   tmp = realloc (config_block->children,
2432       (config_block->children_num + ci_copy->children_num) * sizeof (*tmp));
2433   if (tmp == NULL)
2434   {
2435     ERROR ("java plugin: realloc failed.");
2436     oconfig_free (ci_copy);
2437     return (-1);
2438   }
2439   config_block->children = tmp;
2440
2441   /* Copy the pointers */
2442   memcpy (config_block->children + config_block->children_num,
2443       ci_copy->children,
2444       ci_copy->children_num * sizeof (*ci_copy->children));
2445   config_block->children_num += ci_copy->children_num;
2446
2447   /* Delete the pointers from the copy, so `oconfig_free' can't free them. */
2448   memset (ci_copy->children, 0,
2449       ci_copy->children_num * sizeof (*ci_copy->children));
2450   ci_copy->children_num = 0;
2451
2452   oconfig_free (ci_copy);
2453
2454   return (0);
2455 } /* }}} int cjni_config_callback */
2456
2457 /* Free the data contained in the `user_data_t' pointer passed to `cjni_read'
2458  * and `cjni_write'. In particular, delete the global reference to the Java
2459  * object. */
2460 static void cjni_callback_info_destroy (void *arg) /* {{{ */
2461 {
2462   JNIEnv *jvm_env;
2463   cjni_callback_info_t *cbi;
2464
2465   DEBUG ("java plugin: cjni_callback_info_destroy (arg = %p);", arg);
2466
2467   cbi = (cjni_callback_info_t *) arg;
2468
2469   /* This condition can occur when shutting down. */
2470   if (jvm == NULL)
2471   {
2472     sfree (cbi);
2473     return;
2474   }
2475
2476   if (arg == NULL)
2477     return;
2478
2479   jvm_env = cjni_thread_attach ();
2480   if (jvm_env == NULL)
2481   {
2482     ERROR ("java plugin: cjni_callback_info_destroy: cjni_thread_attach failed.");
2483     return;
2484   }
2485
2486   (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
2487
2488   cbi->method = NULL;
2489   cbi->object = NULL;
2490   cbi->class  = NULL;
2491   free (cbi);
2492
2493   cjni_thread_detach ();
2494 } /* }}} void cjni_callback_info_destroy */
2495
2496 /* Call the CB_TYPE_READ callback pointed to by the `user_data_t' pointer. */
2497 static int cjni_read (user_data_t *ud) /* {{{ */
2498 {
2499   JNIEnv *jvm_env;
2500   cjni_callback_info_t *cbi;
2501   int ret_status;
2502
2503   if (jvm == NULL)
2504   {
2505     ERROR ("java plugin: cjni_read: jvm == NULL");
2506     return (-1);
2507   }
2508
2509   if ((ud == NULL) || (ud->data == NULL))
2510   {
2511     ERROR ("java plugin: cjni_read: Invalid user data.");
2512     return (-1);
2513   }
2514
2515   jvm_env = cjni_thread_attach ();
2516   if (jvm_env == NULL)
2517     return (-1);
2518
2519   cbi = (cjni_callback_info_t *) ud->data;
2520
2521   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object,
2522       cbi->method);
2523
2524   cjni_thread_detach ();
2525   return (ret_status);
2526 } /* }}} int cjni_read */
2527
2528 /* Call the CB_TYPE_WRITE callback pointed to by the `user_data_t' pointer. */
2529 static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
2530     user_data_t *ud)
2531 {
2532   JNIEnv *jvm_env;
2533   cjni_callback_info_t *cbi;
2534   jobject vl_java;
2535   int ret_status;
2536
2537   if (jvm == NULL)
2538   {
2539     ERROR ("java plugin: cjni_write: jvm == NULL");
2540     return (-1);
2541   }
2542
2543   if ((ud == NULL) || (ud->data == NULL))
2544   {
2545     ERROR ("java plugin: cjni_write: Invalid user data.");
2546     return (-1);
2547   }
2548
2549   jvm_env = cjni_thread_attach ();
2550   if (jvm_env == NULL)
2551     return (-1);
2552
2553   cbi = (cjni_callback_info_t *) ud->data;
2554
2555   vl_java = ctoj_value_list (jvm_env, ds, vl);
2556   if (vl_java == NULL)
2557   {
2558     ERROR ("java plugin: cjni_write: ctoj_value_list failed.");
2559     cjni_thread_detach ();
2560     return (-1);
2561   }
2562
2563   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
2564       cbi->object, cbi->method, vl_java);
2565
2566   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
2567
2568   cjni_thread_detach ();
2569   return (ret_status);
2570 } /* }}} int cjni_write */
2571
2572 /* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */
2573 static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
2574     user_data_t *ud)
2575 {
2576   JNIEnv *jvm_env;
2577   cjni_callback_info_t *cbi;
2578   jobject o_timeout;
2579   jobject o_identifier;
2580   int ret_status;
2581
2582   if (jvm == NULL)
2583   {
2584     ERROR ("java plugin: cjni_flush: jvm == NULL");
2585     return (-1);
2586   }
2587
2588   if ((ud == NULL) || (ud->data == NULL))
2589   {
2590     ERROR ("java plugin: cjni_flush: Invalid user data.");
2591     return (-1);
2592   }
2593
2594   jvm_env = cjni_thread_attach ();
2595   if (jvm_env == NULL)
2596     return (-1);
2597
2598   cbi = (cjni_callback_info_t *) ud->data;
2599
2600   o_timeout = ctoj_jdouble_to_number (jvm_env,
2601       (jdouble) CDTIME_T_TO_DOUBLE (timeout));
2602   if (o_timeout == NULL)
2603   {
2604     ERROR ("java plugin: cjni_flush: Converting double "
2605         "to Number object failed.");
2606     cjni_thread_detach ();
2607     return (-1);
2608   }
2609
2610   o_identifier = NULL;
2611   if (identifier != NULL)
2612   {
2613     o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier);
2614     if (o_identifier == NULL)
2615     {
2616       (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
2617       ERROR ("java plugin: cjni_flush: NewStringUTF failed.");
2618       cjni_thread_detach ();
2619       return (-1);
2620     }
2621   }
2622
2623   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
2624       cbi->object, cbi->method, o_timeout, o_identifier);
2625
2626   (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier);
2627   (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
2628
2629   cjni_thread_detach ();
2630   return (ret_status);
2631 } /* }}} int cjni_flush */
2632
2633 /* Call the CB_TYPE_LOG callback pointed to by the `user_data_t' pointer. */
2634 static void cjni_log (int severity, const char *message, /* {{{ */
2635     user_data_t *ud)
2636 {
2637   JNIEnv *jvm_env;
2638   cjni_callback_info_t *cbi;
2639   jobject o_message;
2640
2641   if (jvm == NULL)
2642     return;
2643
2644   if ((ud == NULL) || (ud->data == NULL))
2645     return;
2646
2647   jvm_env = cjni_thread_attach ();
2648   if (jvm_env == NULL)
2649     return;
2650
2651   cbi = (cjni_callback_info_t *) ud->data;
2652
2653   o_message = (*jvm_env)->NewStringUTF (jvm_env, message);
2654   if (o_message == NULL)
2655   {
2656     cjni_thread_detach ();
2657     return;
2658   }
2659
2660   (*jvm_env)->CallVoidMethod (jvm_env,
2661       cbi->object, cbi->method, (jint) severity, o_message);
2662
2663   (*jvm_env)->DeleteLocalRef (jvm_env, o_message);
2664
2665   cjni_thread_detach ();
2666 } /* }}} void cjni_log */
2667
2668 /* Call the CB_TYPE_NOTIFICATION callback pointed to by the `user_data_t'
2669  * pointer. */
2670 static int cjni_notification (const notification_t *n, /* {{{ */
2671     user_data_t *ud)
2672 {
2673   JNIEnv *jvm_env;
2674   cjni_callback_info_t *cbi;
2675   jobject o_notification;
2676   int ret_status;
2677
2678   if (jvm == NULL)
2679   {
2680     ERROR ("java plugin: cjni_read: jvm == NULL");
2681     return (-1);
2682   }
2683
2684   if ((ud == NULL) || (ud->data == NULL))
2685   {
2686     ERROR ("java plugin: cjni_read: Invalid user data.");
2687     return (-1);
2688   }
2689
2690   jvm_env = cjni_thread_attach ();
2691   if (jvm_env == NULL)
2692     return (-1);
2693
2694   cbi = (cjni_callback_info_t *) ud->data;
2695
2696   o_notification = ctoj_notification (jvm_env, n);
2697   if (o_notification == NULL)
2698   {
2699     ERROR ("java plugin: cjni_notification: ctoj_notification failed.");
2700     cjni_thread_detach ();
2701     return (-1);
2702   }
2703
2704   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
2705       cbi->object, cbi->method, o_notification);
2706
2707   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
2708
2709   cjni_thread_detach ();
2710   return (ret_status);
2711 } /* }}} int cjni_notification */
2712
2713 /* Callbacks for matches implemented in Java */
2714 static int cjni_match_target_create (const oconfig_item_t *ci, /* {{{ */
2715     void **user_data)
2716 {
2717   JNIEnv *jvm_env;
2718   cjni_callback_info_t *cbi_ret;
2719   cjni_callback_info_t *cbi_factory;
2720   const char *name;
2721   jobject o_ci;
2722   jobject o_tmp;
2723   int type;
2724   size_t i;
2725
2726   cbi_ret = NULL;
2727   o_ci = NULL;
2728   jvm_env = NULL;
2729
2730 #define BAIL_OUT(status) \
2731   if (cbi_ret != NULL) { \
2732     free (cbi_ret->name); \
2733     if ((jvm_env != NULL) && (cbi_ret->object != NULL)) \
2734       (*jvm_env)->DeleteLocalRef (jvm_env, cbi_ret->object); \
2735   } \
2736   free (cbi_ret); \
2737   if (o_ci != NULL) \
2738     (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
2739   cjni_thread_detach (); \
2740   return (status)
2741
2742   if (jvm == NULL)
2743   {
2744     ERROR ("java plugin: cjni_read: jvm == NULL");
2745     return (-1);
2746   }
2747
2748   jvm_env = cjni_thread_attach ();
2749   if (jvm_env == NULL)
2750     return (-1);
2751
2752   /* Find out whether to create a match or a target. */
2753   if (strcasecmp ("Match", ci->key) == 0)
2754     type = CB_TYPE_MATCH;
2755   else if (strcasecmp ("Target", ci->key) == 0)
2756     type = CB_TYPE_TARGET;
2757   else
2758   {
2759     ERROR ("java plugin: cjni_match_target_create: Can't figure out whether "
2760         "to create a match or a target.");
2761     BAIL_OUT (-1);
2762   }
2763
2764   /* This is the name of the match we should create. */
2765   name = ci->values[0].value.string;
2766
2767   /* Lets see if we have a matching factory here.. */
2768   cbi_factory = NULL;
2769   for (i = 0; i < java_callbacks_num; i++)
2770   {
2771     if (java_callbacks[i].type != type)
2772       continue;
2773
2774     if (strcmp (name, java_callbacks[i].name) != 0)
2775       continue;
2776
2777     cbi_factory = java_callbacks + i;
2778     break;
2779   }
2780
2781   /* Nope, no factory for that name.. */
2782   if (cbi_factory == NULL)
2783   {
2784     ERROR ("java plugin: cjni_match_target_create: "
2785         "No such match factory registered: %s",
2786         name);
2787     BAIL_OUT (-1);
2788   }
2789
2790   /* We convert `ci' to its Java equivalent.. */
2791   o_ci = ctoj_oconfig_item (jvm_env, ci);
2792   if (o_ci == NULL)
2793   {
2794     ERROR ("java plugin: cjni_match_target_create: "
2795         "ctoj_oconfig_item failed.");
2796     BAIL_OUT (-1);
2797   }
2798
2799   /* Allocate a new callback info structure. This is going to be our user_data
2800    * pointer. */
2801   cbi_ret = calloc (1, sizeof (*cbi_ret));
2802   if (cbi_ret == NULL)
2803   {
2804     ERROR ("java plugin: cjni_match_target_create: calloc failed.");
2805     BAIL_OUT (-1);
2806   }
2807
2808   cbi_ret->object = NULL;
2809   cbi_ret->type = type;
2810
2811   /* Lets fill the callback info structure.. First, the name: */
2812   cbi_ret->name = strdup (name);
2813   if (cbi_ret->name == NULL)
2814   {
2815     ERROR ("java plugin: cjni_match_target_create: strdup failed.");
2816     BAIL_OUT (-1);
2817   }
2818
2819   /* Then call the factory method so it creates a new object for us. */
2820   o_tmp = (*jvm_env)->CallObjectMethod (jvm_env,
2821       cbi_factory->object, cbi_factory->method, o_ci);
2822   if (o_tmp == NULL)
2823   {
2824     ERROR ("java plugin: cjni_match_target_create: CallObjectMethod failed.");
2825     BAIL_OUT (-1);
2826   }
2827
2828   cbi_ret->object = (*jvm_env)->NewGlobalRef (jvm_env, o_tmp);
2829   if (o_tmp == NULL)
2830   {
2831     ERROR ("java plugin: cjni_match_target_create: NewGlobalRef failed.");
2832     BAIL_OUT (-1);
2833   }
2834
2835   /* This is the class of the match. It is possibly different from the class of
2836    * the match-factory! */
2837   cbi_ret->class = (*jvm_env)->GetObjectClass (jvm_env, cbi_ret->object);
2838   if (cbi_ret->class == NULL)
2839   {
2840     ERROR ("java plugin: cjni_match_target_create: GetObjectClass failed.");
2841     BAIL_OUT (-1);
2842   }
2843
2844   /* Lookup the `int match (DataSet, ValueList)' method. */
2845   cbi_ret->method = (*jvm_env)->GetMethodID (jvm_env, cbi_ret->class,
2846       /* method name = */ (type == CB_TYPE_MATCH) ? "match" : "invoke",
2847       "(Lorg/collectd/api/DataSet;Lorg/collectd/api/ValueList;)I");
2848   if (cbi_ret->method == NULL)
2849   {
2850     ERROR ("java plugin: cjni_match_target_create: GetMethodID failed.");
2851     BAIL_OUT (-1);
2852   }
2853
2854   /* Return the newly created match via the user_data pointer. */
2855   *user_data = (void *) cbi_ret;
2856
2857   cjni_thread_detach ();
2858
2859   DEBUG ("java plugin: cjni_match_target_create: "
2860       "Successfully created a `%s' %s.",
2861       cbi_ret->name, (type == CB_TYPE_MATCH) ? "match" : "target");
2862
2863   /* Success! */
2864   return (0);
2865 #undef BAIL_OUT
2866 } /* }}} int cjni_match_target_create */
2867
2868 static int cjni_match_target_destroy (void **user_data) /* {{{ */
2869 {
2870   cjni_callback_info_destroy (*user_data);
2871   *user_data = NULL;
2872
2873   return (0);
2874 } /* }}} int cjni_match_target_destroy */
2875
2876 static int cjni_match_target_invoke (const data_set_t *ds, /* {{{ */
2877     value_list_t *vl, notification_meta_t **meta, void **user_data)
2878 {
2879   JNIEnv *jvm_env;
2880   cjni_callback_info_t *cbi;
2881   jobject o_vl;
2882   jobject o_ds;
2883   int ret_status;
2884   int status;
2885
2886   if (jvm == NULL)
2887   {
2888     ERROR ("java plugin: cjni_match_target_invoke: jvm == NULL");
2889     return (-1);
2890   }
2891
2892   jvm_env = cjni_thread_attach ();
2893   if (jvm_env == NULL)
2894     return (-1);
2895
2896   cbi = (cjni_callback_info_t *) *user_data;
2897
2898   o_vl = ctoj_value_list (jvm_env, ds, vl);
2899   if (o_vl == NULL)
2900   {
2901     ERROR ("java plugin: cjni_match_target_invoke: ctoj_value_list failed.");
2902     cjni_thread_detach ();
2903     return (-1);
2904   }
2905
2906   o_ds = ctoj_data_set (jvm_env, ds);
2907   if (o_ds == NULL)
2908   {
2909     ERROR ("java plugin: cjni_match_target_invoke: ctoj_value_list failed.");
2910     cjni_thread_detach ();
2911     return (-1);
2912   }
2913
2914   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object, cbi->method,
2915       o_ds, o_vl);
2916
2917   DEBUG ("java plugin: cjni_match_target_invoke: Method returned %i.", ret_status);
2918
2919   /* If we're executing a target, copy the `ValueList' back to our
2920    * `value_list_t'. */
2921   if (cbi->type == CB_TYPE_TARGET)
2922   {
2923     value_list_t new_vl;
2924
2925     memset (&new_vl, 0, sizeof (new_vl));
2926     status = jtoc_value_list (jvm_env, &new_vl, o_vl);
2927     if (status != 0)
2928     {
2929       ERROR ("java plugin: cjni_match_target_invoke: "
2930           "jtoc_value_list failed.");
2931     }
2932     else /* if (status == 0) */
2933     {
2934       /* plugin_dispatch_values assures that this is dynamically allocated
2935        * memory. */
2936       sfree (vl->values);
2937
2938       /* This will replace the vl->values pointer to a new, dynamically
2939        * allocated piece of memory. */
2940       memcpy (vl, &new_vl, sizeof (*vl));
2941     }
2942   } /* if (cbi->type == CB_TYPE_TARGET) */
2943
2944   cjni_thread_detach ();
2945   return (ret_status);
2946 } /* }}} int cjni_match_target_invoke */
2947
2948 /* Iterate over `java_callbacks' and call all CB_TYPE_INIT callbacks. */
2949 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
2950 {
2951   int status;
2952   size_t i;
2953
2954   for (i = 0; i < java_callbacks_num; i++)
2955   {
2956     if (java_callbacks[i].type != CB_TYPE_INIT)
2957       continue;
2958
2959     DEBUG ("java plugin: Initializing %s", java_callbacks[i].name);
2960
2961     status = (*jvm_env)->CallIntMethod (jvm_env,
2962         java_callbacks[i].object, java_callbacks[i].method);
2963     if (status != 0)
2964     {
2965       ERROR ("java plugin: Initializing `%s' failed with status %i. "
2966           "Removing read function.",
2967           java_callbacks[i].name, status);
2968       plugin_unregister_read (java_callbacks[i].name);
2969     }
2970   }
2971
2972   return (0);
2973 } /* }}} int cjni_init_plugins */
2974
2975 /* Iterate over `java_callbacks' and call all CB_TYPE_SHUTDOWN callbacks. */
2976 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
2977 {
2978   int status;
2979   size_t i;
2980
2981   for (i = 0; i < java_callbacks_num; i++)
2982   {
2983     if (java_callbacks[i].type != CB_TYPE_SHUTDOWN)
2984       continue;
2985
2986     DEBUG ("java plugin: Shutting down %s", java_callbacks[i].name);
2987
2988     status = (*jvm_env)->CallIntMethod (jvm_env,
2989         java_callbacks[i].object, java_callbacks[i].method);
2990     if (status != 0)
2991     {
2992       ERROR ("java plugin: Shutting down `%s' failed with status %i. ",
2993           java_callbacks[i].name, status);
2994     }
2995   }
2996
2997   return (0);
2998 } /* }}} int cjni_shutdown_plugins */
2999
3000
3001 static int cjni_shutdown (void) /* {{{ */
3002 {
3003   JNIEnv *jvm_env;
3004   JavaVMAttachArgs args;
3005   int status;
3006   size_t i;
3007
3008   if (jvm == NULL)
3009     return (0);
3010
3011   jvm_env = NULL;
3012   memset (&args, 0, sizeof (args));
3013   args.version = JNI_VERSION_1_2;
3014
3015   status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, &args);
3016   if (status != 0)
3017   {
3018     ERROR ("java plugin: cjni_shutdown: AttachCurrentThread failed with status %i.",
3019         status);
3020     return (-1);
3021   }
3022
3023   /* Execute all the shutdown functions registered by plugins. */
3024   cjni_shutdown_plugins (jvm_env);
3025
3026   /* Release all the global references to callback functions */
3027   for (i = 0; i < java_callbacks_num; i++)
3028   {
3029     if (java_callbacks[i].object != NULL)
3030     {
3031       (*jvm_env)->DeleteGlobalRef (jvm_env, java_callbacks[i].object);
3032       java_callbacks[i].object = NULL;
3033     }
3034     sfree (java_callbacks[i].name);
3035   }
3036   java_callbacks_num = 0;
3037   sfree (java_callbacks);
3038
3039   /* Release all the global references to directly loaded classes. */
3040   for (i = 0; i < java_classes_list_len; i++)
3041   {
3042     if (java_classes_list[i].object != NULL)
3043     {
3044       (*jvm_env)->DeleteGlobalRef (jvm_env, java_classes_list[i].object);
3045       java_classes_list[i].object = NULL;
3046     }
3047     sfree (java_classes_list[i].name);
3048   }
3049   java_classes_list_len = 0;
3050   sfree (java_classes_list);
3051
3052   /* Destroy the JVM */
3053   DEBUG ("java plugin: Destroying the JVM.");
3054   (*jvm)->DestroyJavaVM (jvm);
3055   jvm = NULL;
3056   jvm_env = NULL;
3057
3058   pthread_key_delete (jvm_env_key);
3059
3060   /* Free the JVM argument list */
3061   for (i = 0; i < jvm_argc; i++)
3062     sfree (jvm_argv[i]);
3063   jvm_argc = 0;
3064   sfree (jvm_argv);
3065
3066   return (0);
3067 } /* }}} int cjni_shutdown */
3068
3069 /* Initialization: Create a JVM, load all configured classes and call their
3070  * `config' and `init' callback methods. */
3071 static int cjni_init (void) /* {{{ */
3072 {
3073   JNIEnv *jvm_env;
3074
3075   if ((config_block == NULL) && (jvm == NULL))
3076   {
3077     ERROR ("java plugin: cjni_init: No configuration block for "
3078         "the java plugin was found.");
3079     return (-1);
3080   }
3081
3082   if (config_block != NULL)
3083   {
3084     cjni_config_perform (config_block);
3085     oconfig_free (config_block);
3086   }
3087
3088   if (jvm == NULL)
3089   {
3090     ERROR ("java plugin: cjni_init: jvm == NULL");
3091     return (-1);
3092   }
3093
3094   jvm_env = cjni_thread_attach ();
3095   if (jvm_env == NULL)
3096     return (-1);
3097
3098   cjni_init_plugins (jvm_env);
3099
3100   cjni_thread_detach ();
3101   return (0);
3102 } /* }}} int cjni_init */
3103
3104 void module_register (void)
3105 {
3106   plugin_register_complex_config ("java", cjni_config_callback);
3107   plugin_register_init ("java", cjni_init);
3108   plugin_register_shutdown ("java", cjni_shutdown);
3109 } /* void module_register (void) */
3110
3111 /* vim: set sw=2 sts=2 et fdm=marker : */