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