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