|
@@ -0,0 +1,268 @@
|
|
|
|
+/**
|
|
|
|
+ * $Id$
|
|
|
|
+ *
|
|
|
|
+ * Copyright (C) 2013 Konstantin Mosesov
|
|
|
|
+ *
|
|
|
|
+ * This file is part of Kamailio, a free SIP server.
|
|
|
|
+ *
|
|
|
|
+ * This file is free software; you can redistribute it and/or modify
|
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
|
+ * (at your option) any later version
|
|
|
|
+ *
|
|
|
|
+ *
|
|
|
|
+ * This file is distributed in the hope that it will be useful,
|
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
+ * GNU General Public License for more details.
|
|
|
|
+ *
|
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <libgen.h>
|
|
|
|
+
|
|
|
|
+#include "../../str.h"
|
|
|
|
+#include "../../sr_module.h"
|
|
|
|
+
|
|
|
|
+#include <jni.h>
|
|
|
|
+
|
|
|
|
+#include "global.h"
|
|
|
|
+
|
|
|
|
+#include "utils.h"
|
|
|
|
+#include "java_mod.h"
|
|
|
|
+#include "java_iface.h"
|
|
|
|
+#include "java_support.h"
|
|
|
|
+
|
|
|
|
+#include "java_native_methods.h"
|
|
|
|
+
|
|
|
|
+MODULE_VERSION
|
|
|
|
+
|
|
|
|
+static str script_name = {.s = "/usr/local/etc/sip-router/handler.java", .len = 0};
|
|
|
|
+static str class_name = {.s = "Kamailio", .len = 10};
|
|
|
|
+static str child_init_mname = { .s = "child_init", .len = 0};
|
|
|
|
+static str java_pkg_tree_path_str = { .s = "pkg_tree_path", .len = 0};
|
|
|
|
+static str java_options_str = { .s = "java_options", .len = 0};
|
|
|
|
+
|
|
|
|
+static int mod_init(void);
|
|
|
|
+static int child_init(int rank);
|
|
|
|
+static void mod_destroy(void);
|
|
|
|
+
|
|
|
|
+char *dname = NULL, *bname = NULL;
|
|
|
|
+
|
|
|
|
+/** module parameters */
|
|
|
|
+static param_export_t params[]={
|
|
|
|
+ {"script_name", STR_PARAM, &script_name },
|
|
|
|
+ {"class_name", STR_PARAM, &class_name },
|
|
|
|
+ {"pkg_tree_path", STR_PARAM, &java_pkg_tree_path_str },
|
|
|
|
+ {"child_init_method", STR_PARAM, &child_init_mname },
|
|
|
|
+ {"java_options", STR_PARAM, &java_options_str },
|
|
|
|
+ {0,0,0}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Exported functions
|
|
|
|
+ */
|
|
|
|
+static cmd_export_t cmds[] = {
|
|
|
|
+ { "java_exec", (cmd_function)java_exec, 1, NULL, 0, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE },
|
|
|
|
+ { 0, 0, 0, 0, 0, 0 }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/** module exports */
|
|
|
|
+struct module_exports exports = {
|
|
|
|
+ "app_java", /* module name */
|
|
|
|
+ RTLD_NOW | RTLD_GLOBAL, /* dlopen flags */
|
|
|
|
+ cmds, /* exported functions */
|
|
|
|
+ params, /* exported parameters */
|
|
|
|
+ 0, /* exported statistics */
|
|
|
|
+ 0, /* exported MI functions */
|
|
|
|
+ 0, /* exported pseudo-variables */
|
|
|
|
+ 0, /* extra processes */
|
|
|
|
+ mod_init, /* module initialization function */
|
|
|
|
+ (response_function) NULL, /* response handling function */
|
|
|
|
+ (destroy_function) mod_destroy, /* destroy function */
|
|
|
|
+ child_init /* per-child init function */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int mod_init(void)
|
|
|
|
+{
|
|
|
|
+ JavaVMInitArgs vm_args;
|
|
|
|
+ jint res;
|
|
|
|
+ JavaVMOption *options;
|
|
|
|
+ char **opts;
|
|
|
|
+ int nOptions;
|
|
|
|
+ char *class_object_name;
|
|
|
|
+ size_t class_object_name_len;
|
|
|
|
+
|
|
|
|
+ options = (JavaVMOption *)pkg_realloc(NULL, sizeof(JavaVMOption));
|
|
|
|
+ if (!options)
|
|
|
|
+ {
|
|
|
|
+ LM_ERR("Couldn't initialize Java VM: not enough memory\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ memset(options, 0, sizeof(JavaVMOption));
|
|
|
|
+
|
|
|
|
+ LM_INFO("Initializing Java VM with options: %s\n", java_options_str.s);
|
|
|
|
+
|
|
|
|
+ opts = split(java_options_str.s, " ");
|
|
|
|
+ for (nOptions=0; opts[nOptions] != NULL; nOptions++)
|
|
|
|
+ {
|
|
|
|
+ options[nOptions].optionString = opts[nOptions];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* IMPORTANT: specify vm_args version # if you use JDK1.1.2 and beyond */
|
|
|
|
+ vm_args.version = JNI_VERSION_1_2;
|
|
|
|
+ vm_args.nOptions = nOptions;
|
|
|
|
+ vm_args.ignoreUnrecognized = JNI_FALSE;
|
|
|
|
+ vm_args.options = options;
|
|
|
|
+
|
|
|
|
+ res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
|
|
|
|
+ if (res < 0)
|
|
|
|
+ {
|
|
|
|
+ handle_VM_init_failure(res);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LM_INFO("app_java: Java VM initialization OK\n");
|
|
|
|
+
|
|
|
|
+ // attach to current thread
|
|
|
|
+ (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
|
|
|
|
+ if ((*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Loading main class
|
|
|
|
+ if (java_pkg_tree_path_str.len <= 0)
|
|
|
|
+ {
|
|
|
|
+ class_object_name_len = strlen(JAVA_MODULE_PKG_PATH) + class_name.len + 1; // default package name plus class name plus package trailing slash
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ class_object_name_len = java_pkg_tree_path_str.len + class_name.len + 1; // package name plus class name plus package trailing slash
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ class_object_name = (char *)pkg_realloc(NULL, (class_object_name_len + 1) * sizeof(char));
|
|
|
|
+ if (!class_object_name)
|
|
|
|
+ {
|
|
|
|
+ LM_ERR("pkg_realloc has failed. Not enough memory!\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ memset(class_object_name, 0, (class_object_name_len + 1) * sizeof(char));
|
|
|
|
+ if (java_pkg_tree_path_str.len <= 0)
|
|
|
|
+ {
|
|
|
|
+ snprintf(class_object_name, class_object_name_len, "%s%s", java_pkg_tree_path_str.s, class_name.s);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ snprintf(class_object_name, class_object_name_len, "%s/%s", JAVA_MODULE_PKG_PATH, class_name.s);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// KamailioClass = (*env)->FindClass(env, class_name.s);
|
|
|
|
+ KamailioClass = (*env)->FindClass(env, class_object_name);
|
|
|
|
+ pkg_free(class_object_name);
|
|
|
|
+ if (!KamailioClass || (*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ KamailioID = (*env)->GetMethodID(env, KamailioClass, "<init>", "()V");
|
|
|
|
+ if (!KamailioID || (*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // calling constructor
|
|
|
|
+ KamailioClassInstance = (*env)->NewObject(env, KamailioClass, KamailioID);
|
|
|
|
+ if (!KamailioClassInstance || (*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // keep a reference to kamailio class instance
|
|
|
|
+ KamailioClassInstanceRef = (*env)->NewGlobalRef(env, KamailioClassInstance);
|
|
|
|
+ if (!KamailioClassInstanceRef || (*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LM_INFO("app_java: initialization OK\n");
|
|
|
|
+
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int child_init(int rank)
|
|
|
|
+{
|
|
|
|
+ int retval;
|
|
|
|
+ jmethodID child_init_id;
|
|
|
|
+
|
|
|
|
+ // attach to current thread
|
|
|
|
+ (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
|
|
|
|
+ if ((*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ child_init_id = (*env)->GetMethodID(env, KamailioClass, "child_init", "(I)I");
|
|
|
|
+ if ((*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ retval = (int)(*env)->CallIntMethod(env, KamailioClassInstanceRef, child_init_id, rank);
|
|
|
|
+ if ((*env)->ExceptionCheck(env))
|
|
|
|
+ {
|
|
|
|
+ handle_exception();
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ (*env)->DeleteLocalRef(env, child_init_id);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void mod_destroy(void)
|
|
|
|
+{
|
|
|
|
+ if (env != NULL)
|
|
|
|
+ {
|
|
|
|
+ if (KamailioClassInstanceRef != NULL)
|
|
|
|
+ (*env)->DeleteGlobalRef(env, KamailioClassInstanceRef);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (jvm != NULL)
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ (*jvm)->DetachCurrentThread(jvm);
|
|
|
|
+ (*jvm)->DestroyJavaVM(jvm);
|
|
|
|
+ }
|
|
|
|
+}
|