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