1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2007, Damjan Jovanovic <d a m j a n d o t j o v a t g m a i l d o t c o m>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Damjan Jovanovic <d a m j a n d o t j o v a t g m a i l d o t c o m>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Damjan Jovanovic <d a m j a n d o t j o v a t g m a i l d o t c o m>
27 *
28 *
29 * mod_java.c -- Java Module
30 *
31 */
32
33 #include <switch.h>
34 #include <jni.h>
35
36
37 static switch_memory_pool_t *memoryPool = NULL;
38 static switch_dso_lib_t javaVMHandle = NULL;
39
40 JavaVM *javaVM = NULL;
41 jclass launcherClass = NULL;
42
43 SWITCH_MODULE_LOAD_FUNCTION(mod_java_load);
44 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_java_shutdown);
45 SWITCH_MODULE_DEFINITION(mod_java, mod_java_load, mod_java_shutdown, NULL);
46
47 struct user_method {
48 const char * class;
49 const char * method;
50 const char * arg;
51 };
52
53 typedef struct user_method user_method_t;
54
55 struct vm_control {
56 struct user_method startup;
57 struct user_method shutdown;
58 };
59
60 typedef struct vm_control vm_control_t;
61 static vm_control_t vmControl;
62
launch_java(switch_core_session_t * session,const char * data,JNIEnv * env)63 static void launch_java(switch_core_session_t *session, const char *data, JNIEnv *env)
64 {
65 jmethodID launch = NULL;
66 jstring uuid = NULL;
67 jstring args = NULL;
68
69 if (launcherClass == NULL)
70 {
71 goto done;
72 }
73
74 launch = (*env)->GetStaticMethodID(env, launcherClass, "launch", "(Ljava/lang/String;Ljava/lang/String;)V");
75 if (launch == NULL)
76 {
77 (*env)->ExceptionDescribe(env);
78 goto done;
79 }
80
81 uuid = (*env)->NewStringUTF(env, switch_core_session_get_uuid(session));
82 if (uuid == NULL)
83 {
84 (*env)->ExceptionDescribe(env);
85 goto done;
86 }
87
88 args = (*env)->NewStringUTF(env, data);
89 if (args == NULL)
90 {
91 (*env)->ExceptionDescribe(env);
92 goto done;
93 }
94
95 (*env)->CallStaticVoidMethod(env, launcherClass, launch, uuid, args);
96 if ((*env)->ExceptionOccurred(env))
97 (*env)->ExceptionDescribe(env);
98
99 done:
100 if (args != NULL)
101 (*env)->DeleteLocalRef(env, args);
102 if (uuid != NULL)
103 (*env)->DeleteLocalRef(env, uuid);
104 }
105
exec_user_method(user_method_t * userMethod)106 static switch_status_t exec_user_method(user_method_t * userMethod) {
107 switch_status_t status = SWITCH_STATUS_SUCCESS;
108 jclass class = NULL;
109 jmethodID method = NULL;
110 jstring arg = NULL;
111 JNIEnv *env;
112 jint res;
113
114 if (javaVM == NULL || userMethod->class == NULL) {
115 return status;
116 }
117
118 res = (*javaVM)->AttachCurrentThread(javaVM, (void*) &env, NULL);
119
120 if (res != JNI_OK) {
121 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error attaching thread to Java VM!\n");
122 (*env)->ExceptionDescribe(env);
123 status = SWITCH_STATUS_FALSE;
124 goto done;
125 }
126
127 class = (*env)->FindClass(env, userMethod->class);
128
129 if (class == NULL) {
130 (*env)->ExceptionDescribe(env);
131 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s Class not found\n",userMethod->class);
132 status = SWITCH_STATUS_FALSE;
133 goto done;
134 }
135
136 method = (*env)->GetStaticMethodID(env, class, userMethod->method, "(Ljava/lang/String;)V");
137
138 if (method == NULL) {
139 (*env)->ExceptionDescribe(env);
140 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s Method not found\n",userMethod->method);
141 status = SWITCH_STATUS_FALSE;
142 goto done;
143 }
144
145 if (userMethod->arg != NULL) {
146 arg = (*env)->NewStringUTF(env, userMethod->arg);
147
148 if (arg == NULL) {
149 (*env)->ExceptionDescribe(env);
150 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Args not found\n");
151 status = SWITCH_STATUS_FALSE;
152 goto done;
153 }
154 }
155
156 (*env)->CallStaticVoidMethod(env, class, method, arg);
157
158 if ((*env)->ExceptionOccurred(env)){
159 (*env)->ExceptionDescribe(env);
160 status = SWITCH_STATUS_FALSE;
161 }
162
163 done:
164 if (arg != NULL)
165 (*env)->DeleteLocalRef(env, arg);
166 if (class != NULL)
167 (*env)->DeleteLocalRef(env, class);
168 (*javaVM)->DetachCurrentThread(javaVM);
169 return status;
170 }
171
SWITCH_STANDARD_APP(java_function)172 SWITCH_STANDARD_APP(java_function)
173 {
174 JNIEnv *env;
175 jint res;
176
177 if (javaVM == NULL)
178 return;
179
180 res = (*javaVM)->AttachCurrentThread(javaVM, (void*) &env, NULL);
181 if (res == JNI_OK)
182 {
183 launch_java(session, data, env);
184 (*javaVM)->DetachCurrentThread(javaVM);
185 }
186 else
187 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error attaching thread to Java VM!\n");
188 }
189
load_config(JavaVMOption ** javaOptions,int * optionCount,vm_control_t * vmControl)190 static switch_status_t load_config(JavaVMOption **javaOptions, int *optionCount, vm_control_t * vmControl)
191 {
192 switch_xml_t cfg, xml;
193 switch_status_t status = SWITCH_STATUS_SUCCESS;
194 char *derr = NULL;
195
196 xml = switch_xml_open_cfg("java.conf", &cfg, NULL);
197 if (xml)
198 {
199 switch_xml_t javavm;
200 switch_xml_t options;
201 switch_xml_t startup;
202 switch_xml_t shutdown;
203
204 javavm = switch_xml_child(cfg, "javavm");
205 if (javavm != NULL)
206 {
207 const char *path = switch_xml_attr_soft(javavm, "path");
208 if (path != NULL)
209 {
210 javaVMHandle = switch_dso_open(path, 0, &derr);
211 if (derr || !javaVMHandle) {
212 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error loading %s\n", path);
213 }
214 }
215 else
216 {
217 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Java VM path specified in java.conf.xml\n");
218 status = SWITCH_STATUS_FALSE;
219 }
220 }
221 else
222 {
223 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Java VM specified in java.conf.xml\n");
224 status = SWITCH_STATUS_FALSE;
225 goto close;
226 }
227
228 options = switch_xml_child(cfg, "options");
229 if (options != NULL)
230 {
231 switch_xml_t option;
232 int i = 0;
233 *optionCount = 0;
234 for (option = switch_xml_child(options, "option"); option; option = option->next)
235 {
236 const char *value = switch_xml_attr_soft(option, "value");
237 if (value != NULL)
238 ++*optionCount;
239 }
240 *optionCount += 1;
241 *javaOptions = switch_core_alloc(memoryPool, (switch_size_t)(*optionCount * sizeof(JavaVMOption)));
242 if (*javaOptions == NULL)
243 {
244 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory!\n");
245 status = SWITCH_STATUS_FALSE;
246 goto close;
247 }
248 for (option = switch_xml_child(options, "option"); option; option = option->next)
249 {
250 const char *value = switch_xml_attr_soft(option, "value");
251 if (value == NULL)
252 continue;
253 (*javaOptions)[i].optionString = switch_core_strdup(memoryPool, value);
254 if ((*javaOptions)[i].optionString == NULL)
255 {
256 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory!\n");
257 status = SWITCH_STATUS_FALSE;
258 goto close;
259 }
260 ++i;
261 }
262 (*javaOptions)[i].optionString = switch_core_sprintf(memoryPool, "-Djava.library.path=%s", SWITCH_GLOBAL_dirs.mod_dir);
263 }
264
265 /*
266 <startup class="net/cog/fs/system/Control" method="startup" arg="start up arg"/>
267 <shutdown class="net/cog/fs/system/Control" method="shutdown" arg="shutdown arg"/>
268 */
269
270 memset(vmControl, 0, sizeof(struct vm_control));
271 startup = switch_xml_child(cfg, "startup");
272 if (startup != NULL) {
273 vmControl->startup.class = switch_xml_attr_soft(startup, "class");
274 vmControl->startup.method = switch_xml_attr_soft(startup, "method");
275 vmControl->startup.arg = switch_xml_attr_soft(startup, "arg");
276 }
277 shutdown = switch_xml_child(cfg, "shutdown");
278 if (shutdown != NULL) {
279 vmControl->shutdown.class = switch_xml_attr_soft(shutdown, "class");
280 vmControl->shutdown.method = switch_xml_attr_soft(shutdown, "method");
281 vmControl->shutdown.arg = switch_xml_attr_soft(shutdown, "arg");
282 }
283
284 close:
285 switch_xml_free(xml);
286 }
287 else
288 {
289 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening java.conf.xml\n");
290 status = SWITCH_STATUS_FALSE;
291 }
292 return status;
293 }
294
create_java_vm(JavaVMOption * options,int optionCount,vm_control_t * vmControl)295 static switch_status_t create_java_vm(JavaVMOption *options, int optionCount, vm_control_t * vmControl)
296 {
297 jint (JNICALL *pJNI_CreateJavaVM)(JavaVM**,void**,void*);
298 switch_status_t status;
299 char *derr = NULL;
300
301 pJNI_CreateJavaVM = (jint (*)(JavaVM **, void **, void *))switch_dso_func_sym(javaVMHandle, "JNI_CreateJavaVM", &derr);
302
303 if (!derr)
304 {
305 JNIEnv *env;
306 JavaVMInitArgs initArgs;
307 jint res;
308
309 memset(&initArgs, 0, sizeof(initArgs));
310 initArgs.version = JNI_VERSION_1_4;
311 initArgs.nOptions = optionCount;
312 initArgs.options = options;
313 initArgs.ignoreUnrecognized = JNI_TRUE;
314
315 res = pJNI_CreateJavaVM(&javaVM, (void*) &env, &initArgs);
316 if (res == JNI_OK)
317 {
318 // call FindClass here already so that the Java VM executes the static
319 // initializer (@see org.freeswitch.Launcher) which loads the jni library
320 // so we can use jni functions right away (for example in the startup method)
321 launcherClass = (*env)->FindClass(env, "org/freeswitch/Launcher");
322 if ( launcherClass == NULL )
323 {
324 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to find 'org.freeswitch.Launcher' class!\n");
325 (*env)->ExceptionDescribe(env);
326 }
327
328 // store a global reference for use in the launch_java() function
329 launcherClass = (*env)->NewGlobalRef(env, launcherClass);
330 if ( launcherClass == NULL )
331 {
332 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory!\n");
333 (*env)->ExceptionDescribe(env);
334 status = SWITCH_STATUS_FALSE;
335 }
336 else
337 {
338 status = SWITCH_STATUS_SUCCESS;
339 }
340
341 (*javaVM)->DetachCurrentThread(javaVM);
342 }
343 else
344 {
345 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating Java VM!\n");
346 status = SWITCH_STATUS_FALSE;
347 }
348 }
349 else
350 {
351 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Specified Java VM doesn't have JNI_CreateJavaVM\n");
352 status = SWITCH_STATUS_FALSE;
353 }
354 return status;
355 }
356
SWITCH_MODULE_LOAD_FUNCTION(mod_java_load)357 SWITCH_MODULE_LOAD_FUNCTION(mod_java_load)
358 {
359 switch_status_t status;
360 JavaVMOption *options = NULL;
361
362 int optionCount = 0;
363 switch_application_interface_t *app_interface;
364
365 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
366 SWITCH_ADD_APP(app_interface, "java", NULL, NULL, java_function, NULL, SAF_SUPPORT_NOMEDIA);
367
368 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Java Framework Loading...\n");
369
370 if (javaVM != NULL)
371 return SWITCH_STATUS_SUCCESS;
372
373 status = switch_core_new_memory_pool(&memoryPool);
374 if (status == SWITCH_STATUS_SUCCESS)
375 {
376 status = load_config(&options, &optionCount, &vmControl);
377
378 if (status == SWITCH_STATUS_SUCCESS) {
379
380 status = create_java_vm(options, optionCount, &vmControl);
381
382 if (status == SWITCH_STATUS_SUCCESS) {
383 status = exec_user_method(&vmControl.startup);
384 if (status == SWITCH_STATUS_SUCCESS){
385 return SWITCH_STATUS_SUCCESS;
386 }
387 }
388
389 if (javaVMHandle) {
390 switch_dso_destroy(&javaVMHandle);
391 }
392 }
393 switch_core_destroy_memory_pool(&memoryPool);
394 }
395 else
396 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating memory pool\n");
397
398 return status == SWITCH_STATUS_SUCCESS ? SWITCH_STATUS_NOUNLOAD : status;
399 }
400
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_java_shutdown)401 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_java_shutdown)
402 {
403 if (javaVM == NULL)
404 return SWITCH_STATUS_FALSE;
405
406 exec_user_method(&vmControl.shutdown);
407 (*javaVM)->DestroyJavaVM(javaVM);
408 javaVM = NULL;
409 if (javaVMHandle) {
410 switch_dso_destroy(&javaVMHandle);
411 }
412 switch_core_destroy_memory_pool(&memoryPool);
413 return SWITCH_STATUS_SUCCESS;
414 }
415
416
417