Many build fixes that turned up with GCC 4.6.
[collectd.git] / src / java.c
index 2c40cf0..a8ffd8e 100644 (file)
@@ -93,6 +93,8 @@ 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;
 
+static oconfig_item_t       *config_block = NULL;
+
 /*
  * Prototypes
  *
@@ -111,9 +113,11 @@ 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);
 
-static int cjni_match_create (const oconfig_item_t *ci, void **user_data);
-static int cjni_match_destroy (void **user_data);
-static int cjni_match_match (const data_set_t *ds, const value_list_t *vl,
+/* Create, destroy, and match/invoke functions, used by both, matches AND
+ * targets. */
+static int cjni_match_target_create (const oconfig_item_t *ci, void **user_data);
+static int cjni_match_target_destroy (void **user_data);
+static int cjni_match_target_invoke (const data_set_t *ds, value_list_t *vl,
     notification_meta_t **meta, void **user_data);
 
 /* 
@@ -225,7 +229,7 @@ static jobject ctoj_jlong_to_number (JNIEnv *jvm_env, jlong value) /* {{{ */
   jmethodID m_long_constructor;
 
   /* Look up the java.lang.Long class */
-  c_long = (*jvm_env)->FindClass (jvm_env, "java.lang.Long");
+  c_long = (*jvm_env)->FindClass (jvm_env, "java/lang/Long");
   if (c_long == NULL)
   {
     ERROR ("java plugin: ctoj_jlong_to_number: Looking up the "
@@ -253,7 +257,7 @@ static jobject ctoj_jdouble_to_number (JNIEnv *jvm_env, jdouble value) /* {{{ */
   jmethodID m_double_constructor;
 
   /* Look up the java.lang.Long class */
-  c_double = (*jvm_env)->FindClass (jvm_env, "java.lang.Double");
+  c_double = (*jvm_env)->FindClass (jvm_env, "java/lang/Double");
   if (c_double == NULL)
   {
     ERROR ("java plugin: ctoj_jdouble_to_number: Looking up the "
@@ -282,11 +286,15 @@ static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
     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));
+  if (ds_type == DS_TYPE_DERIVE)
+    return (ctoj_jlong_to_number (jvm_env, (jlong) value.derive));
+  if (ds_type == DS_TYPE_ABSOLUTE)
+    return (ctoj_jlong_to_number (jvm_env, (jlong) value.absolute));
   else
     return (NULL);
 } /* }}} jobject ctoj_value_to_number */
 
-/* Convert a data_source_t to a org.collectd.api.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)
 {
@@ -297,11 +305,11 @@ static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
 
   /* Look up the DataSource class */
   c_datasource = (*jvm_env)->FindClass (jvm_env,
-      "org.collectd.api.DataSource");
+      "org/collectd/api/DataSource");
   if (c_datasource == NULL)
   {
     ERROR ("java plugin: ctoj_data_source: "
-        "FindClass (org.collectd.api.DataSource) failed.");
+        "FindClass (org/collectd/api/DataSource) failed.");
     return (NULL);
   }
 
@@ -372,7 +380,7 @@ static jobject ctoj_data_source (JNIEnv *jvm_env, /* {{{ */
   return (o_datasource);
 } /* }}} jobject ctoj_data_source */
 
-/* Convert a oconfig_value_t to a org.collectd.api.OConfigValue */
+/* Convert a oconfig_value_t to a org/collectd/api/OConfigValue */
 static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
     oconfig_value_t ocvalue)
 {
@@ -385,11 +393,11 @@ static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
   o_argument = NULL;
 
   c_ocvalue = (*jvm_env)->FindClass (jvm_env,
-      "org.collectd.api.OConfigValue");
+      "org/collectd/api/OConfigValue");
   if (c_ocvalue == NULL)
   {
     ERROR ("java plugin: ctoj_oconfig_value: "
-        "FindClass (org.collectd.api.OConfigValue) failed.");
+        "FindClass (org/collectd/api/OConfigValue) failed.");
     return (NULL);
   }
 
@@ -472,7 +480,7 @@ static jobject ctoj_oconfig_value (JNIEnv *jvm_env, /* {{{ */
   return (o_ocvalue);
 } /* }}} jobject ctoj_oconfig_value */
 
