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