Turbostat: use ssnprintf instead of snprintf
[collectd.git] / src / java.c
index 64e51d3..d0423be 100644 (file)
@@ -17,7 +17,7 @@
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  *   Justo Alonso Achaques <justo.alonso at gmail.com>
  **/
 
@@ -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
  *
@@ -107,7 +109,7 @@ static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name,
 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 int cjni_flush (cdtime_t 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);
 
@@ -118,7 +120,7 @@ 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);
 
-/* 
+/*
  * C to Java conversion functions
  */
 static int ctoj_string (JNIEnv *jvm_env, /* {{{ */
@@ -157,6 +159,23 @@ static int ctoj_string (JNIEnv *jvm_env, /* {{{ */
   return (0);
 } /* }}} int ctoj_string */
 
+static jstring ctoj_output_string (JNIEnv *jvm_env, /* {{{ */
+    const char *string)
+{
+  jstring o_string;
+
+  /* Create a java.lang.String */
+  o_string = (*jvm_env)->NewStringUTF (jvm_env,
+      (string != NULL) ? string : "");
+  if (o_string == NULL)
+  {
+    ERROR ("java plugin: ctoj_output_string: NewStringUTF failed.");
+    return NULL;
+  }
+
+  return (o_string);
+} /* }}} int ctoj_output_string */
+
 static int ctoj_int (JNIEnv *jvm_env, /* {{{ */
     jint value,
     jclass class_ptr, jobject object_ptr, const char *method_name)
@@ -227,7 +246,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 "
@@ -255,7 +274,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 "
@@ -612,7 +631,7 @@ static jobject ctoj_data_set (JNIEnv *jvm_env, const data_set_t *ds) /* {{{ */
 
   /* 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 "
@@ -807,7 +826,7 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
 #undef SET_STRING
 
   /* Set the `time' member. Java stores time in milliseconds. */
-  status = ctoj_long (jvm_env, ((jlong) vl->time) * ((jlong) 1000),
+  status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (vl->time),
       c_valuelist, o_valuelist, "setTime");
   if (status != 0)
   {
@@ -817,7 +836,8 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */
   }
 
   /* Set the `interval' member.. */
-  status = ctoj_long (jvm_env, (jlong) vl->interval,
+  status = ctoj_long (jvm_env,
+      (jlong) CDTIME_T_TO_MS (vl->interval),
       c_valuelist, o_valuelist, "setInterval");
   if (status != 0)
   {
@@ -903,7 +923,7 @@ static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
 #undef SET_STRING
 
   /* Set the `time' member. Java stores time in milliseconds. */
-  status = ctoj_long (jvm_env, ((jlong) n->time) * ((jlong) 1000),
+  status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (n->time),
       c_notification, o_notification, "setTime");
   if (status != 0)
   {
@@ -912,7 +932,7 @@ static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */
     return (NULL);
   }
 
-  /* Set the `interval' member.. */
+  /* Set the `severity' member.. */
   status = ctoj_int (jvm_env, (jint) n->severity,
       c_notification, o_notification, "setSeverity");
   if (status != 0)
@@ -1046,33 +1066,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);
@@ -1234,7 +1260,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
     return (-1);
   }
   /* Java measures time in milliseconds. */
-  vl->time = (time_t) (tmp_long / ((jlong) 1000));
+  vl->time = MS_TO_CDTIME_T (tmp_long);
 
   status = jtoc_long (jvm_env, &tmp_long,
       class_ptr, object_ptr, "getInterval");
@@ -1243,7 +1269,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */
     ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed.");
     return (-1);
   }
-  vl->interval = (int) tmp_long;
+  vl->interval = MS_TO_CDTIME_T (tmp_long);
 
   status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr);
   if (status != 0)
@@ -1297,7 +1323,7 @@ static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */
     return (-1);
   }
   /* Java measures time in milliseconds. */
-  n->time = (time_t) (tmp_long / ((jlong) 1000));
+  n->time = MS_TO_CDTIME_T(tmp_long);
 
   status = jtoc_int (jvm_env, &tmp_int,
       class_ptr, object_ptr, "getSeverity");
