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