java plugin: Change the name of all functions to conform to the Java convention.
[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 static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */
1258     jobject this, jint severity, jobject o_message)
1259 {
1260   const char *c_str;
1261
1262   c_str = (*jvm_env)->GetStringUTFChars (jvm_env, o_message, 0);
1263   if (c_str == NULL)
1264   {
1265     ERROR ("java plugin: cjni_api_log: GetStringUTFChars failed.");
1266     return;
1267   }
1268
1269   if (severity < LOG_ERR)
1270     severity = LOG_ERR;
1271   if (severity > LOG_DEBUG)
1272     severity = LOG_DEBUG;
1273
1274   plugin_log (severity, "%s", c_str);
1275
1276   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_message, c_str);
1277 } /* }}} void cjni_api_log */
1278
1279 /* List of ``native'' functions, i. e. C-functions that can be called from
1280  * Java. */
1281 static JNINativeMethod jni_api_functions[] = /* {{{ */
1282 {
1283   { "dispatchValues",
1284     "(Lorg/collectd/api/ValueList;)I",
1285     cjni_api_dispatch_values },
1286
1287   { "getDS",
1288     "(Ljava/lang/String;)Lorg/collectd/api/DataSet;",
1289     cjni_api_get_ds },
1290
1291   { "registerConfig",
1292     "(Ljava/lang/String;Lorg/collectd/api/CollectdConfigInterface;)I",
1293     cjni_api_register_config },
1294
1295   { "registerInit",
1296     "(Ljava/lang/String;Lorg/collectd/api/CollectdInitInterface;)I",
1297     cjni_api_register_init },
1298
1299   { "registerRead",
1300     "(Ljava/lang/String;Lorg/collectd/api/CollectdReadInterface;)I",
1301     cjni_api_register_read },
1302
1303   { "registerWrite",
1304     "(Ljava/lang/String;Lorg/collectd/api/CollectdWriteInterface;)I",
1305     cjni_api_register_write },
1306
1307   { "registerShutdown",
1308     "(Ljava/lang/String;Lorg/collectd/api/CollectdShutdownInterface;)I",
1309     cjni_api_register_shutdown },
1310
1311   { "log",
1312     "(ILjava/lang/String;)V",
1313     cjni_api_log },
1314 };
1315 static size_t jni_api_functions_num = sizeof (jni_api_functions)
1316   / sizeof (jni_api_functions[0]);
1317 /* }}} */
1318
1319 /*
1320  * Functions
1321  */
1322 /* Allocate a `cjni_callback_info_t' given the type and objects necessary for
1323  * all registration functions. */
1324 static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{ */
1325     jobject o_name, jobject o_callback, int type)
1326 {
1327   const char *c_name;
1328   cjni_callback_info_t *cbi;
1329   const char *method_name;
1330   const char *method_signature;
1331
1332   switch (type)
1333   {
1334     case CB_TYPE_CONFIG:
1335       method_name = "config";
1336       method_signature = "(Lorg/collectd/api/OConfigItem;)I";
1337       break;
1338
1339     case CB_TYPE_INIT:
1340       method_name = "init";
1341       method_signature = "()I";
1342       break;
1343
1344     case CB_TYPE_READ:
1345       method_name = "read";
1346       method_signature = "()I";
1347       break;
1348
1349     case CB_TYPE_WRITE:
1350       method_name = "write";
1351       method_signature = "(Lorg/collectd/api/ValueList;)I";
1352       break;
1353
1354     case CB_TYPE_SHUTDOWN:
1355       method_name = "shutdown";
1356       method_signature = "()I";
1357       break;
1358
1359     default:
1360       ERROR ("java plugin: cjni_callback_info_create: Unknown type: %#x",
1361           type);
1362       return (NULL);
1363   }
1364
1365   c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0);
1366   if (c_name == NULL)
1367   {
1368     ERROR ("java plugin: cjni_callback_info_create: "
1369         "GetStringUTFChars failed.");
1370     return (NULL);
1371   }
1372
1373   cbi = (cjni_callback_info_t *) malloc (sizeof (*cbi));
1374   if (cbi == NULL)
1375   {
1376     ERROR ("java plugin: cjni_callback_info_create: malloc failed.");
1377     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1378     return (NULL);
1379   }
1380   memset (cbi, 0, sizeof (*cbi));
1381   cbi->type = type;
1382
1383   cbi->name = strdup (c_name);
1384   if (cbi->name == NULL)
1385   {
1386     pthread_mutex_unlock (&java_callbacks_lock);
1387     ERROR ("java plugin: cjni_callback_info_create: strdup failed.");
1388     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1389     return (NULL);
1390   }
1391
1392   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
1393
1394   cbi->class  = (*jvm_env)->GetObjectClass (jvm_env, o_callback);
1395   if (cbi->class == NULL)
1396   {
1397     ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed.");
1398     free (cbi);
1399     return (NULL);
1400   }
1401
1402   cbi->object = o_callback;
1403
1404   cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class,
1405       method_name, method_signature);
1406   if (cbi->method == NULL)
1407   {
1408     ERROR ("java plugin: cjni_callback_info_create: "
1409         "Cannot find the `%s' method with signature `%s'.",
1410         method_name, method_signature);
1411     free (cbi);
1412     return (NULL);
1413   }
1414
1415   (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
1416
1417   return (cbi);
1418 } /* }}} cjni_callback_info_t cjni_callback_info_create */
1419
1420 /* Allocate a `cjni_callback_info_t' via `cjni_callback_info_create' and add it
1421  * to the global `java_callbacks' variable. This is used for `config', `init',
1422  * and `shutdown' callbacks. */
1423 static int cjni_callback_register (JNIEnv *jvm_env, /* {{{ */
1424     jobject o_name, jobject o_callback, int type)
1425 {
1426   cjni_callback_info_t *cbi;
1427   cjni_callback_info_t *tmp;
1428 #if COLLECT_DEBUG
1429   const char *type_str;
1430 #endif
1431
1432   cbi = cjni_callback_info_create (jvm_env, o_name, o_callback, type);
1433   if (cbi == NULL)
1434     return (-1);
1435
1436 #if COLLECT_DEBUG
1437   switch (type)
1438   {
1439     case CB_TYPE_CONFIG:
1440       type_str = "config";
1441       break;
1442
1443     case CB_TYPE_INIT:
1444       type_str = "init";
1445       break;
1446
1447     case CB_TYPE_SHUTDOWN:
1448       type_str = "shutdown";
1449       break;
1450
1451     default:
1452       type_str = "<unknown>";
1453   }
1454   DEBUG ("java plugin: Registering new %s callback: %s",
1455       type_str, cbi->name);
1456 #endif
1457
1458   pthread_mutex_lock (&java_callbacks_lock);
1459
1460   tmp = (cjni_callback_info_t *) realloc (java_callbacks,
1461       (java_callbacks_num + 1) * sizeof (*java_callbacks));
1462   if (tmp == NULL)
1463   {
1464     pthread_mutex_unlock (&java_callbacks_lock);
1465     ERROR ("java plugin: cjni_callback_register: realloc failed.");
1466
1467     (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
1468     free (cbi);
1469
1470     return (-1);
1471   }
1472   java_callbacks = tmp;
1473   java_callbacks[java_callbacks_num] = *cbi;
1474   java_callbacks_num++;
1475
1476   pthread_mutex_unlock (&java_callbacks_lock);
1477
1478   free (cbi);
1479   return (0);
1480 } /* }}} int cjni_callback_register */
1481
1482 /* Increase the reference counter to the JVM for this thread. If it was zero,
1483  * attach the JVM first. */
1484 static JNIEnv *cjni_thread_attach (void) /* {{{ */
1485 {
1486   cjni_jvm_env_t *cjni_env;
1487   JNIEnv *jvm_env;
1488
1489   cjni_env = pthread_getspecific (jvm_env_key);
1490   if (cjni_env == NULL)
1491   {
1492     /* This pointer is free'd in `cjni_jvm_env_destroy'. */
1493     cjni_env = (cjni_jvm_env_t *) malloc (sizeof (*cjni_env));
1494     if (cjni_env == NULL)
1495     {
1496       ERROR ("java plugin: cjni_thread_attach: malloc failed.");
1497       return (NULL);
1498     }
1499     memset (cjni_env, 0, sizeof (*cjni_env));
1500     cjni_env->reference_counter = 0;
1501     cjni_env->jvm_env = NULL;
1502
1503     pthread_setspecific (jvm_env_key, cjni_env);
1504   }
1505
1506   if (cjni_env->reference_counter > 0)
1507   {
1508     cjni_env->reference_counter++;
1509     jvm_env = cjni_env->jvm_env;
1510   }
1511   else
1512   {
1513     int status;
1514     JavaVMAttachArgs args;
1515
1516     assert (cjni_env->jvm_env == NULL);
1517
1518     memset (&args, 0, sizeof (args));
1519     args.version = JNI_VERSION_1_2;
1520
1521     status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args);
1522     if (status != 0)
1523     {
1524       ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed "
1525           "with status %i.", status);
1526       return (NULL);
1527     }
1528
1529     cjni_env->reference_counter = 1;
1530     cjni_env->jvm_env = jvm_env;
1531   }
1532
1533   DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i",
1534       cjni_env->reference_counter);
1535   assert (jvm_env != NULL);
1536   return (jvm_env);
1537 } /* }}} JNIEnv *cjni_thread_attach */
1538
1539 /* Decrease the reference counter of this thread. If it reaches zero, detach
1540  * from the JVM. */
1541 static int cjni_thread_detach (void) /* {{{ */
1542 {
1543   cjni_jvm_env_t *cjni_env;
1544   int status;
1545
1546   cjni_env = pthread_getspecific (jvm_env_key);
1547   if (cjni_env == NULL)
1548   {
1549     ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed.");
1550     return (-1);
1551   }
1552
1553   assert (cjni_env->reference_counter > 0);
1554   assert (cjni_env->jvm_env != NULL);
1555
1556   cjni_env->reference_counter--;
1557   DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i",
1558       cjni_env->reference_counter);
1559
1560   if (cjni_env->reference_counter > 0)
1561     return (0);
1562
1563   status = (*jvm)->DetachCurrentThread (jvm);
1564   if (status != 0)
1565   {
1566     ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed "
1567         "with status %i.", status);
1568   }
1569
1570   cjni_env->reference_counter = 0;
1571   cjni_env->jvm_env = NULL;
1572
1573   return (0);
1574 } /* }}} JNIEnv *cjni_thread_attach */
1575
1576 /* Callback for `pthread_key_create'. It frees the data contained in
1577  * `jvm_env_key' and prints a warning if the reference counter is not zero. */
1578 static void cjni_jvm_env_destroy (void *args) /* {{{ */
1579 {
1580   cjni_jvm_env_t *cjni_env;
1581
1582   if (args == NULL)
1583     return;
1584
1585   cjni_env = (cjni_jvm_env_t *) args;
1586
1587   if (cjni_env->reference_counter > 0)
1588   {
1589     ERROR ("java plugin: cjni_jvm_env_destroy: "
1590         "cjni_env->reference_counter = %i;", cjni_env->reference_counter);
1591   }
1592
1593   if (cjni_env->jvm_env != NULL)
1594   {
1595     ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;",
1596         (void *) cjni_env->jvm_env);
1597   }
1598
1599   /* The pointer is allocated in `cjni_thread_attach' */
1600   free (cjni_env);
1601 } /* }}} void cjni_jvm_env_destroy */
1602
1603 /* Boring configuration functions.. {{{ */
1604 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
1605 {
1606   char **tmp;
1607
1608   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1609   {
1610     WARNING ("java plugin: `JVMArg' needs exactly one string argument.");
1611     return (-1);
1612   }
1613
1614   tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1));
1615   if (tmp == NULL)
1616   {
1617     ERROR ("java plugin: realloc failed.");
1618     return (-1);
1619   }
1620   jvm_argv = tmp;
1621
1622   jvm_argv[jvm_argc] = strdup (ci->values[0].value.string);
1623   if (jvm_argv[jvm_argc] == NULL)
1624   {
1625     ERROR ("java plugin: strdup failed.");
1626     return (-1);
1627   }
1628   jvm_argc++;
1629
1630   return (0);
1631 } /* }}} int cjni_config_add_jvm_arg */
1632
1633 static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
1634 {
1635   java_plugin_class_t *tmp;
1636
1637   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1638   {
1639     WARNING ("java plugin: `LoadPlugin' needs exactly one string argument.");
1640     return (-1);
1641   }
1642
1643   tmp = (java_plugin_class_t *) realloc (java_classes_list,
1644       (java_classes_list_len + 1) * sizeof (*java_classes_list));
1645   if (tmp == NULL)
1646   {
1647     ERROR ("java plugin: realloc failed.");
1648     return (-1);
1649   }
1650   java_classes_list = tmp;
1651   tmp = java_classes_list + java_classes_list_len;
1652
1653   memset (tmp, 0, sizeof (*tmp));
1654   tmp->name = strdup (ci->values[0].value.string);
1655   if (tmp->name == NULL)
1656   {
1657     ERROR ("java plugin: strdup failed.");
1658     return (-1);
1659   }
1660   tmp->class = NULL;
1661   tmp->object = NULL;
1662
1663   java_classes_list_len++;
1664
1665   return (0);
1666 } /* }}} int cjni_config_load_plugin */
1667
1668 static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
1669 {
1670   java_plugin_config_t *tmp;
1671   char *name;
1672   size_t i;
1673
1674   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1675   {
1676     WARNING ("java plugin: `Plugin' blocks "
1677         "need exactly one string argument.");
1678     return (-1);
1679   }
1680
1681   name = strdup (ci->values[0].value.string);
1682   if (name == NULL)
1683   {
1684     ERROR ("java plugin: cjni_config_plugin_block: strdup faiiled.");
1685     return (-1);
1686   }
1687
1688   for (i = 0; i < java_plugin_configs_num; i++)
1689   {
1690     if (strcmp (java_plugin_configs[i].name, name) == 0)
1691     {
1692       WARNING ("java plugin: There is more than one <Plugin \"%s\"> block. "
1693           "This is currently not supported - "
1694           "only the first block will be used!",
1695           name);
1696       free (name);
1697       return (0);
1698     }
1699   }
1700
1701   tmp = (java_plugin_config_t *) realloc (java_plugin_configs,
1702       (java_plugin_configs_num + 1) * sizeof (*java_plugin_configs));
1703   if (tmp == NULL)
1704   {
1705     ERROR ("java plugin: cjni_config_plugin_block: realloc failed.");
1706     free (name);
1707     return (-1);
1708   }
1709   java_plugin_configs = tmp;
1710   tmp = java_plugin_configs + java_plugin_configs_num;
1711
1712   tmp->name = name;
1713   tmp->ci = oconfig_clone (ci);
1714   if (tmp->ci == NULL)
1715   {
1716     ERROR ("java plugin: cjni_config_plugin_block: "
1717         "oconfig_clone failed for `%s'.",
1718         name);
1719     free (name);
1720     return (-1);
1721   }
1722
1723   DEBUG ("java plugin: cjni_config_plugin_block: "
1724       "Successfully copied config for `%s'.",
1725       name);
1726
1727   java_plugin_configs_num++;
1728   return (0);
1729 } /* }}} int cjni_config_plugin_block */
1730
1731 static int cjni_config (oconfig_item_t *ci) /* {{{ */
1732 {
1733   int success;
1734   int errors;
1735   int status;
1736   int i;
1737
1738   success = 0;
1739   errors = 0;
1740
1741   for (i = 0; i < ci->children_num; i++)
1742   {
1743     oconfig_item_t *child = ci->children + i;
1744
1745     if (strcasecmp ("JVMArg", child->key) == 0)
1746     {
1747       status = cjni_config_add_jvm_arg (child);
1748       if (status == 0)
1749         success++;
1750       else
1751         errors++;
1752     }
1753     else if (strcasecmp ("LoadPlugin", child->key) == 0)
1754     {
1755       status = cjni_config_load_plugin (child);
1756       if (status == 0)
1757         success++;
1758       else
1759         errors++;
1760     }
1761     else if (strcasecmp ("Plugin", child->key) == 0)
1762     {
1763       status = cjni_config_plugin_block (child);
1764       if (status == 0)
1765         success++;
1766       else
1767         errors++;
1768     }
1769     else
1770     {
1771       WARNING ("java plugin: Option `%s' not allowed here.", child->key);
1772       errors++;
1773     }
1774   }
1775
1776   DEBUG ("java plugin: jvm_argc = %zu;", jvm_argc);
1777   DEBUG ("java plugin: java_classes_list_len = %zu;", java_classes_list_len);
1778   DEBUG ("java plugin: java_plugin_configs_num = %zu;",
1779       java_plugin_configs_num);
1780
1781   if ((success == 0) && (errors > 0))
1782   {
1783     ERROR ("java plugin: All statements failed.");
1784     return (-1);
1785   }
1786
1787   return (0);
1788 } /* }}} int cjni_config */
1789 /* }}} */
1790
1791 /* Free the data contained in the `user_data_t' pointer passed to `cjni_read'
1792  * and `cjni_write'. In particular, delete the global reference to the Java
1793  * object. */
1794 static void cjni_callback_info_destroy (void *arg) /* {{{ */
1795 {
1796   JNIEnv *jvm_env;
1797   cjni_callback_info_t *cbi;
1798
1799   DEBUG ("java plugin: cjni_callback_info_destroy (arg = %p);", arg);
1800
1801   if (arg == NULL)
1802     return;
1803
1804   jvm_env = cjni_thread_attach ();
1805   if (jvm_env == NULL)
1806   {
1807     ERROR ("java plugin: cjni_callback_info_destroy: cjni_thread_attach failed.");
1808     return;
1809   }
1810
1811   cbi = (cjni_callback_info_t *) arg;
1812
1813   (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object);
1814
1815   cbi->method = NULL;
1816   cbi->object = NULL;
1817   cbi->class  = NULL;
1818   free (cbi);
1819
1820   cjni_thread_detach ();
1821 } /* }}} void cjni_callback_info_destroy */
1822
1823 /* Call the CB_TYPE_READ callback pointed to by the `user_data_t' pointer. */
1824 static int cjni_read (user_data_t *ud) /* {{{ */
1825 {
1826   JNIEnv *jvm_env;
1827   cjni_callback_info_t *cbi;
1828   int status;
1829
1830   if (jvm == NULL)
1831   {
1832     ERROR ("java plugin: cjni_read: jvm == NULL");
1833     return (-1);
1834   }
1835
1836   if ((ud == NULL) || (ud->data == NULL))
1837   {
1838     ERROR ("java plugin: cjni_read: Invalid user data.");
1839     return (-1);
1840   }
1841
1842   jvm_env = cjni_thread_attach ();
1843   if (jvm_env == NULL)
1844     return (-1);
1845
1846   cbi = (cjni_callback_info_t *) ud->data;
1847
1848   status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object,
1849       cbi->method);
1850
1851   status = cjni_thread_detach ();
1852   if (status != 0)
1853   {
1854     ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
1855     return (-1);
1856   }
1857
1858   return (status);
1859 } /* }}} int cjni_read */
1860
1861 /* Call the CB_TYPE_WRITE callback pointed to by the `user_data_t' pointer. */
1862 static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
1863     user_data_t *ud)
1864 {
1865   JNIEnv *jvm_env;
1866   cjni_callback_info_t *cbi;
1867   jobject vl_java;
1868   int status;
1869
1870   if (jvm == NULL)
1871   {
1872     ERROR ("java plugin: cjni_write: jvm == NULL");
1873     return (-1);
1874   }
1875
1876   if ((ud == NULL) || (ud->data == NULL))
1877   {
1878     ERROR ("java plugin: cjni_write: Invalid user data.");
1879     return (-1);
1880   }
1881
1882   jvm_env = cjni_thread_attach ();
1883   if (jvm_env == NULL)
1884     return (-1);
1885
1886   cbi = (cjni_callback_info_t *) ud->data;
1887
1888   vl_java = ctoj_value_list (jvm_env, ds, vl);
1889   if (vl_java == NULL)
1890   {
1891     ERROR ("java plugin: cjni_write: ctoj_value_list failed.");
1892     return (-1);
1893   }
1894
1895   status = (*jvm_env)->CallIntMethod (jvm_env,
1896       cbi->object, cbi->method, vl_java);
1897
1898   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
1899
1900   status = cjni_thread_detach ();
1901   if (status != 0)
1902   {
1903     ERROR ("java plugin: cjni_write: cjni_thread_detach failed.");
1904     return (-1);
1905   }
1906
1907   return (status);
1908 } /* }}} int cjni_write */
1909
1910 /* Iterate over `java_classes_list' and create one object of each class. This
1911  * will trigger the object's constructors, to the objects can register callback
1912  * methods. */
1913 static int cjni_load_plugins (JNIEnv *jvm_env) /* {{{ */
1914 {
1915   size_t i;
1916
1917   for (i = 0; i < java_classes_list_len; i++)
1918   {
1919     java_plugin_class_t *class;
1920     jmethodID constructor_id;
1921
1922     class = java_classes_list + i;
1923
1924     DEBUG ("java plugin: Loading class %s", class->name);
1925
1926     class->class = (*jvm_env)->FindClass (jvm_env, class->name);
1927     if (class->class == NULL)
1928     {
1929       ERROR ("java plugin: cjni_load_plugins: FindClass (%s) failed.",
1930           class->name);
1931       continue;
1932     }
1933
1934     constructor_id = (*jvm_env)->GetMethodID (jvm_env, class->class,
1935         "<init>", "()V");
1936     if (constructor_id == NULL)
1937     {
1938       ERROR ("java plugin: cjni_load_plugins: Could not find the constructor for `%s'.",
1939           class->name);
1940       continue;
1941     }
1942
1943     class->object = (*jvm_env)->NewObject (jvm_env, class->class,
1944         constructor_id);
1945     if (class->object == NULL)
1946     {
1947       ERROR ("java plugin: cjni_load_plugins: Could create a new `%s' object.",
1948           class->name);
1949       continue;
1950     }
1951
1952     (*jvm_env)->NewGlobalRef (jvm_env, class->object);
1953   } /* for (i = 0; i < java_classes_list_len; i++) */
1954
1955   return (0);
1956 } /* }}} int cjni_load_plugins */
1957
1958 /* Iterate over `java_plugin_configs' and `java_callbacks' and call all
1959  * `config' callback methods for which a configuration is available. */
1960 static int cjni_config_plugins (JNIEnv *jvm_env) /* {{{ */
1961 {
1962   int status;
1963   size_t i;
1964   size_t j;
1965
1966   for (i = 0; i < java_plugin_configs_num; i++)
1967   {
1968     jobject o_ocitem;
1969
1970     if (java_plugin_configs[i].ci == NULL)
1971       continue;
1972
1973     for (j = 0; j < java_callbacks_num; j++)
1974     {
1975       if (java_callbacks[j].type != CB_TYPE_CONFIG)
1976         continue;
1977
1978       if (strcmp (java_plugin_configs[i].name, java_callbacks[j].name) == 0)
1979         break;
1980     }
1981
1982     if (j >= java_callbacks_num)
1983     {
1984       NOTICE ("java plugin: Configuration for `%s' is present, but no such "
1985           "configuration callback has been registered.",
1986           java_plugin_configs[i].name);
1987       continue;
1988     }
1989
1990     DEBUG ("java plugin: Configuring %s", java_plugin_configs[i].name);
1991
1992     o_ocitem = ctoj_oconfig_item (jvm_env, java_plugin_configs[i].ci);
1993     if (o_ocitem == NULL)
1994     {
1995       ERROR ("java plugin: cjni_config_plugins: ctoj_oconfig_item failed.");
1996       continue;
1997     }
1998
1999     status = (*jvm_env)->CallIntMethod (jvm_env,
2000         java_callbacks[j].object, java_callbacks[j].method, o_ocitem);
2001     WARNING ("java plugin: Config callback for `%s' returned status %i.",
2002         java_plugin_configs[i].name, status);
2003   } /* for (i = 0; i < java_plugin_configs; i++) */
2004
2005   return (0);
2006 } /* }}} int cjni_config_plugins */
2007
2008 /* Iterate over `java_callbacks' and call all CB_TYPE_INIT callbacks. */
2009 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
2010 {
2011   int status;
2012   size_t i;
2013
2014   for (i = 0; i < java_callbacks_num; i++)
2015   {
2016     if (java_callbacks[i].type != CB_TYPE_INIT)
2017       continue;
2018
2019     DEBUG ("java plugin: Initializing %s", java_callbacks[i].name);
2020
2021     status = (*jvm_env)->CallIntMethod (jvm_env,
2022         java_callbacks[i].object, java_callbacks[i].method);
2023     if (status != 0)
2024     {
2025       ERROR ("java plugin: Initializing `%s' failed with status %i. "
2026           "Removing read function.",
2027           java_callbacks[i].name, status);
2028       plugin_unregister_read (java_callbacks[i].name);
2029     }
2030   }
2031
2032   return (0);
2033 } /* }}} int cjni_init_plugins */
2034
2035 /* Iterate over `java_callbacks' and call all CB_TYPE_SHUTDOWN callbacks. */
2036 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
2037 {
2038   int status;
2039   size_t i;
2040
2041   for (i = 0; i < java_callbacks_num; i++)
2042   {
2043     if (java_callbacks[i].type != CB_TYPE_SHUTDOWN)
2044       continue;
2045
2046     DEBUG ("java plugin: Shutting down %s", java_callbacks[i].name);
2047
2048     status = (*jvm_env)->CallIntMethod (jvm_env,
2049         java_callbacks[i].object, java_callbacks[i].method);
2050     if (status != 0)
2051     {
2052       ERROR ("java plugin: Shutting down `%s' failed with status %i. ",
2053           java_callbacks[i].name, status);
2054     }
2055   }
2056
2057   return (0);
2058 } /* }}} int cjni_shutdown_plugins */
2059
2060
2061 static int cjni_shutdown (void) /* {{{ */
2062 {
2063   JNIEnv *jvm_env;
2064   JavaVMAttachArgs args;
2065   int status;
2066   size_t i;
2067
2068   if (jvm == NULL)
2069     return (0);
2070
2071   jvm_env = NULL;
2072   memset (&args, 0, sizeof (args));
2073   args.version = JNI_VERSION_1_2;
2074
2075   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
2076   if (status != 0)
2077   {
2078     ERROR ("java plugin: cjni_shutdown: AttachCurrentThread failed with status %i.",
2079         status);
2080     return (-1);
2081   }
2082
2083   /* Execute all the shutdown functions registered by plugins. */
2084   cjni_shutdown_plugins (jvm_env);
2085
2086   /* Release all the global references to callback functions */
2087   for (i = 0; i < java_callbacks_num; i++)
2088   {
2089     if (java_callbacks[i].object != NULL)
2090     {
2091       (*jvm_env)->DeleteGlobalRef (jvm_env, java_callbacks[i].object);
2092       java_callbacks[i].object = NULL;
2093     }
2094     sfree (java_callbacks[i].name);
2095   }
2096   java_callbacks_num = 0;
2097   sfree (java_callbacks);
2098
2099   /* Release all the global references to directly loaded classes. */
2100   for (i = 0; i < java_classes_list_len; i++)
2101   {
2102     if (java_classes_list[i].object != NULL)
2103     {
2104       (*jvm_env)->DeleteGlobalRef (jvm_env, java_classes_list[i].object);
2105       java_classes_list[i].object = NULL;
2106     }
2107     sfree (java_classes_list[i].name);
2108   }
2109   java_classes_list_len = 0;
2110   sfree (java_classes_list);
2111
2112   /* Destroy the JVM */
2113   (*jvm)->DestroyJavaVM (jvm);
2114   jvm = NULL;
2115   jvm_env = NULL;
2116
2117   pthread_key_delete (jvm_env_key);
2118
2119   /* Free the JVM argument list */
2120   for (i = 0; i < jvm_argc; i++)
2121     sfree (jvm_argv[i]);
2122   jvm_argc = 0;
2123   sfree (jvm_argv);
2124
2125   /* Free the copied configuration */
2126   for (i = 0; i < java_plugin_configs_num; i++)
2127   {
2128     sfree (java_plugin_configs[i].name);
2129     oconfig_free (java_plugin_configs[i].ci);
2130   }
2131   java_plugin_configs_num = 0;
2132   sfree (java_plugin_configs);
2133
2134   return (0);
2135 } /* }}} int cjni_shutdown */
2136
2137 /* Register ``native'' functions with the JVM. Native functions are C-functions
2138  * that can be called by Java code. */
2139 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
2140 {
2141   jclass api_class_ptr;
2142   int status;
2143
2144   api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.CollectdAPI");
2145   if (api_class_ptr == NULL)
2146   {
2147     ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.CollectdAPI'.");
2148     return (-1);
2149   }
2150
2151   status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
2152       jni_api_functions, (jint) jni_api_functions_num);
2153   if (status != 0)
2154   {
2155     ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
2156     return (-1);
2157   }
2158
2159   return (0);
2160 } /* }}} int cjni_init_native */
2161
2162 /* Initialization: Create a JVM, load all configured classes and call their
2163  * `config' and `init' callback methods. */
2164 static int cjni_init (void) /* {{{ */
2165 {
2166   JNIEnv *jvm_env;
2167   JavaVMInitArgs vm_args;
2168   JavaVMOption vm_options[jvm_argc];
2169
2170   int status;
2171   size_t i;
2172
2173   if (jvm != NULL)
2174     return (0);
2175
2176   status = pthread_key_create (&jvm_env_key, cjni_jvm_env_destroy);
2177   if (status != 0)
2178   {
2179     ERROR ("java plugin: cjni_init: pthread_key_create failed "
2180         "with status %i.", status);
2181     return (-1);
2182   }
2183
2184   jvm_env = NULL;
2185
2186   memset (&vm_args, 0, sizeof (vm_args));
2187   vm_args.version = JNI_VERSION_1_2;
2188   vm_args.options = vm_options;
2189   vm_args.nOptions = (jint) jvm_argc;
2190
2191   for (i = 0; i < jvm_argc; i++)
2192   {
2193     DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]);
2194     vm_args.options[i].optionString = jvm_argv[i];
2195   }
2196   /*
2197   vm_args.options[0].optionString = "-verbose:jni";
2198   vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
2199   */
2200
2201   status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
2202   if (status != 0)
2203   {
2204     ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.",
2205         status);
2206     return (-1);
2207   }
2208   assert (jvm != NULL);
2209   assert (jvm_env != NULL);
2210
2211   /* Call RegisterNatives */
2212   status = cjni_init_native (jvm_env);
2213   if (status != 0)
2214   {
2215     ERROR ("cjni_init: cjni_init_native failed.");
2216     return (-1);
2217   }
2218
2219   cjni_load_plugins (jvm_env);
2220   cjni_config_plugins (jvm_env);
2221   cjni_init_plugins (jvm_env);
2222
2223   return (0);
2224 } /* }}} int cjni_init */
2225
2226 void module_register (void)
2227 {
2228   plugin_register_complex_config ("java", cjni_config);
2229   plugin_register_init ("java", cjni_init);
2230   plugin_register_shutdown ("java", cjni_shutdown);
2231 } /* void module_register (void) */
2232
2233 /* vim: set sw=2 sts=2 et fdm=marker : */