bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / tomcat-connectors-1.2.32-src / native / common / jk_jni_worker.c
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 /***************************************************************************
19  * Description: In process JNI worker                                      *
20  * Author:      Gal Shachor <shachor@il.ibm.com>                           *
21  * Based on:                                                               *
22  * Version:     $Revision: 750428 $                                           *
23  ***************************************************************************/
24
25 #if !defined(WIN32) && !defined(NETWARE) && !defined(AS400)
26 #include <dlfcn.h>
27 #endif
28
29 #include <jni.h>
30
31 #include "jk_pool.h"
32 #include "jk_jni_worker.h"
33 #include "jk_util.h"
34
35 #if !defined(JNI_VERSION_1_6)
36 #if defined LINUX && defined APACHE2_SIGHACK
37 #include <pthread.h>
38 #include <signal.h>
39 #include <bits/signum.h>
40 #endif
41
42 #ifdef NETWARE
43 #ifdef __NOVELL_LIBC__
44 #include <dlfcn.h>
45 #else
46 #include <nwthread.h>
47 #include <nwadv.h>
48 #endif
49 #endif
50
51 #ifndef JNI_VERSION_1_1
52 #define JNI_VERSION_1_1 0x00010001
53 #endif
54
55 /* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY.
56  * The below define is a lie since we are really doing RTLD_LAZY since the
57  * system doesn't support RTLD_NOW.
58  */
59 #ifndef RTLD_NOW
60 #define RTLD_NOW 1
61 #endif
62
63 #ifndef RTLD_GLOBAL
64 #define RTLD_GLOBAL 0
65 #endif
66
67 #define null_check(e) if ((e) == 0) return JK_FALSE
68
69 jint(JNICALL * jni_get_default_java_vm_init_args) (void *) = NULL;
70 jint(JNICALL * jni_create_java_vm) (JavaVM **, JNIEnv **, void *) = NULL;
71 #ifdef AS400
72 jint(JNICALL * jni_get_created_java_vms) (JavaVM **, long, long *) = NULL;
73 #else
74 jint(JNICALL * jni_get_created_java_vms) (JavaVM **, int, int *) = NULL;
75 #endif
76
77 #define TC33_JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/modules/server/JNIEndpoint")
78 #define TC32_JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/service/JNIEndpoint")
79
80 static jk_worker_t *the_singleton_jni_worker = NULL;
81
82 struct jni_worker
83 {
84
85     int was_verified;
86     int was_initialized;
87
88     jk_pool_t p;
89     jk_pool_atom_t buf[TINY_POOL_SIZE];
90
91     /*
92      * JVM Object pointer.
93      */
94     JavaVM *jvm;
95
96     /*
97      * [V] JNIEnv used for boostraping from validate -> init w/o an attach
98      */
99     JNIEnv *tmp_env;
100
101     /*
102      * Web Server to Java bridge, instance and class.
103      */
104     jobject jk_java_bridge_object;
105     jclass jk_java_bridge_class;
106
107     /*
108      * Java methods ids, to jump into the JVM
109      */
110     jmethodID jk_startup_method;
111     jmethodID jk_service_method;
112     jmethodID jk_shutdown_method;
113
114     /*
115      * Command line for tomcat startup
116      */
117     char *tomcat_cmd_line;
118
119     /*
120      * Bridge Type, Tomcat 32/33/40/41/5
121      */
122     unsigned bridge_type;
123
124     /*
125      * Classpath
126      */
127     char *tomcat_classpath;
128
129     /*
130      * Full path to the jni javai/jvm dll
131      */
132     char *jvm_dll_path;
133
134     /*
135      * Initial Java heap size
136      */
137     unsigned tomcat_ms;
138
139     /*
140      * Max Java heap size
141      */
142     unsigned tomcat_mx;
143
144     /*
145      * Java system properties
146      */
147     char **sysprops;
148
149 #ifdef JNI_VERSION_1_2
150     /*
151      * Java 2 initialization options (-X... , -verbose etc.)
152      */
153     char **java2opts;
154
155     /*
156      * Java 2 lax/strict option checking (bool)
157      */
158     int java2lax;
159 #endif
160
161     /*
162      * stdout and stderr file names for Java
163      */
164     char *stdout_name;
165     char *stderr_name;
166
167     char *name;
168     jk_worker_t worker;
169 };
170 typedef struct jni_worker jni_worker_t;
171
172 struct jni_endpoint
173 {
174     int attached;
175     JNIEnv *env;
176     jni_worker_t *worker;
177
178     jk_endpoint_t endpoint;
179 };
180 typedef struct jni_endpoint jni_endpoint_t;
181
182
183 static int load_jvm_dll(jni_worker_t * p, jk_logger_t *l);
184
185 static int open_jvm(jni_worker_t * p, JNIEnv ** env, jk_logger_t *l);
186
187 static int open_jvm1(jni_worker_t * p, JNIEnv ** env, jk_logger_t *l);
188
189 #ifdef JNI_VERSION_1_2
190 static int detect_jvm_version(jk_logger_t *l);
191
192 static int open_jvm2(jni_worker_t * p, JNIEnv ** env, jk_logger_t *l);
193 #endif
194
195
196 static int get_bridge_object(jni_worker_t * p, JNIEnv * env, jk_logger_t *l);
197
198 static int get_method_ids(jni_worker_t * p, JNIEnv * env, jk_logger_t *l);
199
200 static JNIEnv *attach_to_jvm(jni_worker_t * p, jk_logger_t *l);
201
202 static void detach_from_jvm(jni_worker_t * p, jk_logger_t *l);
203
204
205 /*
206    Duplicate string and convert it to ASCII on EBDIC based systems
207    Needed for at least AS/400 (before V5R4 ?) and BS2000 but what about other EBDIC systems ?
208 */
209 static void *strdup_ascii(jk_pool_t *p, char *s)
210 {
211     char *rc;
212     rc = jk_pool_strdup(p, s);
213
214 #if defined(AS400) || defined(_OSD_POSIX)
215     jk_xlate_to_ascii(rc, strlen(rc));
216 #endif
217
218     return rc;
219 }
220
221 #if defined LINUX && defined APACHE2_SIGHACK
222 static void linux_signal_hack()
223 {
224     sigset_t newM;
225     sigset_t old;
226
227     sigemptyset(&newM);
228     pthread_sigmask(SIG_SETMASK, &newM, &old);
229
230     sigdelset(&old, SIGUSR1);
231     sigdelset(&old, SIGUSR2);
232     sigdelset(&old, SIGUNUSED);
233     sigdelset(&old, SIGRTMIN);
234     sigdelset(&old, SIGRTMIN + 1);
235     sigdelset(&old, SIGRTMIN + 2);
236     pthread_sigmask(SIG_SETMASK, &old, NULL);
237 }
238
239 static void print_signals(sigset_t * sset)
240 {
241     int sig;
242     for (sig = 1; sig < 20; sig++) {
243         if (sigismember(sset, sig)) {
244             printf(" %d", sig);
245         }
246     }
247     printf("\n");
248 }
249 #endif
250
251 /*
252  * Return values of service() method for jni worker:
253  * return value  is_error              reason
254  * JK_FALSE      JK_HTTP_SERVER_ERROR  Invalid parameters (null values)
255  *                                     Error during attach to the JNI backend
256  *                                     Error during JNI call
257  * JK_TRUE       JK_HTTP_OK            All other cases
258  */
259 static int JK_METHOD service(jk_endpoint_t *e,
260                              jk_ws_service_t *s,
261                              jk_logger_t *l, int *is_error)
262 {
263     jni_endpoint_t *p;
264     jint rc;
265
266     JK_TRACE_ENTER(l);
267
268     if (!e || !e->endpoint_private || !s || !is_error) {
269         JK_LOG_NULL_PARAMS(l);
270         if (is_error)
271             *is_error = JK_HTTP_SERVER_ERROR;
272         JK_TRACE_EXIT(l);
273         return JK_FALSE;
274     }
275
276     p = e->endpoint_private;
277
278     /* Set returned error to OK */
279     *is_error = JK_HTTP_OK;
280
281     if (!p->attached) {
282         /* Try to attach */
283         if (!(p->env = attach_to_jvm(p->worker, l))) {
284             jk_log(l, JK_LOG_EMERG, "Attach failed");
285             *is_error = JK_HTTP_SERVER_ERROR;
286             JK_TRACE_EXIT(l);
287             return JK_FALSE;
288         }
289         p->attached = JK_TRUE;
290     }
291
292     /* we are attached now */
293
294     /*
295      * When we call the JVM we cannot know what happens
296      * So we can not recover !!!
297      */
298     jk_log(l, JK_LOG_DEBUG, "In service, calling Tomcat...");
299
300     rc = (*(p->env))->CallIntMethod(p->env,
301                                     p->worker->jk_java_bridge_object,
302                                     p->worker->jk_service_method,
303                                     /* [V] For some reason gcc likes this pointer -> int -> jlong conversion, */
304                                     /*     but not the direct pointer -> jlong conversion. I hope it's okay.  */
305 #ifdef AS400
306                                     s, l
307 #else
308                                     (jlong) (int)s, (jlong) (int)l
309 #endif
310         );
311
312     /* [V] Righ now JNIEndpoint::service() only returns 1 or 0 */
313     if (rc) {
314         jk_log(l, JK_LOG_DEBUG, "Tomcat returned OK, done");
315         JK_TRACE_EXIT(l);
316         return JK_TRUE;
317     }
318     else {
319         jk_log(l, JK_LOG_ERROR, "Tomcat FAILED!");
320         *is_error = JK_HTTP_SERVER_ERROR;
321         JK_TRACE_EXIT(l);
322         return JK_FALSE;
323     }
324 }
325
326 static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l)
327 {
328     jni_endpoint_t *p;
329
330     JK_TRACE_ENTER(l);
331     if (!e || !*e || !(*e)->endpoint_private) {
332         JK_LOG_NULL_PARAMS(l);
333         JK_TRACE_EXIT(l);
334         return JK_FALSE;
335     }
336
337     p = (*e)->endpoint_private;
338
339     if (p->attached) {
340         detach_from_jvm(p->worker, l);
341     }
342
343     free(p);
344     *e = NULL;
345     JK_TRACE_EXIT(l);
346     return JK_TRUE;
347 }
348
349 static int JK_METHOD validate(jk_worker_t *pThis,
350                               jk_map_t *props,
351                               jk_worker_env_t *we, jk_logger_t *l)
352 {
353     jni_worker_t *p;
354     int mem_config = 0;
355     int btype = 0;
356     const char *str_config = NULL;
357     JNIEnv *env;
358
359     JK_TRACE_ENTER(l);
360
361     if (!pThis || !pThis->worker_private) {
362         JK_LOG_NULL_PARAMS(l);
363         JK_TRACE_EXIT(l);
364         return JK_FALSE;
365     }
366
367     p = pThis->worker_private;
368
369     if (p->was_verified) {
370         jk_log(l, JK_LOG_DEBUG, "been here before, done");
371         JK_TRACE_EXIT(l);
372         return JK_TRUE;
373     }
374
375     if (jk_get_worker_mx(props, p->name, (unsigned int *)&mem_config)) {
376         p->tomcat_mx = mem_config;
377     }
378
379     if (jk_get_worker_ms(props, p->name, (unsigned int *)&mem_config)) {
380         p->tomcat_ms = mem_config;
381     }
382
383     if (jk_get_worker_classpath(props, p->name, &str_config)) {
384         p->tomcat_classpath = jk_pool_strdup(&p->p, str_config);
385     }
386
387     if (!p->tomcat_classpath) {
388         jk_log(l, JK_LOG_EMERG, "no classpath");
389         JK_TRACE_EXIT(l);
390         return JK_FALSE;
391     }
392
393     if (jk_get_worker_bridge_type(props, p->name, (unsigned int *)&btype)) {
394         p->bridge_type = btype;
395     }
396
397     if (jk_get_worker_jvm_path(props, p->name, &str_config)) {
398         p->jvm_dll_path = jk_pool_strdup(&p->p, str_config);
399     }
400
401     if (!p->jvm_dll_path || !jk_file_exists(p->jvm_dll_path)) {
402         jk_log(l, JK_LOG_EMERG, "no jvm_dll_path");
403         JK_TRACE_EXIT(l);
404         return JK_FALSE;
405     }
406
407     if (jk_get_worker_cmd_line(props, p->name, &str_config)) {
408         p->tomcat_cmd_line = jk_pool_strdup(&p->p, str_config);
409     }
410
411     if (jk_get_worker_stdout(props, p->name, &str_config)) {
412         p->stdout_name = jk_pool_strdup(&p->p, str_config);
413     }
414
415     if (jk_get_worker_stderr(props, p->name, &str_config)) {
416         p->stderr_name = jk_pool_strdup(&p->p, str_config);
417     }
418
419     if (jk_get_worker_sysprops(props, p->name, &str_config)) {
420         p->sysprops = jk_parse_sysprops(&p->p, str_config);
421     }
422
423 #ifdef JNI_VERSION_1_2
424     if (jk_get_worker_str_prop(props, p->name, "java2opts", &str_config)) {
425         /* jk_log(l, JK_LOG_DEBUG, "Got opts: %s", str_config); */
426         p->java2opts = jk_parse_sysprops(&p->p, str_config);
427     }
428     if (jk_get_worker_int_prop(props, p->name, "java2lax", &mem_config)) {
429         p->java2lax = mem_config ? JK_TRUE : JK_FALSE;
430     }
431 #endif
432
433     if (jk_get_worker_libpath(props, p->name, &str_config)) {
434         jk_append_libpath(&p->p, str_config);
435     }
436
437     if (!load_jvm_dll(p, l)) {
438         jk_log(l, JK_LOG_EMERG, "can't load jvm dll");
439         /* [V] no detach needed here */
440         JK_TRACE_EXIT(l);
441         return JK_FALSE;
442     }
443
444     if (!open_jvm(p, &env, l)) {
445         jk_log(l, JK_LOG_EMERG, "can't open jvm");
446         /* [V] no detach needed here */
447         JK_TRACE_EXIT(l);
448         return JK_FALSE;
449     }
450
451     if (!get_bridge_object(p, env, l)) {
452         jk_log(l, JK_LOG_EMERG, "can't get bridge object");
453         /* [V] the detach here may segfault on 1.1 JVM... */
454         detach_from_jvm(p, l);
455         JK_TRACE_EXIT(l);
456         return JK_FALSE;
457     }
458
459     if (!get_method_ids(p, env, l)) {
460         jk_log(l, JK_LOG_EMERG, "can't get method ids");
461         /* [V] the detach here may segfault on 1.1 JVM... */
462         detach_from_jvm(p, l);
463         JK_TRACE_EXIT(l);
464         return JK_FALSE;
465     }
466
467     p->was_verified = JK_TRUE;
468     p->tmp_env = env;
469
470     JK_TRACE_EXIT(l);
471     return JK_TRUE;
472 }
473
474 static int JK_METHOD init(jk_worker_t *pThis,
475                           jk_map_t *props,
476                           jk_worker_env_t *we, jk_logger_t *l)
477 {
478     jni_worker_t *p;
479     JNIEnv *env;
480
481     JK_TRACE_ENTER(l);
482
483     if (!pThis || !pThis->worker_private) {
484         JK_LOG_NULL_PARAMS(l);
485         JK_TRACE_EXIT(l);
486         return JK_FALSE;
487     }
488
489     p = pThis->worker_private;
490
491     if (p->was_initialized) {
492         jk_log(l, JK_LOG_DEBUG, "done (been here!)");
493         JK_TRACE_EXIT(l);
494         return JK_TRUE;
495     }
496
497     if (!p->jvm ||
498         !p->jk_java_bridge_object ||
499         !p->jk_service_method ||
500         !p->jk_startup_method || !p->jk_shutdown_method) {
501         jk_log(l, JK_LOG_EMERG, "worker not set completely");
502         JK_TRACE_EXIT(l);
503         return JK_FALSE;
504     }
505
506     /* [V] init is called from the same thread that called validate */
507     /* there is no need to attach to the JVM, just get the env */
508
509     /* if(env = attach_to_jvm(p,l)) { */
510     if ((env = p->tmp_env)) {
511         jstring cmd_line = (jstring) NULL;
512         jstring stdout_name = (jstring) NULL;
513         jstring stderr_name = (jstring) NULL;
514         jint rc = 0;
515
516         /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
517
518         if (p->tomcat_cmd_line) {
519             cmd_line =
520                 (*env)->NewStringUTF(env,
521                                      strdup_ascii(&p->p, p->tomcat_cmd_line));
522         }
523         if (p->stdout_name) {
524             stdout_name =
525                 (*env)->NewStringUTF(env,
526                                      strdup_ascii(&p->p, p->stdout_name));
527         }
528         if (p->stderr_name) {
529             stderr_name =
530                 (*env)->NewStringUTF(env,
531                                      strdup_ascii(&p->p, p->stderr_name));
532         }
533
534         jk_log(l, JK_LOG_DEBUG,
535                "calling Tomcat to intialize itself...");
536         rc = (*env)->CallIntMethod(env, p->jk_java_bridge_object,
537                                    p->jk_startup_method, cmd_line,
538                                    stdout_name, stderr_name);
539
540         detach_from_jvm(p, l);
541
542         if (rc) {
543             p->was_initialized = JK_TRUE;
544             jk_log(l, JK_LOG_DEBUG, "Tomcat initialized OK, done");
545             JK_TRACE_EXIT(l);
546             return JK_TRUE;
547         }
548         else {
549             jk_log(l, JK_LOG_EMERG, "could not initialize Tomcat");
550             JK_TRACE_EXIT(l);
551             return JK_FALSE;
552         }
553     }
554     else {
555         jk_log(l, JK_LOG_ERROR,
556                "In init, FIXME: init didn't gen env from validate!");
557         JK_TRACE_EXIT(l);
558         return JK_FALSE;
559     }
560 }
561
562 static int JK_METHOD get_endpoint(jk_worker_t *pThis,
563                                   jk_endpoint_t **pend, jk_logger_t *l)
564 {
565     /* [V] This slow, needs replacement */
566     jni_endpoint_t *p;
567
568     JK_TRACE_ENTER(l);
569
570     if (!pThis || !pThis->worker_private || !pend) {
571         JK_LOG_NULL_PARAMS(l);
572         JK_TRACE_EXIT(l);
573         return JK_FALSE;
574     }
575
576     p = (jni_endpoint_t *) calloc(1, sizeof(jni_endpoint_t));
577     if (p) {
578         p->attached = JK_FALSE;
579         p->env = NULL;
580         p->worker = pThis->worker_private;
581         p->endpoint.endpoint_private = p;
582         p->endpoint.service = service;
583         p->endpoint.done = done;
584         *pend = &p->endpoint;
585
586         JK_TRACE_EXIT(l);
587         return JK_TRUE;
588     }
589     else {
590         jk_log(l, JK_LOG_ERROR,
591                "could not allocate endpoint");
592         JK_TRACE_EXIT(l);
593         return JK_FALSE;
594     }
595 }
596
597 static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
598 {
599     jni_worker_t *p;
600     JNIEnv *env;
601
602     JK_TRACE_ENTER(l);
603
604     if (!pThis || !*pThis || !(*pThis)->worker_private) {
605         JK_LOG_NULL_PARAMS(l);
606         JK_TRACE_EXIT(l);
607         return JK_FALSE;
608     }
609
610     p = (*pThis)->worker_private;
611
612     if (!p->jvm) {
613         jk_log(l, JK_LOG_DEBUG, "JVM not intantiated");
614         JK_TRACE_EXIT(l);
615         return JK_FALSE;
616     }
617
618     if (!p->jk_java_bridge_object || !p->jk_shutdown_method) {
619         jk_log(l, JK_LOG_DEBUG, "Tomcat not intantiated");
620         JK_TRACE_EXIT(l);
621         return JK_FALSE;
622     }
623
624     if ((env = attach_to_jvm(p, l))) {
625         jk_log(l, JK_LOG_DEBUG, "shutting down Tomcat...");
626         (*env)->CallVoidMethod(env,
627                                p->jk_java_bridge_object,
628                                p->jk_shutdown_method);
629         detach_from_jvm(p, l);
630     }
631
632     jk_close_pool(&p->p);
633     free(p);
634
635     jk_log(l, JK_LOG_DEBUG, "destroyed");
636
637     JK_TRACE_EXIT(l);
638     return JK_TRUE;
639 }
640
641 int JK_METHOD jni_worker_factory(jk_worker_t **w,
642                                  const char *name, jk_logger_t *l)
643 {
644     jni_worker_t *private_data;
645
646     JK_TRACE_ENTER(l);
647
648     jk_log(l, JK_LOG_WARNING,
649            "Worker '%s' is of type jni, which is deprecated "
650            "and will be removed in a future release.",
651            name ? name : "(null)");
652
653     if (!name || !w) {
654         JK_LOG_NULL_PARAMS(l);
655         JK_TRACE_EXIT(l);
656         return 0;
657     }
658
659     if (the_singleton_jni_worker) {
660         jk_log(l, JK_LOG_DEBUG,
661                "instance already created");
662         *w = the_singleton_jni_worker;
663         JK_TRACE_EXIT(l);
664         return JK_JNI_WORKER_TYPE;
665     }
666
667     private_data = (jni_worker_t *) malloc(sizeof(jni_worker_t));
668
669     if (!private_data) {
670         jk_log(l, JK_LOG_ERROR,
671                "memory allocation error");
672         JK_TRACE_EXIT(l);
673         return 0;
674     }
675
676     jk_open_pool(&private_data->p,
677                  private_data->buf, sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
678
679     private_data->name = jk_pool_strdup(&private_data->p, name);
680
681     if (!private_data->name) {
682         jk_log(l, JK_LOG_ERROR,
683                "memory allocation error");
684         jk_close_pool(&private_data->p);
685         free(private_data);
686         JK_TRACE_EXIT(l);
687         return 0;
688     }
689
690     private_data->was_verified = JK_FALSE;
691     private_data->was_initialized = JK_FALSE;
692     private_data->jvm = NULL;
693     private_data->tmp_env = NULL;
694     private_data->jk_java_bridge_object = (jobject) NULL;
695     private_data->jk_java_bridge_class = (jclass) NULL;
696     private_data->jk_startup_method = (jmethodID) NULL;
697     private_data->jk_service_method = (jmethodID) NULL;
698     private_data->jk_shutdown_method = (jmethodID) NULL;
699     private_data->tomcat_cmd_line = NULL;
700     private_data->tomcat_classpath = NULL;
701     private_data->bridge_type = TC33_BRIDGE_TYPE;
702     private_data->jvm_dll_path = NULL;
703     private_data->tomcat_ms = 0;
704     private_data->tomcat_mx = 0;
705     private_data->sysprops = NULL;
706 #ifdef JNI_VERSION_1_2
707     private_data->java2opts = NULL;
708     private_data->java2lax = JK_TRUE;
709 #endif
710     private_data->stdout_name = NULL;
711     private_data->stderr_name = NULL;
712
713     private_data->worker.worker_private = private_data;
714     private_data->worker.validate = validate;
715     private_data->worker.init = init;
716     private_data->worker.get_endpoint = get_endpoint;
717     private_data->worker.destroy = destroy;
718
719     *w = &private_data->worker;
720     the_singleton_jni_worker = &private_data->worker;
721
722     JK_TRACE_EXIT(l);
723     return JK_JNI_WORKER_TYPE;
724 }
725
726 static int load_jvm_dll(jni_worker_t * p, jk_logger_t *l)
727 {
728 #ifdef WIN32
729     HINSTANCE hInst = LoadLibrary(p->jvm_dll_path);
730     if (hInst) {
731         (FARPROC) jni_create_java_vm =
732             GetProcAddress(hInst, "JNI_CreateJavaVM");
733
734         (FARPROC) jni_get_created_java_vms =
735             GetProcAddress(hInst, "JNI_GetCreatedJavaVMs");
736
737         (FARPROC) jni_get_default_java_vm_init_args =
738             GetProcAddress(hInst, "JNI_GetDefaultJavaVMInitArgs");
739
740         jk_log(l, JK_LOG_DEBUG, "Loaded all JNI procs");
741
742         if (jni_create_java_vm && jni_get_default_java_vm_init_args
743             && jni_get_created_java_vms) {
744             return JK_TRUE;
745         }
746
747         FreeLibrary(hInst);
748     }
749 #elif defined(NETWARE) && !defined(__NOVELL_LIBC__)
750     int javaNlmHandle = FindNLMHandle("JVM");
751     if (0 == javaNlmHandle) {
752         /* if we didn't get a handle, try to load java and retry getting the */
753         /* handle */
754         spawnlp(P_NOWAIT, "JVM.NLM", NULL);
755         ThreadSwitchWithDelay();
756         javaNlmHandle = FindNLMHandle("JVM");
757         if (0 == javaNlmHandle)
758             printf("Error loading Java.");
759
760     }
761     if (0 != javaNlmHandle) {
762         jni_create_java_vm = ImportSymbol(GetNLMHandle(), "JNI_CreateJavaVM");
763         jni_get_created_java_vms =
764             ImportSymbol(GetNLMHandle(), "JNI_GetCreatedJavaVMs");
765         jni_get_default_java_vm_init_args =
766             ImportSymbol(GetNLMHandle(), "JNI_GetDefaultJavaVMInitArgs");
767     }
768     if (jni_create_java_vm && jni_get_default_java_vm_init_args
769         && jni_get_created_java_vms) {
770         return JK_TRUE;
771     }
772 #elif defined(AS400)
773     jk_log(l, JK_LOG_DEBUG,
774            "Direct reference to JNI entry points (no SRVPGM)");
775     jni_create_java_vm = &JNI_CreateJavaVM;
776     jni_get_default_java_vm_init_args = &JNI_GetDefaultJavaVMInitArgs;
777     jni_get_created_java_vms = &JNI_GetCreatedJavaVMs;
778 #else
779     void *handle;
780     jk_log(l, JK_LOG_DEBUG, "loading JVM %s", p->jvm_dll_path);
781
782     handle = dlopen(p->jvm_dll_path, RTLD_NOW | RTLD_GLOBAL);
783
784     if (!handle) {
785         jk_log(l, JK_LOG_EMERG,
786                "Can't load native library %s : %s", p->jvm_dll_path,
787                dlerror());
788     }
789     else {
790         jni_create_java_vm = dlsym(handle, "JNI_CreateJavaVM");
791         jni_get_default_java_vm_init_args =
792             dlsym(handle, "JNI_GetDefaultJavaVMInitArgs");
793         jni_get_created_java_vms = dlsym(handle, "JNI_GetCreatedJavaVMs");
794
795         if (jni_create_java_vm && jni_get_default_java_vm_init_args &&
796             jni_get_created_java_vms) {
797             jk_log(l, JK_LOG_DEBUG,
798                    "In load_jvm_dll, symbols resolved, done");
799             return JK_TRUE;
800         }
801         jk_log(l, JK_LOG_EMERG,
802                "Can't resolve JNI_CreateJavaVM or JNI_GetDefaultJavaVMInitArgs");
803         dlclose(handle);
804     }
805 #endif
806     return JK_FALSE;
807 }
808
809 static int open_jvm(jni_worker_t * p, JNIEnv ** env, jk_logger_t *l)
810 {
811 #ifdef JNI_VERSION_1_2
812     int jvm_version = detect_jvm_version(l);
813
814     switch (jvm_version) {
815     case JNI_VERSION_1_1:
816         return open_jvm1(p, env, l);
817     case JNI_VERSION_1_2:
818         return open_jvm2(p, env, l);
819     default:
820         return JK_FALSE;
821     }
822 #else
823     /* [V] Make sure this is _really_ visible */
824 #warning -------------------------------------------------------
825 #warning NO JAVA 2 HEADERS! SUPPORT FOR JAVA 2 FEATURES DISABLED
826 #warning -------------------------------------------------------
827     return open_jvm1(p, env, l);
828 #endif
829 }
830
831 static int open_jvm1(jni_worker_t * p, JNIEnv ** env, jk_logger_t *l)
832 {
833     JDK1_1InitArgs vm_args;
834     JNIEnv *penv;
835     int err;
836     *env = NULL;
837
838     JK_TRACE_ENTER(l);
839
840     vm_args.version = JNI_VERSION_1_1;
841
842     if (0 != jni_get_default_java_vm_init_args(&vm_args)) {
843         jk_log(l, JK_LOG_EMERG, "can't get default vm init args");
844         JK_TRACE_EXIT(l);
845         return JK_FALSE;
846     }
847     jk_log(l, JK_LOG_DEBUG, "got default jvm args");
848
849     if (vm_args.classpath) {
850         size_t len = strlen(vm_args.classpath) +
851             strlen(p->tomcat_classpath) + 3;
852         char *tmp = jk_pool_alloc(&p->p, len);
853         if (tmp) {
854             sprintf(tmp, "%s%c%s",
855                     strdup_ascii(&p->p, p->tomcat_classpath),
856                     PATH_SEPERATOR, vm_args.classpath);
857             p->tomcat_classpath = tmp;
858         }
859         else {
860             jk_log(l, JK_LOG_EMERG,
861                    "allocation error for classpath");
862             JK_TRACE_EXIT(l);
863             return JK_FALSE;
864         }
865     }
866     vm_args.classpath = p->tomcat_classpath;
867
868     if (p->tomcat_mx) {
869         vm_args.maxHeapSize = p->tomcat_mx;
870     }
871
872     if (p->tomcat_ms) {
873         vm_args.minHeapSize = p->tomcat_ms;
874     }
875
876     if (p->sysprops) {
877         /* No EBCDIC to ASCII conversion here for AS/400 - later */
878         vm_args.properties = p->sysprops;
879     }
880
881     jk_log(l, JK_LOG_DEBUG, "In open_jvm1, about to create JVM...");
882     if ((err = jni_create_java_vm(&(p->jvm), &penv, &vm_args)) != 0) {
883         jk_log(l, JK_LOG_EMERG,
884                "could not create JVM, code: %d ", err);
885         JK_TRACE_EXIT(l);
886         return JK_FALSE;
887     }
888     jk_log(l, JK_LOG_DEBUG, "JVM created, done");
889
890     *env = penv;
891
892     JK_TRACE_EXIT(l);
893     return JK_TRUE;
894 }
895
896 #ifdef JNI_VERSION_1_2
897 static int detect_jvm_version(jk_logger_t *l)
898 {
899     JNIEnv *env = NULL;
900     JDK1_1InitArgs vm_args;
901
902     JK_TRACE_ENTER(l);
903
904     /* [V] Idea: ask for 1.2. If the JVM is 1.1 it will return 1.1 instead  */
905     /*     Note: asking for 1.1 won't work, 'cause 1.2 JVMs will return 1.1 */
906     vm_args.version = JNI_VERSION_1_2;
907
908     if (0 != jni_get_default_java_vm_init_args(&vm_args)) {
909         jk_log(l, JK_LOG_EMERG, "can't get default vm init args");
910         JK_TRACE_EXIT(l);
911         return JK_FALSE;
912     }
913     jk_log(l, JK_LOG_DEBUG,
914            "found version: %X, done", vm_args.version);
915
916     JK_TRACE_EXIT(l);
917     return vm_args.version;
918 }
919
920 static char *build_opt_str(jk_pool_t *p,
921                            char *opt_name, char *opt_value, jk_logger_t *l)
922 {
923     size_t len = strlen(opt_name) + strlen(opt_value) + 2;
924
925     /* [V] IMHO, these should not be deallocated as long as the JVM runs */
926     char *tmp = jk_pool_alloc(p, len);
927
928     if (tmp) {
929         sprintf(tmp, "%s%s", opt_name, opt_value);
930         return tmp;
931     }
932     else {
933         jk_log(l, JK_LOG_EMERG,
934                "allocation error for %s", opt_name);
935         return NULL;
936     }
937 }
938
939 static char *build_opt_int(jk_pool_t *p,
940                            char *opt_name, int opt_value, jk_logger_t *l)
941 {
942     /* [V] this should suffice even for 64-bit int */
943     size_t len = strlen(opt_name) + 20 + 2;
944     /* [V] IMHO, these should not be deallocated as long as the JVM runs */
945     char *tmp = jk_pool_alloc(p, len);
946
947     if (tmp) {
948         sprintf(tmp, "%s%d", opt_name, opt_value);
949         return tmp;
950     }
951     else {
952         jk_log(l, JK_LOG_EMERG,
953                "allocation error for %s", opt_name);
954         return NULL;
955     }
956 }
957
958 static int open_jvm2(jni_worker_t * p, JNIEnv ** env, jk_logger_t *l)
959 {
960     JavaVMInitArgs vm_args;
961     JNIEnv *penv = NULL;
962     JavaVMOption options[100];
963     int optn = 0, err;
964     char *tmp;
965
966     *env = NULL;
967
968     JK_TRACE_ENTER(l);
969
970     vm_args.version = JNI_VERSION_1_2;
971     vm_args.options = options;
972
973 /* AS/400 need EBCDIC to ASCII conversion to parameters passed to JNI */
974 /* No conversion for ASCII based systems (what about BS2000 ?) */
975
976     if (p->tomcat_classpath) {
977         jk_log(l, JK_LOG_DEBUG, "setting classpath to %s",
978                p->tomcat_classpath);
979         tmp =
980             build_opt_str(&p->p, "-Djava.class.path=", p->tomcat_classpath,
981                           l);
982         null_check(tmp);
983         options[optn++].optionString = strdup_ascii(&p->p, tmp);
984     }
985
986     if (p->tomcat_mx) {
987         jk_log(l, JK_LOG_DEBUG, "setting max heap to %d",
988                p->tomcat_mx);
989         tmp = build_opt_int(&p->p, "-Xmx", p->tomcat_mx, l);
990         null_check(tmp);
991         options[optn++].optionString = strdup_ascii(&p->p, tmp);
992     }
993
994     if (p->tomcat_ms) {
995         jk_log(l, JK_LOG_DEBUG, "setting start heap to %d",
996                p->tomcat_ms);
997         tmp = build_opt_int(&p->p, "-Xms", p->tomcat_ms, l);
998         null_check(tmp);
999         options[optn++].optionString = strdup_ascii(&p->p, tmp);
1000     }
1001
1002     if (p->sysprops) {
1003         int i = 0;
1004         while (p->sysprops[i]) {
1005             jk_log(l, JK_LOG_DEBUG, "setting %s",
1006                    p->sysprops[i]);
1007             tmp = build_opt_str(&p->p, "-D", p->sysprops[i], l);
1008             null_check(tmp);
1009             options[optn++].optionString = strdup_ascii(&p->p, tmp);
1010             i++;
1011         }
1012     }
1013
1014     if (p->java2opts) {
1015         int i = 0;
1016
1017         while (p->java2opts[i]) {
1018             jk_log(l, JK_LOG_DEBUG, "using option: %s",
1019                    p->java2opts[i]);
1020             /* Pass it "as is" */
1021             options[optn++].optionString =
1022                 strdup_ascii(&p->p, p->java2opts[i++]);
1023         }
1024     }
1025
1026     vm_args.nOptions = optn;
1027
1028     if (p->java2lax) {
1029         jk_log(l, JK_LOG_DEBUG,
1030                "the JVM will ignore unknown options");
1031         vm_args.ignoreUnrecognized = JNI_TRUE;
1032     }
1033     else {
1034         jk_log(l, JK_LOG_DEBUG,
1035                "the JVM will FAIL if it finds unknown options");
1036         vm_args.ignoreUnrecognized = JNI_FALSE;
1037     }
1038
1039     jk_log(l, JK_LOG_DEBUG, "about to create JVM...");
1040
1041     err = jni_create_java_vm(&(p->jvm), &penv, &vm_args);
1042
1043     if (JNI_EEXIST == err) {
1044 #ifdef AS400
1045         long vmCount;
1046 #else
1047         int vmCount;
1048 #endif
1049         jk_log(l, JK_LOG_DEBUG, "JVM alread instantiated."
1050                "Trying to attach instead.");
1051
1052         jni_get_created_java_vms(&(p->jvm), 1, &vmCount);
1053         if (NULL != p->jvm)
1054             penv = attach_to_jvm(p, l);
1055
1056         if (NULL != penv)
1057             err = 0;
1058     }
1059
1060     if (err != 0) {
1061         jk_log(l, JK_LOG_EMERG, "Fail-> could not create JVM, code: %d ",
1062                err);
1063         JK_TRACE_EXIT(l);
1064         return JK_FALSE;
1065     }
1066
1067     *env = penv;
1068     jk_log(l, JK_LOG_DEBUG, "JVM created");
1069
1070     JK_TRACE_EXIT(l);
1071     return JK_TRUE;
1072 }
1073 #endif
1074
1075 static int get_bridge_object(jni_worker_t * p, JNIEnv * env, jk_logger_t *l)
1076 {
1077     char *btype;
1078     char *ctype;
1079
1080     jmethodID constructor_method_id;
1081
1082     JK_TRACE_ENTER(l);
1083
1084     switch (p->bridge_type) {
1085     case TC32_BRIDGE_TYPE:
1086         btype = TC32_JAVA_BRIDGE_CLASS_NAME;
1087         break;
1088
1089     case TC33_BRIDGE_TYPE:
1090         btype = TC33_JAVA_BRIDGE_CLASS_NAME;
1091         break;
1092
1093     case TC40_BRIDGE_TYPE:
1094     case TC41_BRIDGE_TYPE:
1095     case TC50_BRIDGE_TYPE:
1096         jk_log(l, JK_LOG_EMERG, "Bridge type %d not supported",
1097                p->bridge_type);
1098         JK_TRACE_EXIT(l);
1099         return JK_FALSE;
1100     }
1101
1102 /* AS400/BS2000 need conversion from EBCDIC to ASCII before passing to JNI */
1103 /* for others, strdup_ascii is just jk_pool_strdup */
1104
1105     ctype = strdup_ascii(&p->p, btype);
1106
1107     p->jk_java_bridge_class = (*env)->FindClass(env, ctype);
1108
1109     if (!p->jk_java_bridge_class) {
1110         jk_log(l, JK_LOG_EMERG, "Can't find class %s", btype);
1111         JK_TRACE_EXIT(l);
1112         return JK_FALSE;
1113     }
1114     jk_log(l, JK_LOG_DEBUG,
1115            "In get_bridge_object, loaded %s bridge class", btype);
1116
1117     constructor_method_id = (*env)->GetMethodID(env, p->jk_java_bridge_class, strdup_ascii(&p->p, "<init>"),    /* method name */
1118                                                 strdup_ascii(&p->p, "()V"));    /* method sign */
1119
1120     if (!constructor_method_id) {
1121         p->jk_java_bridge_class = (jclass) NULL;
1122         jk_log(l, JK_LOG_EMERG, "Can't find constructor");
1123         JK_TRACE_EXIT(l);
1124         return JK_FALSE;
1125     }
1126
1127     p->jk_java_bridge_object = (*env)->NewObject(env,
1128                                                  p->jk_java_bridge_class,
1129                                                  constructor_method_id);
1130     if (!p->jk_java_bridge_object) {
1131         p->jk_java_bridge_class = (jclass) NULL;
1132         jk_log(l, JK_LOG_EMERG, "Can't create new bridge object");
1133         JK_TRACE_EXIT(l);
1134         return JK_FALSE;
1135     }
1136
1137     p->jk_java_bridge_object =
1138         (jobject) (*env)->NewGlobalRef(env, p->jk_java_bridge_object);
1139     if (!p->jk_java_bridge_object) {
1140         jk_log(l, JK_LOG_EMERG, "Can't create global ref to bridge object");
1141         p->jk_java_bridge_class = (jclass) NULL;
1142         p->jk_java_bridge_object = (jobject) NULL;
1143         JK_TRACE_EXIT(l);
1144         return JK_FALSE;
1145     }
1146
1147     JK_TRACE_EXIT(l);
1148     return JK_TRUE;
1149 }
1150
1151 static int get_method_ids(jni_worker_t * p, JNIEnv * env, jk_logger_t *l)
1152 {
1153
1154 /* AS400/BS2000 need conversion from EBCDIC to ASCII before passing to JNI */
1155
1156     p->jk_startup_method = (*env)->GetMethodID(env,
1157                                                p->jk_java_bridge_class,
1158                                                strdup_ascii(&p->p, "startup"),
1159                                                strdup_ascii(&p->p,
1160                                                             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I"));
1161
1162     if (!p->jk_startup_method) {
1163         jk_log(l, JK_LOG_EMERG, "Can't find startup()");
1164         return JK_FALSE;
1165     }
1166
1167     p->jk_service_method = (*env)->GetMethodID(env,
1168                                                p->jk_java_bridge_class,
1169                                                strdup_ascii(&p->p, "service"),
1170                                                strdup_ascii(&p->p, "(JJ)I"));
1171
1172     if (!p->jk_service_method) {
1173         jk_log(l, JK_LOG_EMERG, "Can't find service()");
1174         return JK_FALSE;
1175     }
1176
1177     p->jk_shutdown_method = (*env)->GetMethodID(env,
1178                                                 p->jk_java_bridge_class,
1179                                                 strdup_ascii(&p->p,
1180                                                              "shutdown"),
1181                                                 strdup_ascii(&p->p, "()V"));
1182
1183     if (!p->jk_shutdown_method) {
1184         jk_log(l, JK_LOG_EMERG, "Can't find shutdown()");
1185         return JK_FALSE;
1186     }
1187
1188     return JK_TRUE;
1189 }
1190
1191 static JNIEnv *attach_to_jvm(jni_worker_t * p, jk_logger_t *l)
1192 {
1193     JNIEnv *rc = NULL;
1194     /* [V] This message is important. If there are signal mask issues,    *
1195      *     the JVM usually hangs when a new thread tries to attach to it  */
1196     JK_TRACE_ENTER(l);
1197
1198 #if defined LINUX && defined APACHE2_SIGHACK
1199     linux_signal_hack();
1200 #endif
1201
1202     if (0 == (*(p->jvm))->AttachCurrentThread(p->jvm,
1203 #ifdef JNI_VERSION_1_2
1204                                               (void **)
1205 #endif
1206                                               &rc, NULL)) {
1207         jk_log(l, JK_LOG_DEBUG, "In attach_to_jvm, attached ok");
1208         JK_TRACE_EXIT(l);
1209         return rc;
1210     }
1211     jk_log(l, JK_LOG_ERROR,
1212            "In attach_to_jvm, cannot attach thread to JVM.");
1213     JK_TRACE_EXIT(l);
1214     return NULL;
1215 }
1216
1217 /*
1218 static JNIEnv *attach_to_jvm(jni_worker_t *p)
1219 {
1220     JNIEnv *rc = NULL;
1221
1222 #ifdef LINUX
1223     linux_signal_hack();
1224 #endif
1225
1226     if(0 == (*(p->jvm))->AttachCurrentThread(p->jvm,
1227 #ifdef JNI_VERSION_1_2
1228            (void **)
1229 #endif
1230                                              &rc,
1231                                              NULL)) {
1232         return rc;
1233     }
1234
1235     return NULL;
1236 }
1237 */
1238 static void detach_from_jvm(jni_worker_t * p, jk_logger_t *l)
1239 {
1240     JK_TRACE_ENTER(l);
1241     if (!p->jvm || !(*(p->jvm))) {
1242         jk_log(l, JK_LOG_ERROR,
1243                "cannot detach from NULL JVM.");
1244     }
1245
1246     if (0 == (*(p->jvm))->DetachCurrentThread(p->jvm)) {
1247         jk_log(l, JK_LOG_DEBUG, "detached ok");
1248     }
1249     else {
1250         jk_log(l, JK_LOG_ERROR,
1251                "cannot detach from JVM.");
1252     }
1253     JK_TRACE_EXIT(l);
1254 }
1255 #else
1256 int JK_METHOD jni_worker_factory(jk_worker_t **w,
1257                                  const char *name, jk_logger_t *l)
1258 {
1259     jk_log(l, JK_LOG_WARNING,
1260            "Worker '%s' is of type jni, which is deprecated "
1261            "and will be removed in a future release.",
1262            name ? name : "(null)");
1263     if (w)
1264         *w = NULL;
1265     return 0;
1266 }
1267 #endif /* JNI_VERSION_1_6 */