1 2 /* 3 * Copyright (C) NGINX, Inc. 4 */ 5 6 7 #include <jni.h> 8 9 #include <nxt_main.h> 10 #include <nxt_runtime.h> 11 #include <nxt_router.h> 12 #include <nxt_unit.h> 13 #include <nxt_unit_field.h> 14 #include <nxt_unit_request.h> 15 #include <nxt_unit_response.h> 16 17 #include <java/nxt_jni.h> 18 19 #include "java/nxt_jni_Thread.h" 20 #include "java/nxt_jni_Context.h" 21 #include "java/nxt_jni_Request.h" 22 #include "java/nxt_jni_Response.h" 23 #include "java/nxt_jni_InputStream.h" 24 #include "java/nxt_jni_OutputStream.h" 25 #include "java/nxt_jni_URLClassLoader.h" 26 27 #include "nxt_jars.h" 28 29 static nxt_int_t nxt_java_pre_init(nxt_task_t *task, 30 nxt_common_app_conf_t *conf); 31 static nxt_int_t nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf); 32 static void nxt_java_request_handler(nxt_unit_request_info_t *req); 33 34 static uint32_t compat[] = { 35 NXT_VERNUM, NXT_DEBUG, 36 }; 37 38 char *nxt_java_modules; 39 40 41 #define NXT_STRING(x) _NXT_STRING(x) 42 #define _NXT_STRING(x) #x 43 44 NXT_EXPORT nxt_app_module_t nxt_app_module = { 45 sizeof(compat), 46 compat, 47 nxt_string("java"), 48 NXT_STRING(NXT_JAVA_VERSION), 49 nxt_java_pre_init, 50 nxt_java_init, 51 }; 52 53 typedef struct { 54 JNIEnv *env; 55 jobject ctx; 56 } nxt_java_data_t; 57 58 59 static nxt_int_t 60 nxt_java_pre_init(nxt_task_t *task, nxt_common_app_conf_t *conf) 61 { 62 const char *unit_jars; 63 64 unit_jars = conf->u.java.unit_jars; 65 if (unit_jars == NULL) { 66 unit_jars = NXT_JARS; 67 } 68 69 nxt_java_modules = realpath(unit_jars, NULL); 70 if (nxt_java_modules == NULL) { 71 nxt_alert(task, "realpath(%s) failed: %E", unit_jars, nxt_errno); 72 return NXT_ERROR; 73 } 74 75 return NXT_OK; 76 } 77 78 79 static char ** 80 nxt_java_module_jars(const char *jars[], int jar_count) 81 { 82 char **res, *jurl; 83 nxt_int_t modules_len, jlen, i; 84 const char **jar; 85 86 res = nxt_malloc(jar_count * sizeof(char*)); 87 if (res == NULL) { 88 return NULL; 89 } 90 91 modules_len = nxt_strlen(nxt_java_modules); 92 93 for (i = 0, jar = jars; *jar != NULL; jar++) { 94 jlen = nxt_length("file:") + modules_len + nxt_length("/") 95 + nxt_strlen(*jar) + 1; 96 jurl = nxt_malloc(jlen); 97 if (jurl == NULL) { 98 return NULL; 99 } 100 101 res[i++] = jurl; 102 103 jurl = nxt_cpymem(jurl, "file:", nxt_length("file:")); 104 jurl = nxt_cpymem(jurl, nxt_java_modules, modules_len); 105 *jurl++ = '/'; 106 jurl = nxt_cpymem(jurl, *jar, nxt_strlen(*jar)); 107 *jurl++ = '\0'; 108 } 109 110 return res; 111 } 112 113 114 static nxt_int_t 115 nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf) 116 { 117 jint rc; 118 char *opt, *real_path; 119 char **classpath_arr, **unit_jars, **system_jars; 120 JavaVM *jvm; 121 JNIEnv *env; 122 jobject cl, classpath; 123 nxt_str_t str; 124 nxt_int_t opt_len, real_path_len; 125 nxt_uint_t i, unit_jars_count, classpath_count, system_jars_count; 126 JavaVMOption *jvm_opt; 127 JavaVMInitArgs jvm_args; 128 nxt_unit_ctx_t *ctx; 129 nxt_unit_init_t java_init; 130 nxt_java_data_t data; 131 nxt_conf_value_t *value; 132 nxt_java_app_conf_t *c; 133 134 //setenv("ASAN_OPTIONS", "handle_segv=0", 1); 135 136 jvm_args.version = JNI_VERSION_1_6; 137 jvm_args.nOptions = 0; 138 jvm_args.ignoreUnrecognized = 0; 139 140 c = &conf->u.java; 141 142 if (c->options != NULL) { 143 jvm_args.nOptions += nxt_conf_array_elements_count(c->options); 144 } 145 146 jvm_opt = nxt_malloc(jvm_args.nOptions * sizeof(JavaVMOption)); 147 if (jvm_opt == NULL) { 148 nxt_alert(task, "failed to allocate jvm_opt"); 149 return NXT_ERROR; 150 } 151 152 jvm_args.options = jvm_opt; 153 154 unit_jars_count = nxt_nitems(nxt_java_unit_jars) - 1; 155 156 unit_jars = nxt_java_module_jars(nxt_java_unit_jars, unit_jars_count); 157 if (unit_jars == NULL) { 158 nxt_alert(task, "failed to allocate buffer for unit_jars array"); 159 160 return NXT_ERROR; 161 } 162 163 system_jars_count = nxt_nitems(nxt_java_system_jars) - 1; 164 165 system_jars = nxt_java_module_jars(nxt_java_system_jars, system_jars_count); 166 if (system_jars == NULL) { 167 nxt_alert(task, "failed to allocate buffer for system_jars array"); 168 169 return NXT_ERROR; 170 } 171 172 if (c->options != NULL) { 173 174 for (i = 0; /* void */ ; i++) { 175 value = nxt_conf_get_array_element(c->options, i); 176 if (value == NULL) { 177 break; 178 } 179 180 nxt_conf_get_string(value, &str); 181 182 opt = nxt_malloc(str.length + 1); 183 if (opt == NULL) { 184 nxt_alert(task, "failed to allocate jvm_opt"); 185 return NXT_ERROR; 186 } 187 188 memcpy(opt, str.start, str.length); 189 opt[str.length] = '\0'; 190 191 jvm_opt[i].optionString = opt; 192 } 193 } 194 195 if (c->classpath != NULL) { 196 classpath_count = nxt_conf_array_elements_count(c->classpath); 197 classpath_arr = nxt_malloc(classpath_count * sizeof(char *)); 198 199 for (i = 0; /* void */ ; i++) { 200 value = nxt_conf_get_array_element(c->classpath, i); 201 if (value == NULL) { 202 break; 203 } 204 205 nxt_conf_get_string(value, &str); 206 207 opt_len = str.length + 1; 208 209 char *sc = memchr(str.start, ':', str.length); 210 if (sc == NULL && str.start[0] == '/') { 211 opt_len += nxt_length("file:"); 212 } 213 214 opt = nxt_malloc(opt_len); 215 if (opt == NULL) { 216 nxt_alert(task, "failed to allocate classpath"); 217 return NXT_ERROR; 218 } 219 220 if (sc == NULL && str.start[0] != '/') { 221 nxt_memcpy(opt, str.start, str.length); 222 opt[str.length] = '\0'; 223 224 real_path = realpath(opt, NULL); 225 if (real_path == NULL) { 226 nxt_alert(task, "realpath(%s) failed: %E", opt, nxt_errno); 227 return NXT_ERROR; 228 } 229 230 real_path_len = nxt_strlen(real_path); 231 232 free(opt); 233 234 opt_len = nxt_length("file:") + real_path_len + 1; 235 236 opt = nxt_malloc(opt_len); 237 if (opt == NULL) { 238 nxt_alert(task, "failed to allocate classpath"); 239 return NXT_ERROR; 240 } 241 242 } else { 243 real_path = (char *) str.start; /* I love this cast! */ 244 real_path_len = str.length; 245 } 246 247 classpath_arr[i] = opt; 248 249 if (sc == NULL) { 250 opt = nxt_cpymem(opt, "file:", nxt_length("file:")); 251 } 252 253 opt = nxt_cpymem(opt, real_path, real_path_len); 254 *opt = '\0'; 255 } 256 257 } else { 258 classpath_count = 0; 259 classpath_arr = NULL; 260 } 261 262 rc = JNI_CreateJavaVM(&jvm, (void **) &env, &jvm_args); 263 if (rc != JNI_OK) { 264 nxt_alert(task, "failed to create Java VM: %d", (int) rc); 265 return NXT_ERROR; 266 } 267 268 rc = nxt_java_initThread(env); 269 if (rc != NXT_UNIT_OK) { 270 nxt_alert(task, "nxt_java_initThread() failed"); 271 goto env_failed; 272 } 273 274 rc = nxt_java_initURLClassLoader(env); 275 if (rc != NXT_UNIT_OK) { 276 nxt_alert(task, "nxt_java_initURLClassLoader() failed"); 277 goto env_failed; 278 } 279 280 cl = nxt_java_newURLClassLoader(env, system_jars_count, system_jars); 281 if (cl == NULL) { 282 nxt_alert(task, "nxt_java_newURLClassLoader failed"); 283 goto env_failed; 284 } 285 286 nxt_java_setContextClassLoader(env, cl); 287 288 cl = nxt_java_newURLClassLoader_parent(env, unit_jars_count, unit_jars, cl); 289 if (cl == NULL) { 290 nxt_alert(task, "nxt_java_newURLClassLoader_parent failed"); 291 goto env_failed; 292 } 293 294 nxt_java_setContextClassLoader(env, cl); 295 296 rc = nxt_java_initContext(env, cl); 297 if (rc != NXT_UNIT_OK) { 298 nxt_alert(task, "nxt_java_initContext() failed"); 299 goto env_failed; 300 } 301 302 rc = nxt_java_initRequest(env, cl); 303 if (rc != NXT_UNIT_OK) { 304 nxt_alert(task, "nxt_java_initRequest() failed"); 305 goto env_failed; 306 } 307 308 rc = nxt_java_initResponse(env, cl); 309 if (rc != NXT_UNIT_OK) { 310 nxt_alert(task, "nxt_java_initResponse() failed"); 311 goto env_failed; 312 } 313 314 rc = nxt_java_initInputStream(env, cl); 315 if (rc != NXT_UNIT_OK) { 316 nxt_alert(task, "nxt_java_initInputStream() failed"); 317 goto env_failed; 318 } 319 320 rc = nxt_java_initOutputStream(env, cl); 321 if (rc != NXT_UNIT_OK) { 322 nxt_alert(task, "nxt_java_initOutputStream() failed"); 323 goto env_failed; 324 } 325 326 nxt_java_jni_init(env); 327 if (rc != NXT_UNIT_OK) { 328 nxt_alert(task, "nxt_java_jni_init() failed"); 329 goto env_failed; 330 } 331 332 classpath = nxt_java_newURLs(env, classpath_count, classpath_arr); 333 if (classpath == NULL) { 334 nxt_alert(task, "nxt_java_newURLs failed"); 335 goto env_failed; 336 } 337 338 data.env = env; 339 data.ctx = nxt_java_startContext(env, c->webapp, classpath); 340 341 if ((*env)->ExceptionCheck(env)) { 342 nxt_alert(task, "Unhandled exception in application start"); 343 (*env)->ExceptionDescribe(env); 344 return NXT_ERROR; 345 } 346 347 nxt_unit_default_init(task, &java_init); 348 349 java_init.callbacks.request_handler = nxt_java_request_handler; 350 java_init.request_data_size = sizeof(nxt_java_request_data_t); 351 java_init.data = &data; 352 353 ctx = nxt_unit_init(&java_init); 354 if (nxt_slow_path(ctx == NULL)) { 355 nxt_alert(task, "nxt_unit_init() failed"); 356 return NXT_ERROR; 357 } 358 359 rc = nxt_unit_run(ctx); 360 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 361 /* TODO report error */ 362 } 363 364 nxt_unit_done(ctx); 365 366 nxt_java_stopContext(env, data.ctx); 367 368 if ((*env)->ExceptionCheck(env)) { 369 (*env)->ExceptionDescribe(env); 370 } 371 372 (*jvm)->DestroyJavaVM(jvm); 373 374 exit(0); 375 376 return NXT_OK; 377 378 env_failed: 379 380 if ((*env)->ExceptionCheck(env)) { 381 (*env)->ExceptionDescribe(env); 382 } 383 384 return NXT_ERROR; 385 } 386 387 388 static void 389 nxt_java_request_handler(nxt_unit_request_info_t *req) 390 { 391 JNIEnv *env; 392 jobject jreq, jresp; 393 nxt_java_data_t *java_data; 394 nxt_java_request_data_t *data; 395 396 java_data = req->unit->data; 397 env = java_data->env; 398 data = req->data; 399 400 jreq = nxt_java_newRequest(env, java_data->ctx, req); 401 if (jreq == NULL) { 402 nxt_unit_req_alert(req, "failed to create Request instance"); 403 404 if ((*env)->ExceptionCheck(env)) { 405 (*env)->ExceptionDescribe(env); 406 (*env)->ExceptionClear(env); 407 } 408 409 nxt_unit_request_done(req, NXT_UNIT_ERROR); 410 return; 411 } 412 413 jresp = nxt_java_newResponse(env, req); 414 if (jresp == NULL) { 415 nxt_unit_req_alert(req, "failed to create Response instance"); 416 417 if ((*env)->ExceptionCheck(env)) { 418 (*env)->ExceptionDescribe(env); 419 (*env)->ExceptionClear(env); 420 } 421 422 (*env)->DeleteLocalRef(env, jreq); 423 424 nxt_unit_request_done(req, NXT_UNIT_ERROR); 425 return; 426 } 427 428 data->header_size = 10 * 1024; 429 data->buf_size = 32 * 1024; /* from Jetty */ 430 data->jreq = jreq; 431 data->jresp = jresp; 432 data->buf = NULL; 433 434 nxt_unit_request_group_dup_fields(req); 435 436 nxt_java_service(env, java_data->ctx, jreq, jresp); 437 438 if ((*env)->ExceptionCheck(env)) { 439 (*env)->ExceptionDescribe(env); 440 (*env)->ExceptionClear(env); 441 } 442 443 if (!nxt_unit_response_is_init(req)) { 444 nxt_unit_response_init(req, 200, 0, 0); 445 } 446 447 if (!nxt_unit_response_is_sent(req)) { 448 nxt_unit_response_send(req); 449 } 450 451 if (data->buf != NULL) { 452 nxt_unit_buf_send(data->buf); 453 454 data->buf = NULL; 455 } 456 457 (*env)->DeleteLocalRef(env, jresp); 458 (*env)->DeleteLocalRef(env, jreq); 459 460 nxt_unit_request_done(req, NXT_UNIT_OK); 461 } 462 463