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