java plugin: Implement `plugin_get_ds' aka. GetDS.
[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 jobject JNICALL cjni_api_get_ds (JNIEnv *jvm_env, /* {{{ */
1129     jobject this, jobject o_string_type)
1130 {
1131   const char *ds_name;
1132   const data_set_t *ds;
1133   jobject o_dataset;
1134
1135   ds_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_string_type, 0);
1136   if (ds_name == NULL)
1137   {
1138     ERROR ("java plugin: cjni_api_get_ds: GetStringUTFChars failed.");
1139     return (NULL);
1140   }
1141
1142   ds = plugin_get_ds (ds_name);
1143   DEBUG ("java plugin: cjni_api_get_ds: "
1144       "plugin_get_ds (%s) = %p;", ds_name, (void *) ds);
1145
1146   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_string_type, ds_name);
1147
1148   if (ds == NULL)
1149     return (NULL);
1150
1151   o_dataset = ctoj_data_set (jvm_env, ds);
1152   return (o_dataset);
1153 } /* }}} jint cjni_api_get_ds */
1154
1155 static JNINativeMethod jni_api_functions[] =
1156 {
1157   { "DispatchValues", "(Lorg/collectd/protocol/ValueList;)I", cjni_api_dispatch_values },
1158   { "GetDS",          "(Ljava/lang/String;)Ljava/util/List;", cjni_api_get_ds }
1159 };
1160 static size_t jni_api_functions_num = sizeof (jni_api_functions)
1161   / sizeof (jni_api_functions[0]);
1162
1163 /*
1164  * Functions
1165  */
1166 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
1167 {
1168   char **tmp;
1169
1170   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1171   {
1172     WARNING ("java plugin: `JVMArg' needs exactly one string argument.");
1173     return (-1);
1174   }
1175
1176   tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1));
1177   if (tmp == NULL)
1178   {
1179     ERROR ("java plugin: realloc failed.");
1180     return (-1);
1181   }
1182   jvm_argv = tmp;
1183
1184   jvm_argv[jvm_argc] = strdup (ci->values[0].value.string);
1185   if (jvm_argv[jvm_argc] == NULL)
1186   {
1187     ERROR ("java plugin: strdup failed.");
1188     return (-1);
1189   }
1190   jvm_argc++;
1191
1192   return (0);
1193 } /* }}} int cjni_config_add_jvm_arg */
1194
1195 static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
1196 {
1197   java_plugin_t *jp;
1198
1199   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1200   {
1201     WARNING ("java plugin: `LoadPlugin' needs exactly one string argument.");
1202     return (-1);
1203   }
1204
1205   jp = (java_plugin_t *) realloc (java_plugins,
1206       sizeof (*java_plugins) * (java_plugins_num + 1));
1207   if (jp == NULL)
1208   {
1209     ERROR ("java plugin: realloc failed.");
1210     return (-1);
1211   }
1212   java_plugins = jp;
1213   jp = java_plugins + java_plugins_num;
1214
1215   memset (jp, 0, sizeof (*jp));
1216   jp->class_name = strdup (ci->values[0].value.string);
1217   if (jp->class_name == NULL)
1218   {
1219     ERROR ("java plugin: strdup failed.");
1220     return (-1);
1221   }
1222
1223   jp->class_ptr  = NULL;
1224   jp->object_ptr = NULL;
1225   jp->ci         = NULL;
1226   jp->flags      = 0;
1227   jp->m_config   = NULL;
1228   jp->m_init     = NULL;
1229   jp->m_read     = NULL;
1230   jp->m_write    = NULL;
1231   jp->m_shutdown = NULL;
1232
1233   java_plugins_num++;
1234
1235   return (0);
1236 } /* }}} int cjni_config_load_plugin */
1237
1238 static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
1239 {
1240   size_t i;
1241   const char *class_name;
1242
1243   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
1244   {
1245     WARNING ("java plugin: `Plugin' blocks "
1246         "need exactly one string argument.");
1247     return (-1);
1248   }
1249
1250   class_name = ci->values[0].value.string;
1251   for (i = 0; i < java_plugins_num; i++)
1252     if (strcmp (java_plugins[i].class_name, class_name) == 0)
1253       break;
1254
1255   if (i >= java_plugins_num)
1256   {
1257     WARNING ("java plugin: Configuration block for the `%s' plugin found, "
1258         "but the plugin has not been loaded. Please note, that the class "
1259         "name is case-sensitive!",
1260         class_name);
1261     return (0);
1262   }
1263
1264   if (java_plugins[i].ci != NULL)
1265   {
1266     WARNING ("java plugin: There are more than one <Plugin> blocks for the "
1267         "`%s' plugin. This is currently not supported - only the first block "
1268         "will be used!",
1269         class_name);
1270     return (0);
1271   }
1272
1273   java_plugins[i].ci = oconfig_clone (ci);
1274   if (java_plugins[i].ci == NULL)
1275   {
1276     ERROR ("java plugin: cjni_config_plugin_block: "
1277         "oconfig_clone failed for `%s'.",
1278         class_name);
1279     return (-1);
1280   }
1281
1282   DEBUG ("java plugin: cjni_config_plugin_block: "
1283       "Successfully copied config for `%s'.",
1284       class_name);
1285
1286   return (0);
1287 } /* }}} int cjni_config_plugin_block */
1288
1289 static int cjni_config (oconfig_item_t *ci) /* {{{ */
1290 {
1291   int success;
1292   int errors;
1293   int status;
1294   int i;
1295
1296   success = 0;
1297   errors = 0;
1298
1299   for (i = 0; i < ci->children_num; i++)
1300   {
1301     oconfig_item_t *child = ci->children + i;
1302
1303     if (strcasecmp ("JVMArg", child->key) == 0)
1304     {
1305       status = cjni_config_add_jvm_arg (child);
1306       if (status == 0)
1307         success++;
1308       else
1309         errors++;
1310     }
1311     else if (strcasecmp ("LoadPlugin", child->key) == 0)
1312     {
1313       status = cjni_config_load_plugin (child);
1314       if (status == 0)
1315         success++;
1316       else
1317         errors++;
1318     }
1319     else if (strcasecmp ("Plugin", child->key) == 0)
1320     {
1321       status = cjni_config_plugin_block (child);
1322       if (status == 0)
1323         success++;
1324       else
1325         errors++;
1326     }
1327     else
1328     {
1329       WARNING ("java plugin: Option `%s' not allowed here.", child->key);
1330       errors++;
1331     }
1332   }
1333
1334   if ((success == 0) && (errors > 0))
1335   {
1336     ERROR ("java plugin: All statements failed.");
1337     return (-1);
1338   }
1339
1340   return (0);
1341 } /* }}} int cjni_config */
1342
1343 static int cjni_init_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
1344 {
1345   jmethodID constructor_id;
1346   int status;
1347
1348   jp->class_ptr = (*jvm_env)->FindClass (jvm_env, jp->class_name);
1349   if (jp->class_ptr == NULL)
1350   {
1351     ERROR ("cjni_init_one_plugin: FindClass (%s) failed.",
1352         jp->class_name);
1353     return (-1);
1354   }
1355
1356   constructor_id = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1357       "<init>", "()V");
1358   if (constructor_id == NULL)
1359   {
1360     ERROR ("cjni_init_one_plugin: Could not find the constructor for `%s'.",
1361         jp->class_name);
1362     return (-1);
1363   }
1364
1365   jp->object_ptr = (*jvm_env)->NewObject (jvm_env, jp->class_ptr,
1366       constructor_id);
1367   if (jp->object_ptr == NULL)
1368   {
1369     ERROR ("cjni_init_one_plugin: Could create a new `%s' object.",
1370         jp->class_name);
1371     return (-1);
1372   }
1373
1374   jp->m_config = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1375       "Config", "(Lorg/collectd/api/OConfigItem;)I");
1376   DEBUG ("java plugin: cjni_init_one_plugin: "
1377       "jp->class_name = %s; jp->m_config = %p;",
1378       jp->class_name, (void *) jp->m_config);
1379
1380   jp->m_init = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1381       "Init", "()I");
1382   DEBUG ("java plugin: cjni_init_one_plugin: "
1383       "jp->class_name = %s; jp->m_init = %p;",
1384       jp->class_name, (void *) jp->m_init);
1385
1386   jp->m_read = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1387       "Read", "()I");
1388   DEBUG ("java plugin: cjni_init_one_plugin: "
1389       "jp->class_name = %s; jp->m_read = %p;",
1390       jp->class_name, (void *) jp->m_read);
1391
1392   jp->m_write = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1393       "Write", "(Lorg/collectd/protocol/ValueList;)I");
1394   DEBUG ("java plugin: cjni_init_one_plugin: "
1395       "jp->class_name = %s; jp->m_write = %p;",
1396       jp->class_name, (void *) jp->m_write);
1397
1398   jp->m_shutdown = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
1399       "Shutdown", "()I");
1400   DEBUG ("java plugin: cjni_init_one_plugin: "
1401       "jp->class_name = %s; jp->m_shutdown = %p;",
1402       jp->class_name, (void *) jp->m_shutdown);
1403
1404   if (jp->ci != NULL)
1405   {
1406     if (jp->m_config == NULL)
1407     {
1408       WARNING ("java plugin: Configuration for the `%s' plugin is present, "
1409           "but plugin doesn't provide a configuration method.",
1410           jp->class_name);
1411     }
1412     else /* if (jp->m_config != NULL) */
1413     {
1414       jobject o_ocitem;
1415
1416       o_ocitem = ctoj_oconfig_item (jvm_env, jp->ci);
1417       if (o_ocitem == NULL)
1418       {
1419         ERROR ("java plugin: Creating an OConfigItem object failed. "
1420             "Can't pass configuration information to the `%s' plugin!",
1421             jp->class_name);
1422       }
1423       else /* if (o_ocitem != NULL) */
1424       {
1425         status = (*jvm_env)->CallIntMethod (jvm_env,
1426             jp->object_ptr, jp->m_config, o_ocitem);
1427         if (status != 0)
1428         {
1429           ERROR ("java plugin: cjni_init_one_plugin: "
1430               "Configuring the `%s' object failed with status %i.",
1431               jp->class_name, status);
1432           (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
1433           return (-1);
1434         }
1435         (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
1436       } /* if (o_ocitem != NULL) */
1437     } /* if (jp->m_config != NULL) */
1438   } /* if (jp->ci != NULL) */
1439
1440   if (jp->m_init != NULL)
1441   {
1442     status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1443         jp->m_init);
1444     if (status != 0)
1445     {
1446       ERROR ("java plugin: cjni_init_one_plugin: "
1447         "Initializing `%s' object failed with status %i.",
1448         jp->class_name, status);
1449       return (-1);
1450     }
1451   }
1452   jp->flags |= CJNI_FLAG_ENABLED;
1453
1454   return (0);
1455 } /* }}} int cjni_init_one_plugin */
1456
1457 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
1458 {
1459   size_t j;
1460
1461   for (j = 0; j < java_plugins_num; j++)
1462     cjni_init_one_plugin (jvm_env, &java_plugins[j]);
1463
1464   return (0);
1465 } /* }}} int cjni_init_plugins */
1466
1467 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
1468 {
1469   jclass api_class_ptr;
1470   int status;
1471
1472   api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.CollectdAPI");
1473   if (api_class_ptr == NULL)
1474   {
1475     ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.CollectdAPI'.");
1476     return (-1);
1477   }
1478
1479   status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
1480       jni_api_functions, (jint) jni_api_functions_num);
1481   if (status != 0)
1482   {
1483     ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
1484     return (-1);
1485   }
1486
1487   return (0);
1488 } /* }}} int cjni_init_native */
1489
1490 static int cjni_init (void) /* {{{ */
1491 {
1492   JNIEnv *jvm_env;
1493   JavaVMInitArgs vm_args;
1494   JavaVMOption vm_options[jvm_argc];
1495
1496   int status;
1497   size_t i;
1498
1499   if (jvm != NULL)
1500     return (0);
1501
1502   jvm_env = NULL;
1503
1504   memset (&vm_args, 0, sizeof (vm_args));
1505   vm_args.version = JNI_VERSION_1_2;
1506   vm_args.options = vm_options;
1507   vm_args.nOptions = (jint) jvm_argc;
1508
1509   for (i = 0; i < jvm_argc; i++)
1510   {
1511     DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]);
1512     vm_args.options[i].optionString = jvm_argv[i];
1513   }
1514   /*
1515   vm_args.options[0].optionString = "-verbose:jni";
1516   vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
1517   */
1518
1519   status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
1520   if (status != 0)
1521   {
1522     ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.",
1523         status);
1524     return (-1);
1525   }
1526   assert (jvm != NULL);
1527   assert (jvm_env != NULL);
1528
1529   /* Call RegisterNatives */
1530   status = cjni_init_native (jvm_env);
1531   if (status != 0)
1532   {
1533     ERROR ("cjni_init: cjni_init_native failed.");
1534     return (-1);
1535   }
1536
1537   cjni_init_plugins (jvm_env);
1538
1539   return (0);
1540 } /* }}} int cjni_init */
1541
1542 static int cjni_read_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
1543 {
1544   int status;
1545
1546   if ((jp == NULL)
1547       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1548       || (jp->m_read == NULL))
1549     return (0);
1550
1551   DEBUG ("java plugin: Calling: %s.Read()", jp->class_name);
1552
1553   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1554       jp->m_read);
1555   if (status != 0)
1556   {
1557     ERROR ("java plugin: cjni_read_one_plugin: "
1558         "Calling `Read' on an `%s' object failed with status %i.",
1559         jp->class_name, status);
1560     return (-1);
1561   }
1562
1563   return (0);
1564 } /* }}} int cjni_read_one_plugin */
1565
1566 static int cjni_read_plugins (JNIEnv *jvm_env) /* {{{ */
1567 {
1568   size_t j;
1569
1570   for (j = 0; j < java_plugins_num; j++)
1571     cjni_read_one_plugin (jvm_env, &java_plugins[j]);
1572
1573   return (0);
1574 } /* }}} int cjni_read_plugins */
1575
1576 static int cjni_read (void) /* {{{ */
1577 {
1578   JNIEnv *jvm_env;
1579   JavaVMAttachArgs args;
1580   int status;
1581
1582   if (jvm == NULL)
1583   {
1584     ERROR ("java plugin: cjni_read: jvm == NULL");
1585     return (-1);
1586   }
1587
1588   jvm_env = NULL;
1589   memset (&args, 0, sizeof (args));
1590   args.version = JNI_VERSION_1_2;
1591
1592   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1593   if (status != 0)
1594   {
1595     ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
1596         status);
1597     return (-1);
1598   }
1599
1600   cjni_read_plugins (jvm_env);
1601
1602   status = (*jvm)->DetachCurrentThread (jvm);
1603   if (status != 0)
1604   {
1605     ERROR ("java plugin: cjni_read: DetachCurrentThread failed with status %i.",
1606         status);
1607     return (-1);
1608   }
1609
1610   return (0);
1611 } /* }}} int cjni_read */
1612
1613 static int cjni_write_one_plugin (JNIEnv *jvm_env, /* {{{ */
1614     java_plugin_t *jp, jobject vl_java)
1615 {
1616   int status;
1617
1618   if ((jp == NULL)
1619       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1620       || (jp->m_write == NULL))
1621     return (0);
1622
1623   DEBUG ("java plugin: Calling: %s.Write(ValueList)", jp->class_name);
1624
1625   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1626       jp->m_write, vl_java);
1627   if (status != 0)
1628   {
1629     ERROR ("java plugin: cjni_write_one_plugin: "
1630         "Calling `Write' on an `%s' object failed with status %i.",
1631         jp->class_name, status);
1632     return (-1);
1633   }
1634
1635   return (0);
1636 } /* }}} int cjni_write_one_plugin */
1637
1638 static int cjni_write_plugins (JNIEnv *jvm_env, /* {{{ */
1639     const data_set_t *ds, const value_list_t *vl)
1640 {
1641   size_t j;
1642
1643   jobject vl_java;
1644
1645   vl_java = ctoj_value_list (jvm_env, ds, vl);
1646   if (vl_java == NULL)
1647   {
1648     ERROR ("java plugin: cjni_write_plugins: ctoj_value_list failed.");
1649     return (-1);
1650   }
1651
1652   for (j = 0; j < java_plugins_num; j++)
1653     cjni_write_one_plugin (jvm_env, &java_plugins[j], vl_java);
1654
1655   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
1656
1657   return (0);
1658 } /* }}} int cjni_write_plugins */
1659
1660 static int cjni_write (const data_set_t *ds, const value_list_t *vl) /* {{{ */
1661 {
1662   JNIEnv *jvm_env;
1663   JavaVMAttachArgs args;
1664   int status;
1665
1666   if (jvm == NULL)
1667   {
1668     ERROR ("java plugin: cjni_write: jvm == NULL");
1669     return (-1);
1670   }
1671
1672   jvm_env = NULL;
1673   memset (&args, 0, sizeof (args));
1674   args.version = JNI_VERSION_1_2;
1675
1676   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1677   if (status != 0)
1678   {
1679     ERROR ("java plugin: cjni_write: AttachCurrentThread failed with status %i.",
1680         status);
1681     return (-1);
1682   }
1683
1684   cjni_write_plugins (jvm_env, ds, vl);
1685
1686   status = (*jvm)->DetachCurrentThread (jvm);
1687   if (status != 0)
1688   {
1689     ERROR ("java plugin: cjni_write: DetachCurrentThread failed with status %i.",
1690         status);
1691     return (-1);
1692   }
1693
1694   return (0);
1695 } /* }}} int cjni_write */
1696
1697 static int cjni_shutdown_one_plugin (JNIEnv *jvm_env, /* {{{ */
1698     java_plugin_t *jp)
1699 {
1700   int status;
1701
1702   if ((jp == NULL)
1703       || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
1704       || (jp->m_shutdown == NULL))
1705     return (0);
1706
1707   status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
1708       jp->m_shutdown);
1709   if (status != 0)
1710   {
1711     ERROR ("cjni_shutdown_one_plugin: Destroying an `%s' object failed "
1712         "with status %i.", jp->class_name, status);
1713     return (-1);
1714   }
1715   jp->flags &= ~CJNI_FLAG_ENABLED;
1716
1717   return (0);
1718 } /* }}} int cjni_shutdown_one_plugin */
1719
1720 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
1721 {
1722   size_t j;
1723
1724   for (j = 0; j < java_plugins_num; j++)
1725     cjni_shutdown_one_plugin (jvm_env, &java_plugins[j]);
1726
1727   return (0);
1728 } /* }}} int cjni_shutdown_plugins */
1729
1730 static int cjni_shutdown (void) /* {{{ */
1731 {
1732   JNIEnv *jvm_env;
1733   JavaVMAttachArgs args;
1734   int status;
1735   size_t i;
1736
1737   if (jvm == NULL)
1738     return (0);
1739
1740   jvm_env = NULL;
1741   memset (&args, 0, sizeof (args));
1742   args.version = JNI_VERSION_1_2;
1743
1744   status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
1745   if (status != 0)
1746   {
1747     ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
1748         status);
1749     return (-1);
1750   }
1751
1752   cjni_shutdown_plugins (jvm_env);
1753
1754   (*jvm)->DestroyJavaVM (jvm);
1755   jvm = NULL;
1756   jvm_env = NULL;
1757
1758   for (i = 0; i < jvm_argc; i++)
1759   {
1760     sfree (jvm_argv[i]);
1761   }
1762   sfree (jvm_argv);
1763   jvm_argc = 0;
1764
1765   for (i = 0; i < java_plugins_num; i++)
1766   {
1767     sfree (java_plugins[i].class_name);
1768     oconfig_free (java_plugins[i].ci);
1769   }
1770   sfree (java_plugins);
1771   java_plugins_num = 0;
1772
1773   return (0);
1774 } /* }}} int cjni_shutdown */
1775
1776 void module_register (void)
1777 {
1778   plugin_register_complex_config ("java", cjni_config);
1779   plugin_register_init ("java", cjni_init);
1780   plugin_register_read ("java", cjni_read);
1781   plugin_register_write ("java", cjni_write);
1782   plugin_register_shutdown ("java", cjni_shutdown);
1783 } /* void module_register (void) */
1784
1785 /* vim: set sw=2 sts=2 et fdm=marker : */