-/* Convert a oconfig_item_t to a org.collectd.api.OConfigItem */
+/* Convert a oconfig_item_t to a org/collectd/api/OConfigItem */
 static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
     const oconfig_item_t *ci)
 {
@@ -484,11 +492,11 @@ static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
   jobject o_ocitem;
   int i;
 
-  c_ocitem = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.OConfigItem");
+  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.");
+        "FindClass (org/collectd/api/OConfigItem) failed.");
     return (NULL);
   }
 
@@ -578,14 +586,14 @@ static jobject ctoj_oconfig_item (JNIEnv *jvm_env, /* {{{ */
       return (NULL);
     }
 
-    (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addvalue, o_child);
+    (*jvm_env)->CallVoidMethod (jvm_env, o_ocitem, m_addchild, 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 */
+/* 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_dataset;
@@ -595,18 +603,18 @@ static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
   jobject o_dataset;
   int i;
 
-  /* Look up the org.collectd.api.DataSet class */
-  c_dataset = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.DataSet");
+  /* 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 "
-        "org.collectd.api.DataSet class failed.");
+        "org/collectd/api/DataSet class failed.");
     return (NULL);
   }
 
   /* Search for the `DataSet (String type)' constructor. */
   m_constructor = (*jvm_env)->GetMethodID (jvm_env,
-      c_dataset, "<init>", "(Ljava.lang.String;)V");
+      c_dataset, "<init>", "(Ljava/lang/String;)V");
   if (m_constructor == NULL)
   {
     ERROR ("java plugin: ctoj_data_set: Looking up the "
@@ -616,7 +624,7 @@ static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
 
   /* Search for the `void addDataSource (DataSource)' method. */
   m_add = (*jvm_env)->GetMethodID (jvm_env,
-      c_dataset, "addDataSource", "(Lorg.collectd.api.DataSource;)V");
+      c_dataset, "addDataSource", "(Lorg/collectd/api/DataSource;)V");
   if (m_add == NULL)
   {
     ERROR ("java plugin: ctoj_data_set: Looking up the "
@@ -703,7 +711,7 @@ static int ctoj_value_list_add_data_set (JNIEnv *jvm_env, /* {{{ */
 
   /* Look for the `void setDataSource (List<DataSource> ds)' method. */
   m_setdataset = (*jvm_env)->GetMethodID (jvm_env, c_valuelist,
-      "setDataSet", "(Lorg.collectd.api.DataSet;)V");
+      "setDataSet", "(Lorg/collectd/api/DataSet;)V");
   if (m_setdataset == NULL)
   {
     ERROR ("java plugin: ctoj_value_list_add_data_set: "
@@ -730,7 +738,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 */
+/* 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)
 {
@@ -743,11 +751,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.api.ValueList");
+      "org/collectd/api/ValueList");
   if (c_valuelist == NULL)
   {
     ERROR ("java plugin: ctoj_value_list: "
-        "FindClass (org.collectd.api.ValueList) failed.");
+        "FindClass (org/collectd/api/ValueList) failed.");
     return (NULL);
   }
 
@@ -836,7 +844,7 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
   return (o_valuelist);
 } /* }}} jobject ctoj_value_list */
 
-/* Convert a notification_t to a org.collectd.api.Notification */
+/* Convert a notification_t to a org/collectd/api/Notification */
 static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
     const notification_t *n)
 {
@@ -848,11 +856,11 @@ static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
   /* First, create a new Notification instance..
    * Look up the class.. */
   c_notification = (*jvm_env)->FindClass (jvm_env,
-      "org.collectd.api.Notification");
+      "org/collectd/api/Notification");
   if (c_notification == NULL)
   {
     ERROR ("java plugin: ctoj_notification: "
-        "FindClass (org.collectd.api.Notification) failed.");
+        "FindClass (org/collectd/api/Notification) failed.");
     return (NULL);
   }
 
@@ -1040,33 +1048,39 @@ static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
 
   class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
 
-  if (ds_type == DS_TYPE_COUNTER)
+  if (ds_type == DS_TYPE_GAUGE)
   {
-    jlong tmp_long;
+    jdouble tmp_double;
 
-    status = jtoc_long (jvm_env, &tmp_long,
-        class_ptr, object_ptr, "longValue");
+    status = jtoc_double (jvm_env, &tmp_double,
+        class_ptr, object_ptr, "doubleValue");
     if (status != 0)
     {
       ERROR ("java plugin: jtoc_value: "
-          "jtoc_long failed.");
+          "jtoc_double failed.");
       return (-1);
     }
-    (*ret_value).counter = (counter_t) tmp_long;
+    (*ret_value).gauge = (gauge_t) tmp_double;
   }
   else
   {
-    jdouble tmp_double;
+    jlong tmp_long;
 
-    status = jtoc_double (jvm_env, &tmp_double,
-        class_ptr, object_ptr, "doubleValue");
+    status = jtoc_long (jvm_env, &tmp_long,
+        class_ptr, object_ptr, "longValue");
     if (status != 0)
     {
       ERROR ("java plugin: jtoc_value: "
-          "jtoc_double failed.");
+          "jtoc_long failed.");
       return (-1);
     }
-    (*ret_value).gauge = (gauge_t) tmp_double;
+
+    if (ds_type == DS_TYPE_DERIVE)
+      (*ret_value).derive = (derive_t) tmp_long;
+    else if (ds_type == DS_TYPE_ABSOLUTE)
+      (*ret_value).absolute = (absolute_t) tmp_long;
+    else
+      (*ret_value).counter = (counter_t) tmp_long;
   }
 
   return (0);
@@ -1177,7 +1191,7 @@ static int jtoc_values_array (JNIEnv *jvm_env, /* {{{ */
   return (0);
 } /* }}} int jtoc_values_array */
 
-/* Convert a org.collectd.api.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)
 {
@@ -1249,7 +1263,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
   return (0);
 } /* }}} int jtoc_value_list */
 
-/* Convert a org.collectd.api.Notification to a notification_t. */
+/* Convert a org/collectd/api/Notification to a notification_t. */
 static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */
     jobject object_ptr)
 {
@@ -1405,7 +1419,8 @@ static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */
   ud.data = (void *) cbi;
   ud.free_func = cjni_callback_info_destroy;
 
-  plugin_register_complex_read (cbi->name, cjni_read, &ud);
+  plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
+      /* interval = */ NULL, &ud);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
 
