core, lib, modules: restructured source code tree
[sip-router] / src / modules / app_java / app_java_mod.c
1 /**
2  * Copyright (C) 2013 Konstantin Mosesov
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * This file is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #include <libgen.h>
23
24 #include "../../str.h"
25 #include "../../sr_module.h"
26
27 #include <jni.h>
28
29 #include "global.h"
30
31 #include "utils.h"
32 #include "app_java_mod.h"
33 #include "java_iface.h"
34 #include "java_support.h"
35
36 #include "java_native_methods.h"
37
38 MODULE_VERSION
39
40 static char* class_name = "Kamailio";
41 static char* child_init_mname = "child_init";
42 static char* java_options_str = "-Djava.compiler=NONE";
43
44 static int mod_init(void);
45 static int child_init(int rank);
46 static void mod_destroy(void);
47
48
49 /** module parameters */
50 static param_export_t params[] = {
51     {"class_name",         PARAM_STRING, &class_name },
52     {"child_init_method",  PARAM_STRING, &child_init_mname }, /* Unused parameter? */
53     {"java_options",       PARAM_STRING, &java_options_str },
54     {"force_cmd_exec", INT_PARAM, &force_cmd_exec },
55     {0,0,0}
56 };
57
58
59 /*
60  * Exported functions
61  */
62 static cmd_export_t cmds[] = {
63     { "java_method_exec",               (cmd_function)j_nst_exec_0,     2,      NULL,   0,      ANY_ROUTE },
64     { "java_method_exec",               (cmd_function)j_nst_exec_1,     3,      NULL,   0,      ANY_ROUTE },
65     { "java_s_method_exec",             (cmd_function)j_s_nst_exec_0,   2,      NULL,   0,      ANY_ROUTE },
66     { "java_s_method_exec",             (cmd_function)j_s_nst_exec_1,   3,      NULL,   0,      ANY_ROUTE },
67
68     { "java_staticmethod_exec",         (cmd_function)j_st_exec_0,      2,      NULL,   0,      ANY_ROUTE },
69     { "java_staticmethod_exec",         (cmd_function)j_st_exec_1,      3,      NULL,   0,      ANY_ROUTE },
70     { "java_s_staticmethod_exec",       (cmd_function)j_s_st_exec_0,    2,      NULL,   0,      ANY_ROUTE },
71     { "java_s_staticmethod_exec",       (cmd_function)j_s_st_exec_1,    3,      NULL,   0,      ANY_ROUTE },
72
73     { 0, 0, 0, 0, 0, 0 }
74 };
75
76 /** module exports */
77 struct module_exports exports = {
78     APP_NAME,                       /* module name */
79 //    RTLD_NOW | RTLD_GLOBAL,         /* dlopen flags */
80     DEFAULT_DLFLAGS,                /* dlopen flags */
81     cmds,                           /* exported functions */
82     params,                         /* exported parameters */
83     0,                              /* exported statistics */
84     0,                              /* exported MI functions */
85     0,                              /* exported pseudo-variables */
86     0,                              /* extra processes */
87     mod_init,                       /* module initialization function */
88     (response_function) NULL,       /* response handling function */
89     (destroy_function) mod_destroy, /* destroy function */
90     child_init                      /* per-child init function */
91 };
92
93 static int mod_init(void)
94 {
95     JavaVMInitArgs  vm_args;
96     jint res;
97     JavaVMOption *options;
98     char **opts;
99     int nOptions;
100
101     if (force_cmd_exec < 0 || force_cmd_exec > 1)
102     {
103         LM_ERR("Parameter force_cmd_exec should be either 0 or 1\n");
104         return -1;
105     }
106
107     if (force_cmd_exec)
108     {
109         LM_NOTICE("%s: Parameter force_cmd_exec may cause a memory leaks if used from embedded languages\n", APP_NAME);
110     }
111
112     options = (JavaVMOption *)pkg_malloc(sizeof(JavaVMOption));
113     if (!options)
114     {
115         LM_ERR("pkg_malloc() failed: Couldn't initialize Java VM: Not enough memory\n");
116         return -1;
117     }
118     memset(options, 0, sizeof(JavaVMOption));
119
120     LM_INFO("Initializing Java VM with options: %s\n", java_options_str);
121
122     opts = split(java_options_str, " ");
123     for (nOptions=0; opts[nOptions] != NULL; nOptions++)
124     {
125         options[nOptions].optionString = opts[nOptions];
126     }
127
128     /* IMPORTANT: specify vm_args version # if you use JDK1.1.2 and beyond */
129     vm_args.version = JNI_VERSION_1_2;
130     vm_args.nOptions = nOptions;
131     vm_args.ignoreUnrecognized = JNI_FALSE;
132     vm_args.options = options;
133
134     res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
135     if (res < 0)
136     {
137         handle_VM_init_failure(res);
138         return -1;
139     }
140
141     LM_INFO("%s: Java VM initialization OK\n", APP_NAME);
142
143     // attach to current thread
144     (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
145     if ((*env)->ExceptionCheck(env))
146     {
147         handle_exception();
148         return -1;
149     }
150
151     KamailioClass = (*env)->FindClass(env, class_name);
152     if (!KamailioClass || (*env)->ExceptionCheck(env))
153     {
154         handle_exception();
155         (*jvm)->DetachCurrentThread(jvm);
156         return -1;
157     }
158
159     KamailioClassRef = (*env)->NewGlobalRef(env, KamailioClass);
160     if (!KamailioClassRef || (*env)->ExceptionCheck(env))
161     {
162         handle_exception();
163         (*jvm)->DetachCurrentThread(jvm);
164         return -1;
165     }
166
167     KamailioID = (*env)->GetMethodID(env, KamailioClass, "<init>", "()V");
168     if (!KamailioID || (*env)->ExceptionCheck(env))
169     {
170         handle_exception();
171         (*jvm)->DetachCurrentThread(jvm);
172         return -1;
173     }
174
175     // calling constructor
176     KamailioClassInstance = (*env)->NewObject(env, KamailioClass, KamailioID);
177     if (!KamailioClassInstance || (*env)->ExceptionCheck(env))
178     {
179         handle_exception();
180         (*jvm)->DetachCurrentThread(jvm);
181         return -1;
182     }
183
184     // keep a reference to kamailio class instance
185     KamailioClassInstanceRef = (*env)->NewGlobalRef(env, KamailioClassInstance);
186     if (!KamailioClassInstanceRef || (*env)->ExceptionCheck(env))
187     {
188         handle_exception();
189         (*jvm)->DetachCurrentThread(jvm);
190         return -1;
191     }
192
193     LM_INFO("%s: module initialization OK\n", APP_NAME);
194
195     if (jvm != NULL)
196         (*jvm)->DetachCurrentThread(jvm);
197
198     return 0;
199 }
200
201 static int child_init(int rank)
202 {
203     int retval;
204     jmethodID child_init_id;
205
206     // attach to current thread
207     (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
208     if ((*env)->ExceptionCheck(env))
209     {
210         handle_exception();
211         return -1;
212     }
213
214     child_init_id = (*env)->GetMethodID(env, KamailioClass, "child_init", "(I)I");
215     if ((*env)->ExceptionCheck(env))
216     {
217         handle_exception();
218         (*jvm)->DetachCurrentThread(jvm);
219         return -1;
220     }
221
222     retval = (int)(*env)->CallIntMethod(env, KamailioClassInstanceRef, child_init_id, rank);
223     if ((*env)->ExceptionCheck(env))
224     {
225         handle_exception();
226         (*jvm)->DetachCurrentThread(jvm);
227         return -1;
228     }
229
230     (*env)->DeleteLocalRef(env, child_init_id);
231     (*jvm)->DetachCurrentThread(jvm);
232
233     msg = NULL;
234
235     return retval;
236 }
237
238 static void mod_destroy(void)
239 {
240     if (env != NULL)
241     {
242         (*env)->DeleteGlobalRef(env, KamailioClassInstanceRef);
243         (*env)->DeleteGlobalRef(env, KamailioClassRef);
244     }
245
246     if (jvm != NULL)
247     {
248         (*jvm)->DetachCurrentThread(jvm);
249         (*jvm)->DestroyJavaVM(jvm);
250     }
251
252     if (msg)
253     {
254         pkg_free(msg);
255     }
256
257 }