@@ -1310,7 +1336,7 @@ static int jtoc_notification (JNIEnv *jvm_env, notification_t *n, /* {{{ */
 
   return (0);
 } /* }}} int jtoc_notification */
-/* 
+/*
  * Functions accessible from Java
  */
 static jint JNICALL cjni_api_dispatch_values (JNIEnv *jvm_env, /* {{{ */
@@ -1411,7 +1437,7 @@ 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,
+  plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
       /* interval = */ NULL, &ud);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
@@ -1620,6 +1646,11 @@ static void JNICALL cjni_api_log (JNIEnv *jvm_env, /* {{{ */
   (*jvm_env)->ReleaseStringUTFChars (jvm_env, o_message, c_str);
 } /* }}} void cjni_api_log */
 
+static jstring JNICALL cjni_api_get_hostname (JNIEnv *jvm_env, jobject this)
+{
+    return ctoj_output_string(jvm_env, hostname_g);
+}
+
 /* List of ``native'' functions, i. e. C-functions that can be called from
  * Java. */
 static JNINativeMethod jni_api_functions[] = /* {{{ */
@@ -1679,6 +1710,11 @@ static JNINativeMethod jni_api_functions[] = /* {{{ */
   { "log",
     "(ILjava/lang/String;)V",
     cjni_api_log },
+
+  { "getHostname",
+    "()Ljava/lang/String;",
+    cjni_api_get_hostname },
+
 };
 static size_t jni_api_functions_num = sizeof (jni_api_functions)
   / sizeof (jni_api_functions[0]);
@@ -1721,7 +1757,7 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{
 
     case CB_TYPE_FLUSH:
       method_name = "flush";
-      method_signature = "(ILjava/lang/String;)I";
+      method_signature = "(Ljava/lang/Number;Ljava/lang/String;)I";
       break;
 
     case CB_TYPE_SHUTDOWN:
@@ -1923,7 +1959,9 @@ static int cjni_init_native (JNIEnv *jvm_env) /* {{{ */
   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);
   }
 
@@ -2104,7 +2142,7 @@ static int cjni_thread_detach (void) /* {{{ */
   cjni_env->jvm_env = NULL;
 
   return (0);
-} /* }}} JNIEnv *cjni_thread_attach */
+} /* }}} int cjni_thread_detach */
 
 static int cjni_config_add_jvm_arg (oconfig_item_t *ci) /* {{{ */
 {
@@ -2183,6 +2221,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);
@@ -2236,7 +2283,6 @@ 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;
@@ -2291,7 +2337,7 @@ static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
   method = (*jvm_env)->GetMethodID (jvm_env, class,
       "config", "(Lorg/collectd/api/OConfigItem;)I");
 
-  status = (*jvm_env)->CallIntMethod (jvm_env,
+  (*jvm_env)->CallIntMethod (jvm_env,
       cbi->object, method, o_ocitem);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_ocitem);
@@ -2299,7 +2345,7 @@ static int cjni_config_plugin_block (oconfig_item_t *ci) /* {{{ */
   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;
@@ -2354,7 +2400,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
@@ -2368,7 +2463,7 @@ static void cjni_callback_info_destroy (void *arg) /* {{{ */
 
   cbi = (cjni_callback_info_t *) arg;
 
