java plugin: Add support for `notification' callbacks.
[collectd.git] / src / java.c
1 /**
2  * collectd - src/java.c
3  * Copyright (C) 2009  Florian octo Forster
4  * Copyright (C) 2008  Justo Alonso Achaques
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  *   Justo Alonso Achaques <justo.alonso at gmail.com>
22  **/
23
24 #include "collectd.h"
25 #include "plugin.h"
26 #include "common.h"
27
28 #include <pthread.h>
29 #include <jni.h>
30
31 #if !defined(JNI_VERSION_1_2)
32 # error "Need JNI 1.2 compatible interface!"
33 #endif
34
35 /*
36  * Types
37  */
38 struct 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 struct java_plugin_config_s /* {{{ */
56 {
57   char *name;
58   oconfig_item_t *ci;
59 };
60 typedef struct java_plugin_config_s java_plugin_config_t;
61 /* }}} */
62
63 #define CB_TYPE_CONFIG       1
64 #define CB_TYPE_INIT         2
65 #define CB_TYPE_READ         3
66 #define CB_TYPE_WRITE        4
67 #define CB_TYPE_FLUSH        5
68 #define CB_TYPE_SHUTDOWN     6
69 #define CB_TYPE_LOG          7
70 #define CB_TYPE_NOTIFICATION 8
71 struct cjni_callback_info_s /* {{{ */
72 {
73   char     *name;
74   int       type;
75   jclass    class;
76   jobject   object;
77   jmethodID method;
78 };
79 typedef struct cjni_callback_info_s cjni_callback_info_t;
80 /* }}} */
81
82 /*
83  * Global variables
84  */
85 static JavaVM *jvm = NULL;
86 static pthread_key_t jvm_env_key;
87
88 /* Configuration options for the JVM. */
89 static char **jvm_argv = NULL;
90 static size_t jvm_argc = 0;
91
92 /* List of class names to load */
93 static java_plugin_class_t  *java_classes_list = NULL;
94 static size_t                java_classes_list_len;
95
96 /* List of `config_item_t's for Java plugins */
97 static java_plugin_config_t *java_plugin_configs     = NULL;
98 static size_t                java_plugin_configs_num = 0;
99
100 /* List of config, init, and shutdown callbacks. */
101 static cjni_callback_info_t *java_callbacks      = NULL;
102 static size_t                java_callbacks_num  = 0;
103 static pthread_mutex_t       java_callbacks_lock = PTHREAD_MUTEX_INITIALIZER;
104
105 /*
106  * Prototypes
107  *
108  * Mostly functions that are needed by the Java interface (``native'')
109  * functions.
110  */
111 static void cjni_callback_info_destroy (void *arg);
112 static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env,
113     jobject o_name, jobject o_callback, int type);
114 static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name,
115     jobject o_callback, int type);
116 static int cjni_read (user_data_t *user_data);
117 static int cjni_write (const data_set_t *ds, const value_list_t *vl,
118     user_data_t *ud);
119 static int cjni_flush (int timeout, const char *identifier, user_data_t *ud);
120 static void cjni_log (int severity, const char *message, user_data_t *ud);
121 static int cjni_notification (const notification_t *n, user_data_t *ud);
122
123 /* 
124  * C to Java conversion functions
125  */
126 static int ctoj_string (JNIEnv *jvm_env, /* {{{ */
127     const char *string,
128     jclass class_ptr, jobject object_ptr, const char *method_name)
129 {
130   jmethodID m_set;
131   jstring o_string;
132
133   /* Create a java.lang.String */
134   o_string = (*jvm_env)->NewStringUTF (jvm_env,
135       (string != NULL) ? string : "");
136   if (o_string == NULL)
137   {
138     ERROR ("java plugin: ctoj_string: NewStringUTF failed.");
139     return (-1);
140   }
141
142   /* Search for the `void setFoo (String s)' method. */
143   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
144       method_name, "(Ljava/lang/String;)V");
145   if (m_set == NULL)
146   {
147     ERROR ("java plugin: ctoj_string: Cannot find method `void %s (String)'.",
148         method_name);
149     (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
150     return (-1);
151   }
152
153   /* Call the method. */
154   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, o_string);
155
156   /* Decrease reference counter on the java.lang.String object. */
157   (*jvm_env)->DeleteLocalRef (jvm_env, o_string);
158
159   return (0);
160 } /* }}} int ctoj_string */
161
162 static int ctoj_int (JNIEnv *jvm_env, /* {{{ */
163     jint value,
164     jclass class_ptr, jobject object_ptr, const char *method_name)
165 {
166   jmethodID m_set;
167
168   /* Search for the `void setFoo (int i)' method. */
169   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
170       method_name, "(I)V");
171   if (m_set == NULL)
172   {
173     ERROR ("java plugin: ctoj_int: Cannot find method `void %s (int)'.",
174         method_name);
175     return (-1);
176   }
177
178   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
179
180   return (0);
181 } /* }}} int ctoj_int */
182
183 static int ctoj_long (JNIEnv *jvm_env, /* {{{ */
184     jlong value,
185     jclass class_ptr, jobject object_ptr, const char *method_name)
186 {
187   jmethodID m_set;
188
189   /* Search for the `void setFoo (long l)' method. */
190   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
191       method_name, "(J)V");
192   if (m_set == NULL)
193   {
194     ERROR ("java plugin: ctoj_long: Cannot find method `void %s (long)'.",
195         method_name);
196     return (-1);
197   }
198
199   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
200
201   return (0);
202 } /* }}} int ctoj_long */
203
204 static int ctoj_double (JNIEnv *jvm_env, /* {{{ */
205     jdouble value,
206     jclass class_ptr, jobject object_ptr, const char *method_name)
207 {
208   jmethodID m_set;
209
210   /* Search for the `void setFoo (double d)' method. */
211   m_set = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
212       method_name, "(D)V");
213   if (m_set == NULL)
214   {
215     ERROR ("java plugin: ctoj_double: Cannot find method `void %s (double)'.",
216         method_name);
217     return (-1);
218   }
219
220   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value);
221
222   return (0);
223 } /* }}} int ctoj_double */
224
225 /* Convert a jlong to a java.lang.Number */
226 static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */
227 {
228   jclass c_long;
229   jmethodID m_long_constructor;
230
231   /* Look up the java.lang.Long class */
232   c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long");
233   if (c_long == NULL)
234   {
235     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
236         "java.lang.Long class failed.");
237     return (NULL);
238   }
239
240   m_long_constructor = (*jvm_env)->GetMethodID (jvm_env,
241       c_long, "<init>", "(J)V");
242   if (m_long_constructor == NULL)
243   {
244     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
245         "`Long (long)' constructor failed.");
246     return (NULL);
247   }
248
249   return ((*jvm_env)->NewObject (jvm_env,
250         c_long, m_long_constructor, value));
251 } /* }}} jobject ctoj_jlong_to_number */
252
253 /* Convert a jdouble to a java.lang.Number */
254 static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */
255 {
256   jclass c_double;
257   jmethodID m_double_constructor;
258
259   /* Look up the java.lang.Long class */
260   c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double");
261   if (c_double == NULL)
262   {
263     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
264         "java.lang.Double class failed.");
265     return (NULL);
266   }
267
268   m_double_constructor = (*jvm_env)->GetMethodID (jvm_env,
269       c_double, "<init>", "(D)V");
270   if (m_double_constructor == NULL)
271   {
272     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
273         "`Double (double)' constructor failed.");
274     return (NULL);
275   }
276
277   return ((*jvm_env)->NewObject (jvm_env,
278         c_double, m_double_constructor, value));
279 } /* }}} jobject ctoj_jdouble_to_number */
280
281 /* Convert a value_t to a java.lang.Number */
282 static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
283     value_t value, int ds_type)
284 {
285   if (ds_type == DS_TYPE_COUNTER)
286     return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter));
287   else if (ds_type == DS_TYPE_GAUGE)
288     return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge));
289   else
290     return (NULL);
291 } /* }}} jobject ctoj_value_to_number */
292
293 /* Convert a data_source_t to a org.collectd.api.DataSource */
294 static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
295     const data_source_t *dsrc)
296 {
297   jclass c_datasource;
298   jmethodID m_datasource_constructor;
299   jobject o_datasource;
300   int status;
301
302   /* Look up the DataSource class */
303   c_datasource = (*jvm_env)->FindClass (jvm_env,
304       "org.collectd.api.DataSource");
305   if (c_datasource == NULL)
306   {
307     ERROR ("java plugin: ctoj_data_source: "
308         "FindClass (org.collectd.api.DataSource) failed.");
309     return (NULL);
310   }
311
312   /* Lookup the `ValueList ()' constructor. */
313   m_datasource_constructor = (*jvm_env)->GetMethodID (jvm_env, c_datasource,
314       "<init>", "()V");
315   if (m_datasource_constructor == NULL)
316   {
317     ERROR ("java plugin: ctoj_data_source: Cannot find the "
318         "`DataSource ()' constructor.");
319     return (NULL);
320   }
321
322   /* Create a new instance. */
323   o_datasource = (*jvm_env)->NewObject (jvm_env, c_datasource,
324       m_datasource_constructor);
325   if (o_datasource == NULL)
326   {
327     ERROR ("java plugin: ctoj_data_source: "
328         "Creating a new DataSource instance failed.");
329     return (NULL);
330   }
331
332   /* Set name via `void setName (String name)' */
333   status = ctoj_string (jvm_env, dsrc->name,
334       c_datasource, o_datasource, "setName");
335   if (status != 0)
336   {
337     ERROR ("java plugin: ctoj_data_source: "
338         "ctoj_string (setName) failed.");
339     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
340     return (NULL);
341   }
342
343   /* Set type via `void setType (int type)' */
344   status = ctoj_int (jvm_env, dsrc->type,
345       c_datasource, o_datasource, "setType");
346   if (status != 0)
347   {
348     ERROR ("java plugin: ctoj_data_source: "
349         "ctoj_int (setType) failed.");
350     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
351     return (NULL);
352   }
353
354   /* Set min via `void setMin (double min)' */
355   status = ctoj_double (jvm_env, dsrc->min,
356       c_datasource, o_datasource, "setMin");
357   if (status != 0)
358   {
359     ERROR ("java plugin: ctoj_data_source: "
360         "ctoj_double (setMin) failed.");
361     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
362     return (NULL);
363   }
364
365   /* Set max via `void setMax (double max)' */
366   status = ctoj_double (jvm_env, dsrc->max,
367       c_datasource, o_datasource, "setMax");
368   if (status != 0)
369   {
370     ERROR ("java plugin: ctoj_data_source: "
371         "ctoj_double (setMax) failed.");
372     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
373     return (NULL);
374   }
375
376   return (o_datasource);
377 } /* }}} jobject ctoj_data_source */
378
379 /* Convert a oconfig_value_t to a org.collectd.api.OConfigValue */
380 static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
381     oconfig_value_t ocvalue)
382 {
383   jclass c_ocvalue;
384   jmethodID m_ocvalue_constructor;
385   jobject o_argument;
386   jobject o_ocvalue;
387
388   m_ocvalue_constructor = NULL;
389   o_argument = NULL;
390
391   c_ocvalue = (*jvm_env)->FindClass (jvm_env,
392       "org.collectd.api.OConfigValue");
393   if (c_ocvalue == NULL)
394   {
395     ERROR ("java plugin: ctoj_oconfig_value: "
396         "FindClass (org.collectd.api.OConfigValue) failed.");
397     return (NULL);
398   }
399
400   if (ocvalue.type == OCONFIG_TYPE_BOOLEAN)
401   {
402     jboolean tmp_boolean;
403
404     tmp_boolean = (ocvalue.value.boolean == 0) ? JNI_FALSE : JNI_TRUE;
405
406     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
407         "<init>", "(Z)V");
408     if (m_ocvalue_constructor == NULL)
409     {
410       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
411           "`OConfigValue (boolean)' constructor.");
412       return (NULL);
413     }
414
415     return ((*jvm_env)->NewObject (jvm_env,
416           c_ocvalue, m_ocvalue_constructor, tmp_boolean));
417   } /* if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) */
418   else if (ocvalue.type == OCONFIG_TYPE_STRING)
419   {
420     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
421         "<init>", "(Ljava/lang/String;)V");
422     if (m_ocvalue_constructor == NULL)
423     {
424       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
425           "`OConfigValue (String)' constructor.");
426       return (NULL);
427     }
428
429     o_argument = (*jvm_env)->NewStringUTF (jvm_env, ocvalue.value.string);
430     if (o_argument == NULL)
431     {
432       ERROR ("java plugin: ctoj_oconfig_value: "
433           "Creating a String object failed.");
434       return (NULL);
435     }
436   }
437   else if (ocvalue.type == OCONFIG_TYPE_NUMBER)
438   {
439     m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue,
440         "<init>", "(Ljava/lang/Number;)V");
441     if (m_ocvalue_constructor == NULL)
442     {
443       ERROR ("java plugin: ctoj_oconfig_value: Cannot find the "
444           "`OConfigValue (Number)' constructor.");
445       return (NULL);
446     }
447
448     o_argument = ctoj_jdouble_to_number (jvm_env,
449         (jdouble) ocvalue.value.number);
450     if (o_argument == NULL)
451     {
452       ERROR ("java plugin: ctoj_oconfig_value: "
453           "Creating a Number object failed.");
454       return (NULL);
455     }
456   }
457   else
458   {
459     return (NULL);
460   }
461
462   assert (m_ocvalue_constructor != NULL);
463   assert (o_argument != NULL);
464
465   o_ocvalue = (*jvm_env)->NewObject (jvm_env,
466       c_ocvalue, m_ocvalue_constructor, o_argument);
467   if (o_ocvalue == NULL)
468   {
469     ERROR ("java plugin: ctoj_oconfig_value: "
470         "Creating an OConfigValue object failed.");
471     (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
472     return (NULL);
473   }
474
475   (*jvm_env)->DeleteLocalRef (jvm_env, o_argument);
476   return (o_ocvalue);
477 } /* }}} jobject ctoj_oconfig_value */
478
479 /* Convert a oconfig_item_t to a org.collectd.api.OConfigItem */
480 static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
481     const oconfig_item_t *ci)
482 {
483   jclass c_ocitem;
484   jmethodID m_ocitem_constructor;
485   jmethodID m_addvalue;
486   jmethodID m_addchild;
487   jobject o_key;
488   jobject o_ocitem;
489   int i;
490
491   c_ocitem = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.OConfigItem");
492   if (c_ocitem == NULL)
493   {
494     ERROR ("java plugin: ctoj_oconfig_item: "
495         "FindClass (org.collectd.api.OConfigItem) failed.");
496     return (NULL);
497   }
498
499   /* Get the required methods: m_ocitem_constructor, m_addvalue, and m_addchild
500    * {{{ */
501   m_ocitem_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
502       "<init>", "(Ljava/lang/String;)V");
503   if (m_ocitem_constructor == NULL)
504   {
505     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
506         "`OConfigItem (String)' constructor.");
507     return (NULL);
508   }
509
510   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
511       "addValue", "(Lorg/collectd/api/OConfigValue;)V");
512   if (m_addvalue == NULL)
513   {
514     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
515         "`addValue (OConfigValue)' method.");
516     return (NULL);
517   }
518
519   m_addchild = (*jvm_env)->GetMethodID (jvm_env, c_ocitem,
520       "addChild", "(Lorg/collectd/api/OConfigItem;)V");
521   if (m_addchild == NULL)
522   {
523     ERROR ("java plugin: ctoj_oconfig_item: Cannot find the "
524         "`addChild (OConfigItem)' method.");
525     return (NULL);
526   }
527   /* }}} */
528
529   /* Create a String object with the key.
530    * Needed for calling the constructor. */
531   o_key = (*jvm_env)->NewStringUTF (jvm_env, ci->key);
532   if (o_key == NULL)
533   {
534     ERROR ("java plugin: ctoj_oconfig_item: "
535         "Creating String object failed.");
536     return (NULL);
537   }
538
539   /* Create an OConfigItem object */
540   o_ocitem = (*jvm_env)->NewObject (jvm_env,
541       c_ocitem, m_ocitem_constructor, o_key);
542   if (o_ocitem == NULL)
543   {
544     ERROR ("java plugin: ctoj_oconfig_item: "
545         "Creating an OConfigItem object failed.");
546     (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
547     return (NULL);
548   }
549
550   /* We don't need the String object any longer.. */
551   (*jvm_env)->DeleteLocalRef (jvm_env, o_key);
552
553   /* Call OConfigItem.addValue for each value */
554   for (i = 0; i < ci->values_num; i++) /* {{{ */
555   {
556     jobject o_value;
557
558     o_value = ctoj_oconfig_value (jvm_env, ci->values[i]);
559     if (o_value == NULL)
560     {
561       ERROR ("java plugin: ctoj_oconfig_item: "
562           "Creating an OConfigValue object failed.");
563       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
564       return (NULL);
565     }
566
567     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_value);
568     (*jvm_env)->DeleteLocalRef (jvm_env, o_value);
569   } /* }}} for (i = 0; i < ci->values_num; i++) */
570
571   /* Call OConfigItem.addChild for each child */
572   for (i = 0; i < ci->children_num; i++) /* {{{ */
573   {
574     jobject o_child;
575
576     o_child = ctoj_oconfig_item (jvm_env, ci->children + i);
577     if (o_child == NULL)
578     {
579       ERROR ("java plugin: ctoj_oconfig_item: "
580           "Creating an OConfigItem object failed.");
581       (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
582       return (NULL);
583     }
584
585     (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_child);
586     (*jvm_env)->DeleteLocalRef (jvm_env, o_child);
587   } /* }}} for (i = 0; i < ci->children_num; i++) */
588
589   return (o_ocitem);
590 } /* }}} jobject ctoj_oconfig_item */
591
592 /* Convert a data_set_t to a org.collectd.api.DataSet */
593 static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
594 {
595   jclass c_dataset;
596   jmethodID m_constructor;
597   jmethodID m_add;
598   jobject o_type;
599   jobject o_dataset;
600   int i;
601
602   /* Look up the org.collectd.api.DataSet class */
603   c_dataset = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.DataSet");
604   if (c_dataset == NULL)
605   {
606     ERROR ("java plugin: ctoj_data_set: Looking up the "
607         "org.collectd.api.DataSet class failed.");
608     return (NULL);
609   }
610
611   /* Search for the `DataSet (String type)' constructor. */
612   m_constructor = (*jvm_env)->GetMethodID (jvm_env,
613       c_dataset, "<init>", "(Ljava.lang.String;)V");
614   if (m_constructor == NULL)
615   {
616     ERROR ("java plugin: ctoj_data_set: Looking up the "
617         "`DataSet (String)' constructor failed.");
618     return (NULL);
619   }
620
621   /* Search for the `void addDataSource (DataSource)' method. */
622   m_add = (*jvm_env)->GetMethodID (jvm_env,
623       c_dataset, "addDataSource", "(Lorg.collectd.api.DataSource;)V");
624   if (m_add == NULL)
625   {
626     ERROR ("java plugin: ctoj_data_set: Looking up the "
627         "`addDataSource (DataSource)' method failed.");
628     return (NULL);
629   }
630
631   o_type = (*jvm_env)->NewStringUTF (jvm_env, ds->type);
632   if (o_type == NULL)
633   {
634     ERROR ("java plugin: ctoj_data_set: Creating a String object failed.");
635     return (NULL);
636   }
637
638   o_dataset = (*jvm_env)->NewObject (jvm_env,
639       c_dataset, m_constructor, o_type);
640   if (o_dataset == NULL)
641   {
642     ERROR ("java plugin: ctoj_data_set: Creating a DataSet object failed.");
643     (*jvm_env)->DeleteLocalRef (jvm_env, o_type);
644     return (NULL);
645   }
646
647   /* Decrease reference counter on the java.lang.String object. */
648   (*jvm_env)->DeleteLocalRef (jvm_env, o_type);
649
650   for (i = 0; i < ds->ds_num; i++)
651   {
652     jobject o_datasource;
653
654     o_datasource = ctoj_data_source (jvm_env, ds->ds + i);
655     if (o_datasource == NULL)
656     {
657       ERROR ("java plugin: ctoj_data_set: ctoj_data_source (%s.%s) failed",
658           ds->type, ds->ds[i].name);
659       (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
660       return (NULL);
661     }
662
663     (*jvm_env)->CallVoidMethod (jvm_env, o_dataset, m_add, o_datasource);
664
665     (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource);
666   } /* for (i = 0; i < ds->ds_num; i++) */
667
668   return (o_dataset);
669 } /* }}} jobject ctoj_data_set */
670
671 static int ctoj_value_list_add_value (JNIEnv *jvm_env, /* {{{ */
672     value_t value, int ds_type,
673     jclass class_ptr, jobject object_ptr)
674 {
675   jmethodID m_addvalue;
676   jobject o_number;
677
678   m_addvalue = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
679       "addValue", "(Ljava/lang/Number;)V");
680   if (m_addvalue == NULL)
681   {
682     ERROR ("java plugin: ctoj_value_list_add_value: "
683         "Cannot find method `void addValue (Number)'.");
684     return (-1);
685   }
686
687   o_number = ctoj_value_to_number (jvm_env, value, ds_type);
688   if (o_number == NULL)
689   {
690     ERROR ("java plugin: ctoj_value_list_add_value: "
691         "ctoj_value_to_number failed.");
692     return (-1);
693   }
694
695   (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_addvalue, o_number);
696
697   (*jvm_env)->DeleteLocalRef (jvm_env, o_number);
698
699   return (0);
700 } /* }}} int ctoj_value_list_add_value */
701
702 static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */
703     jclass c_valuelist, jobject o_valuelist, const data_set_t *ds)
704 {
705   jmethodID m_setdataset;
706   jobject o_dataset;
707
708   /* Look for the `void setDataSource (List<DataSource> ds)' method. */
709   m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
710       "setDataSet", "(Lorg.collectd.api.DataSet;)V");
711   if (m_setdataset == NULL)
712   {
713     ERROR ("java plugin: ctoj_value_list_add_data_set: "
714         "Cannot find the `void setDataSet (DataSet)' method.");
715     return (-1);
716   }
717
718   /* Create a DataSet object. */
719   o_dataset = ctoj_data_set (jvm_env, ds);
720   if (o_dataset == NULL)
721   {
722     ERROR ("java plugin: ctoj_value_list_add_data_set: "
723         "ctoj_data_set (%s) failed.", ds->type);
724     return (-1);
725   }
726
727   /* Actually call the method. */
728   (*jvm_env)->CallVoidMethod (jvm_env,
729       o_valuelist, m_setdataset, o_dataset);
730
731   /* Decrease reference counter on the List<DataSource> object. */
732   (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset);
733
734   return (0);
735 } /* }}} int ctoj_value_list_add_data_set */
736
737 /* Convert a value_list_t (and data_set_t) to a org.collectd.api.ValueList */
738 static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
739     const data_set_t *ds, const value_list_t *vl)
740 {
741   jclass c_valuelist;
742   jmethodID m_valuelist_constructor;
743   jobject o_valuelist;
744   int status;
745   int i;
746
747   /* First, create a new ValueList instance..
748    * Look up the class.. */
749   c_valuelist = (*jvm_env)->FindClass (jvm_env,
750       "org.collectd.api.ValueList");
751   if (c_valuelist == NULL)
752   {
753     ERROR ("java plugin: ctoj_value_list: "
754         "FindClass (org.collectd.api.ValueList) failed.");
755     return (NULL);
756   }
757
758   /* Lookup the `ValueList ()' constructor. */
759   m_valuelist_constructor = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
760       "<init>", "()V");
761   if (m_valuelist_constructor == NULL)
762   {
763     ERROR ("java plugin: ctoj_value_list: Cannot find the "
764         "`ValueList ()' constructor.");
765     return (NULL);
766   }
767
768   /* Create a new instance. */
769   o_valuelist = (*jvm_env)->NewObject (jvm_env, c_valuelist,
770       m_valuelist_constructor);
771   if (o_valuelist == NULL)
772   {
773     ERROR ("java plugin: ctoj_value_list: Creating a new ValueList instance "
774         "failed.");
775     return (NULL);
776   }
777
778   status = ctoj_value_list_add_data_set (jvm_env,
779       c_valuelist, o_valuelist, ds);
780   if (status != 0)
781   {
782     ERROR ("java plugin: ctoj_value_list: "
783         "ctoj_value_list_add_data_set failed.");
784     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
785     return (NULL);
786   }
787
788   /* Set the strings.. */
789 #define SET_STRING(str,method_name) do { \
790   status = ctoj_string (jvm_env, str, \
791       c_valuelist, o_valuelist, method_name); \
792   if (status != 0) { \
793     ERROR ("java plugin: ctoj_value_list: jtoc_string (%s) failed.", \
794         method_name); \
795     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist); \
796     return (NULL); \
797   } } while (0)
798
799   SET_STRING (vl->host,            "setHost");
800   SET_STRING (vl->plugin,          "setPlugin");
801   SET_STRING (vl->plugin_instance, "setPluginInstance");
802   SET_STRING (vl->type,            "setType");
803   SET_STRING (vl->type_instance,   "setTypeInstance");
804
805 #undef SET_STRING
806
807   /* Set the `time' member. Java stores time in milliseconds. */
808   status = ctoj_long (jvm_env, ((jlong) vl->time) * ((jlong) 1000),
809       c_valuelist, o_valuelist, "setTime");
810   if (status != 0)
811   {
812     ERROR ("java plugin: ctoj_value_list: ctoj_long (setTime) failed.");
813     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
814     return (NULL);
815   }
816
817   /* Set the `interval' member.. */
818   status = ctoj_long (jvm_env, (jlong) vl->interval,
819       c_valuelist, o_valuelist, "setInterval");
820   if (status != 0)
821   {
822     ERROR ("java plugin: ctoj_value_list: ctoj_long (setInterval) failed.");
823     (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
824     return (NULL);
825   }
826
827   for (i = 0; i < vl->values_len; i++)
828   {
829     status = ctoj_value_list_add_value (jvm_env, vl->values[i], ds->ds[i].type,
830         c_valuelist, o_valuelist);
831     if (status != 0)
832     {
833       ERROR ("java plugin: ctoj_value_list: "
834           "ctoj_value_list_add_value failed.");
835       (*jvm_env)->DeleteLocalRef (jvm_env, o_valuelist);
836       return (NULL);
837     }
838   }
839
840   return (o_valuelist);
841 } /* }}} jobject ctoj_value_list */
842
843 /* Convert a notification_t to a org.collectd.api.Notification */
844 static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
845     const notification_t *n)
846 {
847   jclass c_notification;
848   jmethodID m_constructor;
849   jobject o_notification;
850   int status;
851
852   /* First, create a new Notification instance..
853    * Look up the class.. */
854   c_notification = (*jvm_env)->FindClass (jvm_env,
855       "org.collectd.api.Notification");
856   if (c_notification == NULL)
857   {
858     ERROR ("java plugin: ctoj_notification: "
859         "FindClass (org.collectd.api.Notification) failed.");
860     return (NULL);
861   }
862
863   /* Lookup the `Notification ()' constructor. */
864   m_constructor = (*jvm_env)->GetMethodID (jvm_env, c_notification,
865       "<init>", "()V");
866   if (m_constructor == NULL)
867   {
868     ERROR ("java plugin: ctoj_notification: Cannot find the "
869         "`Notification ()' constructor.");
870     return (NULL);
871   }
872
873   /* Create a new instance. */
874   o_notification = (*jvm_env)->NewObject (jvm_env, c_notification,
875       m_constructor);
876   if (o_notification == NULL)
877   {
878     ERROR ("java plugin: ctoj_notification: Creating a new Notification "
879         "instance failed.");
880     return (NULL);
881   }
882
883   /* Set the strings.. */
884 #define SET_STRING(str,method_name) do { \
885   status = ctoj_string (jvm_env, str, \
886       c_notification, o_notification, method_name); \
887   if (status != 0) { \
888     ERROR ("java plugin: ctoj_notification: jtoc_string (%s) failed.", \
889         method_name); \
890     (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); \
891     return (NULL); \
892   } } while (0)
893
894   SET_STRING (n->host,            "setHost");
895   SET_STRING (n->plugin,          "setPlugin");
896   SET_STRING (n->plugin_instance, "setPluginInstance");
897   SET_STRING (n->type,            "setType");
898   SET_STRING (n->type_instance,   "setTypeInstance");
899   SET_STRING (n->message,         "setMessage");
900
901 #undef SET_STRING
902
903   /* Set the `time' member. Java stores time in milliseconds. */
904   status = ctoj_long (jvm_env, ((jlong) n->time) * ((jlong) 1000),
905       c_notification, o_notification, "setTime");
906   if (status != 0)
907   {
908     ERROR ("java plugin: ctoj_notification: ctoj_long (setTime) failed.");
909     (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
910     return (NULL);
911   }
912
913   /* Set the `interval' member.. */
914   status = ctoj_int (jvm_env, (jint) n->severity,
915       c_notification, o_notification, "setSeverity");
916   if (status != 0)
917   {
918     ERROR ("java plugin: ctoj_notification: ctoj_int (setSeverity) failed.");
919     (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
920     return (NULL);
921   }
922
923   return (o_notification);
924 } /* }}} jobject ctoj_notification */
925
926 /*
927  * Java to C conversion functions
928  */
929 /* Call a `String <method> ()' method. */
930 static int jtoc_string (JNIEnv *jvm_env, /* {{{ */
931     char *buffer, size_t buffer_size,
932     jclass class_ptr, jobject object_ptr, const char *method_name)
933 {
934   jmethodID method_id;
935   jobject string_obj;
936   const char *c_str;
937
938   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
939       method_name, "()Ljava/lang/String;");
940   if (method_id == NULL)
941   {
942     ERROR ("java plugin: jtoc_string: Cannot find method `String %s ()'.",
943         method_name);
944     return (-1);
945   }
946
947   string_obj = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, method_id);
948   if (string_obj == NULL)
949   {
950     ERROR ("java plugin: jtoc_string: CallObjectMethod (%s) failed.",
951         method_name);
952     return (-1);
953   }
954
955   c_str = (*jvm_env)->GetStringUTFChars (jvm_env, string_obj, 0);
956   if (c_str == NULL)
957   {
958     ERROR ("java plugin: jtoc_string: GetStringUTFChars failed.");
959     (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
960     return (-1);
961   }
962
963   sstrncpy (buffer, c_str, buffer_size);
964
965   (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str);
966   (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
967
968   return (0);
969 } /* }}} int jtoc_string */
970
971 /* Call a `long <method> ()' method. */
972 static int jtoc_long (JNIEnv *jvm_env, /* {{{ */
973     jlong *ret_value,
974     jclass class_ptr, jobject object_ptr, const char *method_name)
975 {
976   jmethodID method_id;
977
978   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
979       method_name, "()J");
980   if (method_id == NULL)
981   {
982     ERROR ("java plugin: jtoc_long: Cannot find method `long %s ()'.",
983         method_name);
984     return (-1);
985   }
986
987   *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id);
988
989   return (0);
990 } /* }}} int jtoc_long */
991
992 /* Call a `double <method> ()' method. */
993 static int jtoc_double (JNIEnv *jvm_env, /* {{{ */
994     jdouble *ret_value,
995     jclass class_ptr, jobject object_ptr, const char *method_name)
996 {
997   jmethodID method_id;
998
999   method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
1000       method_name, "()D");
1001   if (method_id == NULL)
1002   {
1003     ERROR ("java plugin: jtoc_string: Cannot find method `double %s ()'.",
1004         method_name);
1005     return (-1);
1006   }
1007
1008   *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id);
1009
1010   return (0);
1011 } /* }}} int jtoc_double */
1012
1013 static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
1014     value_t *ret_value, int ds_type, jobject object_ptr)
1015 {
1016   jclass class_ptr;
1017   int status;
1018
1019   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1020
1021   if (ds_type == DS_TYPE_COUNTER)
1022   {
1023     jlong tmp_long;
1024
1025     status = jtoc_long (jvm_env, &tmp_long,
1026         class_ptr, object_ptr, "longValue");
1027     if (status != 0)
1028     {
1029       ERROR ("java plugin: jtoc_value: "
1030           "jtoc_long failed.");
1031       return (-1);
1032     }
1033     (*ret_value).counter = (counter_t) tmp_long;
1034   }
1035   else
1036   {
1037     jdouble tmp_double;
1038
1039     status = jtoc_double (jvm_env, &tmp_double,
1040         class_ptr, object_ptr, "doubleValue");
1041     if (status != 0)
1042     {
1043       ERROR ("java plugin: jtoc_value: "
1044           "jtoc_double failed.");
1045       return (-1);
1046     }
1047     (*ret_value).gauge = (gauge_t) tmp_double;
1048   }
1049
1050   return (0);
1051 } /* }}} int jtoc_value */
1052
1053 /* Read a List<Number>, convert it to `value_t' and add it to the given
1054  * `value_list_t'. */
1055 static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */
1056     const data_set_t *ds, value_list_t *vl,
1057     jclass class_ptr, jobject object_ptr)
1058 {
1059   jmethodID m_getvalues;
1060   jmethodID m_toarray;
1061   jobject o_list;
1062   jobjectArray o_number_array;
1063
1064   value_t *values;
1065   int values_num;
1066   int i;
1067
1068   values_num = ds->ds_num;
1069
1070   values = NULL;
1071   o_number_array = NULL;
1072   o_list = NULL;
1073
1074 #define BAIL_OUT(status) \
1075   free (values); \
1076   if (o_number_array != NULL) \
1077     (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); \
1078   if (o_list != NULL) \
1079     (*jvm_env)->DeleteLocalRef (jvm_env, o_list); \
1080   return (status);
1081
1082   /* Call: List<Number> ValueList.getValues () */
1083   m_getvalues = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
1084       "getValues", "()Ljava/util/List;");
1085   if (m_getvalues == NULL)
1086   {
1087     ERROR ("java plugin: jtoc_values_array: "
1088         "Cannot find method `List getValues ()'.");
1089     BAIL_OUT (-1);
1090   }
1091
1092   o_list = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, m_getvalues);
1093   if (o_list == NULL)
1094   {
1095     ERROR ("java plugin: jtoc_values_array: "
1096         "CallObjectMethod (getValues) failed.");
1097     BAIL_OUT (-1);
1098   }
1099
1100   /* Call: Number[] List.toArray () */
1101   m_toarray = (*jvm_env)->GetMethodID (jvm_env,
1102       (*jvm_env)->GetObjectClass (jvm_env, o_list),
1103       "toArray", "()[Ljava/lang/Object;");
1104   if (m_toarray == NULL)
1105   {
1106     ERROR ("java plugin: jtoc_values_array: "
1107         "Cannot find method `Object[] toArray ()'.");
1108     BAIL_OUT (-1);
1109   }
1110
1111   o_number_array = (*jvm_env)->CallObjectMethod (jvm_env, o_list, m_toarray);
1112   if (o_number_array == NULL)
1113   {
1114     ERROR ("java plugin: jtoc_values_array: "
1115         "CallObjectMethod (toArray) failed.");
1116     BAIL_OUT (-1);
1117   }
1118
1119   values = (value_t *) calloc (values_num, sizeof (value_t));
1120   if (values == NULL)
1121   {
1122     ERROR ("java plugin: jtoc_values_array: calloc failed.");
1123     BAIL_OUT (-1);
1124   }
1125
1126   for (i = 0; i < values_num; i++)
1127   {
1128     jobject o_number;
1129     int status;
1130
1131     o_number = (*jvm_env)->GetObjectArrayElement (jvm_env,
1132         o_number_array, (jsize) i);
1133     if (o_number == NULL)
1134     {
1135       ERROR ("java plugin: jtoc_values_array: "
1136           "GetObjectArrayElement (%i) failed.", i);
1137       BAIL_OUT (-1);
1138     }
1139
1140     status = jtoc_value (jvm_env, values + i, ds->ds[i].type, o_number);
1141     if (status != 0)
1142     {
1143       ERROR ("java plugin: jtoc_values_array: "
1144           "jtoc_value (%i) failed.", i);
1145       BAIL_OUT (-1);
1146     }
1147   } /* for (i = 0; i < values_num; i++) */
1148
1149   vl->values = values;
1150   vl->values_len = values_num;
1151
1152 #undef BAIL_OUT
1153   (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array);
1154   (*jvm_env)->DeleteLocalRef (jvm_env, o_list);
1155   return (0);
1156 } /* }}} int jtoc_values_array */
1157
1158 /* Convert a org.collectd.api.ValueList to a value_list_t. */
1159 static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
1160     jobject object_ptr)
1161 {
1162   jclass class_ptr;
1163   int status;
1164   jlong tmp_long;
1165   const data_set_t *ds;
1166
1167   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
1168   if (class_ptr == NULL)
1169   {
1170     ERROR ("java plugin: jtoc_value_list: GetObjectClass failed.");
1171     return (-1);
1172   }
1173
1174 #define SET_STRING(buffer,method) do { \
1175   status = jtoc_string (jvm_env, buffer, sizeof (buffer), \
1176       class_ptr, object_ptr, method); \
1177   if (status != 0) { \
1178     ERROR ("java plugin: jtoc_value_list: jtoc_string (%s) failed.", \
1179         method); \
1180     return (-1); \
1181   } } while (0)
1182
1183   SET_STRING(vl->type, "getType");
1184
1185   ds = plugin_get_ds (vl->type);
1186   if (ds == NULL)
1187   {
1188     ERROR ("java plugin: jtoc_value_list: Data-set `%s' is not defined. "
1189         "Please consult the types.db(5) manpage for mor information.",
1190         vl->type);
1191     return (-1);
1192   }
1193
1194   SET_STRING(vl->host, "getHost");
1195   SET_STRING(vl->plugin, "getPlugin");
1196   SET_STRING(vl->plugin_instance, "getPluginInstance");
1197   SET_STRING(vl->type_instance, "getTypeInstance");
1198
1199 #undef SET_STRING
1200
1201   status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime");
1202   if (status != 0)
1203   {
1204     ERROR ("java plugin: jtoc_value_list: jtoc_long (getTime) failed.");
1205     return (-1);
1206   }
1207   /* Java measures time in milliseconds. */
1208   vl->time = (time_t) (tmp_long / ((jlong) 1000));
1209
1210   status = jtoc_long (jvm_env, &tmp_long,
1211       class_ptr, object_ptr, "getInterval");
1212   if (status != 0)
1213   {
1214     ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed.");
1215     return (-1);
1216   }
1217   vl->interval = (int) tmp_long;
1218
1219   status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
1220   if (status != 0)
1221   {
1222     ERROR ("java plugin: jtoc_value_list: jtoc_values_array failed.");
1223     return (-1);
1224   }
1225
1226   return (0);
1227 } /* }}} int jtoc_value_list */
1228
1229 /* 
1230  * Functions accessible from Java
1231  */
1232 static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */
1233     jobject this, jobject java_vl)
1234 {
1235   value_list_t vl = VALUE_LIST_INIT;
1236   int status;
1237
1238   DEBUG ("cjni_api_dispatch_values: java_vl = %p;", (void *) java_vl);
1239
1240   status = jtoc_value_list (jvm_env, &vl, java_vl);
1241   if (status != 0)
1242   {
1243     ERROR ("java plugin: cjni_api_dispatch_values: jtoc_value_list failed.");
1244     return (-1);
1245   }
1246
1247   status = plugin_dispatch_values (&vl);
1248
1249   sfree (vl.values);
1250
1251   return (status);
1252 } /* }}} jint cjni_api_dispatch_values */
1253
1254 static jobject JNICALL cjni_api_get_ds (JNIEnv *jvm_env, /* {{{ */
1255     jobject this, jobject o_string_type)
1256 {
1257   const char *ds_name;
1258   const data_set_t *ds;
1259   jobject o_dataset;
1260
1261   ds_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_string_type, 0);
1262   if (ds_name == NULL)
1263   {
1264     ERROR ("java plugin: cjni_api_get_ds: GetStringUTFChars failed.");
1265     return (NULL);
1266   }
1267
1268   ds = plugin_get_ds (ds_name);
1269   DEBUG ("java plugin: cjni_api_get_ds: "
1270       "plugin_get_ds (%s) = %p;", ds_name, (void *) ds);
1271
1272   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_string_type, ds_name);
1273
1274   if (ds == NULL)
1275     return (NULL);
1276
1277   o_dataset = ctoj_data_set (jvm_env, ds);
1278   return (o_dataset);
1279 } /* }}} jint cjni_api_get_ds */
1280
1281 static jint JNICALL cjni_api_register_config (JNIEnv *jvm_env, /* {{{ */
1282     jobject this, jobject o_name, jobject o_config)
1283 {
1284   return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_CONFIG));
1285 } /* }}} jint cjni_api_register_config */
1286
1287 static jint JNICALL cjni_api_register_init (JNIEnv *jvm_env, /* {{{ */
1288     jobject this, jobject o_name, jobject o_config)
1289 {
1290   return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_INIT));
1291 } /* }}} jint cjni_api_register_init */
1292
1293 static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */
1294     jobject this, jobject o_name, jobject o_read)
1295 {
1296   user_data_t ud;
1297   cjni_callback_info_t *cbi;
1298
1299   cbi = cjni_callback_info_create (jvm_env, o_name, o_read, CB_TYPE_READ);
1300   if (cbi == NULL)
1301     return (-1);
1302
1303   DEBUG ("java plugin: Registering new read callback: %s", cbi->name);
1304
1305   memset (&ud, 0, sizeof (ud));
1306   ud.data = (void *) cbi;
1307   ud.free_func = cjni_callback_info_destroy;
1308
1309   plugin_register_complex_read (cbi->name, cjni_read, &ud);
1310
1311   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
1312
1313   return (0);
1314 } /* }}} jint cjni_api_register_read */
1315
1316 static jint JNICALL cjni_api_register_write (JNIEnv *jvm_env, /* {{{ */
1317     jobject this, jobject o_name, jobject o_write)
1318 {
1319   user_data_t ud;
1320   cjni_callback_info_t *cbi;
1321
1322   cbi = cjni_callback_info_create (jvm_env, o_name, o_write, CB_TYPE_WRITE);
1323   if (cbi == NULL)
1324     return (-1);
1325
1326   DEBUG ("java plugin: Registering new write callback: %s", cbi->name);
1327
1328   memset (&ud, 0, sizeof (ud));
1329   ud.data = (void *) cbi;
1330   ud.free_func = cjni_callback_info_destroy;
1331
1332   plugin_register_write (cbi->name, cjni_write, &ud);
1333
1334   (*jvm_env)->DeleteLocalRef (jvm_env, o_write);
1335
1336   return (0);
1337 } /* }}} jint cjni_api_register_write */
1338
1339 static jint JNICALL cjni_api_register_flush (JNIEnv *jvm_env, /* {{{ */
1340     jobject this, jobject o_name, jobject o_flush)
1341 {
1342   user_data_t ud;
1343   cjni_callback_info_t *cbi;
1344
1345   cbi = cjni_callback_info_create (jvm_env, o_name, o_flush, CB_TYPE_FLUSH);
1346   if (cbi == NULL)
1347     return (-1);
1348
1349   DEBUG ("java plugin: Registering new flush callback: %s", cbi->name);
1350
1351   memset (&ud, 0, sizeof (ud));
1352   ud.data = (void *) cbi;
1353   ud.free_func = cjni_callback_info_destroy;
1354
1355   plugin_register_flush (cbi->name, cjni_flush, &ud);
1356
1357   (*jvm_env)->DeleteLocalRef (jvm_env, o_flush);
1358
1359   return (0);
1360 } /* }}} jint cjni_api_register_flush */
1361
1362 static jint JNICALL cjni_api_register_shutdown (JNIEnv *jvm_env, /* {{{ */
1363     jobject this, jobject o_name, jobject o_shutdown)
1364 {
1365   return (cjni_callback_register (jvm_env, o_name, o_shutdown,
1366         CB_TYPE_SHUTDOWN));
1367 } /* }}} jint cjni_api_register_shutdown */
1368
1369 static jint JNICALL cjni_api_register_log (JNIEnv *jvm_env, /* {{{ */
1370     jobject this, jobject o_name, jobject o_log)
1371 {
1372   user_data_t ud;
1373   cjni_callback_info_t *cbi;
1374
1375   cbi = cjni_callback_info_create (jvm_env, o_name, o_log, CB_TYPE_LOG);
1376   if (cbi == NULL)
1377     return (-1);
1378
1379   DEBUG ("java plugin: Registering new log callback: %s", cbi->name);
1380
1381   memset (&ud, 0, sizeof (ud));
1382   ud.data = (void *) cbi;
1383   ud.free_func = cjni_callback_info_destroy;
1384
1385   plugin_register_log (cbi->name, cjni_log, &ud);
1386
1387   (*jvm_env)->DeleteLocalRef (jvm_env, o_log);
1388
1389   return (0);
1390 } /* }}} jint cjni_api_register_log */
1391
1392 static jint JNICALL cjni_api_register_notification (JNIEnv *jvm_env, /* {{{ */
1393     jobject this, jobject o_name, jobject o_notification)
1394 {
1395   user_data_t ud;
1396   cjni_callback_info_t *cbi;
1397
1398   cbi = cjni_callback_info_create (jvm_env, o_name, o_notification,
1399       CB_TYPE_NOTIFICATION);
1400   if (cbi == NULL)
1401     return (-1);
1402
1403   DEBUG ("java plugin: Registering new notification callback: %s", cbi->name);
1404
1405   memset (&ud, 0, sizeof (ud));
1406   ud.data = (void *) cbi;
1407   ud.free_func = cjni_callback_info_destroy;
1408
1409   plugin_register_notification (cbi->name, cjni_notification, &ud);
1410
1411   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
1412
1413   return (0);
1414 } /* }}} jint cjni_api_register_notification */
1415
1416 static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */
1417     jobject this, jint severity, jobject o_message)
1418 {
1419   const char *c_str;
1420
1421   c_str = (*jvm_env)->GetStringUTFChars (jvm_env, o_message, 0);
1422   if (c_str == NULL)
1423   {
1424     ERROR ("java plugin: cjni_api_log: GetStringUTFChars failed.");
1425     return;
1426   }
1427
1428   if (severity < LOG_ERR)
1429     severity = LOG_ERR;
1430   if (severity > LOG_DEBUG)
1431     severity = LOG_DEBUG;
1432
1433   plugin_log (severity, "%s", c_str);
1434
1435   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_message, c_str);
1436 } /* }}} void cjni_api_log */
1437
1438 /* List of ``native'' functions, i. e. C-functions that can be called from
1439  * Java. */
1440 static JNINativeMethod jni_api_functions[] = /* {{{ */
1441 {
1442   { "dispatchValues",
1443     "(Lorg/collectd/api/ValueList;)I",
1444     cjni_api_dispatch_values },
1445
1446   { "getDS",
1447     "(Ljava/lang/String;)Lorg/collectd/api/DataSet;",
1448     cjni_api_get_ds },
1449
1450   { "registerConfig",
1451     "(Ljava/lang/String;Lorg/collectd/api/CollectdConfigInterface;)I",
1452     cjni_api_register_config },
1453
1454   { "registerInit",
1455     "(Ljava/lang/String;Lorg/collectd/api/CollectdInitInterface;)I",
1456     cjni_api_register_init },
1457
1458   { "registerRead",
1459     "(Ljava/lang/String;Lorg/collectd/api/CollectdReadInterface;)I",
1460     cjni_api_register_read },
1461
1462   { "registerWrite",
1463     "(Ljava/lang/String;Lorg/collectd/api/CollectdWriteInterface;)I",
1464     cjni_api_register_write },
1465
1466   { "registerFlush",
1467     "(Ljava/lang/String;Lorg/collectd/api/CollectdFlushInterface;)I",
1468     cjni_api_register_flush },
1469
1470   { "registerShutdown",
1471     "(Ljava/lang/String;Lorg/collectd/api/CollectdShutdownInterface;)I",
1472     cjni_api_register_shutdown },
1473
1474   { "registerLog",
1475     "(Ljava/lang/String;Lorg/collectd/api/CollectdLogInterface;)I",
1476     cjni_api_register_log },
1477
1478   { "registerNotification",
1479     "(Ljava/lang/String;Lorg/collectd/api/CollectdNotificationInterface;)I",
1480     cjni_api_register_notification },
1481
1482   { "log",
1483     "(ILjava/lang/String;)V",
1484     cjni_api_log },
1485 };
1486 static size_t jni_api_functions_num = sizeof (jni_api_functions)
1487   / sizeof (jni_api_functions[0]);
1488 /* }}} */
1489
1490 /*
1491  * Functions
1492  */
1493 /* Allocate a `cjni_callback_info_t' given the type and objects necessary for
1494  * all registration functions. */
1495 static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{ */
1496     jobject o_name, jobject o_callback, int type)
1497 {
1498   const char *c_name;
1499   cjni_callback_info_t *cbi;
1500   const char *method_name;
1501   const char *method_signature;
1502
1503   switch (type)
1504   {
1505     case CB_TYPE_CONFIG:
1506       method_name = "config";
1507       method_signature = "(Lorg/collectd/api/OConfigItem;)I";
1508       break;
1509
1510     case CB_TYPE_INIT:
1511       method_name = "init";
1512       method_signature = "()I";
1513       break;
1514
1515     case CB_TYPE_READ:
1516       method_name = "read";
1517       method_signature = "()I";
1518       break;
1519
1520     case CB_TYPE_WRITE:
1521       method_name = "write";
1522       method_signature = "(Lorg/collectd/api/ValueList;)I";
1523       break;
1524
1525     case CB_TYPE_FLUSH:
1526       method_name = "flush";
1527       method_signature = "(ILjava/lang/String;)I";
1528       break;
1529
1530     case CB_TYPE_SHUTDOWN:
1531       method_name = "shutdown";
1532       method_signature = "()I";
1533       break;
1534
1535     case CB_TYPE_LOG:
1536       method_name = "log";
1537       method_signature = "(ILjava/lang/String;)V";
1538       break;
1539
1540     case CB_TYPE_NOTIFICATION:
1541       method_name = "notification";
1542       method_signature = "(LLorg/collectd/api/Notification;)I";
1543       break;
1544
1545     default:
1546       ERROR ("java plugin: cjni_callback_info_create: Unknown type: %#x",
1547           type);
1548       return (NULL);
1549   }
1550
1551   c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0);
1552   if (c_name == NULL)
1553   {
1554     ERROR ("java plugin: cjni_callback_info_create: "
1555         "GetStringUTFChars failed.");
1556     return (NULL);
1557   }
1558
1559   cbi = (cjni_callback_info_t *) malloc (sizeof (*cbi));
1560   if (cbi == NULL)
1561   {
1562     ERROR ("java plugin: cjni_callback_info_create: malloc failed.");
1563     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1564     return (NULL);
1565   }
1566   memset (cbi, 0, sizeof (*cbi));
1567   cbi->type = type;
1568
1569   cbi->name = strdup (c_name);
1570   if (cbi->name == NULL)
1571   {
1572     pthread_mutex_unlock (&java_callbacks_lock);
1573     ERROR ("java plugin: cjni_callback_info_create: strdup failed.");
1574     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1575     return (NULL);
1576   }
1577
1578   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1579
1580   cbi->class  = (*jvm_env)->GetObjectClass (jvm_env, o_callback);
1581   if (cbi->class == NULL)
1582   {
1583     ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed.");
1584     free (cbi);
1585     return (NULL);
1586   }
1587
1588   cbi->object = o_callback;
1589
1590   cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class,
1591       method_name, method_signature);
1592   if (cbi->method == NULL)
1593   {
1594     ERROR ("java plugin: cjni_callback_info_create: "
1595         "Cannot find the `%s' method with signature `%s'.",
1596         method_name, method_signature);
1597     free (cbi);
1598     return (NULL);
1599   }
1600
1601   (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
1602
1603   return (cbi);
1604 } /* }}} cjni_callback_info_t cjni_callback_info_create */
1605
1606 /* Allocate a `cjni_callback_info_t' via `cjni_callback_info_create' and add it
1607  * to the global `java_callbacks' variable. This is used for `config', `init',
1608  * and `shutdown' callbacks. */
1609 static int cjni_callback_register (JNIEnv *jvm_env, /* {{{ */
1610     jobject o_name, jobject o_callback, int type)
1611 {
1612   cjni_callback_info_t *cbi;
1613   cjni_callback_info_t *tmp;
1614 #if COLLECT_DEBUG
1615   const char *type_str;
1616 #endif
1617
1618   cbi = cjni_callback_info_create (jvm_env, o_name, o_callback, type);
1619   if (cbi == NULL)
1620     return (-1);
1621
1622 #if COLLECT_DEBUG
1623   switch (type)
1624   {
1625     case CB_TYPE_CONFIG:
1626       type_str = "config";
1627       break;
1628
1629     case CB_TYPE_INIT:
1630       type_str = "init";
1631       break;
1632
1633     case CB_TYPE_SHUTDOWN:
1634       type_str = "shutdown";
1635       break;
1636
1637     default:
1638       type_str = "<unknown>";
1639   }
1640   DEBUG ("java plugin: Registering new %s callback: %s",
1641       type_str, cbi->name);
1642 #endif
1643
1644   pthread_mutex_lock (&java_callbacks_lock);
1645
1646   tmp = (cjni_callback_info_t *) realloc (java_callbacks,
1647       (java_callbacks_num + 1) * sizeof (*java_callbacks));
1648   if (tmp == NULL)
1649   {
1650     pthread_mutex_unlock (&java_callbacks_lock);
1651     ERROR ("java plugin: cjni_callback_register: realloc failed.");
1652
1653     (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
1654     free (cbi);
1655
1656     return (-1);
1657   }
1658   java_callbacks = tmp;
1659   java_callbacks[java_callbacks_num] = *cbi;
1660   java_callbacks_num++;
1661
1662   pthread_mutex_unlock (&java_callbacks_lock);
1663
1664   free (cbi);
1665   return (0);
1666 } /* }}} int cjni_callback_register */
1667
1668 /* Increase the reference counter to the JVM for this thread. If it was zero,
1669  * attach the JVM first. */
1670 static JNIEnv *cjni_thread_attach (void) /* {{{ */
1671 {
1672   cjni_jvm_env_t *cjni_env;
1673   JNIEnv *jvm_env;
1674
1675   cjni_env = pthread_getspecific (jvm_env_key);
1676   if (cjni_env == NULL)
1677   {
1678     /* This pointer is free'd in `cjni_jvm_env_destroy'. */
1679     cjni_env = (cjni_jvm_env_t *) malloc (sizeof (*cjni_env));
1680     if (cjni_env == NULL)
1681     {
1682       ERROR ("java plugin: cjni_thread_attach: malloc failed.");
1683       return (NULL);
1684     }
1685     memset (cjni_env, 0, sizeof (*cjni_env));
1686     cjni_env->reference_counter = 0;
1687     cjni_env->jvm_env = NULL;
1688
1689     pthread_setspecific (jvm_env_key, cjni_env);
1690   }
1691
1692   if (cjni_env->reference_counter > 0)
1693   {
1694     cjni_env->reference_counter++;
1695     jvm_env = cjni_env->jvm_env;
1696   }
1697   else
1698   {
1699     int status;
1700     JavaVMAttachArgs args;
1701
1702     assert (cjni_env->jvm_env == NULL);
1703
1704     memset (&args, 0, sizeof (args));
1705     args.version = JNI_VERSION_1_2;
1706
1707     status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args);
1708     if (status != 0)
1709     {
1710       ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed "
1711           "with status %i.", status);
1712       return (NULL);
1713     }
1714
1715     cjni_env->reference_counter = 1;
1716     cjni_env->jvm_env = jvm_env;
1717   }
1718
1719   DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i",
1720       cjni_env->reference_counter);
1721   assert (jvm_env != NULL);
1722   return (jvm_env);
1723 } /* }}} JNIEnv *cjni_thread_attach */
1724
1725 /* Decrease the reference counter of this thread. If it reaches zero, detach
1726  * from the JVM. */
1727 static int cjni_thread_detach (void) /* {{{ */
1728 {
1729   cjni_jvm_env_t *cjni_env;
1730   int status;
1731
1732   cjni_env = pthread_getspecific (jvm_env_key);
1733   if (cjni_env == NULL)
1734   {
1735     ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed.");
1736     return (-1);
1737   }
1738
1739   assert (cjni_env->reference_counter > 0);
1740   assert (cjni_env->jvm_env != NULL);
1741
1742   cjni_env->reference_counter--;
1743   DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i",
1744       cjni_env->reference_counter);
1745
1746   if (cjni_env->reference_counter > 0)
1747     return (0);
1748
1749   status = (*jvm)->DetachCurrentThread (jvm);
1750   if (status != 0)
1751   {
1752     ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed "
1753         "with status %i.", status);
1754   }
1755
1756   cjni_env->reference_counter = 0;
1757   cjni_env->jvm_env = NULL;
1758
1759   return (0);
1760 } /* }}} JNIEnv *cjni_thread_attach */
1761
1762 /* Callback for `pthread_key_create'. It frees the data contained in
1763  * `jvm_env_key' and prints a warning if the reference counter is not zero. */
1764 static void cjni_jvm_env_destroy (void *args) /* {{{ */
1765 {
1766   cjni_jvm_env_t *cjni_env;
1767
1768   if (args == NULL)
1769     return;
1770
1771   cjni_env = (cjni_jvm_env_t *) args;
1772
1773   if (cjni_env->reference_counter > 0)
1774   {
1775     ERROR ("java plugin: cjni_jvm_env_destroy: "
1776         "cjni_env->reference_counter = %i;", cjni_env->reference_counter);
1777   }
1778
1779   if (cjni_env->jvm_env != NULL)
1780   {
1781     ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;",
1782         (void *) cjni_env->jvm_env);
1783   }
1784
1785   /* The pointer is allocated in `cjni_thread_attach' */
1786   free (cjni_env);
1787 } /* }}} void cjni_jvm_env_destroy */
1788
1789 /* Boring configuration functions.. {{{ */
1790 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
1791 {
1792   char **tmp;
1793
1794   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1795   {
1796     WARNING ("java plugin: `JVMArg' needs exactly one string argument.");
1797     return (-1);
1798   }
1799
1800   tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1));
1801   if (tmp == NULL)
1802   {
1803     ERROR ("java plugin: realloc failed.");
1804     return (-1);
1805   }
1806   jvm_argv = tmp;
1807
1808   jvm_argv[jvm_argc] = strdup (ci->values[0].value.string);
1809   if (jvm_argv[jvm_argc] == NULL)
1810   {
1811     ERROR ("java plugin: strdup failed.");
1812     return (-1);
1813   }
1814   jvm_argc++;
1815
1816   return (0);
1817 } /* }}} int cjni_config_add_jvm_arg */
1818
1819 static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
1820 {
1821   java_plugin_class_t *tmp;
1822
1823   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1824   {
1825     WARNING ("java plugin: `LoadPlugin' needs exactly one string argument.");
1826     return (-1);
1827   }
1828
1829   tmp = (java_plugin_class_t *) realloc (java_classes_list,
1830       (java_classes_list_len + 1) * sizeof (*java_classes_list));
1831   if (tmp == NULL)
1832   {
1833     ERROR ("java plugin: realloc failed.");
1834     return (-1);
1835   }
1836   java_classes_list = tmp;
1837   tmp = java_classes_list + java_classes_list_len;
1838
1839   memset (tmp, 0, sizeof (*tmp));
1840   tmp->name = strdup (ci->values[0].value.string);
1841   if (tmp->name == NULL)
1842   {
1843     ERROR ("java plugin: strdup failed.");
1844     return (-1);
1845   }
1846   tmp->class = NULL;
1847   tmp->object = NULL;
1848
1849   java_classes_list_len++;
1850
1851   return (0);
1852 } /* }}} int cjni_config_load_plugin */
1853
1854 static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
1855 {
1856   java_plugin_config_t *tmp;
1857   char *name;
1858   size_t i;
1859
1860   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1861   {
1862     WARNING ("java plugin: `Plugin' blocks "
1863         "need exactly one string argument.");
1864     return (-1);
1865   }
1866
1867   name = strdup (ci->values[0].value.string);
1868   if (name == NULL)
1869   {
1870     ERROR ("java plugin: cjni_config_plugin_block: strdup faiiled.");
1871     return (-1);
1872   }
1873
1874   for (i = 0; i < java_plugin_configs_num; i++)
1875   {
1876     if (strcmp (java_plugin_configs[i].name, name) == 0)
1877     {
1878       WARNING ("java plugin: There is more than one <Plugin \"%s\"> block. "
1879           "This is currently not supported - "
1880           "only the first block will be used!",
1881           name);
1882       free (name);
1883       return (0);
1884     }
1885   }
1886
1887   tmp = (java_plugin_config_t *) realloc (java_plugin_configs,
1888       (java_plugin_configs_num + 1) * sizeof (*java_plugin_configs));
1889   if (tmp == NULL)
1890   {
1891     ERROR ("java plugin: cjni_config_plugin_block: realloc failed.");
1892     free (name);
1893     return (-1);
1894   }
1895   java_plugin_configs = tmp;
1896   tmp = java_plugin_configs + java_plugin_configs_num;
1897
1898   tmp->name = name;
1899   tmp->ci = oconfig_clone (ci);
1900   if (tmp->ci == NULL)
1901   {
1902     ERROR ("java plugin: cjni_config_plugin_block: "
1903         "oconfig_clone failed for `%s'.",
1904         name);
1905     free (name);
1906     return (-1);
1907   }
1908
1909   DEBUG ("java plugin: cjni_config_plugin_block: "
1910       "Successfully copied config for `%s'.",
1911       name);
1912
1913   java_plugin_configs_num++;
1914   return (0);
1915 } /* }}} int cjni_config_plugin_block */
1916
1917 static int cjni_config (oconfig_item_t *ci) /* {{{ */
1918 {
1919   int success;
1920   int errors;
1921   int status;
1922   int i;
1923
1924   success = 0;
1925   errors = 0;
1926
1927   for (i = 0; i < ci->children_num; i++)
1928   {
1929     oconfig_item_t *child = ci->children + i;
1930
1931     if (strcasecmp ("JVMArg", child->key) == 0)
1932     {
1933       status = cjni_config_add_jvm_arg (child);
1934       if (status == 0)
1935         success++;
1936       else
1937         errors++;
1938     }
1939     else if (strcasecmp ("LoadPlugin", child->key) == 0)
1940     {
1941       status = cjni_config_load_plugin (child);
1942       if (status == 0)
1943         success++;
1944       else
1945         errors++;
1946     }
1947     else if (strcasecmp ("Plugin", child->key) == 0)
1948     {
1949       status = cjni_config_plugin_block (child);
1950       if (status == 0)
1951         success++;
1952       else
1953         errors++;
1954     }
1955     else
1956     {
1957       WARNING ("java plugin: Option `%s' not allowed here.", child->key);
1958       errors++;
1959     }
1960   }
1961
1962   DEBUG ("java plugin: jvm_argc = %zu;", jvm_argc);
1963   DEBUG ("java plugin: java_classes_list_len = %zu;", java_classes_list_len);
1964   DEBUG ("java plugin: java_plugin_configs_num = %zu;",
1965       java_plugin_configs_num);
1966
1967   if ((success == 0) && (errors > 0))
1968   {
1969     ERROR ("java plugin: All statements failed.");
1970     return (-1);
1971   }
1972
1973   return (0);
1974 } /* }}} int cjni_config */
1975 /* }}} */
1976
1977 /* Free the data contained in the `user_data_t' pointer passed to `cjni_read'
1978  * and `cjni_write'. In particular, delete the global reference to the Java
1979  * object. */
1980 static void cjni_callback_info_destroy (void *arg) /* {{{ */
1981 {
1982   JNIEnv *jvm_env;
1983   cjni_callback_info_t *cbi;
1984
1985   DEBUG ("java plugin: cjni_callback_info_destroy (arg = %p);", arg);
1986
1987   cbi = (cjni_callback_info_t *) arg;
1988
1989   /* This condition can occurr when shutting down. */
1990   if (jvm == NULL)
1991   {
1992     sfree (cbi);
1993     return;
1994   }
1995
1996   if (arg == NULL)
1997     return;
1998
1999   jvm_env = cjni_thread_attach ();
2000   if (jvm_env == NULL)
2001   {
2002     ERROR ("java plugin: cjni_callback_info_destroy: cjni_thread_attach failed.");
2003     return;
2004   }
2005
2006   (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
2007
2008   cbi->method = NULL;
2009   cbi->object = NULL;
2010   cbi->class  = NULL;
2011   free (cbi);
2012
2013   cjni_thread_detach ();
2014 } /* }}} void cjni_callback_info_destroy */
2015
2016 /* Call the CB_TYPE_READ callback pointed to by the `user_data_t' pointer. */
2017 static int cjni_read (user_data_t *ud) /* {{{ */
2018 {
2019   JNIEnv *jvm_env;
2020   cjni_callback_info_t *cbi;
2021   int status;
2022   int ret_status;
2023
2024   if (jvm == NULL)
2025   {
2026     ERROR ("java plugin: cjni_read: jvm == NULL");
2027     return (-1);
2028   }
2029
2030   if ((ud == NULL) || (ud->data == NULL))
2031   {
2032     ERROR ("java plugin: cjni_read: Invalid user data.");
2033     return (-1);
2034   }
2035
2036   jvm_env = cjni_thread_attach ();
2037   if (jvm_env == NULL)
2038     return (-1);
2039
2040   cbi = (cjni_callback_info_t *) ud->data;
2041
2042   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object,
2043       cbi->method);
2044
2045   status = cjni_thread_detach ();
2046   if (status != 0)
2047   {
2048     ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
2049     return (-1);
2050   }
2051
2052   return (ret_status);
2053 } /* }}} int cjni_read */
2054
2055 /* Call the CB_TYPE_WRITE callback pointed to by the `user_data_t' pointer. */
2056 static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
2057     user_data_t *ud)
2058 {
2059   JNIEnv *jvm_env;
2060   cjni_callback_info_t *cbi;
2061   jobject vl_java;
2062   int status;
2063   int ret_status;
2064
2065   if (jvm == NULL)
2066   {
2067     ERROR ("java plugin: cjni_write: jvm == NULL");
2068     return (-1);
2069   }
2070
2071   if ((ud == NULL) || (ud->data == NULL))
2072   {
2073     ERROR ("java plugin: cjni_write: Invalid user data.");
2074     return (-1);
2075   }
2076
2077   jvm_env = cjni_thread_attach ();
2078   if (jvm_env == NULL)
2079     return (-1);
2080
2081   cbi = (cjni_callback_info_t *) ud->data;
2082
2083   vl_java = ctoj_value_list (jvm_env, ds, vl);
2084   if (vl_java == NULL)
2085   {
2086     ERROR ("java plugin: cjni_write: ctoj_value_list failed.");
2087     return (-1);
2088   }
2089
2090   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
2091       cbi->object, cbi->method, vl_java);
2092
2093   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
2094
2095   status = cjni_thread_detach ();
2096   if (status != 0)
2097   {
2098     ERROR ("java plugin: cjni_write: cjni_thread_detach failed.");
2099     return (-1);
2100   }
2101
2102   return (ret_status);
2103 } /* }}} int cjni_write */
2104
2105 /* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */
2106 static int cjni_flush (int timeout, const char *identifier, /* {{{ */
2107     user_data_t *ud)
2108 {
2109   JNIEnv *jvm_env;
2110   cjni_callback_info_t *cbi;
2111   jobject o_identifier;
2112   int status;
2113   int ret_status;
2114
2115   if (jvm == NULL)
2116   {
2117     ERROR ("java plugin: cjni_flush: jvm == NULL");
2118     return (-1);
2119   }
2120
2121   if ((ud == NULL) || (ud->data == NULL))
2122   {
2123     ERROR ("java plugin: cjni_flush: Invalid user data.");
2124     return (-1);
2125   }
2126
2127   jvm_env = cjni_thread_attach ();
2128   if (jvm_env == NULL)
2129     return (-1);
2130
2131   cbi = (cjni_callback_info_t *) ud->data;
2132
2133   o_identifier = NULL;
2134   if (identifier != NULL)
2135   {
2136     o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier);
2137     if (o_identifier == NULL)
2138     {
2139       ERROR ("java plugin: cjni_flush: NewStringUTF failed.");
2140       return (-1);
2141     }
2142   }
2143
2144   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
2145       cbi->object, cbi->method, (jint) timeout, o_identifier);
2146
2147   (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier);
2148
2149   status = cjni_thread_detach ();
2150   if (status != 0)
2151   {
2152     ERROR ("java plugin: cjni_flush: cjni_thread_detach failed.");
2153     return (-1);
2154   }
2155
2156   return (ret_status);
2157 } /* }}} int cjni_flush */
2158
2159 /* Call the CB_TYPE_LOG callback pointed to by the `user_data_t' pointer. */
2160 static void cjni_log (int severity, const char *message, /* {{{ */
2161     user_data_t *ud)
2162 {
2163   JNIEnv *jvm_env;
2164   cjni_callback_info_t *cbi;
2165   jobject o_message;
2166
2167   if (jvm == NULL)
2168     return;
2169
2170   if ((ud == NULL) || (ud->data == NULL))
2171     return;
2172
2173   jvm_env = cjni_thread_attach ();
2174   if (jvm_env == NULL)
2175     return;
2176
2177   cbi = (cjni_callback_info_t *) ud->data;
2178
2179   o_message = (*jvm_env)->NewStringUTF (jvm_env, message);
2180   if (o_message == NULL)
2181     return;
2182
2183   (*jvm_env)->CallVoidMethod (jvm_env,
2184       cbi->object, cbi->method, (jint) severity, o_message);
2185
2186   (*jvm_env)->DeleteLocalRef (jvm_env, o_message);
2187
2188   cjni_thread_detach ();
2189 } /* }}} void cjni_log */
2190
2191 /* Call the CB_TYPE_NOTIFICATION callback pointed to by the `user_data_t'
2192  * pointer. */
2193 static int cjni_notification (const notification_t *n, /* {{{ */
2194     user_data_t *ud)
2195 {
2196   JNIEnv *jvm_env;
2197   cjni_callback_info_t *cbi;
2198   jobject o_notification;
2199   int status;
2200   int ret_status;
2201
2202   if (jvm == NULL)
2203   {
2204     ERROR ("java plugin: cjni_read: jvm == NULL");
2205     return (-1);
2206   }
2207
2208   if ((ud == NULL) || (ud->data == NULL))
2209   {
2210     ERROR ("java plugin: cjni_read: Invalid user data.");
2211     return (-1);
2212   }
2213
2214   jvm_env = cjni_thread_attach ();
2215   if (jvm_env == NULL)
2216     return (-1);
2217
2218   cbi = (cjni_callback_info_t *) ud->data;
2219
2220   o_notification = ctoj_notification (jvm_env, n);
2221   if (o_notification == NULL)
2222   {
2223     ERROR ("java plugin: cjni_notification: ctoj_notification failed.");
2224     return (-1);
2225   }
2226
2227   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
2228       cbi->object, cbi->method, o_notification);
2229
2230   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
2231
2232   status = cjni_thread_detach ();
2233   if (status != 0)
2234   {
2235     ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
2236     return (-1);
2237   }
2238
2239   return (ret_status);
2240 } /* }}} int cjni_notification */
2241
2242 /* Iterate over `java_classes_list' and create one object of each class. This
2243  * will trigger the object's constructors, to the objects can register callback
2244  * methods. */
2245 static int cjni_load_plugins (JNIEnv *jvm_env) /* {{{ */
2246 {
2247   size_t i;
2248
2249   for (i = 0; i < java_classes_list_len; i++)
2250   {
2251     java_plugin_class_t *class;
2252     jmethodID constructor_id;
2253
2254     class = java_classes_list + i;
2255
2256     DEBUG ("java plugin: Loading class %s", class->name);
2257
2258     class->class = (*jvm_env)->FindClass (jvm_env, class->name);
2259     if (class->class == NULL)
2260     {
2261       ERROR ("java plugin: cjni_load_plugins: FindClass (%s) failed.",
2262           class->name);
2263       continue;
2264     }
2265
2266     constructor_id = (*jvm_env)->GetMethodID (jvm_env, class->class,
2267         "<init>", "()V");
2268     if (constructor_id == NULL)
2269     {
2270       ERROR ("java plugin: cjni_load_plugins: Could not find the constructor for `%s'.",
2271           class->name);
2272       continue;
2273     }
2274
2275     class->object = (*jvm_env)->NewObject (jvm_env, class->class,
2276         constructor_id);
2277     if (class->object == NULL)
2278     {
2279       ERROR ("java plugin: cjni_load_plugins: Could create a new `%s' object.",
2280           class->name);
2281       continue;
2282     }
2283
2284     (*jvm_env)->NewGlobalRef (jvm_env, class->object);
2285   } /* for (i = 0; i < java_classes_list_len; i++) */
2286
2287   return (0);
2288 } /* }}} int cjni_load_plugins */
2289
2290 /* Iterate over `java_plugin_configs' and `java_callbacks' and call all
2291  * `config' callback methods for which a configuration is available. */
2292 static int cjni_config_plugins (JNIEnv *jvm_env) /* {{{ */
2293 {
2294   int status;
2295   size_t i;
2296   size_t j;
2297
2298   for (i = 0; i < java_plugin_configs_num; i++)
2299   {
2300     jobject o_ocitem;
2301
2302     if (java_plugin_configs[i].ci == NULL)
2303       continue;
2304
2305     for (j = 0; j < java_callbacks_num; j++)
2306     {
2307       if (java_callbacks[j].type != CB_TYPE_CONFIG)
2308         continue;
2309
2310       if (strcmp (java_plugin_configs[i].name, java_callbacks[j].name) == 0)
2311         break;
2312     }
2313
2314     if (j >= java_callbacks_num)
2315     {
2316       NOTICE ("java plugin: Configuration for `%s' is present, but no such "
2317           "configuration callback has been registered.",
2318           java_plugin_configs[i].name);
2319       continue;
2320     }
2321
2322     DEBUG ("java plugin: Configuring %s", java_plugin_configs[i].name);
2323
2324     o_ocitem = ctoj_oconfig_item (jvm_env, java_plugin_configs[i].ci);
2325     if (o_ocitem == NULL)
2326     {
2327       ERROR ("java plugin: cjni_config_plugins: ctoj_oconfig_item failed.");
2328       continue;
2329     }
2330
2331     status = (*jvm_env)->CallIntMethod (jvm_env,
2332         java_callbacks[j].object, java_callbacks[j].method, o_ocitem);
2333     WARNING ("java plugin: Config callback for `%s' returned status %i.",
2334         java_plugin_configs[i].name, status);
2335   } /* for (i = 0; i < java_plugin_configs; i++) */
2336
2337   return (0);
2338 } /* }}} int cjni_config_plugins */
2339
2340 /* Iterate over `java_callbacks' and call all CB_TYPE_INIT callbacks. */
2341 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
2342 {
2343   int status;
2344   size_t i;
2345
2346   for (i = 0; i < java_callbacks_num; i++)
2347   {
2348     if (java_callbacks[i].type != CB_TYPE_INIT)
2349       continue;
2350
2351     DEBUG ("java plugin: Initializing %s", java_callbacks[i].name);
2352
2353     status = (*jvm_env)->CallIntMethod (jvm_env,
2354         java_callbacks[i].object, java_callbacks[i].method);
2355     if (status != 0)
2356     {
2357       ERROR ("java plugin: Initializing `%s' failed with status %i. "
2358           "Removing read function.",
2359           java_callbacks[i].name, status);
2360       plugin_unregister_read (java_callbacks[i].name);
2361     }
2362   }
2363
2364   return (0);
2365 } /* }}} int cjni_init_plugins */
2366
2367 /* Iterate over `java_callbacks' and call all CB_TYPE_SHUTDOWN callbacks. */
2368 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
2369 {
2370   int status;
2371   size_t i;
2372
2373   for (i = 0; i < java_callbacks_num; i++)
2374   {
2375     if (java_callbacks[i].type != CB_TYPE_SHUTDOWN)
2376       continue;
2377
2378     DEBUG ("java plugin: Shutting down %s", java_callbacks[i].name);
2379
2380     status = (*jvm_env)->CallIntMethod (jvm_env,
2381         java_callbacks[i].object, java_callbacks[i].method);
2382     if (status != 0)
2383     {
2384       ERROR ("java plugin: Shutting down `%s' failed with status %i. ",
2385           java_callbacks[i].name, status);
2386     }
2387   }
2388
2389   return (0);
2390 } /* }}} int cjni_shutdown_plugins */
2391
2392
2393 static int cjni_shutdown (void) /* {{{ */
2394 {
2395   JNIEnv *jvm_env;
2396   JavaVMAttachArgs args;
2397   int status;
2398   size_t i;
2399
2400   if (jvm == NULL)
2401     return (0);
2402
2403   jvm_env = NULL;
2404   memset (&args, 0, sizeof (args));
2405   args.version = JNI_VERSION_1_2;
2406
2407   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
2408   if (status != 0)
2409   {
2410     ERROR ("java plugin: cjni_shutdown: AttachCurrentThread failed with status %i.",
2411         status);
2412     return (-1);
2413   }
2414
2415   /* Execute all the shutdown functions registered by plugins. */
2416   cjni_shutdown_plugins (jvm_env);
2417
2418   /* Release all the global references to callback functions */
2419   for (i = 0; i < java_callbacks_num; i++)
2420   {
2421     if (java_callbacks[i].object != NULL)
2422     {
2423       (*jvm_env)->DeleteGlobalRef (jvm_env, java_callbacks[i].object);
2424       java_callbacks[i].object = NULL;
2425     }
2426     sfree (java_callbacks[i].name);
2427   }
2428   java_callbacks_num = 0;
2429   sfree (java_callbacks);
2430
2431   /* Release all the global references to directly loaded classes. */
2432   for (i = 0; i < java_classes_list_len; i++)
2433   {
2434     if (java_classes_list[i].object != NULL)
2435     {
2436       (*jvm_env)->DeleteGlobalRef (jvm_env, java_classes_list[i].object);
2437       java_classes_list[i].object = NULL;
2438     }
2439     sfree (java_classes_list[i].name);
2440   }
2441   java_classes_list_len = 0;
2442   sfree (java_classes_list);
2443
2444   /* Destroy the JVM */
2445   DEBUG ("java plugin: Destroying the JVM.");
2446   (*jvm)->DestroyJavaVM (jvm);
2447   jvm = NULL;
2448   jvm_env = NULL;
2449
2450   pthread_key_delete (jvm_env_key);
2451
2452   /* Free the JVM argument list */
2453   for (i = 0; i < jvm_argc; i++)
2454     sfree (jvm_argv[i]);
2455   jvm_argc = 0;
2456   sfree (jvm_argv);
2457
2458   /* Free the copied configuration */
2459   for (i = 0; i < java_plugin_configs_num; i++)
2460   {
2461     sfree (java_plugin_configs[i].name);
2462     oconfig_free (java_plugin_configs[i].ci);
2463   }
2464   java_plugin_configs_num = 0;
2465   sfree (java_plugin_configs);
2466
2467   return (0);
2468 } /* }}} int cjni_shutdown */
2469
2470 /* Register ``native'' functions with the JVM. Native functions are C-functions
2471  * that can be called by Java code. */
2472 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
2473 {
2474   jclass api_class_ptr;
2475   int status;
2476
2477   api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.Collectd");
2478   if (api_class_ptr == NULL)
2479   {
2480     ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.Collectd'.");
2481     return (-1);
2482   }
2483
2484   status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
2485       jni_api_functions, (jint) jni_api_functions_num);
2486   if (status != 0)
2487   {
2488     ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
2489     return (-1);
2490   }
2491
2492   return (0);
2493 } /* }}} int cjni_init_native */
2494
2495 /* Initialization: Create a JVM, load all configured classes and call their
2496  * `config' and `init' callback methods. */
2497 static int cjni_init (void) /* {{{ */
2498 {
2499   JNIEnv *jvm_env;
2500   JavaVMInitArgs vm_args;
2501   JavaVMOption vm_options[jvm_argc];
2502
2503   int status;
2504   size_t i;
2505
2506   if (jvm != NULL)
2507     return (0);
2508
2509   status = pthread_key_create (&jvm_env_key, cjni_jvm_env_destroy);
2510   if (status != 0)
2511   {
2512     ERROR ("java plugin: cjni_init: pthread_key_create failed "
2513         "with status %i.", status);
2514     return (-1);
2515   }
2516
2517   jvm_env = NULL;
2518
2519   memset (&vm_args, 0, sizeof (vm_args));
2520   vm_args.version = JNI_VERSION_1_2;
2521   vm_args.options = vm_options;
2522   vm_args.nOptions = (jint) jvm_argc;
2523
2524   for (i = 0; i < jvm_argc; i++)
2525   {
2526     DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]);
2527     vm_args.options[i].optionString = jvm_argv[i];
2528   }
2529   /*
2530   vm_args.options[0].optionString = "-verbose:jni";
2531   vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
2532   */
2533
2534   status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
2535   if (status != 0)
2536   {
2537     ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.",
2538         status);
2539     return (-1);
2540   }
2541   assert (jvm != NULL);
2542   assert (jvm_env != NULL);
2543
2544   /* Call RegisterNatives */
2545   status = cjni_init_native (jvm_env);
2546   if (status != 0)
2547   {
2548     ERROR ("cjni_init: cjni_init_native failed.");
2549     return (-1);
2550   }
2551
2552   cjni_load_plugins (jvm_env);
2553   cjni_config_plugins (jvm_env);
2554   cjni_init_plugins (jvm_env);
2555
2556   return (0);
2557 } /* }}} int cjni_init */
2558
2559 void module_register (void)
2560 {
2561   plugin_register_complex_config ("java", cjni_config);
2562   plugin_register_init ("java", cjni_init);
2563   plugin_register_shutdown ("java", cjni_shutdown);
2564 } /* void module_register (void) */
2565
2566 /* vim: set sw=2 sts=2 et fdm=marker : */