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