@@ -1512,38 +1527,62 @@ static jint JNICALL cjni_api_register_notification (JNIEnv *jvm_env, /* {{{ */
   return (0);
 } /* }}} jint cjni_api_register_notification */
 
-static jint JNICALL cjni_api_register_match (JNIEnv *jvm_env, /* {{{ */
-    jobject this, jobject o_name, jobject o_match)
+static jint JNICALL cjni_api_register_match_target (JNIEnv *jvm_env, /* {{{ */
+    jobject this, jobject o_name, jobject o_match, int type)
 {
-  match_proc_t proc;
   int status;
   const char *c_name;
 
   c_name = (*jvm_env)->GetStringUTFChars (jvm_env, o_name, 0);
   if (c_name == NULL)
   {
-    ERROR ("java plugin: cjni_api_register_match: "
+    ERROR ("java plugin: cjni_api_register_match_target: "
         "GetStringUTFChars failed.");
     return (-1);
   }
 
-  status = cjni_callback_register (jvm_env, o_name, o_match, CB_TYPE_MATCH);
+  status = cjni_callback_register (jvm_env, o_name, o_match, type);
   if (status != 0)
   {
     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
     return (-1);
   }
 
-  memset (&proc, 0, sizeof (proc));
-  proc.create  = cjni_match_create;
-  proc.destroy = cjni_match_destroy;
-  proc.match   = cjni_match_match;
+  if (type == CB_TYPE_MATCH)
+  {
+    match_proc_t m_proc;
+
+    memset (&m_proc, 0, sizeof (m_proc));
+    m_proc.create  = cjni_match_target_create;
+    m_proc.destroy = cjni_match_target_destroy;
+    m_proc.match   = (void *) cjni_match_target_invoke;
+
+    status = fc_register_match (c_name, m_proc);
+  }
+  else if (type == CB_TYPE_TARGET)
+  {
+    target_proc_t t_proc;
+
+    memset (&t_proc, 0, sizeof (t_proc));
+    t_proc.create  = cjni_match_target_create;
+    t_proc.destroy = cjni_match_target_destroy;
+    t_proc.invoke  = cjni_match_target_invoke;
+
+    status = fc_register_target (c_name, t_proc);
+  }
+  else
+  {
+    ERROR ("java plugin: cjni_api_register_match_target: "
+        "Don't know whether to create a match or a target.");
+    (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
+    return (-1);
+  }
 
-  status = fc_register_match (c_name, proc);
   if (status != 0)
   {
-    ERROR ("java plugin: cjni_api_register_match: "
-        "fc_register_match failed.");
+    ERROR ("java plugin: cjni_api_register_match_target: "
+        "%s failed.",
+        (type == CB_TYPE_MATCH) ? "fc_register_match" : "fc_register_target");
     (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
     return (-1);
   }
@@ -1551,8 +1590,22 @@ static jint JNICALL cjni_api_register_match (JNIEnv *jvm_env, /* {{{ */
   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
 
   return (0);
+} /* }}} jint cjni_api_register_match_target */
+
+static jint JNICALL cjni_api_register_match (JNIEnv *jvm_env, /* {{{ */
+    jobject this, jobject o_name, jobject o_match)
+{
+  return (cjni_api_register_match_target (jvm_env, this, o_name, o_match,
+        CB_TYPE_MATCH));
 } /* }}} jint cjni_api_register_match */
 
+static jint JNICALL cjni_api_register_target (JNIEnv *jvm_env, /* {{{ */
+    jobject this, jobject o_name, jobject o_target)
+{
+  return (cjni_api_register_match_target (jvm_env, this, o_name, o_target,
+        CB_TYPE_TARGET));
+} /* }}} jint cjni_api_register_target */
+
 static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */
     jobject this, jint severity, jobject o_message)
 {
@@ -1627,6 +1680,10 @@ static JNINativeMethod jni_api_functions[] = /* {{{ */
     "(Ljava/lang/String;Lorg/collectd/api/CollectdMatchFactoryInterface;)I",
     cjni_api_register_match },
 
+  { "registerTarget",
+    "(Ljava/lang/String;Lorg/collectd/api/CollectdTargetFactoryInterface;)I",
+    cjni_api_register_target },
+
   { "log",
     "(ILjava/lang/String;)V",
     cjni_api_log },
@@ -1696,6 +1753,12 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{
         "Lorg/collectd/api/CollectdMatchInterface;";
       break;
 
+    case CB_TYPE_TARGET:
+      method_name = "createTarget";
+      method_signature = "(Lorg/collectd/api/OConfigItem;)"
+        "Lorg/collectd/api/CollectdTargetInterface;";
+      break;
+
     default:
       ERROR ("java plugin: cjni_callback_info_create: Unknown type: %#x",
           type);
@@ -1731,7 +1794,15 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{
 
   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_name, c_name);
 
-  cbi->class  = (*jvm_env)->GetObjectClass (jvm_env, o_callback);
+  cbi->object = (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
+  if (cbi->object == NULL)
+  {
+    ERROR ("java plugin: cjni_callback_info_create: NewGlobalRef failed.");
+    free (cbi);
+    return (NULL);
+  }
+
+  cbi->class  = (*jvm_env)->GetObjectClass (jvm_env, cbi->object);
   if (cbi->class == NULL)
   {
     ERROR ("java plugin: cjni_callback_info_create: GetObjectClass failed.");
@@ -1739,8 +1810,6 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{
     return (NULL);
   }
 
-  cbi->object = o_callback;
-
   cbi->method = (*jvm_env)->GetMethodID (jvm_env, cbi->class,
       method_name, method_signature);
   if (cbi->method == NULL)
@@ -1752,8 +1821,6 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{
     return (NULL);
   }
 
-  (*jvm_env)->NewGlobalRef (jvm_env, o_callback);
-
   return (cbi);
 } /* }}} cjni_callback_info_t cjni_callback_info_create */
 
@@ -1792,6 +1859,10 @@ static int cjni_callback_register (JNIEnv *jvm_env, /* {{{ */
       type_str = "match";
       break;
 
+    case CB_TYPE_TARGET:
+      type_str = "target";
+      break;
+
     default:
       type_str = "<unknown>";
   }
@@ -1857,10 +1928,12 @@ static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
   jclass api_class_ptr;
   int status;
 
-  api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org.collectd.api.Collectd");
+  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'.");
+    ERROR ("cjni_init_native: Cannot find the API class \"org.collectd.api"
+        ".Collectd\". Please set the correct class path "
+        "using 'JVMArg \"-Djava.class.path=...\"'.");
     return (-1);
   }
 
@@ -1910,12 +1983,8 @@ static int cjni_create_jvm (void) /* {{{ */
         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";
-  */
 
-  status = JNI_CreateJavaVM (&jvm, (void **) &jvm_env, (void **) &vm_args);
+  status = JNI_CreateJavaVM (&jvm, (void *) &jvm_env, (void *) &vm_args);
   if (status != 0)
   {
     ERROR ("java plugin: cjni_create_jvm: "
@@ -2090,6 +2159,7 @@ static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
   JNIEnv *jvm_env;
   java_plugin_class_t *class;
   jmethodID constructor_id;
+  jobject tmp_object;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
   {
@@ -2123,6 +2193,15 @@ static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
   class->class = NULL;
   class->object = NULL;
 
+  { /* Replace all dots ('.') with slashes ('/'). Dots are usually used
+       thorough the Java community, but (Sun's) `FindClass' and friends need
+       slashes. */
+    size_t i;
+    for (i = 0; class->name[i] != 0; i++)
+      if (class->name[i] == '.')
+        class->name[i] = '/';
+  }
+
   DEBUG ("java plugin: Loading class %s", class->name);
 
   class->class = (*jvm_env)->FindClass (jvm_env, class->name);
@@ -2147,8 +2226,12 @@ static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  class->object = (*jvm_env)->NewObject (jvm_env, class->class,
+  tmp_object = (*jvm_env)->NewObject (jvm_env, class->class,
       constructor_id);
+  if (tmp_object != NULL)
+    class->object = (*jvm_env)->NewGlobalRef (jvm_env, tmp_object);
+  else
+    class->object = NULL;
   if (class->object == NULL)
   {
     ERROR ("java plugin: cjni_config_load_plugin: "
@@ -2159,7 +2242,6 @@ static int cjni_config_load_plugin (oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  (*jvm_env)->NewGlobalRef (jvm_env, class->object);
   cjni_thread_detach ();
 
   java_classes_list_len++;
@@ -2173,9 +2255,11 @@ static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
   cjni_callback_info_t *cbi;
   jobject o_ocitem;
   const char *name;
-  int status;
   size_t i;
 
+  jclass class;
+  jmethodID method;
+
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
   {
     WARNING ("java plugin: `Plugin' blocks "
@@ -2221,15 +2305,19 @@ static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
     return (-1);
   }
 
-  status = (*jvm_env)->CallIntMethod (jvm_env,
-      cbi->object, cbi->method, o_ocitem);
+  class = (*jvm_env)->GetObjectClass (jvm_env, cbi->object);
+  method = (*jvm_env)->GetMethodID (jvm_env, class,
+      "config", "(Lorg/collectd/api/OConfigItem;)I");
+
+  (*jvm_env)->CallIntMethod (jvm_env,
+      cbi->object, method, o_ocitem);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
   cjni_thread_detach ();
   return (0);
 } /* }}} int cjni_config_plugin_block */
 
-static int cjni_config (oconfig_item_t *ci) /* {{{ */
+static int cjni_config_perform (oconfig_item_t *ci) /* {{{ */
 {
   int success;
   int errors;
@@ -2284,7 +2372,56 @@ static int cjni_config (oconfig_item_t *ci) /* {{{ */
   }
 
   return (0);
-} /* }}} int cjni_config */
+} /* }}} int cjni_config_perform */
+
+/* Copy the children of `ci' to the global `config_block' variable. */
+static int cjni_config_callback (oconfig_item_t *ci) /* {{{ */
+{
+  oconfig_item_t *ci_copy;
+  oconfig_item_t *tmp;
+
+  assert (ci != NULL);
+  if (ci->children_num == 0)
+    return (0); /* nothing to do */
+
+  ci_copy = oconfig_clone (ci);
+  if (ci_copy == NULL)
+  {
+    ERROR ("java plugin: oconfig_clone failed.");
+    return (-1);
+  }
+
+  if (config_block == NULL)
+  {
+    config_block = ci_copy;
+    return (0);
+  }
+
+  tmp = realloc (config_block->children,
+      (config_block->children_num + ci_copy->children_num) * sizeof (*tmp));
+  if (tmp == NULL)
+  {
+    ERROR ("java plugin: realloc failed.");
+    oconfig_free (ci_copy);
+    return (-1);
+  }
+  config_block->children = tmp;
+
+  /* Copy the pointers */
+  memcpy (config_block->children + config_block->children_num,
+      ci_copy->children,
+      ci_copy->children_num * sizeof (*ci_copy->children));
+  config_block->children_num += ci_copy->children_num;
+
+  /* Delete the pointers from the copy, so `oconfig_free' can't free them. */
+  memset (ci_copy->children, 0,
+      ci_copy->children_num * sizeof (*ci_copy->children));
+  ci_copy->children_num = 0;
+
+  oconfig_free (ci_copy);
+
+  return (0);
+} /* }}} int cjni_config_callback */
 
 /* 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
@@ -2552,7 +2689,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
 } /* }}} int cjni_notification */
 
 /* Callbacks for matches implemented in Java */
-static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
+static int cjni_match_target_create (const oconfig_item_t *ci, /* {{{ */
     void **user_data)
 {
   JNIEnv *jvm_env;
@@ -2560,6 +2697,8 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
   cjni_callback_info_t *cbi_factory;
   const char *name;
   jobject o_ci;
+  jobject o_tmp;
+  int type;
   size_t i;
 
   cbi_ret = NULL;
@@ -2592,6 +2731,18 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
     BAIL_OUT (-1);
   }
 
+  /* Find out whether to create a match or a target. */
+  if (strcasecmp ("Match", ci->key) == 0)
+    type = CB_TYPE_MATCH;
+  else if (strcasecmp ("Target", ci->key) == 0)
+    type = CB_TYPE_TARGET;
+  else
+  {
+    ERROR ("java plugin: cjni_match_target_create: Can't figure out whether "
+        "to create a match or a target.");
+    BAIL_OUT (-1);
+  }
+
   /* This is the name of the match we should create. */
   name = ci->values[0].value.string;
 
@@ -2599,7 +2750,7 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
   cbi_factory = NULL;
   for (i = 0; i < java_callbacks_num; i++)
   {
-    if (java_callbacks[i].type != CB_TYPE_MATCH)
+    if (java_callbacks[i].type != type)
       continue;
 
     if (strcmp (name, java_callbacks[i].name) != 0)
@@ -2612,7 +2763,7 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
   /* Nope, no factory for that name.. */
   if (cbi_factory == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: "
+    ERROR ("java plugin: cjni_match_target_create: "
         "No such match factory registered: %s",
         name);
     BAIL_OUT (-1);
@@ -2622,7 +2773,8 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
   o_ci = ctoj_oconfig_item (jvm_env, ci);
   if (o_ci == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: ctoj_oconfig_item failed.");
+    ERROR ("java plugin: cjni_match_target_create: "
+        "ctoj_oconfig_item failed.");
     BAIL_OUT (-1);
   }
 
@@ -2631,26 +2783,34 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
   cbi_ret = (cjni_callback_info_t *) malloc (sizeof (*cbi_ret));
   if (cbi_ret == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: ctoj_oconfig_item failed.");
+    ERROR ("java plugin: cjni_match_target_create: malloc failed.");
     BAIL_OUT (-1);
   }
   memset (cbi_ret, 0, sizeof (*cbi_ret));
   cbi_ret->object = NULL;
+  cbi_ret->type = type;
 
   /* Lets fill the callback info structure.. First, the name: */
   cbi_ret->name = strdup (name);
   if (cbi_ret->name == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: strdup failed.");
+    ERROR ("java plugin: cjni_match_target_create: strdup failed.");
     BAIL_OUT (-1);
   }
 
   /* Then call the factory method so it creates a new object for us. */
-  cbi_ret->object = (*jvm_env)->CallObjectMethod (jvm_env,
+  o_tmp = (*jvm_env)->CallObjectMethod (jvm_env,
       cbi_factory->object, cbi_factory->method, o_ci);
-  if (cbi_ret->object == NULL)
+  if (o_tmp == NULL)
+  {
+    ERROR ("java plugin: cjni_match_target_create: CallObjectMethod failed.");
+    BAIL_OUT (-1);
+  }
+
+  cbi_ret->object = (*jvm_env)->NewGlobalRef (jvm_env, o_tmp);
+  if (o_tmp == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: CallObjectMethod failed.");
+    ERROR ("java plugin: cjni_match_target_create: NewGlobalRef failed.");
     BAIL_OUT (-1);
   }
 
@@ -2659,46 +2819,44 @@ static int cjni_match_create (const oconfig_item_t *ci, /* {{{ */
   cbi_ret->class = (*jvm_env)->GetObjectClass (jvm_env, cbi_ret->object);
   if (cbi_ret->class == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: GetObjectClass failed.");
+    ERROR ("java plugin: cjni_match_target_create: GetObjectClass failed.");
     BAIL_OUT (-1);
   }
 
   /* Lookup the `int match (DataSet, ValueList)' method. */
   cbi_ret->method = (*jvm_env)->GetMethodID (jvm_env, cbi_ret->class,
-      "match", "(Lorg/collectd/api/DataSet;Lorg/collectd/api/ValueList;)I");
+      /* method name = */ (type == CB_TYPE_MATCH) ? "match" : "invoke",
+      "(Lorg/collectd/api/DataSet;Lorg/collectd/api/ValueList;)I");
   if (cbi_ret->method == NULL)
   {
-    ERROR ("java plugin: cjni_match_create: GetMethodID failed.");
+    ERROR ("java plugin: cjni_match_target_create: GetMethodID failed.");
     BAIL_OUT (-1);
   }
 
-  /* We have everything we hoped for. Now we add a new global reference so this
-   * match isn't freed immediately after we return.. */
-  (*jvm_env)->NewGlobalRef (jvm_env, cbi_ret->object);
-
   /* Return the newly created match via the user_data pointer. */
   *user_data = (void *) cbi_ret;
 
   cjni_thread_detach ();
 
-  DEBUG ("java plugin: cjni_match_create: Successfully created a `%s' match.",
-      cbi_ret->name);
+  DEBUG ("java plugin: cjni_match_target_create: "
+      "Successfully created a `%s' %s.",
+      cbi_ret->name, (type == CB_TYPE_MATCH) ? "match" : "target");
 
   /* Success! */
   return (0);
 #undef BAIL_OUT
-} /* }}} int cjni_match_create */
+} /* }}} int cjni_match_target_create */
 
-static int cjni_match_destroy (void **user_data) /* {{{ */
+static int cjni_match_target_destroy (void **user_data) /* {{{ */
 {
   cjni_callback_info_destroy (*user_data);
   *user_data = NULL;
 
   return (0);
-} /* }}} int cjni_match_destroy */
+} /* }}} int cjni_match_target_destroy */
 
-static int cjni_match_match (const data_set_t *ds, /* {{{ */
-    const value_list_t *vl, notification_meta_t **meta, void **user_data)
+static int cjni_match_target_invoke (const data_set_t *ds, /* {{{ */
+    value_list_t *vl, notification_meta_t **meta, void **user_data)
 {
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
@@ -2709,7 +2867,7 @@ static int cjni_match_match (const data_set_t *ds, /* {{{ */
 
   if (jvm == NULL)
   {
-    ERROR ("java plugin: cjni_match_match: jvm == NULL");
+    ERROR ("java plugin: cjni_match_target_invoke: jvm == NULL");
     return (-1);
   }
 
@@ -2722,7 +2880,7 @@ static int cjni_match_match (const data_set_t *ds, /* {{{ */
   o_vl = ctoj_value_list (jvm_env, ds, vl);
   if (o_vl == NULL)
   {
-    ERROR ("java plugin: cjni_match_match: ctoj_value_list failed.");
+    ERROR ("java plugin: cjni_match_target_invoke: ctoj_value_list failed.");
     cjni_thread_detach ();
     return (-1);
   }
@@ -2730,7 +2888,7 @@ static int cjni_match_match (const data_set_t *ds, /* {{{ */
   o_ds = ctoj_data_set (jvm_env, ds);
   if (o_ds == NULL)
   {
-    ERROR ("java plugin: cjni_match_match: ctoj_value_list failed.");
+    ERROR ("java plugin: cjni_match_target_invoke: ctoj_value_list failed.");
     cjni_thread_detach ();
     return (-1);
   }
@@ -2738,14 +2896,39 @@ static int cjni_match_match (const data_set_t *ds, /* {{{ */
   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object, cbi->method,
       o_ds, o_vl);
 
-  DEBUG ("java plugin: cjni_match_match: Method returned %i.", ret_status);
+  DEBUG ("java plugin: cjni_match_target_invoke: Method returned %i.", ret_status);
+
+  /* If we're executing a target, copy the `ValueList' back to our
+   * `value_list_t'. */
+  if (cbi->type == CB_TYPE_TARGET)
+  {
+    value_list_t new_vl;
+
+    memset (&new_vl, 0, sizeof (new_vl));
+    status = jtoc_value_list (jvm_env, &new_vl, o_vl);
+    if (status != 0)
+    {
+      ERROR ("java plugin: cjni_match_target_invoke: "
+          "jtoc_value_list failed.");
+    }
+    else /* if (status == 0) */
+    {
+      /* plugin_dispatch_values assures that this is dynamically allocated
+       * memory. */
+      sfree (vl->values);
+
+      /* This will replace the vl->values pointer to a new, dynamically
+       * allocated piece of memory. */
+      memcpy (vl, &new_vl, sizeof (*vl));
+    }
+  } /* if (cbi->type == CB_TYPE_TARGET) */
 
   status = cjni_thread_detach ();
   if (status != 0)
     ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
 
   return (ret_status);
-} /* }}} int cjni_match_match */
+} /* }}} int cjni_match_target_invoke */
 
 /* Iterate over `java_callbacks' and call all CB_TYPE_INIT callbacks. */
 static int cjni_init_plugins (JNIEnv *jvm_env) /* {{{ */
@@ -2814,7 +2997,7 @@ static int cjni_shutdown (void) /* {{{ */
   memset (&args, 0, sizeof (args));
   args.version = JNI_VERSION_1_2;
 
-  status = (*jvm)->AttachCurrentThread (jvm, (void **) &jvm_env, &args);
+  status = (*jvm)->AttachCurrentThread (jvm, (void *) &jvm_env, &args);
   if (status != 0)
   {
     ERROR ("java plugin: cjni_shutdown: AttachCurrentThread failed with status %i.",
@@ -2874,9 +3057,24 @@ static int cjni_init (void) /* {{{ */
 {
   JNIEnv *jvm_env;
 
+  if ((config_block == NULL) && (jvm == NULL))
+  {
+    ERROR ("java plugin: cjni_init: No configuration block for "
+        "the java plugin was found.");
+    return (-1);
+  }
+
+  if (config_block != NULL)
+  {
+
+    cjni_config_perform (config_block);
+    oconfig_free (config_block);
+    config_block = NULL;
+  }
+
   if (jvm == NULL)
   {
-    ERROR ("java plugin: cjni_match_match: jvm == NULL");
+    ERROR ("java plugin: cjni_init: jvm == NULL");
     return (-1);
   }
 
@@ -2892,7 +3090,7 @@ static int cjni_init (void) /* {{{ */
 
 void module_register (void)
 {
-  plugin_register_complex_config ("java", cjni_config);
+  plugin_register_complex_config ("java", cjni_config_callback);
   plugin_register_init ("java", cjni_init);
   plugin_register_shutdown ("java", cjni_shutdown);
 } /* void module_register (void) */