core, lib, modules: restructured source code tree
[sip-router] / src / modules / app_java / java_support.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  *
12  * This file is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #include <libgen.h>
24
25 #include "../../str.h"
26 #include "../../sr_module.h"
27
28 #include <jni.h>
29
30 #include "global.h"
31
32 #include "utils.h"
33 #include "app_java_mod.h"
34 #include "java_iface.h"
35 #include "java_support.h"
36
37
38 static char *_append_exception_trace_messages(char *msg_str, jthrowable a_exception, jmethodID a_mid_throwable_getCause, jmethodID a_mid_throwable_getStackTrace, jmethodID a_mid_throwable_toString, jmethodID a_mid_frame_toString)
39 {
40     jobjectArray frames;
41     jsize frames_length, i;
42     jstring msg_obj;
43     jobject frame;
44     jthrowable cause;
45     jclass exClass;
46     jmethodID mid;
47     jboolean isCopy;
48
49     const char *tmpbuf;
50
51     // Get the array of StackTraceElements.
52     frames = (jobjectArray) (*env)->CallObjectMethod(env, a_exception, a_mid_throwable_getStackTrace);
53     if (!frames)
54     {
55         exClass = (*env)->GetObjectClass(env, a_exception);
56         mid = (*env)->GetMethodID(env, exClass, "toString", "()Ljava/lang/String;");
57         msg_obj = (jstring) (*env)->CallObjectMethod(env, a_exception, mid);
58
59         isCopy = JNI_FALSE;
60         tmpbuf = (*env)->GetStringUTFChars(env, msg_obj, &isCopy);
61
62         strcat(msg_str, tmpbuf);
63         strcat(msg_str, "\n    <<No stacktrace available>>");
64
65         (*env)->ReleaseStringUTFChars(env, msg_obj, tmpbuf);
66         (*env)->DeleteLocalRef(env, msg_obj);
67
68         return msg_str;
69     }
70     else
71     {
72         frames_length = (*env)->GetArrayLength(env, frames);
73     }
74
75     // Add Throwable.toString() before descending stack trace messages.
76     if (frames != 0)
77     {
78         msg_obj = (jstring) (*env)->CallObjectMethod(env, a_exception, a_mid_throwable_toString);
79         tmpbuf = (*env)->GetStringUTFChars(env, msg_obj, 0);
80         
81         strcat(msg_str, "Exception in thread \"main\" ");
82         strcat(msg_str, tmpbuf);
83
84         (*env)->ReleaseStringUTFChars(env, msg_obj, tmpbuf);
85         (*env)->DeleteLocalRef(env, msg_obj);
86     }
87
88
89     // Append stack trace messages if there are any.
90     if (frames_length > 0)
91     {
92         for (i=0; i<frames_length; i++)
93         {
94             // Get the string returned from the 'toString()'
95             // method of the next frame and append it to
96             // the error message.
97             frame = (*env)->GetObjectArrayElement(env, frames, i);
98             msg_obj = (jstring) (*env)->CallObjectMethod(env, frame, a_mid_frame_toString);
99
100             tmpbuf = (*env)->GetStringUTFChars(env, msg_obj, 0);
101
102             strcat(msg_str, "\n    at ");
103             strcat(msg_str, tmpbuf);
104
105             (*env)->ReleaseStringUTFChars(env, msg_obj, tmpbuf);
106             (*env)->DeleteLocalRef(env, msg_obj);
107             (*env)->DeleteLocalRef(env, frame);
108         }
109     }
110     else
111     {
112         strcat(msg_str, "\n    <<No stacktrace available>>");
113     }
114
115
116     // If 'a_exception' has a cause then append the
117     // stack trace messages from the cause.
118     if (frames != 0)
119     {
120         cause = (jthrowable) (*env)->CallObjectMethod(env, a_exception, a_mid_throwable_getCause);
121         if (cause != 0)
122         {
123             tmpbuf = _append_exception_trace_messages(msg_str, cause, a_mid_throwable_getCause, a_mid_throwable_getStackTrace, a_mid_throwable_toString, a_mid_frame_toString);
124             strcat(msg_str, tmpbuf);
125         }
126     }
127
128     if (msg_str != NULL)
129         return strdup(msg_str);
130     else
131         return NULL;
132 }
133
134 void handle_exception(void)
135 {
136     char *error_msg = NULL;
137     char msg_str[8192];
138
139     jthrowable exception;
140     jclass throwable_class, frame_class;
141     jmethodID mid_throwable_getCause, mid_throwable_getStackTrace;
142     jmethodID mid_throwable_toString, mid_frame_toString;
143
144     if (!(*env)->ExceptionCheck(env))
145         return;
146
147     memset(&msg_str, 0, sizeof(msg_str));
148
149     // Get the exception and clear as no
150     // JNI calls can be made while an exception exists.
151     exception = (*env)->ExceptionOccurred(env);
152     if (exception)
153     {
154 //      (*env)->ExceptionDescribe(env);
155         (*env)->ExceptionClear(env);
156
157         throwable_class = (*env)->FindClass(env, "java/lang/Throwable");
158
159         mid_throwable_getCause = (*env)->GetMethodID(env, throwable_class, "getCause", "()Ljava/lang/Throwable;");
160         mid_throwable_getStackTrace =  (*env)->GetMethodID(env, throwable_class, "getStackTrace",  "()[Ljava/lang/StackTraceElement;");
161         mid_throwable_toString = (*env)->GetMethodID(env, throwable_class, "toString", "()Ljava/lang/String;");
162
163         frame_class = (*env)->FindClass(env, "java/lang/StackTraceElement");
164         mid_frame_toString = (*env)->GetMethodID(env, frame_class, "toString", "()Ljava/lang/String;");
165
166         error_msg = _append_exception_trace_messages(msg_str, exception, mid_throwable_getCause, mid_throwable_getStackTrace, mid_throwable_toString, mid_frame_toString);    
167
168
169         (*env)->DeleteLocalRef(env, exception);
170     }
171
172     LM_ERR("%s: Exception:\n%s\n", APP_NAME, error_msg == NULL ? "(no info)" : error_msg);
173
174 }
175
176 void ThrowNewException(JNIEnv *env, char *fmt, ...)
177 {
178     va_list ap;
179     char buf[1024];
180
181     memset(buf, 0, sizeof(char));
182
183     va_start(ap, fmt);
184     vsnprintf(buf, 1024, fmt, ap);
185     va_end(ap);
186
187     (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), buf);
188 }
189
190 void handle_VM_init_failure(int res)
191 {
192     switch(res)
193     {
194             case -1:
195                 LM_ERR("%s: Couldn't initialize Java VM: unknown error\n", APP_NAME);
196                 break;
197             case -2:
198                 LM_ERR("%s: Couldn't initialize Java VM: thread detached from the VM\n", APP_NAME);
199                 break;
200             case -3:
201                 LM_ERR("%s: Couldn't initialize Java VM: JNI version error\n", APP_NAME);
202                 break;
203             case -4:
204                 LM_ERR("%s: Couldn't initialize Java VM: not enough memory\n", APP_NAME);
205                 break;
206             case -5:
207                 LM_ERR("%s: Couldn't initialize Java VM: VM already created\n", APP_NAME);
208                 break;
209             case -6:
210                 LM_ERR("%s: Couldn't initialize Java VM: invalid arguments\n", APP_NAME);
211                 break;
212             default:
213                 LM_ERR("%s: Couldn't initialize Java VM. Error code: %d\n", APP_NAME, res);
214                 break;
215     }
216 }
217
218