X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fjava.c;h=2d310c6f4c2947f2df3cf311d8d7f8c496aaa03b;hb=fb9f56e5af1a63d94c80a2ea6f067aeb7787f397;hp=83fec35c556ec3ddbdaefe1afaf1fae3ef838d43;hpb=aede1532622a544e16fbb3fbd1a096230f047745;p=collectd.git diff --git a/src/java.c b/src/java.c index 83fec35c..2d310c6f 100644 --- a/src/java.c +++ b/src/java.c @@ -35,33 +35,90 @@ /* * Types */ -struct java_plugin_s /* {{{ */ +struct cjni_jvm_env_s /* {{{ */ { - char *class_name; - jclass class_ptr; - jobject object_ptr; + JNIEnv *jvm_env; + int reference_counter; +}; +typedef struct cjni_jvm_env_s cjni_jvm_env_t; +/* }}} */ + +struct java_plugin_class_s /* {{{ */ +{ + char *name; + jclass class; + jobject object; +}; +typedef struct java_plugin_class_s java_plugin_class_t; +/* }}} */ -#define CJNI_FLAG_ENABLED 0x0001 - int flags; +struct java_plugin_config_s /* {{{ */ +{ + char *name; + oconfig_item_t *ci; +}; +typedef struct java_plugin_config_s java_plugin_config_t; +/* }}} */ - jmethodID m_init; - jmethodID m_read; - jmethodID m_write; - jmethodID m_shutdown; +#define CB_TYPE_CONFIG 1 +#define CB_TYPE_INIT 2 +#define CB_TYPE_READ 3 +#define CB_TYPE_WRITE 4 +#define CB_TYPE_FLUSH 5 +#define CB_TYPE_SHUTDOWN 6 +#define CB_TYPE_LOG 7 +#define CB_TYPE_NOTIFICATION 8 +struct cjni_callback_info_s /* {{{ */ +{ + char *name; + int type; + jclass class; + jobject object; + jmethodID method; }; -typedef struct java_plugin_s java_plugin_t; +typedef struct cjni_callback_info_s cjni_callback_info_t; /* }}} */ /* * Global variables */ static JavaVM *jvm = NULL; +static pthread_key_t jvm_env_key; +/* Configuration options for the JVM. */ static char **jvm_argv = NULL; static size_t jvm_argc = 0; -static java_plugin_t *java_plugins = NULL; -static size_t java_plugins_num = 0; +/* List of class names to load */ +static java_plugin_class_t *java_classes_list = NULL; +static size_t java_classes_list_len; + +/* List of `config_item_t's for Java plugins */ +static java_plugin_config_t *java_plugin_configs = NULL; +static size_t java_plugin_configs_num = 0; + +/* List of config, init, and shutdown callbacks. */ +static cjni_callback_info_t *java_callbacks = NULL; +static size_t java_callbacks_num = 0; +static pthread_mutex_t java_callbacks_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Prototypes + * + * Mostly functions that are needed by the Java interface (``native'') + * functions. + */ +static void cjni_callback_info_destroy (void *arg); +static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, + jobject o_name, jobject o_callback, int type); +static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name, + jobject o_callback, int type); +static int cjni_read (user_data_t *user_data); +static int cjni_write (const data_set_t *ds, const value_list_t *vl, + user_data_t *ud); +static int cjni_flush (int timeout, const char *identifier, user_data_t *ud); +static void cjni_log (int severity, const char *message, user_data_t *ud); +static int cjni_notification (const notification_t *n, user_data_t *ud); /* * C to Java conversion functions @@ -99,9 +156,6 @@ static int ctoj_string (JNIEnv *jvm_env, /* {{{ */ /* Decrease reference counter on the java.lang.String object. */ (*jvm_env)->DeleteLocalRef (jvm_env, o_string); - DEBUG ("java plugin: ctoj_string: ->%s (%s);", - method_name, (string != NULL) ? string : ""); - return (0); } /* }}} int ctoj_string */ @@ -123,9 +177,6 @@ static int ctoj_int (JNIEnv *jvm_env, /* {{{ */ (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value); - DEBUG ("java plugin: ctoj_int: ->%s (%i);", - method_name, (int) value); - return (0); } /* }}} int ctoj_int */ @@ -147,9 +198,6 @@ static int ctoj_long (JNIEnv *jvm_env, /* {{{ */ (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value); - DEBUG ("java plugin: ctoj_long: ->%s (%"PRIi64");", - method_name, (int64_t) value); - return (0); } /* }}} int ctoj_long */ @@ -171,81 +219,78 @@ static int ctoj_double (JNIEnv *jvm_env, /* {{{ */ (*jvm_env)->CallVoidMethod (jvm_env, object_ptr, m_set, value); - DEBUG ("java plugin: ctoj_double: ->%s (%g);", - method_name, (double) value); - return (0); } /* }}} int ctoj_double */ -/* Convert a value_t to a java.lang.Number */ -static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */ - value_t value, int ds_type) +/* Convert a jlong to a java.lang.Number */ +static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */ { - if (ds_type == DS_TYPE_COUNTER) + jclass c_long; + jmethodID m_long_constructor; + + /* Look up the java.lang.Long class */ + c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long"); + if (c_long == NULL) { - jclass c_long; - jmethodID m_long_constructor; + ERROR ("java plugin: ctoj_jlong_to_number: Looking up the " + "java.lang.Long class failed."); + return (NULL); + } - jlong tmp_long; + m_long_constructor = (*jvm_env)->GetMethodID (jvm_env, + c_long, "", "(J)V"); + if (m_long_constructor == NULL) + { + ERROR ("java plugin: ctoj_jlong_to_number: Looking up the " + "`Long (long)' constructor failed."); + return (NULL); + } - /* Look up the java.lang.Long class */ - c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long"); - if (c_long == NULL) - { - ERROR ("java plugin: ctoj_value_to_number: Looking up the " - "java.lang.Long class failed."); - return (NULL); - } + return ((*jvm_env)->NewObject (jvm_env, + c_long, m_long_constructor, value)); +} /* }}} jobject ctoj_jlong_to_number */ - m_long_constructor = (*jvm_env)->GetMethodID (jvm_env, - c_long, "", "(J)V"); - if (m_long_constructor == NULL) - { - ERROR ("java plugin: ctoj_value_to_number: Looking up the " - "`Long (long)' constructor failed."); - return (NULL); - } +/* Convert a jdouble to a java.lang.Number */ +static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */ +{ + jclass c_double; + jmethodID m_double_constructor; - tmp_long = (jlong) value.counter; - return ((*jvm_env)->NewObject (jvm_env, - c_long, m_long_constructor, tmp_long)); - } - else if (ds_type == DS_TYPE_GAUGE) + /* Look up the java.lang.Long class */ + c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double"); + if (c_double == NULL) { - jclass c_double; - jmethodID m_double_constructor; - - jdouble tmp_double; + ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the " + "java.lang.Double class failed."); + return (NULL); + } - /* Look up the java.lang.Long class */ - c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double"); - if (c_double == NULL) - { - ERROR ("java plugin: ctoj_value_to_number: Looking up the " - "java.lang.Double class failed."); - return (NULL); - } + m_double_constructor = (*jvm_env)->GetMethodID (jvm_env, + c_double, "", "(D)V"); + if (m_double_constructor == NULL) + { + ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the " + "`Double (double)' constructor failed."); + return (NULL); + } - m_double_constructor = (*jvm_env)->GetMethodID (jvm_env, - c_double, "", "(D)V"); - if (m_double_constructor == NULL) - { - ERROR ("java plugin: ctoj_value_to_number: Looking up the " - "`Double (double)' constructor failed."); - return (NULL); - } + return ((*jvm_env)->NewObject (jvm_env, + c_double, m_double_constructor, value)); +} /* }}} jobject ctoj_jdouble_to_number */ - tmp_double = (jdouble) value.gauge; - return ((*jvm_env)->NewObject (jvm_env, - c_double, m_double_constructor, tmp_double)); - } +/* Convert a value_t to a java.lang.Number */ +static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */ + value_t value, int ds_type) +{ + if (ds_type == DS_TYPE_COUNTER) + return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter)); + else if (ds_type == DS_TYPE_GAUGE) + return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge)); else - { return (NULL); - } } /* }}} jobject ctoj_value_to_number */ -/* Convert a data_source_t to a org.collectd.protocol.DataSource */ +/* Convert a data_source_t to a org.collectd.api.DataSource */ static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */ const data_source_t *dsrc) { @@ -256,11 +301,11 @@ static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */ /* Look up the DataSource class */ c_datasource = (*jvm_env)->FindClass (jvm_env, - "org.collectd.protocol.DataSource"); + "org.collectd.api.DataSource"); if (c_datasource == NULL) { ERROR ("java plugin: ctoj_data_source: " - "FindClass (org.collectd.protocol.DataSource) failed."); + "FindClass (org.collectd.api.DataSource) failed."); return (NULL); } @@ -331,56 +376,280 @@ static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */ return (o_datasource); } /* }}} jobject ctoj_data_source */ -/* Convert a data_set_t to a java.util.List */ +/* Convert a oconfig_value_t to a org.collectd.api.OConfigValue */ +static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */ + oconfig_value_t ocvalue) +{ + jclass c_ocvalue; + jmethodID m_ocvalue_constructor; + jobject o_argument; + jobject o_ocvalue; + + m_ocvalue_constructor = NULL; + o_argument = NULL; + + c_ocvalue = (*jvm_env)->FindClass (jvm_env, + "org.collectd.api.OConfigValue"); + if (c_ocvalue == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "FindClass (org.collectd.api.OConfigValue) failed."); + return (NULL); + } + + if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) + { + jboolean tmp_boolean; + + tmp_boolean = (ocvalue.value.boolean == 0) ? JNI_FALSE : JNI_TRUE; + + m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue, + "", "(Z)V"); + if (m_ocvalue_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: Cannot find the " + "`OConfigValue (boolean)' constructor."); + return (NULL); + } + + return ((*jvm_env)->NewObject (jvm_env, + c_ocvalue, m_ocvalue_constructor, tmp_boolean)); + } /* if (ocvalue.type == OCONFIG_TYPE_BOOLEAN) */ + else if (ocvalue.type == OCONFIG_TYPE_STRING) + { + m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue, + "", "(Ljava/lang/String;)V"); + if (m_ocvalue_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: Cannot find the " + "`OConfigValue (String)' constructor."); + return (NULL); + } + + o_argument = (*jvm_env)->NewStringUTF (jvm_env, ocvalue.value.string); + if (o_argument == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "Creating a String object failed."); + return (NULL); + } + } + else if (ocvalue.type == OCONFIG_TYPE_NUMBER) + { + m_ocvalue_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocvalue, + "", "(Ljava/lang/Number;)V"); + if (m_ocvalue_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: Cannot find the " + "`OConfigValue (Number)' constructor."); + return (NULL); + } + + o_argument = ctoj_jdouble_to_number (jvm_env, + (jdouble) ocvalue.value.number); + if (o_argument == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "Creating a Number object failed."); + return (NULL); + } + } + else + { + return (NULL); + } + + assert (m_ocvalue_constructor != NULL); + assert (o_argument != NULL); + + o_ocvalue = (*jvm_env)->NewObject (jvm_env, + c_ocvalue, m_ocvalue_constructor, o_argument); + if (o_ocvalue == NULL) + { + ERROR ("java plugin: ctoj_oconfig_value: " + "Creating an OConfigValue object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_argument); + return (NULL); + } + + (*jvm_env)->DeleteLocalRef (jvm_env, o_argument); + return (o_ocvalue); +} /* }}} jobject ctoj_oconfig_value */ + +/* Convert a oconfig_item_t to a org.collectd.api.OConfigItem */ +static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */ + const oconfig_item_t *ci) +{ + jclass c_ocitem; + jmethodID m_ocitem_constructor; + jmethodID m_addvalue; + jmethodID m_addchild; + jobject o_key; + jobject o_ocitem; + int i; + + c_ocitem = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.OConfigItem"); + if (c_ocitem == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "FindClass (org.collectd.api.OConfigItem) failed."); + return (NULL); + } + + /* Get the required methods: m_ocitem_constructor, m_addvalue, and m_addchild + * {{{ */ + m_ocitem_constructor = (*jvm_env)->GetMethodID (jvm_env, c_ocitem, + "", "(Ljava/lang/String;)V"); + if (m_ocitem_constructor == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: Cannot find the " + "`OConfigItem (String)' constructor."); + return (NULL); + } + + m_addvalue = (*jvm_env)->GetMethodID (jvm_env, c_ocitem, + "addValue", "(Lorg/collectd/api/OConfigValue;)V"); + if (m_addvalue == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: Cannot find the " + "`addValue (OConfigValue)' method."); + return (NULL); + } + + m_addchild = (*jvm_env)->GetMethodID (jvm_env, c_ocitem, + "addChild", "(Lorg/collectd/api/OConfigItem;)V"); + if (m_addchild == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: Cannot find the " + "`addChild (OConfigItem)' method."); + return (NULL); + } + /* }}} */ + + /* Create a String object with the key. + * Needed for calling the constructor. */ + o_key = (*jvm_env)->NewStringUTF (jvm_env, ci->key); + if (o_key == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating String object failed."); + return (NULL); + } + + /* Create an OConfigItem object */ + o_ocitem = (*jvm_env)->NewObject (jvm_env, + c_ocitem, m_ocitem_constructor, o_key); + if (o_ocitem == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating an OConfigItem object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_key); + return (NULL); + } + + /* We don't need the String object any longer.. */ + (*jvm_env)->DeleteLocalRef (jvm_env, o_key); + + /* Call OConfigItem.addValue for each value */ + for (i = 0; i < ci->values_num; i++) /* {{{ */ + { + jobject o_value; + + o_value = ctoj_oconfig_value (jvm_env, ci->values[i]); + if (o_value == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating an OConfigValue object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem); + return (NULL); + } + + (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_value); + (*jvm_env)->DeleteLocalRef (jvm_env, o_value); + } /* }}} for (i = 0; i < ci->values_num; i++) */ + + /* Call OConfigItem.addChild for each child */ + for (i = 0; i < ci->children_num; i++) /* {{{ */ + { + jobject o_child; + + o_child = ctoj_oconfig_item (jvm_env, ci->children + i); + if (o_child == NULL) + { + ERROR ("java plugin: ctoj_oconfig_item: " + "Creating an OConfigItem object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem); + return (NULL); + } + + (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_child); + (*jvm_env)->DeleteLocalRef (jvm_env, o_child); + } /* }}} for (i = 0; i < ci->children_num; i++) */ + + return (o_ocitem); +} /* }}} jobject ctoj_oconfig_item */ + +/* Convert a data_set_t to a org.collectd.api.DataSet */ static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */ { - jclass c_arraylist; + jclass c_dataset; jmethodID m_constructor; jmethodID m_add; + jobject o_type; jobject o_dataset; int i; - /* Look up the java.util.ArrayList class */ - c_arraylist = (*jvm_env)->FindClass (jvm_env, "java.util.ArrayList"); - if (c_arraylist == NULL) + /* Look up the org.collectd.api.DataSet class */ + c_dataset = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.DataSet"); + if (c_dataset == NULL) { ERROR ("java plugin: ctoj_data_set: Looking up the " - "java.util.ArrayList class failed."); + "org.collectd.api.DataSet class failed."); return (NULL); } - /* Search for the `ArrayList (int capacity)' constructor. */ + /* Search for the `DataSet (String type)' constructor. */ m_constructor = (*jvm_env)->GetMethodID (jvm_env, - c_arraylist, "", "()V"); + c_dataset, "", "(Ljava.lang.String;)V"); if (m_constructor == NULL) { ERROR ("java plugin: ctoj_data_set: Looking up the " - "`ArrayList (void)' constructor failed."); + "`DataSet (String)' constructor failed."); return (NULL); } - /* Search for the `boolean add (Object element)' method. */ + /* Search for the `void addDataSource (DataSource)' method. */ m_add = (*jvm_env)->GetMethodID (jvm_env, - c_arraylist, "add", "(Ljava/lang/Object;)Z"); + c_dataset, "addDataSource", "(Lorg.collectd.api.DataSource;)V"); if (m_add == NULL) { ERROR ("java plugin: ctoj_data_set: Looking up the " - "`add (Object)' method failed."); + "`addDataSource (DataSource)' method failed."); + return (NULL); + } + + o_type = (*jvm_env)->NewStringUTF (jvm_env, ds->type); + if (o_type == NULL) + { + ERROR ("java plugin: ctoj_data_set: Creating a String object failed."); return (NULL); } - o_dataset = (*jvm_env)->NewObject (jvm_env, c_arraylist, m_constructor); + o_dataset = (*jvm_env)->NewObject (jvm_env, + c_dataset, m_constructor, o_type); if (o_dataset == NULL) { - ERROR ("java plugin: ctoj_data_set: " - "Creating an ArrayList object failed."); + ERROR ("java plugin: ctoj_data_set: Creating a DataSet object failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_type); return (NULL); } + /* Decrease reference counter on the java.lang.String object. */ + (*jvm_env)->DeleteLocalRef (jvm_env, o_type); + for (i = 0; i < ds->ds_num; i++) { jobject o_datasource; - jboolean status; o_datasource = ctoj_data_source (jvm_env, ds->ds + i); if (o_datasource == NULL) @@ -391,15 +660,7 @@ static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */ return (NULL); } - status = (*jvm_env)->CallBooleanMethod (jvm_env, - o_dataset, m_add, o_datasource); - if (!status) - { - ERROR ("java plugin: ctoj_data_set: ArrayList.add returned FALSE."); - (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); - (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset); - return (NULL); - } + (*jvm_env)->CallVoidMethod (jvm_env, o_dataset, m_add, o_datasource); (*jvm_env)->DeleteLocalRef (jvm_env, o_datasource); } /* for (i = 0; i < ds->ds_num; i++) */ @@ -441,20 +702,20 @@ static int ctoj_value_list_add_value (JNIEnv *jvm_env, /* {{{ */ static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */ jclass c_valuelist, jobject o_valuelist, const data_set_t *ds) { - jmethodID m_setdatasource; + jmethodID m_setdataset; jobject o_dataset; /* Look for the `void setDataSource (List ds)' method. */ - m_setdatasource = (*jvm_env)->GetMethodID (jvm_env, c_valuelist, - "setDataSource", "(Ljava/util/List;)V"); - if (m_setdatasource == NULL) + m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist, + "setDataSet", "(Lorg.collectd.api.DataSet;)V"); + if (m_setdataset == NULL) { ERROR ("java plugin: ctoj_value_list_add_data_set: " - "Cannot find the `void setDataSource (List ds)' method."); + "Cannot find the `void setDataSet (DataSet)' method."); return (-1); } - /* Create a List object. */ + /* Create a DataSet object. */ o_dataset = ctoj_data_set (jvm_env, ds); if (o_dataset == NULL) { @@ -465,7 +726,7 @@ static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */ /* Actually call the method. */ (*jvm_env)->CallVoidMethod (jvm_env, - o_valuelist, m_setdatasource, o_dataset); + o_valuelist, m_setdataset, o_dataset); /* Decrease reference counter on the List object. */ (*jvm_env)->DeleteLocalRef (jvm_env, o_dataset); @@ -473,6 +734,7 @@ static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */ return (0); } /* }}} int ctoj_value_list_add_data_set */ +/* Convert a value_list_t (and data_set_t) to a org.collectd.api.ValueList */ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */ const data_set_t *ds, const value_list_t *vl) { @@ -485,11 +747,11 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */ /* First, create a new ValueList instance.. * Look up the class.. */ c_valuelist = (*jvm_env)->FindClass (jvm_env, - "org.collectd.protocol.ValueList"); + "org.collectd.api.ValueList"); if (c_valuelist == NULL) { ERROR ("java plugin: ctoj_value_list: " - "FindClass (org.collectd.protocol.ValueList) failed."); + "FindClass (org.collectd.api.ValueList) failed."); return (NULL); } @@ -576,11 +838,95 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */ } return (o_valuelist); -} /* }}} int ctoj_value_list */ +} /* }}} jobject ctoj_value_list */ + +/* Convert a notification_t to a org.collectd.api.Notification */ +static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */ + const notification_t *n) +{ + jclass c_notification; + jmethodID m_constructor; + jobject o_notification; + int status; + + /* First, create a new Notification instance.. + * Look up the class.. */ + c_notification = (*jvm_env)->FindClass (jvm_env, + "org.collectd.api.Notification"); + if (c_notification == NULL) + { + ERROR ("java plugin: ctoj_notification: " + "FindClass (org.collectd.api.Notification) failed."); + return (NULL); + } + + /* Lookup the `Notification ()' constructor. */ + m_constructor = (*jvm_env)->GetMethodID (jvm_env, c_notification, + "", "()V"); + if (m_constructor == NULL) + { + ERROR ("java plugin: ctoj_notification: Cannot find the " + "`Notification ()' constructor."); + return (NULL); + } + + /* Create a new instance. */ + o_notification = (*jvm_env)->NewObject (jvm_env, c_notification, + m_constructor); + if (o_notification == NULL) + { + ERROR ("java plugin: ctoj_notification: Creating a new Notification " + "instance failed."); + return (NULL); + } + + /* Set the strings.. */ +#define SET_STRING(str,method_name) do { \ + status = ctoj_string (jvm_env, str, \ + c_notification, o_notification, method_name); \ + if (status != 0) { \ + ERROR ("java plugin: ctoj_notification: jtoc_string (%s) failed.", \ + method_name); \ + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); \ + return (NULL); \ + } } while (0) + + SET_STRING (n->host, "setHost"); + SET_STRING (n->plugin, "setPlugin"); + SET_STRING (n->plugin_instance, "setPluginInstance"); + SET_STRING (n->type, "setType"); + SET_STRING (n->type_instance, "setTypeInstance"); + SET_STRING (n->message, "setMessage"); + +#undef SET_STRING + + /* Set the `time' member. Java stores time in milliseconds. */ + status = ctoj_long (jvm_env, ((jlong) n->time) * ((jlong) 1000), + c_notification, o_notification, "setTime"); + if (status != 0) + { + ERROR ("java plugin: ctoj_notification: ctoj_long (setTime) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + return (NULL); + } + + /* Set the `interval' member.. */ + status = ctoj_int (jvm_env, (jint) n->severity, + c_notification, o_notification, "setSeverity"); + if (status != 0) + { + ERROR ("java plugin: ctoj_notification: ctoj_int (setSeverity) failed."); + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + return (NULL); + } + + return (o_notification); +} /* }}} jobject ctoj_notification */ /* * Java to C conversion functions */ +/* Call a `String ()' method. */ static int jtoc_string (JNIEnv *jvm_env, /* {{{ */ char *buffer, size_t buffer_size, jclass class_ptr, jobject object_ptr, const char *method_name) @@ -614,8 +960,6 @@ static int jtoc_string (JNIEnv *jvm_env, /* {{{ */ return (-1); } - DEBUG ("java plugin: jtoc_string: ->%s() = %s", method_name, c_str); - sstrncpy (buffer, c_str, buffer_size); (*jvm_env)->ReleaseStringUTFChars (jvm_env, string_obj, c_str); @@ -624,6 +968,7 @@ static int jtoc_string (JNIEnv *jvm_env, /* {{{ */ return (0); } /* }}} int jtoc_string */ +/* Call a `long ()' method. */ static int jtoc_long (JNIEnv *jvm_env, /* {{{ */ jlong *ret_value, jclass class_ptr, jobject object_ptr, const char *method_name) @@ -641,12 +986,10 @@ static int jtoc_long (JNIEnv *jvm_env, /* {{{ */ *ret_value = (*jvm_env)->CallLongMethod (jvm_env, object_ptr, method_id); - DEBUG ("java plugin: jtoc_long: ->%s() = %li", - method_name, (long int) *ret_value); - return (0); } /* }}} int jtoc_long */ +/* Call a `double ()' method. */ static int jtoc_double (JNIEnv *jvm_env, /* {{{ */ jdouble *ret_value, jclass class_ptr, jobject object_ptr, const char *method_name) @@ -664,9 +1007,6 @@ static int jtoc_double (JNIEnv *jvm_env, /* {{{ */ *ret_value = (*jvm_env)->CallDoubleMethod (jvm_env, object_ptr, method_id); - DEBUG ("java plugin: jtoc_double: ->%s() = %g", - method_name, (double) *ret_value); - return (0); } /* }}} int jtoc_double */ @@ -710,6 +1050,8 @@ static int jtoc_value (JNIEnv *jvm_env, /* {{{ */ return (0); } /* }}} int jtoc_value */ +/* Read a List, convert it to `value_t' and add it to the given + * `value_list_t'. */ static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */ const data_set_t *ds, value_list_t *vl, jclass class_ptr, jobject object_ptr) @@ -774,7 +1116,7 @@ static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */ BAIL_OUT (-1); } - values = calloc (values_num, sizeof (value_t)); + values = (value_t *) calloc (values_num, sizeof (value_t)); if (values == NULL) { ERROR ("java plugin: jtoc_values_array: calloc failed."); @@ -813,7 +1155,7 @@ static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */ return (0); } /* }}} int jtoc_values_array */ -/* Convert a org.collectd.protocol.ValueList to a value_list_t. */ +/* Convert a org.collectd.api.ValueList to a value_list_t. */ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */ jobject object_ptr) { @@ -862,7 +1204,8 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */ ERROR ("java plugin: jtoc_value_list: jtoc_long (getTime) failed."); return (-1); } - vl->time = (time_t) tmp_long; + /* Java measures time in milliseconds. */ + vl->time = (time_t) (tmp_long / ((jlong) 1000)); status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getInterval"); @@ -901,93 +1244,676 @@ static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */ return (-1); } - plugin_dispatch_values (&vl); + status = plugin_dispatch_values (&vl); sfree (vl.values); - return (0); + return (status); } /* }}} jint cjni_api_dispatch_values */ -static JNINativeMethod jni_api_functions[] = -{ - { "DispatchValues", "(Lorg/collectd/protocol/ValueList;)I", cjni_api_dispatch_values } -}; -static size_t jni_api_functions_num = sizeof (jni_api_functions) - / sizeof (jni_api_functions[0]); - -/* - * Functions - */ -static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */ +static jobject JNICALL cjni_api_get_ds (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_string_type) { - char **tmp; + const char *ds_name; + const data_set_t *ds; + jobject o_dataset; - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + ds_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_string_type, 0); + if (ds_name == NULL) { - WARNING ("java plugin: `JVMArg' needs exactly one string argument."); - return (-1); + ERROR ("java plugin: cjni_api_get_ds: GetStringUTFChars failed."); + return (NULL); } - tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1)); - if (tmp == NULL) - { - ERROR ("java plugin: realloc failed."); - return (-1); - } - jvm_argv = tmp; + ds = plugin_get_ds (ds_name); + DEBUG ("java plugin: cjni_api_get_ds: " + "plugin_get_ds (%s) = %p;", ds_name, (void *) ds); - jvm_argv[jvm_argc] = strdup (ci->values[0].value.string); - if (jvm_argv[jvm_argc] == NULL) - { - ERROR ("java plugin: strdup failed."); - return (-1); - } - jvm_argc++; + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_string_type, ds_name); - return (0); -} /* }}} int cjni_config_add_jvm_arg */ + if (ds == NULL) + return (NULL); -static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */ + o_dataset = ctoj_data_set (jvm_env, ds); + return (o_dataset); +} /* }}} jint cjni_api_get_ds */ + +static jint JNICALL cjni_api_register_config (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_config) { - java_plugin_t *jp; + return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_CONFIG)); +} /* }}} jint cjni_api_register_config */ - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("java plugin: `LoadPlugin' needs exactly one string argument."); - return (-1); +static jint JNICALL cjni_api_register_init (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_config) +{ + return (cjni_callback_register (jvm_env, o_name, o_config, CB_TYPE_INIT)); +} /* }}} jint cjni_api_register_init */ + +static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_read) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_read, CB_TYPE_READ); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new read callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_complex_read (cbi->name, cjni_read, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_read); + + return (0); +} /* }}} jint cjni_api_register_read */ + +static jint JNICALL cjni_api_register_write (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_write) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_write, CB_TYPE_WRITE); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new write callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_write (cbi->name, cjni_write, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_write); + + return (0); +} /* }}} jint cjni_api_register_write */ + +static jint JNICALL cjni_api_register_flush (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_flush) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_flush, CB_TYPE_FLUSH); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new flush callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_flush (cbi->name, cjni_flush, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_flush); + + return (0); +} /* }}} jint cjni_api_register_flush */ + +static jint JNICALL cjni_api_register_shutdown (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_shutdown) +{ + return (cjni_callback_register (jvm_env, o_name, o_shutdown, + CB_TYPE_SHUTDOWN)); +} /* }}} jint cjni_api_register_shutdown */ + +static jint JNICALL cjni_api_register_log (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_log) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_log, CB_TYPE_LOG); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new log callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_log (cbi->name, cjni_log, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_log); + + return (0); +} /* }}} jint cjni_api_register_log */ + +static jint JNICALL cjni_api_register_notification (JNIEnv *jvm_env, /* {{{ */ + jobject this, jobject o_name, jobject o_notification) +{ + user_data_t ud; + cjni_callback_info_t *cbi; + + cbi = cjni_callback_info_create (jvm_env, o_name, o_notification, + CB_TYPE_NOTIFICATION); + if (cbi == NULL) + return (-1); + + DEBUG ("java plugin: Registering new notification callback: %s", cbi->name); + + memset (&ud, 0, sizeof (ud)); + ud.data = (void *) cbi; + ud.free_func = cjni_callback_info_destroy; + + plugin_register_notification (cbi->name, cjni_notification, &ud); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + + return (0); +} /* }}} jint cjni_api_register_notification */ + +static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */ + jobject this, jint severity, jobject o_message) +{ + const char *c_str; + + c_str = (*jvm_env)->GetStringUTFChars (jvm_env, o_message, 0); + if (c_str == NULL) + { + ERROR ("java plugin: cjni_api_log: GetStringUTFChars failed."); + return; + } + + if (severity < LOG_ERR) + severity = LOG_ERR; + if (severity > LOG_DEBUG) + severity = LOG_DEBUG; + + plugin_log (severity, "%s", c_str); + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_message, c_str); +} /* }}} void cjni_api_log */ + +/* List of ``native'' functions, i. e. C-functions that can be called from + * Java. */ +static JNINativeMethod jni_api_functions[] = /* {{{ */ +{ + { "dispatchValues", + "(Lorg/collectd/api/ValueList;)I", + cjni_api_dispatch_values }, + + { "getDS", + "(Ljava/lang/String;)Lorg/collectd/api/DataSet;", + cjni_api_get_ds }, + + { "registerConfig", + "(Ljava/lang/String;Lorg/collectd/api/CollectdConfigInterface;)I", + cjni_api_register_config }, + + { "registerInit", + "(Ljava/lang/String;Lorg/collectd/api/CollectdInitInterface;)I", + cjni_api_register_init }, + + { "registerRead", + "(Ljava/lang/String;Lorg/collectd/api/CollectdReadInterface;)I", + cjni_api_register_read }, + + { "registerWrite", + "(Ljava/lang/String;Lorg/collectd/api/CollectdWriteInterface;)I", + cjni_api_register_write }, + + { "registerFlush", + "(Ljava/lang/String;Lorg/collectd/api/CollectdFlushInterface;)I", + cjni_api_register_flush }, + + { "registerShutdown", + "(Ljava/lang/String;Lorg/collectd/api/CollectdShutdownInterface;)I", + cjni_api_register_shutdown }, + + { "registerLog", + "(Ljava/lang/String;Lorg/collectd/api/CollectdLogInterface;)I", + cjni_api_register_log }, + + { "registerNotification", + "(Ljava/lang/String;Lorg/collectd/api/CollectdNotificationInterface;)I", + cjni_api_register_notification }, + + { "log", + "(ILjava/lang/String;)V", + cjni_api_log }, +}; +static size_t jni_api_functions_num = sizeof (jni_api_functions) + / sizeof (jni_api_functions[0]); +/* }}} */ + +/* + * Functions + */ +/* Allocate a `cjni_callback_info_t' given the type and objects necessary for + * all registration functions. */ +static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{ */ + jobject o_name, jobject o_callback, int type) +{ + const char *c_name; + cjni_callback_info_t *cbi; + const char *method_name; + const char *method_signature; + + switch (type) + { + case CB_TYPE_CONFIG: + method_name = "config"; + method_signature = "(Lorg/collectd/api/OConfigItem;)I"; + break; + + case CB_TYPE_INIT: + method_name = "init"; + method_signature = "()I"; + break; + + case CB_TYPE_READ: + method_name = "read"; + method_signature = "()I"; + break; + + case CB_TYPE_WRITE: + method_name = "write"; + method_signature = "(Lorg/collectd/api/ValueList;)I"; + break; + + case CB_TYPE_FLUSH: + method_name = "flush"; + method_signature = "(ILjava/lang/String;)I"; + break; + + case CB_TYPE_SHUTDOWN: + method_name = "shutdown"; + method_signature = "()I"; + break; + + case CB_TYPE_LOG: + method_name = "log"; + method_signature = "(ILjava/lang/String;)V"; + break; + + case CB_TYPE_NOTIFICATION: + method_name = "notification"; + method_signature = "(LLorg/collectd/api/Notification;)I"; + break; + + default: + ERROR ("java plugin: cjni_callback_info_create: Unknown type: %#x", + type); + return (NULL); + } + + c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0); + if (c_name == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: " + "GetStringUTFChars failed."); + return (NULL); + } + + cbi = (cjni_callback_info_t *) malloc (sizeof (*cbi)); + if (cbi == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: malloc failed."); + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (NULL); + } + memset (cbi, 0, sizeof (*cbi)); + cbi->type = type; + + cbi->name = strdup (c_name); + if (cbi->name == NULL) + { + pthread_mutex_unlock (&java_callbacks_lock); + ERROR ("java plugin: cjni_callback_info_create: strdup failed."); + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + return (NULL); + } + + (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name); + + cbi->class = (*jvm_env)->GetObjectClass (jvm_env, o_callback); + if (cbi->class == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed."); + free (cbi); + return (NULL); + } + + cbi->object = o_callback; + + cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class, + method_name, method_signature); + if (cbi->method == NULL) + { + ERROR ("java plugin: cjni_callback_info_create: " + "Cannot find the `%s' method with signature `%s'.", + method_name, method_signature); + free (cbi); + return (NULL); + } + + (*jvm_env)->NewGlobalRef (jvm_env, o_callback); + + return (cbi); +} /* }}} cjni_callback_info_t cjni_callback_info_create */ + +/* Allocate a `cjni_callback_info_t' via `cjni_callback_info_create' and add it + * to the global `java_callbacks' variable. This is used for `config', `init', + * and `shutdown' callbacks. */ +static int cjni_callback_register (JNIEnv *jvm_env, /* {{{ */ + jobject o_name, jobject o_callback, int type) +{ + cjni_callback_info_t *cbi; + cjni_callback_info_t *tmp; +#if COLLECT_DEBUG + const char *type_str; +#endif + + cbi = cjni_callback_info_create (jvm_env, o_name, o_callback, type); + if (cbi == NULL) + return (-1); + +#if COLLECT_DEBUG + switch (type) + { + case CB_TYPE_CONFIG: + type_str = "config"; + break; + + case CB_TYPE_INIT: + type_str = "init"; + break; + + case CB_TYPE_SHUTDOWN: + type_str = "shutdown"; + break; + + default: + type_str = ""; } + DEBUG ("java plugin: Registering new %s callback: %s", + type_str, cbi->name); +#endif + + pthread_mutex_lock (&java_callbacks_lock); + + tmp = (cjni_callback_info_t *) realloc (java_callbacks, + (java_callbacks_num + 1) * sizeof (*java_callbacks)); + if (tmp == NULL) + { + pthread_mutex_unlock (&java_callbacks_lock); + ERROR ("java plugin: cjni_callback_register: realloc failed."); + + (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object); + free (cbi); + + return (-1); + } + java_callbacks = tmp; + java_callbacks[java_callbacks_num] = *cbi; + java_callbacks_num++; + + pthread_mutex_unlock (&java_callbacks_lock); + + free (cbi); + return (0); +} /* }}} int cjni_callback_register */ + +/* Increase the reference counter to the JVM for this thread. If it was zero, + * attach the JVM first. */ +static JNIEnv *cjni_thread_attach (void) /* {{{ */ +{ + cjni_jvm_env_t *cjni_env; + JNIEnv *jvm_env; + + cjni_env = pthread_getspecific (jvm_env_key); + if (cjni_env == NULL) + { + /* This pointer is free'd in `cjni_jvm_env_destroy'. */ + cjni_env = (cjni_jvm_env_t *) malloc (sizeof (*cjni_env)); + if (cjni_env == NULL) + { + ERROR ("java plugin: cjni_thread_attach: malloc failed."); + return (NULL); + } + memset (cjni_env, 0, sizeof (*cjni_env)); + cjni_env->reference_counter = 0; + cjni_env->jvm_env = NULL; - jp = (java_plugin_t *) realloc (java_plugins, - sizeof (*java_plugins) * (java_plugins_num + 1)); - if (jp == NULL) + pthread_setspecific (jvm_env_key, cjni_env); + } + + if (cjni_env->reference_counter > 0) + { + cjni_env->reference_counter++; + jvm_env = cjni_env->jvm_env; + } + else + { + int status; + JavaVMAttachArgs args; + + assert (cjni_env->jvm_env == NULL); + + memset (&args, 0, sizeof (args)); + args.version = JNI_VERSION_1_2; + + status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, (void *) &args); + if (status != 0) + { + ERROR ("java plugin: cjni_thread_attach: AttachCurrentThread failed " + "with status %i.", status); + return (NULL); + } + + cjni_env->reference_counter = 1; + cjni_env->jvm_env = jvm_env; + } + + DEBUG ("java plugin: cjni_thread_attach: cjni_env->reference_counter = %i", + cjni_env->reference_counter); + assert (jvm_env != NULL); + return (jvm_env); +} /* }}} JNIEnv *cjni_thread_attach */ + +/* Decrease the reference counter of this thread. If it reaches zero, detach + * from the JVM. */ +static int cjni_thread_detach (void) /* {{{ */ +{ + cjni_jvm_env_t *cjni_env; + int status; + + cjni_env = pthread_getspecific (jvm_env_key); + if (cjni_env == NULL) + { + ERROR ("java plugin: cjni_thread_detach: pthread_getspecific failed."); + return (-1); + } + + assert (cjni_env->reference_counter > 0); + assert (cjni_env->jvm_env != NULL); + + cjni_env->reference_counter--; + DEBUG ("java plugin: cjni_thread_detach: cjni_env->reference_counter = %i", + cjni_env->reference_counter); + + if (cjni_env->reference_counter > 0) + return (0); + + status = (*jvm)->DetachCurrentThread (jvm); + if (status != 0) + { + ERROR ("java plugin: cjni_thread_detach: DetachCurrentThread failed " + "with status %i.", status); + } + + cjni_env->reference_counter = 0; + cjni_env->jvm_env = NULL; + + return (0); +} /* }}} JNIEnv *cjni_thread_attach */ + +/* Callback for `pthread_key_create'. It frees the data contained in + * `jvm_env_key' and prints a warning if the reference counter is not zero. */ +static void cjni_jvm_env_destroy (void *args) /* {{{ */ +{ + cjni_jvm_env_t *cjni_env; + + if (args == NULL) + return; + + cjni_env = (cjni_jvm_env_t *) args; + + if (cjni_env->reference_counter > 0) + { + ERROR ("java plugin: cjni_jvm_env_destroy: " + "cjni_env->reference_counter = %i;", cjni_env->reference_counter); + } + + if (cjni_env->jvm_env != NULL) + { + ERROR ("java plugin: cjni_jvm_env_destroy: cjni_env->jvm_env = %p;", + (void *) cjni_env->jvm_env); + } + + /* The pointer is allocated in `cjni_thread_attach' */ + free (cjni_env); +} /* }}} void cjni_jvm_env_destroy */ + +/* Boring configuration functions.. {{{ */ +static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */ +{ + char **tmp; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("java plugin: `JVMArg' needs exactly one string argument."); + return (-1); + } + + tmp = (char **) realloc (jvm_argv, sizeof (char *) * (jvm_argc + 1)); + if (tmp == NULL) { ERROR ("java plugin: realloc failed."); return (-1); } - java_plugins = jp; - jp = java_plugins + java_plugins_num; + jvm_argv = tmp; - memset (jp, 0, sizeof (*jp)); - jp->class_name = strdup (ci->values[0].value.string); - if (jp->class_name == NULL) + jvm_argv[jvm_argc] = strdup (ci->values[0].value.string); + if (jvm_argv[jvm_argc] == NULL) { ERROR ("java plugin: strdup failed."); return (-1); } + jvm_argc++; + + return (0); +} /* }}} int cjni_config_add_jvm_arg */ + +static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */ +{ + java_plugin_class_t *tmp; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("java plugin: `LoadPlugin' needs exactly one string argument."); + return (-1); + } + + tmp = (java_plugin_class_t *) realloc (java_classes_list, + (java_classes_list_len + 1) * sizeof (*java_classes_list)); + if (tmp == NULL) + { + ERROR ("java plugin: realloc failed."); + return (-1); + } + java_classes_list = tmp; + tmp = java_classes_list + java_classes_list_len; - jp->class_ptr = NULL; - jp->object_ptr = NULL; - jp->flags = 0; - jp->m_init = NULL; - jp->m_read = NULL; - jp->m_write = NULL; - jp->m_shutdown = NULL; + memset (tmp, 0, sizeof (*tmp)); + tmp->name = strdup (ci->values[0].value.string); + if (tmp->name == NULL) + { + ERROR ("java plugin: strdup failed."); + return (-1); + } + tmp->class = NULL; + tmp->object = NULL; - java_plugins_num++; + java_classes_list_len++; return (0); } /* }}} int cjni_config_load_plugin */ +static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */ +{ + java_plugin_config_t *tmp; + char *name; + size_t i; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("java plugin: `Plugin' blocks " + "need exactly one string argument."); + return (-1); + } + + name = strdup (ci->values[0].value.string); + if (name == NULL) + { + ERROR ("java plugin: cjni_config_plugin_block: strdup faiiled."); + return (-1); + } + + for (i = 0; i < java_plugin_configs_num; i++) + { + if (strcmp (java_plugin_configs[i].name, name) == 0) + { + WARNING ("java plugin: There is more than one block. " + "This is currently not supported - " + "only the first block will be used!", + name); + free (name); + return (0); + } + } + + tmp = (java_plugin_config_t *) realloc (java_plugin_configs, + (java_plugin_configs_num + 1) * sizeof (*java_plugin_configs)); + if (tmp == NULL) + { + ERROR ("java plugin: cjni_config_plugin_block: realloc failed."); + free (name); + return (-1); + } + java_plugin_configs = tmp; + tmp = java_plugin_configs + java_plugin_configs_num; + + tmp->name = name; + tmp->ci = oconfig_clone (ci); + if (tmp->ci == NULL) + { + ERROR ("java plugin: cjni_config_plugin_block: " + "oconfig_clone failed for `%s'.", + name); + free (name); + return (-1); + } + + DEBUG ("java plugin: cjni_config_plugin_block: " + "Successfully copied config for `%s'.", + name); + + java_plugin_configs_num++; + return (0); +} /* }}} int cjni_config_plugin_block */ + static int cjni_config (oconfig_item_t *ci) /* {{{ */ { int success; @@ -1018,6 +1944,14 @@ static int cjni_config (oconfig_item_t *ci) /* {{{ */ else errors++; } + else if (strcasecmp ("Plugin", child->key) == 0) + { + status = cjni_config_plugin_block (child); + if (status == 0) + success++; + else + errors++; + } else { WARNING ("java plugin: Option `%s' not allowed here.", child->key); @@ -1025,6 +1959,11 @@ static int cjni_config (oconfig_item_t *ci) /* {{{ */ } } + DEBUG ("java plugin: jvm_argc = %zu;", jvm_argc); + DEBUG ("java plugin: java_classes_list_len = %zu;", java_classes_list_len); + DEBUG ("java plugin: java_plugin_configs_num = %zu;", + java_plugin_configs_num); + if ((success == 0) && (errors > 0)) { ERROR ("java plugin: All statements failed."); @@ -1033,202 +1972,232 @@ static int cjni_config (oconfig_item_t *ci) /* {{{ */ return (0); } /* }}} int cjni_config */ +/* }}} */ -static int cjni_init_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */ +/* Free the data contained in the `user_data_t' pointer passed to `cjni_read' + * and `cjni_write'. In particular, delete the global reference to the Java + * object. */ +static void cjni_callback_info_destroy (void *arg) /* {{{ */ { - jmethodID constructor_id; - int status; + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + + DEBUG ("java plugin: cjni_callback_info_destroy (arg = %p);", arg); + + cbi = (cjni_callback_info_t *) arg; - jp->class_ptr = (*jvm_env)->FindClass (jvm_env, jp->class_name); - if (jp->class_ptr == NULL) + /* This condition can occurr when shutting down. */ + if (jvm == NULL) { - ERROR ("cjni_init_one_plugin: FindClass (%s) failed.", - jp->class_name); - return (-1); + sfree (cbi); + return; } - constructor_id = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr, - "", "()V"); - if (constructor_id == NULL) + if (arg == NULL) + return; + + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) { - ERROR ("cjni_init_one_plugin: Could not find the constructor for `%s'.", - jp->class_name); - return (-1); + ERROR ("java plugin: cjni_callback_info_destroy: cjni_thread_attach failed."); + return; } - jp->object_ptr = (*jvm_env)->NewObject (jvm_env, jp->class_ptr, - constructor_id); - if (jp->object_ptr == NULL) + (*jvm_env)->DeleteGlobalRef (jvm_env, cbi->object); + + cbi->method = NULL; + cbi->object = NULL; + cbi->class = NULL; + free (cbi); + + cjni_thread_detach (); +} /* }}} void cjni_callback_info_destroy */ + +/* Call the CB_TYPE_READ callback pointed to by the `user_data_t' pointer. */ +static int cjni_read (user_data_t *ud) /* {{{ */ +{ + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + int status; + int ret_status; + + if (jvm == NULL) { - ERROR ("cjni_init_one_plugin: Could create a new `%s' object.", - jp->class_name); + ERROR ("java plugin: cjni_read: jvm == NULL"); return (-1); } - jp->m_init = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr, - "Init", "()I"); - DEBUG ("java plugin: cjni_init_one_plugin: " - "jp->class_name = %s; jp->m_init = %p;", - jp->class_name, (void *) jp->m_init); + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_read: Invalid user data."); + return (-1); + } - jp->m_read = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr, - "Read", "()I"); - DEBUG ("java plugin: cjni_init_one_plugin: " - "jp->class_name = %s; jp->m_read = %p;", - jp->class_name, (void *) jp->m_read); + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); - jp->m_write = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr, - "Write", "(Lorg/collectd/protocol/ValueList;)I"); - DEBUG ("java plugin: cjni_init_one_plugin: " - "jp->class_name = %s; jp->m_write = %p;", - jp->class_name, (void *) jp->m_write); + cbi = (cjni_callback_info_t *) ud->data; - jp->m_shutdown = (*jvm_env)->GetMethodID (jvm_env, jp->class_ptr, - "Shutdown", "()I"); - DEBUG ("java plugin: cjni_init_one_plugin: " - "jp->class_name = %s; jp->m_shutdown = %p;", - jp->class_name, (void *) jp->m_shutdown); + ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object, + cbi->method); - if (jp->m_init != NULL) + status = cjni_thread_detach (); + if (status != 0) { - status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr, - jp->m_init); - if (status != 0) - { - ERROR ("cjni_init_one_plugin: Initializing `%s' object failed " - "with status %i.", jp->class_name, status); - return (-1); - } + ERROR ("java plugin: cjni_read: cjni_thread_detach failed."); + return (-1); } - jp->flags |= CJNI_FLAG_ENABLED; - return (0); -} /* }}} int cjni_init_one_plugin */ + return (ret_status); +} /* }}} int cjni_read */ -static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */ +/* Call the CB_TYPE_WRITE callback pointed to by the `user_data_t' pointer. */ +static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ + user_data_t *ud) { - size_t j; + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject vl_java; + int status; + int ret_status; - for (j = 0; j < java_plugins_num; j++) - cjni_init_one_plugin (jvm_env, &java_plugins[j]); + if (jvm == NULL) + { + ERROR ("java plugin: cjni_write: jvm == NULL"); + return (-1); + } - return (0); -} /* }}} int cjni_init_plugins */ + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_write: Invalid user data."); + return (-1); + } -static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */ -{ - jclass api_class_ptr; - int status; + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); - api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.java.CollectdAPI"); - if (api_class_ptr == NULL) + cbi = (cjni_callback_info_t *) ud->data; + + vl_java = ctoj_value_list (jvm_env, ds, vl); + if (vl_java == NULL) { - ERROR ("cjni_init_native: Cannot find API class `org.collectd.java.CollectdAPI'."); + ERROR ("java plugin: cjni_write: ctoj_value_list failed."); return (-1); } - status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr, - jni_api_functions, (jint) jni_api_functions_num); + ret_status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, cbi->method, vl_java); + + (*jvm_env)->DeleteLocalRef (jvm_env, vl_java); + + status = cjni_thread_detach (); if (status != 0) { - ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status); + ERROR ("java plugin: cjni_write: cjni_thread_detach failed."); return (-1); } - return (0); -} /* }}} int cjni_init_native */ + return (ret_status); +} /* }}} int cjni_write */ -static int cjni_init (void) /* {{{ */ +/* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */ +static int cjni_flush (int timeout, const char *identifier, /* {{{ */ + user_data_t *ud) { JNIEnv *jvm_env; - JavaVMInitArgs vm_args; - JavaVMOption vm_options[jvm_argc]; - + cjni_callback_info_t *cbi; + jobject o_identifier; int status; - size_t i; - - if (jvm != NULL) - return (0); - - jvm_env = NULL; - - memset (&vm_args, 0, sizeof (vm_args)); - vm_args.version = JNI_VERSION_1_2; - vm_args.options = vm_options; - vm_args.nOptions = (jint) jvm_argc; - - for (i = 0; i < jvm_argc; i++) - { - DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]); - vm_args.options[i].optionString = jvm_argv[i]; - } - /* - vm_args.options[0].optionString = "-verbose:jni"; - vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java"; - */ + int ret_status; - status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args); - if (status != 0) + if (jvm == NULL) { - ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.", - status); + ERROR ("java plugin: cjni_flush: jvm == NULL"); return (-1); } - assert (jvm != NULL); - assert (jvm_env != NULL); - /* Call RegisterNatives */ - status = cjni_init_native (jvm_env); - if (status != 0) + if ((ud == NULL) || (ud->data == NULL)) { - ERROR ("cjni_init: cjni_init_native failed."); + ERROR ("java plugin: cjni_flush: Invalid user data."); return (-1); } - cjni_init_plugins (jvm_env); + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); - return (0); -} /* }}} int cjni_init */ + cbi = (cjni_callback_info_t *) ud->data; -static int cjni_read_one_plugin (JNIEnv *jvm_env, java_plugin_t *jp) /* {{{ */ -{ - int status; + o_identifier = NULL; + if (identifier != NULL) + { + o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier); + if (o_identifier == NULL) + { + ERROR ("java plugin: cjni_flush: NewStringUTF failed."); + return (-1); + } + } - if ((jp == NULL) - || ((jp->flags & CJNI_FLAG_ENABLED) == 0) - || (jp->m_read == NULL)) - return (0); + ret_status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, cbi->method, (jint) timeout, o_identifier); - DEBUG ("java plugin: Calling: %s.Read()", jp->class_name); + (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier); - status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr, - jp->m_read); + status = cjni_thread_detach (); if (status != 0) { - ERROR ("java plugin: cjni_read_one_plugin: " - "Calling `Read' on an `%s' object failed with status %i.", - jp->class_name, status); + ERROR ("java plugin: cjni_flush: cjni_thread_detach failed."); return (-1); } - return (0); -} /* }}} int cjni_read_one_plugin */ + return (ret_status); +} /* }}} int cjni_flush */ -static int cjni_read_plugins (JNIEnv *jvm_env) /* {{{ */ +/* Call the CB_TYPE_LOG callback pointed to by the `user_data_t' pointer. */ +static void cjni_log (int severity, const char *message, /* {{{ */ + user_data_t *ud) { - size_t j; + JNIEnv *jvm_env; + cjni_callback_info_t *cbi; + jobject o_message; + + if (jvm == NULL) + return; - for (j = 0; j < java_plugins_num; j++) - cjni_read_one_plugin (jvm_env, &java_plugins[j]); + if ((ud == NULL) || (ud->data == NULL)) + return; - return (0); -} /* }}} int cjni_read_plugins */ + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return; + + cbi = (cjni_callback_info_t *) ud->data; + + o_message = (*jvm_env)->NewStringUTF (jvm_env, message); + if (o_message == NULL) + return; -static int cjni_read (void) /* {{{ */ + (*jvm_env)->CallVoidMethod (jvm_env, + cbi->object, cbi->method, (jint) severity, o_message); + + (*jvm_env)->DeleteLocalRef (jvm_env, o_message); + + cjni_thread_detach (); +} /* }}} void cjni_log */ + +/* Call the CB_TYPE_NOTIFICATION callback pointed to by the `user_data_t' + * pointer. */ +static int cjni_notification (const notification_t *n, /* {{{ */ + user_data_t *ud) { JNIEnv *jvm_env; - JavaVMAttachArgs args; + cjni_callback_info_t *cbi; + jobject o_notification; int status; + int ret_status; if (jvm == NULL) { @@ -1236,89 +2205,200 @@ static int cjni_read (void) /* {{{ */ return (-1); } - jvm_env = NULL; - memset (&args, 0, sizeof (args)); - args.version = JNI_VERSION_1_2; + if ((ud == NULL) || (ud->data == NULL)) + { + ERROR ("java plugin: cjni_read: Invalid user data."); + return (-1); + } - status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args); - if (status != 0) + jvm_env = cjni_thread_attach (); + if (jvm_env == NULL) + return (-1); + + cbi = (cjni_callback_info_t *) ud->data; + + o_notification = ctoj_notification (jvm_env, n); + if (o_notification == NULL) { - ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.", - status); + ERROR ("java plugin: cjni_notification: ctoj_notification failed."); return (-1); } - cjni_read_plugins (jvm_env); + ret_status = (*jvm_env)->CallIntMethod (jvm_env, + cbi->object, cbi->method, o_notification); - status = (*jvm)->DetachCurrentThread (jvm); + (*jvm_env)->DeleteLocalRef (jvm_env, o_notification); + + status = cjni_thread_detach (); if (status != 0) { - ERROR ("java plugin: cjni_read: DetachCurrentThread failed with status %i.", - status); + ERROR ("java plugin: cjni_read: cjni_thread_detach failed."); return (-1); } + return (ret_status); +} /* }}} int cjni_notification */ + +/* Iterate over `java_classes_list' and create one object of each class. This + * will trigger the object's constructors, to the objects can register callback + * methods. */ +static int cjni_load_plugins (JNIEnv *jvm_env) /* {{{ */ +{ + size_t i; + + for (i = 0; i < java_classes_list_len; i++) + { + java_plugin_class_t *class; + jmethodID constructor_id; + + class = java_classes_list + i; + + DEBUG ("java plugin: Loading class %s", class->name); + + class->class = (*jvm_env)->FindClass (jvm_env, class->name); + if (class->class == NULL) + { + ERROR ("java plugin: cjni_load_plugins: FindClass (%s) failed.", + class->name); + continue; + } + + constructor_id = (*jvm_env)->GetMethodID (jvm_env, class->class, + "", "()V"); + if (constructor_id == NULL) + { + ERROR ("java plugin: cjni_load_plugins: Could not find the constructor for `%s'.", + class->name); + continue; + } + + class->object = (*jvm_env)->NewObject (jvm_env, class->class, + constructor_id); + if (class->object == NULL) + { + ERROR ("java plugin: cjni_load_plugins: Could create a new `%s' object.", + class->name); + continue; + } + + (*jvm_env)->NewGlobalRef (jvm_env, class->object); + } /* for (i = 0; i < java_classes_list_len; i++) */ + return (0); -} /* }}} int cjni_read */ +} /* }}} int cjni_load_plugins */ -static int cjni_write_one_plugin (JNIEnv *jvm_env, /* {{{ */ - java_plugin_t *jp, jobject vl_java) +/* Iterate over `java_plugin_configs' and `java_callbacks' and call all + * `config' callback methods for which a configuration is available. */ +static int cjni_config_plugins (JNIEnv *jvm_env) /* {{{ */ { int status; + size_t i; + size_t j; - if ((jp == NULL) - || ((jp->flags & CJNI_FLAG_ENABLED) == 0) - || (jp->m_write == NULL)) - return (0); + for (i = 0; i < java_plugin_configs_num; i++) + { + jobject o_ocitem; - DEBUG ("java plugin: Calling: %s.Write(ValueList)", jp->class_name); + if (java_plugin_configs[i].ci == NULL) + continue; - status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr, - jp->m_write, vl_java); - if (status != 0) + for (j = 0; j < java_callbacks_num; j++) + { + if (java_callbacks[j].type != CB_TYPE_CONFIG) + continue; + + if (strcmp (java_plugin_configs[i].name, java_callbacks[j].name) == 0) + break; + } + + if (j >= java_callbacks_num) + { + NOTICE ("java plugin: Configuration for `%s' is present, but no such " + "configuration callback has been registered.", + java_plugin_configs[i].name); + continue; + } + + DEBUG ("java plugin: Configuring %s", java_plugin_configs[i].name); + + o_ocitem = ctoj_oconfig_item (jvm_env, java_plugin_configs[i].ci); + if (o_ocitem == NULL) + { + ERROR ("java plugin: cjni_config_plugins: ctoj_oconfig_item failed."); + continue; + } + + status = (*jvm_env)->CallIntMethod (jvm_env, + java_callbacks[j].object, java_callbacks[j].method, o_ocitem); + WARNING ("java plugin: Config callback for `%s' returned status %i.", + java_plugin_configs[i].name, status); + } /* for (i = 0; i < java_plugin_configs; i++) */ + + return (0); +} /* }}} int cjni_config_plugins */ + +/* Iterate over `java_callbacks' and call all CB_TYPE_INIT callbacks. */ +static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */ +{ + int status; + size_t i; + + for (i = 0; i < java_callbacks_num; i++) { - ERROR ("java plugin: cjni_write_one_plugin: " - "Calling `Write' on an `%s' object failed with status %i.", - jp->class_name, status); - return (-1); + if (java_callbacks[i].type != CB_TYPE_INIT) + continue; + + DEBUG ("java plugin: Initializing %s", java_callbacks[i].name); + + status = (*jvm_env)->CallIntMethod (jvm_env, + java_callbacks[i].object, java_callbacks[i].method); + if (status != 0) + { + ERROR ("java plugin: Initializing `%s' failed with status %i. " + "Removing read function.", + java_callbacks[i].name, status); + plugin_unregister_read (java_callbacks[i].name); + } } return (0); -} /* }}} int cjni_write_one_plugin */ +} /* }}} int cjni_init_plugins */ -static int cjni_write_plugins (JNIEnv *jvm_env, /* {{{ */ - const data_set_t *ds, const value_list_t *vl) +/* Iterate over `java_callbacks' and call all CB_TYPE_SHUTDOWN callbacks. */ +static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */ { - size_t j; - - jobject vl_java; + int status; + size_t i; - vl_java = ctoj_value_list (jvm_env, ds, vl); - if (vl_java == NULL) + for (i = 0; i < java_callbacks_num; i++) { - ERROR ("java plugin: cjni_write_plugins: ctoj_value_list failed."); - return (-1); - } + if (java_callbacks[i].type != CB_TYPE_SHUTDOWN) + continue; - for (j = 0; j < java_plugins_num; j++) - cjni_write_one_plugin (jvm_env, &java_plugins[j], vl_java); + DEBUG ("java plugin: Shutting down %s", java_callbacks[i].name); - (*jvm_env)->DeleteLocalRef (jvm_env, vl_java); + status = (*jvm_env)->CallIntMethod (jvm_env, + java_callbacks[i].object, java_callbacks[i].method); + if (status != 0) + { + ERROR ("java plugin: Shutting down `%s' failed with status %i. ", + java_callbacks[i].name, status); + } + } return (0); -} /* }}} int cjni_write_plugins */ +} /* }}} int cjni_shutdown_plugins */ + -static int cjni_write (const data_set_t *ds, const value_list_t *vl) /* {{{ */ +static int cjni_shutdown (void) /* {{{ */ { JNIEnv *jvm_env; JavaVMAttachArgs args; int status; + size_t i; if (jvm == NULL) - { - ERROR ("java plugin: cjni_write: jvm == NULL"); - return (-1); - } + return (0); jvm_env = NULL; memset (&args, 0, sizeof (args)); @@ -1327,108 +2407,159 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl) /* {{{ */ status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args); if (status != 0) { - ERROR ("java plugin: cjni_write: AttachCurrentThread failed with status %i.", + ERROR ("java plugin: cjni_shutdown: AttachCurrentThread failed with status %i.", status); return (-1); } - cjni_write_plugins (jvm_env, ds, vl); + /* Execute all the shutdown functions registered by plugins. */ + cjni_shutdown_plugins (jvm_env); + + /* Release all the global references to callback functions */ + for (i = 0; i < java_callbacks_num; i++) + { + if (java_callbacks[i].object != NULL) + { + (*jvm_env)->DeleteGlobalRef (jvm_env, java_callbacks[i].object); + java_callbacks[i].object = NULL; + } + sfree (java_callbacks[i].name); + } + java_callbacks_num = 0; + sfree (java_callbacks); - status = (*jvm)->DetachCurrentThread (jvm); - if (status != 0) + /* Release all the global references to directly loaded classes. */ + for (i = 0; i < java_classes_list_len; i++) { - ERROR ("java plugin: cjni_write: DetachCurrentThread failed with status %i.", - status); - return (-1); + if (java_classes_list[i].object != NULL) + { + (*jvm_env)->DeleteGlobalRef (jvm_env, java_classes_list[i].object); + java_classes_list[i].object = NULL; + } + sfree (java_classes_list[i].name); } + java_classes_list_len = 0; + sfree (java_classes_list); - return (0); -} /* }}} int cjni_write */ + /* Destroy the JVM */ + DEBUG ("java plugin: Destroying the JVM."); + (*jvm)->DestroyJavaVM (jvm); + jvm = NULL; + jvm_env = NULL; -static int cjni_shutdown_one_plugin (JNIEnv *jvm_env, /* {{{ */ - java_plugin_t *jp) -{ - int status; + pthread_key_delete (jvm_env_key); - if ((jp == NULL) - || ((jp->flags & CJNI_FLAG_ENABLED) == 0) - || (jp->m_shutdown == NULL)) - return (0); + /* Free the JVM argument list */ + for (i = 0; i < jvm_argc; i++) + sfree (jvm_argv[i]); + jvm_argc = 0; + sfree (jvm_argv); - status = (*jvm_env)->CallIntMethod (jvm_env, jp->object_ptr, - jp->m_shutdown); - if (status != 0) + /* Free the copied configuration */ + for (i = 0; i < java_plugin_configs_num; i++) { - ERROR ("cjni_shutdown_one_plugin: Destroying an `%s' object failed " - "with status %i.", jp->class_name, status); - return (-1); + sfree (java_plugin_configs[i].name); + oconfig_free (java_plugin_configs[i].ci); } - jp->flags &= ~CJNI_FLAG_ENABLED; + java_plugin_configs_num = 0; + sfree (java_plugin_configs); return (0); -} /* }}} int cjni_shutdown_one_plugin */ +} /* }}} int cjni_shutdown */ -static int cjni_shutdown_plugins (JNIEnv *jvm_env) /* {{{ */ +/* Register ``native'' functions with the JVM. Native functions are C-functions + * that can be called by Java code. */ +static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */ { - size_t j; + jclass api_class_ptr; + int status; + + api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.Collectd"); + if (api_class_ptr == NULL) + { + ERROR ("cjni_init_native: Cannot find API class `org.collectd.api.Collectd'."); + return (-1); + } - for (j = 0; j < java_plugins_num; j++) - cjni_shutdown_one_plugin (jvm_env, &java_plugins[j]); + status = (*jvm_env)->RegisterNatives (jvm_env, api_class_ptr, + jni_api_functions, (jint) jni_api_functions_num); + if (status != 0) + { + ERROR ("cjni_init_native: RegisterNatives failed with status %i.", status); + return (-1); + } return (0); -} /* }}} int cjni_shutdown_plugins */ +} /* }}} int cjni_init_native */ -static int cjni_shutdown (void) /* {{{ */ +/* Initialization: Create a JVM, load all configured classes and call their + * `config' and `init' callback methods. */ +static int cjni_init (void) /* {{{ */ { JNIEnv *jvm_env; - JavaVMAttachArgs args; + JavaVMInitArgs vm_args; + JavaVMOption vm_options[jvm_argc]; + int status; size_t i; - if (jvm == NULL) + if (jvm != NULL) return (0); - jvm_env = NULL; - memset (&args, 0, sizeof (args)); - args.version = JNI_VERSION_1_2; - - status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args); + status = pthread_key_create (&jvm_env_key, cjni_jvm_env_destroy); if (status != 0) { - ERROR ("java plugin: cjni_read: AttachCurrentThread failed with status %i.", - status); + ERROR ("java plugin: cjni_init: pthread_key_create failed " + "with status %i.", status); return (-1); } - cjni_shutdown_plugins (jvm_env); - - (*jvm)->DestroyJavaVM (jvm); - jvm = NULL; jvm_env = NULL; + memset (&vm_args, 0, sizeof (vm_args)); + vm_args.version = JNI_VERSION_1_2; + vm_args.options = vm_options; + vm_args.nOptions = (jint) jvm_argc; + for (i = 0; i < jvm_argc; i++) { - sfree (jvm_argv[i]); + DEBUG ("java plugin: cjni_init: jvm_argv[%zu] = %s", i, jvm_argv[i]); + vm_args.options[i].optionString = jvm_argv[i]; } - sfree (jvm_argv); - jvm_argc = 0; + /* + vm_args.options[0].optionString = "-verbose:jni"; + vm_args.options[1].optionString = "-Djava.class.path=/home/octo/collectd/bindings/java"; + */ + + status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args); + if (status != 0) + { + ERROR ("cjni_init: JNI_CreateJavaVM failed with status %i.", + status); + return (-1); + } + assert (jvm != NULL); + assert (jvm_env != NULL); - for (i = 0; i < java_plugins_num; i++) + /* Call RegisterNatives */ + status = cjni_init_native (jvm_env); + if (status != 0) { - sfree (java_plugins[i].class_name); + ERROR ("cjni_init: cjni_init_native failed."); + return (-1); } - sfree (java_plugins); - java_plugins_num = 0; + + cjni_load_plugins (jvm_env); + cjni_config_plugins (jvm_env); + cjni_init_plugins (jvm_env); return (0); -} /* }}} int cjni_shutdown */ +} /* }}} int cjni_init */ void module_register (void) { plugin_register_complex_config ("java", cjni_config); plugin_register_init ("java", cjni_init); - plugin_register_read ("java", cjni_read); - plugin_register_write ("java", cjni_write); plugin_register_shutdown ("java", cjni_shutdown); } /* void module_register (void) */