e2a04e1303579feb9f672c497f25cd4d0e8947dd
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 **/
24 #include "collectd.h"
25 #include "plugin.h"
26 #include "common.h"
28 #include <pthread.h>
29 #include <jni.h>
31 #if !defined(JNI_VERSION_1_2)
32 # error "Need JNI 1.2 compatible interface!"
33 #endif
35 /*
36 * Types
37 */
38 struct java_plugin_s /* {{{ */
39 {
40 char *class_name;
41 jclass class_ptr;
42 jobject object_ptr;
44 #define CJNI_FLAG_ENABLED 0x0001
45 int flags;
47 jmethodID method_init;
48 jmethodID method_read;
49 jmethodID method_shutdown;
50 };
51 typedef struct java_plugin_s java_plugin_t;
52 /* }}} */
54 /*
55 * Global variables
56 */
57 static JavaVM *jvm = NULL;
59 static java_plugin_t java_plugins[] =
60 {
61 { "org.collectd.java.Foobar", NULL, NULL, 0, NULL, NULL, NULL }
62 };
63 static size_t java_plugins_num = sizeof (java_plugins) / sizeof (java_plugins[0]);
65 /*
66 * Conversion functons
67 *
68 * - jtoc_*: From Java to C
69 * - ctoj_*: From C to Java
70 */
71 static int jtoc_string (JNIEnv *jvm_env, /* {{{ */
72 char *buffer, size_t buffer_size,
73 jclass class_ptr, jobject object_ptr, const char *method_name)
74 {
75 jmethodID method_id;
76 jobject string_obj;
77 const char *c_str;
79 method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
80 method_name, "()Ljava/lang/String;");
81 if (method_id == NULL)
82 {
83 ERROR ("java plugin: jtoc_string: Cannot find method `String %s ()'.",
84 method_name);
85 return (-1);
86 }
88 string_obj = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, method_id);
89 if (string_obj == NULL)
90 {
91 ERROR ("java plugin: jtoc_string: CallObjectMethod (%s) failed.",
92 method_name);
93 return (-1);
94 }
96 c_str = (*jvm_env)->GetStringUTFChars (jvm_env, string_obj, 0);
97 if (c_str == NULL)
98 {
99 ERROR ("java plugin: jtoc_string: GetStringUTFChars failed.");
100 (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
101 return (-1);
102 }
104 DEBUG ("java plugin: jtoc_string: ->%s() = %s", method_name, c_str);
106 sstrncpy (buffer, c_str, buffer_size);
108 (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str);
109 (*jvm_env)->DeleteLocalRef (jvm_env, string_obj);
111 return (0);
112 } /* }}} int jtoc_string */
114 static int jtoc_long (JNIEnv *jvm_env, /* {{{ */
115 jlong *ret_value,
116 jclass class_ptr, jobject object_ptr, const char *method_name)
117 {
118 jmethodID method_id;
120 method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
121 method_name, "()J");
122 if (method_id == NULL)
123 {
124 ERROR ("java plugin: jtoc_string: Cannot find method `long %s ()'.",
125 method_name);
126 return (-1);
127 }
129 *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id);
131 DEBUG ("java plugin: jtoc_long: ->%s() = %li",
132 method_name, (long int) *ret_value);
134 return (0);
135 } /* }}} int jtoc_long */
137 static int jtoc_double (JNIEnv *jvm_env, /* {{{ */
138 jdouble *ret_value,
139 jclass class_ptr, jobject object_ptr, const char *method_name)
140 {
141 jmethodID method_id;
143 method_id = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
144 method_name, "()D");
145 if (method_id == NULL)
146 {
147 ERROR ("java plugin: jtoc_string: Cannot find method `double %s ()'.",
148 method_name);
149 return (-1);
150 }
152 *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id);
154 DEBUG ("java plugin: jtoc_double: ->%s() = %g",
155 method_name, (double) *ret_value);
157 return (0);
158 } /* }}} int jtoc_double */
160 static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
161 value_t *ret_value, int ds_type, jobject object_ptr)
162 {
163 jclass class_ptr;
164 int status;
166 class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
168 if (ds_type == DS_TYPE_COUNTER)
169 {
170 jlong tmp_long;
172 status = jtoc_long (jvm_env, &tmp_long,
173 class_ptr, object_ptr, "longValue");
174 if (status != 0)
175 {
176 ERROR ("java plugin: jtoc_value: "
177 "jtoc_long failed.");
178 return (-1);
179 }
180 (*ret_value).counter = (counter_t) tmp_long;
181 }
182 else
183 {
184 jdouble tmp_double;
186 status = jtoc_double (jvm_env, &tmp_double,
187 class_ptr, object_ptr, "doubleValue");
188 if (status != 0)
189 {
190 ERROR ("java plugin: jtoc_value: "
191 "jtoc_double failed.");
192 return (-1);
193 }
194 (*ret_value).gauge = (gauge_t) tmp_double;
195 }
197 return (0);
198 } /* }}} int jtoc_value */
200 static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */
201 const data_set_t *ds, value_list_t *vl,
202 jclass class_ptr, jobject object_ptr)
203 {
204 jmethodID m_getvalues;
205 jmethodID m_toarray;
206 jobject o_list;
207 jobjectArray o_number_array;
209 value_t *values;
210 int values_num;
211 int i;
213 values_num = ds->ds_num;
215 values = NULL;
216 o_number_array = NULL;
217 o_list = NULL;
219 #define BAIL_OUT(status) \
220 free (values); \
221 if (o_number_array != NULL) \
222 (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array); \
223 if (o_list != NULL) \
224 (*jvm_env)->DeleteLocalRef (jvm_env, o_list); \
225 return (status);
227 /* Call: List<Number> ValueList.getValues () */
228 m_getvalues = (*jvm_env)->GetMethodID (jvm_env, class_ptr,
229 "getValues", "()Ljava/util/List;");
230 if (m_getvalues == NULL)
231 {
232 ERROR ("java plugin: jtoc_values_array: "
233 "Cannot find method `List getValues ()'.");
234 BAIL_OUT (-1);
235 }
237 o_list = (*jvm_env)->CallObjectMethod (jvm_env, object_ptr, m_getvalues);
238 if (o_list == NULL)
239 {
240 ERROR ("java plugin: jtoc_values_array: "
241 "CallObjectMethod (getValues) failed.");
242 BAIL_OUT (-1);
243 }
245 /* Call: Number[] List.toArray () */
246 m_toarray = (*jvm_env)->GetMethodID (jvm_env,
247 (*jvm_env)->GetObjectClass (jvm_env, o_list),
248 "toArray", "()[Ljava/lang/Object;");
249 if (m_toarray == NULL)
250 {
251 ERROR ("java plugin: jtoc_values_array: "
252 "Cannot find method `Object[] toArray ()'.");
253 BAIL_OUT (-1);
254 }
256 o_number_array = (*jvm_env)->CallObjectMethod (jvm_env, o_list, m_toarray);
257 if (o_number_array == NULL)
258 {
259 ERROR ("java plugin: jtoc_values_array: "
260 "CallObjectMethod (toArray) failed.");
261 BAIL_OUT (-1);
262 }
264 values = calloc (values_num, sizeof (value_t));
265 if (values == NULL)
266 {
267 ERROR ("java plugin: jtoc_values_array: calloc failed.");
268 BAIL_OUT (-1);
269 }
271 for (i = 0; i < values_num; i++)
272 {
273 jobject o_number;
274 int status;
276 o_number = (*jvm_env)->GetObjectArrayElement (jvm_env,
277 o_number_array, (jsize) i);
278 if (o_number == NULL)
279 {
280 ERROR ("java plugin: jtoc_values_array: "
281 "GetObjectArrayElement (%i) failed.", i);
282 BAIL_OUT (-1);
283 }
285 status = jtoc_value (jvm_env, values + i, ds->ds[i].type, o_number);
286 if (status != 0)
287 {
288 ERROR ("java plugin: jtoc_values_array: "
289 "jtoc_value (%i) failed.", i);
290 BAIL_OUT (-1);
291 }
292 } /* for (i = 0; i < values_num; i++) */
294 vl->values = values;
295 vl->values_len = values_num;
297 #undef BAIL_OUT
298 (*jvm_env)->DeleteLocalRef (jvm_env, o_number_array);
299 (*jvm_env)->DeleteLocalRef (jvm_env, o_list);
300 return (0);
301 } /* }}} int jtoc_values_array */
303 static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
304 jobject object_ptr)
305 {
306 jclass class_ptr;
307 int status;
308 jlong tmp_long;
309 const data_set_t *ds;
311 class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
312 if (class_ptr == NULL)
313 {
314 ERROR ("java plugin: jtoc_value_list: GetObjectClass failed.");
315 return (-1);
316 }
318 #define SET_STRING(buffer,method) do { \
319 status = jtoc_string (jvm_env, buffer, sizeof (buffer), \
320 class_ptr, object_ptr, method); \
321 if (status != 0) { \
322 ERROR ("java plugin: jtoc_value_list: jtoc_string (%s) failed.", \
323 method); \
324 return (-1); \
325 } } while (0)
327 SET_STRING(vl->type, "getType");
329 ds = plugin_get_ds (vl->type);
330 if (ds == NULL)
331 {
332 ERROR ("java plugin: jtoc_value_list: Data-set `%s' is not defined. "
333 "Please consult the types.db(5) manpage for mor information.",
334 vl->type);
335 return (-1);
336 }
338 SET_STRING(vl->host, "getHost");
339 SET_STRING(vl->plugin, "getPlugin");
340 SET_STRING(vl->plugin_instance, "getPluginInstance");
341 SET_STRING(vl->type_instance, "getTypeInstance");
343 #undef SET_STRING
345 status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getTime");
346 if (status != 0)
347 {
348 ERROR ("java plugin: jtoc_value_list: jtoc_string (getTime) failed.");
349 return (-1);
350 }
351 vl->time = (time_t) tmp_long;
353 status = jtoc_long (jvm_env, &tmp_long,
354 class_ptr, object_ptr, "getInterval");
355 if (status != 0)
356 {
357 ERROR ("java plugin: jtoc_value_list: jtoc_string (getInterval) failed.");
358 return (-1);
359 }
360 vl->interval = (int) tmp_long;
362 status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
363 if (status != 0)
364 {
365 ERROR ("java plugin: jtoc_value_list: jtoc_values_array failed.");
366 return (-1);
367 }
369 return (0);
370 } /* }}} int jtoc_value_list */
372 /*
373 * Functions accessible from Java
374 */
375 static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */
376 jobject this, jobject java_vl)
377 {
378 value_list_t vl = VALUE_LIST_INIT;
379 int status;
381 DEBUG ("cjni_api_dispatch_values: java_vl = %p;", (void *) java_vl);
383 status = jtoc_value_list (jvm_env, &vl, java_vl);
384 if (status != 0)
385 {
386 ERROR ("java plugin: cjni_api_dispatch_values: jtoc_value_list failed.");
387 return (-1);
388 }
390 plugin_dispatch_values (&vl);
392 sfree (vl.values);
394 return (0);
395 } /* }}} jint cjni_api_dispatch_values */
397 static JNINativeMethod jni_api_functions[] =
398 {
399 { "DispatchValues", "(Lorg/collectd/protocol/ValueList;)I", cjni_api_dispatch_values }
400 };
401 static size_t jni_api_functions_num = sizeof (jni_api_functions)
402 / sizeof (jni_api_functions[0]);
404 /*
405 * Functions
406 */
407 static int cjni_init_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
408 {
409 jmethodID constructor_id;
410 int status;
412 jp->class_ptr = (*jvm_env)->FindClass (jvm_env, jp->class_name);
413 if (jp->class_ptr == NULL)
414 {
415 ERROR ("cjni_init_one_plugin: FindClass (%s) failed.",
416 jp->class_name);
417 return (-1);
418 }
420 constructor_id = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
421 "<init>", "()V");
422 if (constructor_id == NULL)
423 {
424 ERROR ("cjni_init_one_plugin: Could not find the constructor for `%s'.",
425 jp->class_name);
426 return (-1);
427 }
429 jp->object_ptr = (*jvm_env)->NewObject (jvm_env, jp->class_ptr,
430 constructor_id);
431 if (jp->object_ptr == NULL)
432 {
433 ERROR ("cjni_init_one_plugin: Could create a new `%s' object.",
434 jp->class_name);
435 return (-1);
436 }
438 jp->method_init = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
439 "Init", "()I");
440 DEBUG ("jp->method_init = %p;", (void *) jp->method_init);
441 jp->method_read = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
442 "Read", "()I");
443 DEBUG ("jp->method_read = %p;", (void *) jp->method_read);
444 jp->method_shutdown = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr,
445 "Shutdown", "()I");
446 DEBUG ("jp->method_shutdown = %p;", (void *) jp->method_shutdown);
448 status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
449 jp->method_init);
450 if (status != 0)
451 {
452 ERROR ("cjni_init_one_plugin: Initializing `%s' object failed "
453 "with status %i.", jp->class_name, status);
454 return (-1);
455 }
456 jp->flags |= CJNI_FLAG_ENABLED;
458 return (0);
459 } /* }}} int cjni_init_one_plugin */
461 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
462 {
463 size_t j;
465 for (j = 0; j < java_plugins_num; j++)
466 cjni_init_one_plugin (jvm_env, &java_plugins[j]);
468 return (0);
469 } /* }}} int cjni_init_plugins */
471 static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
472 {
473 jclass api_class_ptr;
474 int status;
476 api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.java.CollectdAPI");
477 if (api_class_ptr == NULL)
478 {
479 ERROR ("cjni_init_native: Cannot find API class `org.collectd.java.CollectdAPI'.");
480 return (-1);
481 }
483 status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr,
484 jni_api_functions, (jint) jni_api_functions_num);
485 if (status != 0)
486 {
487 ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status);
488 return (-1);
489 }
491 return (0);
492 } /* }}} int cjni_init_native */
494 static int cjni_init (void) /* {{{ */
495 {
496 JNIEnv *jvm_env;
497 JavaVMInitArgs vm_args;
498 JavaVMOption vm_options[2];
500 int status;
502 if (jvm != NULL)
503 return (0);
505 jvm_env = NULL;
507 memset (&vm_args, 0, sizeof (vm_args));
508 vm_args.version = JNI_VERSION_1_2;
509 vm_args.options = vm_options;
510 vm_args.nOptions = sizeof (vm_options) / sizeof (vm_options[0]);
512 vm_args.options[0].optionString = "-verbose:jni";
513 vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java";
515 status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
516 if (status != 0)
517 {
518 ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.",
519 status);
520 return (-1);
521 }
522 assert (jvm != NULL);
523 assert (jvm_env != NULL);
525 /* Call RegisterNatives */
526 status = cjni_init_native (jvm_env);
527 if (status != 0)
528 {
529 ERROR ("cjni_init: cjni_init_native failed.");
530 return (-1);
531 }
533 cjni_init_plugins (jvm_env);
535 return (0);
536 } /* }}} int cjni_init */
538 static int cjni_read_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */
539 {
540 int status;
542 if ((jp == NULL)
543 || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
544 || (jp->method_read == NULL))
545 return (0);
547 DEBUG ("java plugin: Calling: %s.Read()", jp->class_name);
549 status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
550 jp->method_read);
551 if (status != 0)
552 {
553 ERROR ("cjni_read_one_plugin: Calling `Read' on an `%s' object failed "
554 "with status %i.", jp->class_name, status);
555 return (-1);
556 }
558 return (0);
559 } /* }}} int cjni_read_one_plugin */
561 static int cjni_read_plugins (JNIEnv *jvm_env) /* {{{ */
562 {
563 size_t j;
565 for (j = 0; j < java_plugins_num; j++)
566 cjni_read_one_plugin (jvm_env, &java_plugins[j]);
568 return (0);
569 } /* }}} int cjni_read_plugins */
571 static int cjni_read (void) /* {{{ */
572 {
573 JNIEnv *jvm_env;
574 JavaVMAttachArgs args;
575 int status;
577 if (jvm == NULL)
578 {
579 ERROR ("java plugin: cjni_read: jvm == NULL");
580 return (-1);
581 }
583 jvm_env = NULL;
584 memset (&args, 0, sizeof (args));
585 args.version = JNI_VERSION_1_2;
587 status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
588 if (status != 0)
589 {
590 ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
591 status);
592 return (-1);
593 }
595 cjni_read_plugins (jvm_env);
597 status = (*jvm)->DetachCurrentThread (jvm);
598 if (status != 0)
599 {
600 ERROR ("java plugin: cjni_read: DetachCurrentThread failed with status %i.",
601 status);
602 return (-1);
603 }
605 return (0);
606 } /* }}} int cjni_read */
608 static int cjni_shutdown_one_plugin (JNIEnv *jvm_env, /* {{{ */
609 java_plugin_t *jp)
610 {
611 int status;
613 if ((jp == NULL)
614 || ((jp->flags & CJNI_FLAG_ENABLED) == 0)
615 || (jp->method_shutdown == NULL))
616 return (0);
618 status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr,
619 jp->method_shutdown);
620 if (status != 0)
621 {
622 ERROR ("cjni_shutdown_one_plugin: Destroying an `%s' object failed "
623 "with status %i.", jp->class_name, status);
624 return (-1);
625 }
626 jp->flags &= ~CJNI_FLAG_ENABLED;
628 return (0);
629 } /* }}} int cjni_shutdown_one_plugin */
631 static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */
632 {
633 size_t j;
635 for (j = 0; j < java_plugins_num; j++)
636 cjni_shutdown_one_plugin (jvm_env, &java_plugins[j]);
638 return (0);
639 } /* }}} int cjni_shutdown_plugins */
641 static int cjni_shutdown (void) /* {{{ */
642 {
643 JNIEnv *jvm_env;
644 JavaVMAttachArgs args;
645 int status;
647 if (jvm == NULL)
648 return (0);
650 jvm_env = NULL;
651 memset (&args, 0, sizeof (args));
652 args.version = JNI_VERSION_1_2;
654 status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
655 if (status != 0)
656 {
657 ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.",
658 status);
659 return (-1);
660 }
662 cjni_shutdown_plugins (jvm_env);
664 (*jvm)->DestroyJavaVM (jvm);
665 jvm = NULL;
666 jvm_env = NULL;
668 return (0);
669 } /* }}} int cjni_shutdown */
671 void module_register (void)
672 {
673 plugin_register_init ("java", cjni_init);
674 plugin_register_read ("java", cjni_read);
675 plugin_register_shutdown ("java", cjni_shutdown);
676 } /* void module_register (void) */
678 /* vim: set sw=2 sts=2 et fdm=marker : */