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