-  /* This condition can occurr when shutting down. */
+  /* This condition can occur when shutting down. */
   if (jvm == NULL)
   {
     sfree (cbi);
@@ -2400,7 +2495,6 @@ static int cjni_read (user_data_t *ud) /* {{{ */
 {
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2424,13 +2518,7 @@ static int cjni_read (user_data_t *ud) /* {{{ */
   ret_status = (*jvm_env)->CallIntMethod (jvm_env, cbi->object,
       cbi->method);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_read */
 
@@ -2441,7 +2529,6 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
   jobject vl_java;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2466,6 +2553,7 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
   if (vl_java == NULL)
   {
     ERROR ("java plugin: cjni_write: ctoj_value_list failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2474,24 +2562,18 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
 
   (*jvm_env)->DeleteLocalRef (jvm_env, vl_java);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_write: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_write */
 
 /* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */
-static int cjni_flush (int timeout, const char *identifier, /* {{{ */
+static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */
     user_data_t *ud)
 {
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
+  jobject o_timeout;
   jobject o_identifier;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2512,29 +2594,36 @@ static int cjni_flush (int timeout, const char *identifier, /* {{{ */
 
   cbi = (cjni_callback_info_t *) ud->data;
 
+  o_timeout = ctoj_jdouble_to_number (jvm_env,
+      (jdouble) CDTIME_T_TO_DOUBLE (timeout));
+  if (o_timeout == NULL)
+  {
+    ERROR ("java plugin: cjni_flush: Converting double "
+        "to Number object failed.");
+    cjni_thread_detach ();
+    return (-1);
+  }
+
   o_identifier = NULL;
   if (identifier != NULL)
   {
     o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier);
     if (o_identifier == NULL)
     {
+      (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
       ERROR ("java plugin: cjni_flush: NewStringUTF failed.");
+      cjni_thread_detach ();
       return (-1);
     }
   }
 
   ret_status = (*jvm_env)->CallIntMethod (jvm_env,
-      cbi->object, cbi->method, (jint) timeout, o_identifier);
+      cbi->object, cbi->method, o_timeout, o_identifier);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier);
+  (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_flush: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_flush */
 
@@ -2560,7 +2649,10 @@ static void cjni_log (int severity, const char *message, /* {{{ */
 
   o_message = (*jvm_env)->NewStringUTF (jvm_env, message);
   if (o_message == NULL)
+  {
+    cjni_thread_detach ();
     return;
+  }
 
   (*jvm_env)->CallVoidMethod (jvm_env,
       cbi->object, cbi->method, (jint) severity, o_message);
@@ -2578,7 +2670,6 @@ static int cjni_notification (const notification_t *n, /* {{{ */
   JNIEnv *jvm_env;
   cjni_callback_info_t *cbi;
   jobject o_notification;
-  int status;
   int ret_status;
 
   if (jvm == NULL)
@@ -2603,6 +2694,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
   if (o_notification == NULL)
   {
     ERROR ("java plugin: cjni_notification: ctoj_notification failed.");
+    cjni_thread_detach ();
     return (-1);
   }
 
@@ -2611,13 +2703,7 @@ static int cjni_notification (const notification_t *n, /* {{{ */
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_notification);
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-  {
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-    return (-1);
-  }
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_notification */
 
@@ -2645,24 +2731,20 @@ static int cjni_match_target_create (const oconfig_item_t *ci, /* {{{ */
       (*jvm_env)->DeleteLocalRef (jvm_env, cbi_ret->object); \
   } \
   free (cbi_ret); \
-  if (jvm_env != NULL) { \
-    if (o_ci != NULL) \
-      (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
-    cjni_thread_detach (); \
-  } \
+  if (o_ci != NULL) \
+    (*jvm_env)->DeleteLocalRef (jvm_env, o_ci); \
+  cjni_thread_detach (); \
   return (status)
 
   if (jvm == NULL)
   {
     ERROR ("java plugin: cjni_read: jvm == NULL");
-    BAIL_OUT (-1);
+    return (-1);
   }
 
   jvm_env = cjni_thread_attach ();
   if (jvm_env == NULL)
-  {
-    BAIL_OUT (-1);
-  }
+    return (-1);
 
   /* Find out whether to create a match or a target. */
   if (strcasecmp ("Match", ci->key) == 0)
@@ -2856,10 +2938,7 @@ static int cjni_match_target_invoke (const data_set_t *ds, /* {{{ */
     }
   } /* if (cbi->type == CB_TYPE_TARGET) */
 
-  status = cjni_thread_detach ();
-  if (status != 0)
-    ERROR ("java plugin: cjni_read: cjni_thread_detach failed.");
-
+  cjni_thread_detach ();
   return (ret_status);
 } /* }}} int cjni_match_target_invoke */
 
@@ -2990,6 +3069,21 @@ 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_init: jvm == NULL");
@@ -3008,7 +3102,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) */