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