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