| /* |
| * Copyright 1999-2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /*************************************************************************** |
| * Description: In process JNI worker * |
| * Author: Gal Shachor <shachor@il.ibm.com> * |
| * Based on: * |
| * Version: $Revision$ * |
| ***************************************************************************/ |
| |
| #if !defined(WIN32) && !defined(NETWARE) && !defined(AS400) |
| #include <dlfcn.h> |
| #endif |
| |
| #include <jni.h> |
| |
| #include "jk_pool.h" |
| #include "jk_jni_worker.h" |
| #include "jk_util.h" |
| |
| #if defined LINUX && defined APACHE2_SIGHACK |
| #include <pthread.h> |
| #include <signal.h> |
| #include <bits/signum.h> |
| #endif |
| |
| #ifdef NETWARE |
| #ifdef __NOVELL_LIBC__ |
| #include <dlfcn.h> |
| #else |
| #include <nwthread.h> |
| #include <nwadv.h> |
| #endif |
| #endif |
| |
| #ifndef JNI_VERSION_1_1 |
| #define JNI_VERSION_1_1 0x00010001 |
| #endif |
| |
| /* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY. |
| * The below define is a lie since we are really doing RTLD_LAZY since the |
| * system doesn't support RTLD_NOW. |
| */ |
| #ifndef RTLD_NOW |
| #define RTLD_NOW 1 |
| #endif |
| |
| #ifndef RTLD_GLOBAL |
| #define RTLD_GLOBAL 0 |
| #endif |
| |
| #define null_check(e) if ((e) == 0) return JK_FALSE |
| |
| jint (JNICALL *jni_get_default_java_vm_init_args)(void *) = NULL; |
| jint (JNICALL *jni_create_java_vm)(JavaVM **, JNIEnv **, void *) = NULL; |
| #ifdef AS400 |
| jint (JNICALL *jni_get_created_java_vms)(JavaVM **, long, long *) = NULL; |
| #else |
| jint (JNICALL *jni_get_created_java_vms)(JavaVM **, int, int *) = NULL; |
| #endif |
| |
| #define TC33_JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/modules/server/JNIEndpoint") |
| #define TC32_JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/service/JNIEndpoint") |
| |
| static jk_worker_t *the_singleton_jni_worker = NULL; |
| |
| struct jni_worker { |
| |
| int was_verified; |
| int was_initialized; |
| |
| jk_pool_t p; |
| jk_pool_atom_t buf[TINY_POOL_SIZE]; |
| |
| /* |
| * JVM Object pointer. |
| */ |
| JavaVM *jvm; |
| |
| /* |
| * [V] JNIEnv used for boostraping from validate -> init w/o an attach |
| */ |
| JNIEnv *tmp_env; |
| |
| /* |
| * Web Server to Java bridge, instance and class. |
| */ |
| jobject jk_java_bridge_object; |
| jclass jk_java_bridge_class; |
| |
| /* |
| * Java methods ids, to jump into the JVM |
| */ |
| jmethodID jk_startup_method; |
| jmethodID jk_service_method; |
| jmethodID jk_shutdown_method; |
| |
| /* |
| * Command line for tomcat startup |
| */ |
| char *tomcat_cmd_line; |
| |
| /* |
| * Bridge Type, Tomcat 32/33/40/41/5 |
| */ |
| unsigned bridge_type; |
| |
| /* |
| * Classpath |
| */ |
| char *tomcat_classpath; |
| |
| /* |
| * Full path to the jni javai/jvm dll |
| */ |
| char *jvm_dll_path; |
| |
| /* |
| * Initial Java heap size |
| */ |
| unsigned tomcat_ms; |
| |
| /* |
| * Max Java heap size |
| */ |
| unsigned tomcat_mx; |
| |
| /* |
| * Java system properties |
| */ |
| char **sysprops; |
| |
| #ifdef JNI_VERSION_1_2 |
| /* |
| * Java 2 initialization options (-X... , -verbose etc.) |
| */ |
| char **java2opts; |
| |
| /* |
| * Java 2 lax/strict option checking (bool) |
| */ |
| int java2lax; |
| #endif |
| |
| /* |
| * stdout and stderr file names for Java |
| */ |
| char *stdout_name; |
| char *stderr_name; |
| |
| char *name; |
| jk_worker_t worker; |
| }; |
| typedef struct jni_worker jni_worker_t; |
| |
| struct jni_endpoint { |
| int attached; |
| JNIEnv *env; |
| jni_worker_t *worker; |
| |
| jk_endpoint_t endpoint; |
| }; |
| typedef struct jni_endpoint jni_endpoint_t; |
| |
| |
| static int load_jvm_dll(jni_worker_t *p, |
| jk_logger_t *l); |
| |
| static int open_jvm(jni_worker_t *p, |
| JNIEnv **env, |
| jk_logger_t *l); |
| |
| static int open_jvm1(jni_worker_t *p, |
| JNIEnv **env, |
| jk_logger_t *l); |
| |
| #ifdef JNI_VERSION_1_2 |
| static int detect_jvm_version(jk_logger_t *l); |
| |
| static int open_jvm2(jni_worker_t *p, |
| JNIEnv **env, |
| jk_logger_t *l); |
| #endif |
| |
| |
| static int get_bridge_object(jni_worker_t *p, |
| JNIEnv *env, |
| jk_logger_t *l); |
| |
| static int get_method_ids(jni_worker_t *p, |
| JNIEnv *env, |
| jk_logger_t *l); |
| |
| static JNIEnv *attach_to_jvm(jni_worker_t *p, |
| jk_logger_t *l); |
| |
| static void detach_from_jvm(jni_worker_t *p, |
| jk_logger_t *l); |
| |
| |
| /* |
| Duplicate string and convert it to ASCII on EBDIC based systems |
| Needed for at least AS/400 and BS2000 but what about other EBDIC systems ? |
| */ |
| static void *strdup_ascii(jk_pool_t *p, |
| char *s) |
| { |
| char * rc; |
| rc = jk_pool_strdup(p, s); |
| |
| #if defined(AS400) || defined(_OSD_POSIX) |
| jk_xlate_to_ascii(rc, strlen(rc)); |
| #endif |
| |
| return rc; |
| } |
| |
| #if defined LINUX && defined APACHE2_SIGHACK |
| static void linux_signal_hack() |
| { |
| sigset_t newM; |
| sigset_t old; |
| |
| sigemptyset(&newM); |
| pthread_sigmask( SIG_SETMASK, &newM, &old ); |
| |
| sigdelset(&old, SIGUSR1 ); |
| sigdelset(&old, SIGUSR2 ); |
| sigdelset(&old, SIGUNUSED ); |
| sigdelset(&old, SIGRTMIN ); |
| sigdelset(&old, SIGRTMIN + 1 ); |
| sigdelset(&old, SIGRTMIN + 2 ); |
| pthread_sigmask( SIG_SETMASK, &old, NULL ); |
| } |
| |
| static void print_signals( sigset_t *sset) { |
| int sig; |
| for (sig = 1; sig < 20; sig++) |
| { if (sigismember(sset, sig)) {printf( " %d", sig);} } |
| printf( "\n"); |
| } |
| #endif |
| |
| static int JK_METHOD service(jk_endpoint_t *e, |
| jk_ws_service_t *s, |
| jk_logger_t *l, |
| int *is_recoverable_error) |
| { |
| jni_endpoint_t *p; |
| jint rc; |
| |
| jk_log(l, JK_LOG_DEBUG, "Into service\n"); |
| if(!e || !e->endpoint_private || !s) { |
| jk_log(l, JK_LOG_EMERG, "In service, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| p = e->endpoint_private; |
| |
| if(!is_recoverable_error) { |
| return JK_FALSE; |
| } |
| |
| if(!p->attached) { |
| /* Try to attach */ |
| if(!(p->env = attach_to_jvm(p->worker, l))) { |
| jk_log(l, JK_LOG_EMERG, "Attach failed\n"); |
| /* Is it recoverable ?? */ |
| *is_recoverable_error = JK_TRUE; |
| return JK_FALSE; |
| } |
| p->attached = JK_TRUE; |
| } |
| |
| /* we are attached now */ |
| |
| /* |
| * When we call the JVM we cannot know what happens |
| * So we can not recover !!! |
| */ |
| *is_recoverable_error = JK_FALSE; |
| |
| jk_log(l, JK_LOG_DEBUG, "In service, calling Tomcat...\n"); |
| |
| rc = (*(p->env))->CallIntMethod(p->env, |
| p->worker->jk_java_bridge_object, |
| p->worker->jk_service_method, |
| /* [V] For some reason gcc likes this pointer -> int -> jlong conversion, */ |
| /* but not the direct pointer -> jlong conversion. I hope it's okay. */ |
| #ifdef AS400 |
| s, |
| l |
| #else |
| (jlong)(int)s, |
| (jlong)(int)l |
| #endif |
| ); |
| |
| /* [V] Righ now JNIEndpoint::service() only returns 1 or 0 */ |
| if(rc) { |
| jk_log(l, JK_LOG_DEBUG, |
| "In service, Tomcat returned OK, done\n"); |
| return JK_TRUE; |
| } else { |
| jk_log(l, JK_LOG_ERROR, |
| "In service, Tomcat FAILED!\n"); |
| return JK_FALSE; |
| } |
| } |
| |
| static int JK_METHOD done(jk_endpoint_t **e, |
| jk_logger_t *l) |
| { |
| jni_endpoint_t *p; |
| |
| jk_log(l, JK_LOG_DEBUG, "Into done\n"); |
| if(!e || !*e || !(*e)->endpoint_private) { |
| jk_log(l, JK_LOG_EMERG, |
| "In done, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| p = (*e)->endpoint_private; |
| |
| if(p->attached) { |
| detach_from_jvm(p->worker,l); |
| } |
| |
| free(p); |
| *e = NULL; |
| jk_log(l, JK_LOG_DEBUG, |
| "Done ok\n"); |
| return JK_TRUE; |
| } |
| |
| static int JK_METHOD validate(jk_worker_t *pThis, |
| jk_map_t *props, |
| jk_worker_env_t *we, |
| jk_logger_t *l) |
| { |
| jni_worker_t *p; |
| int mem_config = 0; |
| int btype = 0; |
| char *str_config = NULL; |
| JNIEnv *env; |
| |
| jk_log(l, JK_LOG_DEBUG, "Into validate\n"); |
| |
| if(! pThis || ! pThis->worker_private) { |
| jk_log(l, JK_LOG_EMERG, "In validate, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| p = pThis->worker_private; |
| |
| if(p->was_verified) { |
| jk_log(l, JK_LOG_DEBUG, "validate, been here before, done\n"); |
| return JK_TRUE; |
| } |
| |
| if(jk_get_worker_mx(props, p->name, (unsigned int *)&mem_config)) { |
| p->tomcat_mx = mem_config; |
| } |
| |
| if(jk_get_worker_ms(props, p->name, (unsigned int *)&mem_config)) { |
| p->tomcat_ms = mem_config; |
| } |
| |
| if(jk_get_worker_classpath(props, p->name, &str_config)) { |
| p->tomcat_classpath = jk_pool_strdup(&p->p, str_config); |
| } |
| |
| if(!p->tomcat_classpath) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> no classpath\n"); |
| return JK_FALSE; |
| } |
| |
| if(jk_get_worker_bridge_type(props, p->name, (unsigned int *)&btype)) { |
| p->bridge_type = btype; |
| } |
| |
| if(jk_get_worker_jvm_path(props, p->name, &str_config)) { |
| p->jvm_dll_path = jk_pool_strdup(&p->p, str_config); |
| } |
| |
| if(!p->jvm_dll_path || !jk_file_exists(p->jvm_dll_path)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> no jvm_dll_path\n"); |
| return JK_FALSE; |
| } |
| |
| if(jk_get_worker_cmd_line(props, p->name, &str_config)) { |
| p->tomcat_cmd_line = jk_pool_strdup(&p->p, str_config); |
| } |
| |
| if(jk_get_worker_stdout(props, p->name, &str_config)) { |
| p->stdout_name = jk_pool_strdup(&p->p, str_config); |
| } |
| |
| if(jk_get_worker_stderr(props, p->name, &str_config)) { |
| p->stderr_name = jk_pool_strdup(&p->p, str_config); |
| } |
| |
| if(jk_get_worker_sysprops(props, p->name, &str_config)) { |
| p->sysprops = jk_parse_sysprops(&p->p, str_config); |
| } |
| |
| #ifdef JNI_VERSION_1_2 |
| if(jk_get_worker_str_prop(props, p->name, "java2opts", &str_config)) { |
| /* jk_log(l, JK_LOG_DEBUG, "Got opts: %s\n", str_config); */ |
| p->java2opts = jk_parse_sysprops(&p->p, str_config); |
| } |
| if(jk_get_worker_int_prop(props, p->name, "java2lax", &mem_config)) { |
| p->java2lax = mem_config ? JK_TRUE : JK_FALSE; |
| } |
| #endif |
| |
| if(jk_get_worker_libpath(props, p->name, &str_config)) { |
| jk_append_libpath(&p->p, str_config); |
| } |
| |
| if(!load_jvm_dll(p, l)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> can't load jvm dll\n"); |
| /* [V] no detach needed here */ |
| return JK_FALSE; |
| } |
| |
| if(!open_jvm(p, &env, l)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> can't open jvm\n"); |
| /* [V] no detach needed here */ |
| return JK_FALSE; |
| } |
| |
| if(!get_bridge_object(p, env, l)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> can't get bridge object\n"); |
| /* [V] the detach here may segfault on 1.1 JVM... */ |
| detach_from_jvm(p, l); |
| return JK_FALSE; |
| } |
| |
| if(!get_method_ids(p, env, l)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> can't get method ids\n"); |
| /* [V] the detach here may segfault on 1.1 JVM... */ |
| detach_from_jvm(p, l); |
| return JK_FALSE; |
| } |
| |
| p->was_verified = JK_TRUE; |
| p->tmp_env = env; |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Done validate\n"); |
| |
| return JK_TRUE; |
| } |
| |
| static int JK_METHOD init(jk_worker_t *pThis, |
| jk_map_t *props, |
| jk_worker_env_t *we, |
| jk_logger_t *l) |
| { |
| jni_worker_t *p; |
| JNIEnv *env; |
| |
| jk_log(l, JK_LOG_DEBUG, "Into init\n"); |
| |
| if(! pThis || ! pThis->worker_private) { |
| jk_log(l, JK_LOG_EMERG, "In init, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| p = pThis->worker_private; |
| |
| if(p->was_initialized) { |
| jk_log(l, JK_LOG_DEBUG, "init, done (been here!)\n"); |
| return JK_TRUE; |
| } |
| |
| if(!p->jvm || |
| !p->jk_java_bridge_object || |
| !p->jk_service_method || |
| !p->jk_startup_method || |
| !p->jk_shutdown_method) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> worker not set completely\n"); |
| return JK_FALSE; |
| } |
| |
| /* [V] init is called from the same thread that called validate */ |
| /* there is no need to attach to the JVM, just get the env */ |
| |
| /* if(env = attach_to_jvm(p,l)) { */ |
| if((env = p->tmp_env)) { |
| jstring cmd_line = (jstring)NULL; |
| jstring stdout_name = (jstring)NULL; |
| jstring stderr_name = (jstring)NULL; |
| jint rc = 0; |
| |
| /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */ |
| |
| if(p->tomcat_cmd_line) { |
| cmd_line = (*env)->NewStringUTF(env, strdup_ascii(&p->p, p->tomcat_cmd_line)); |
| } |
| if(p->stdout_name) { |
| stdout_name = (*env)->NewStringUTF(env, strdup_ascii(&p->p, p->stdout_name)); |
| } |
| if(p->stderr_name) { |
| stderr_name = (*env)->NewStringUTF(env, strdup_ascii(&p->p, p->stderr_name)); |
| } |
| |
| jk_log(l, JK_LOG_DEBUG, "In init, calling Tomcat to intialize itself...\n"); |
| rc = (*env)->CallIntMethod(env, |
| p->jk_java_bridge_object, |
| p->jk_startup_method, |
| cmd_line, |
| stdout_name, |
| stderr_name); |
| |
| detach_from_jvm(p, l); |
| |
| if(rc) { |
| p->was_initialized = JK_TRUE; |
| jk_log(l, JK_LOG_DEBUG, |
| "In init, Tomcat initialized OK, done\n"); |
| return JK_TRUE; |
| } else { |
| jk_log(l, JK_LOG_EMERG, |
| "Fail-> could not initialize Tomcat\n"); |
| return JK_FALSE; |
| } |
| } else { |
| jk_log(l, JK_LOG_ERROR, |
| "In init, FIXME: init didn't gen env from validate!\n"); |
| return JK_FALSE; |
| } |
| } |
| |
| static int JK_METHOD get_endpoint(jk_worker_t *pThis, |
| jk_endpoint_t **pend, |
| jk_logger_t *l) |
| { |
| /* [V] This slow, needs replacement */ |
| jni_endpoint_t *p = (jni_endpoint_t *)malloc(sizeof(jni_endpoint_t)); |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Into get_endpoint\n"); |
| |
| if(!pThis || ! pThis->worker_private || !pend) { |
| jk_log(l, JK_LOG_EMERG, |
| "In get_endpoint, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| if(p) { |
| p->attached = JK_FALSE; |
| p->env = NULL; |
| p->worker = pThis->worker_private; |
| p->endpoint.endpoint_private = p; |
| p->endpoint.service = service; |
| p->endpoint.done = done; |
| *pend = &p->endpoint; |
| |
| return JK_TRUE; |
| } else { |
| jk_log(l, JK_LOG_ERROR, "In get_endpoint, could not allocate endpoint\n"); |
| return JK_FALSE; |
| } |
| } |
| |
| static int JK_METHOD destroy(jk_worker_t **pThis, |
| jk_logger_t *l) |
| { |
| jni_worker_t *p; |
| JNIEnv *env; |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Into destroy\n"); |
| |
| if(!pThis || !*pThis || ! (*pThis)->worker_private) { |
| jk_log(l, JK_LOG_EMERG, "In destroy, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| p = (*pThis)->worker_private; |
| |
| if(!p->jvm) { |
| jk_log(l, JK_LOG_DEBUG, "In destroy, JVM not intantiated\n"); |
| return JK_FALSE; |
| } |
| |
| if(!p->jk_java_bridge_object || ! p->jk_shutdown_method) { |
| jk_log(l, JK_LOG_DEBUG, "In destroy, Tomcat not intantiated\n"); |
| return JK_FALSE; |
| } |
| |
| if((env = attach_to_jvm(p,l))) { |
| jk_log(l, JK_LOG_DEBUG, |
| "In destroy, shutting down Tomcat...\n"); |
| (*env)->CallVoidMethod(env, |
| p->jk_java_bridge_object, |
| p->jk_shutdown_method); |
| detach_from_jvm(p, l); |
| } |
| |
| jk_close_pool(&p->p); |
| free(p); |
| |
| jk_log(l, JK_LOG_DEBUG, "Done destroy\n"); |
| |
| return JK_TRUE; |
| } |
| |
| int JK_METHOD jni_worker_factory(jk_worker_t **w, |
| const char *name, |
| jk_logger_t *l) |
| { |
| jni_worker_t *private_data; |
| |
| jk_log(l, JK_LOG_DEBUG, "Into jni_worker_factory\n"); |
| |
| if(!name || !w) { |
| jk_log(l, JK_LOG_EMERG, |
| "In jni_worker_factory, assert failed - invalid parameters\n"); |
| return JK_FALSE; |
| } |
| |
| if(the_singleton_jni_worker) { |
| jk_log(l, JK_LOG_DEBUG, |
| "In jni_worker_factory, instance already created\n"); |
| *w = the_singleton_jni_worker; |
| return JK_TRUE; |
| } |
| |
| private_data = (jni_worker_t *)malloc(sizeof(jni_worker_t )); |
| |
| if(!private_data) { |
| jk_log(l, JK_LOG_ERROR, |
| "In jni_worker_factory, memory allocation error\n"); |
| return JK_FALSE; |
| } |
| |
| jk_open_pool(&private_data->p, |
| private_data->buf, |
| sizeof(jk_pool_atom_t) * TINY_POOL_SIZE); |
| |
| private_data->name = jk_pool_strdup(&private_data->p, name); |
| |
| if(!private_data->name) { |
| jk_log(l, JK_LOG_ERROR, |
| "In jni_worker_factory, memory allocation error\n"); |
| jk_close_pool(&private_data->p); |
| free(private_data); |
| return JK_FALSE; |
| } |
| |
| private_data->was_verified = JK_FALSE; |
| private_data->was_initialized = JK_FALSE; |
| private_data->jvm = NULL; |
| private_data->tmp_env = NULL; |
| private_data->jk_java_bridge_object = (jobject)NULL; |
| private_data->jk_java_bridge_class = (jclass)NULL; |
| private_data->jk_startup_method = (jmethodID)NULL; |
| private_data->jk_service_method = (jmethodID)NULL; |
| private_data->jk_shutdown_method = (jmethodID)NULL; |
| private_data->tomcat_cmd_line = NULL; |
| private_data->tomcat_classpath = NULL; |
| private_data->bridge_type = TC33_BRIDGE_TYPE; |
| private_data->jvm_dll_path = NULL; |
| private_data->tomcat_ms = 0; |
| private_data->tomcat_mx = 0; |
| private_data->sysprops = NULL; |
| #ifdef JNI_VERSION_1_2 |
| private_data->java2opts = NULL; |
| private_data->java2lax = JK_TRUE; |
| #endif |
| private_data->stdout_name = NULL; |
| private_data->stderr_name = NULL; |
| |
| private_data->worker.worker_private = private_data; |
| private_data->worker.validate = validate; |
| private_data->worker.init = init; |
| private_data->worker.get_endpoint = get_endpoint; |
| private_data->worker.destroy = destroy; |
| |
| *w = &private_data->worker; |
| the_singleton_jni_worker = &private_data->worker; |
| |
| jk_log(l, JK_LOG_DEBUG, "Done jni_worker_factory\n"); |
| return JK_TRUE; |
| } |
| |
| static int load_jvm_dll(jni_worker_t *p, |
| jk_logger_t *l) |
| { |
| #ifdef WIN32 |
| HINSTANCE hInst = LoadLibrary(p->jvm_dll_path); |
| if(hInst) { |
| (FARPROC)jni_create_java_vm = |
| GetProcAddress(hInst, "JNI_CreateJavaVM"); |
| |
| (FARPROC)jni_get_created_java_vms = |
| GetProcAddress(hInst, "JNI_GetCreatedJavaVMs"); |
| |
| (FARPROC)jni_get_default_java_vm_init_args = |
| GetProcAddress(hInst, "JNI_GetDefaultJavaVMInitArgs"); |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Loaded all JNI procs\n"); |
| |
| if(jni_create_java_vm && jni_get_default_java_vm_init_args && jni_get_created_java_vms) { |
| return JK_TRUE; |
| } |
| |
| FreeLibrary(hInst); |
| } |
| #elif defined(NETWARE) && !defined(__NOVELL_LIBC__) |
| int javaNlmHandle = FindNLMHandle("JVM"); |
| if (0 == javaNlmHandle) { |
| /* if we didn't get a handle, try to load java and retry getting the */ |
| /* handle */ |
| spawnlp(P_NOWAIT, "JVM.NLM", NULL); |
| ThreadSwitchWithDelay(); |
| javaNlmHandle = FindNLMHandle("JVM"); |
| if (0 == javaNlmHandle) |
| printf("Error loading Java."); |
| |
| } |
| if (0 != javaNlmHandle) { |
| jni_create_java_vm = ImportSymbol(GetNLMHandle(), "JNI_CreateJavaVM"); |
| jni_get_created_java_vms = ImportSymbol(GetNLMHandle(), "JNI_GetCreatedJavaVMs"); |
| jni_get_default_java_vm_init_args = ImportSymbol(GetNLMHandle(), "JNI_GetDefaultJavaVMInitArgs"); |
| } |
| if(jni_create_java_vm && jni_get_default_java_vm_init_args && jni_get_created_java_vms) { |
| return JK_TRUE; |
| } |
| #elif defined(AS400) |
| jk_log(l, JK_LOG_DEBUG, |
| "Direct reference to JNI entry points (no SRVPGM)\n"); |
| jni_create_java_vm = &JNI_CreateJavaVM; |
| jni_get_default_java_vm_init_args = &JNI_GetDefaultJavaVMInitArgs; |
| jni_get_created_java_vms = &JNI_GetCreatedJavaVMs; |
| #else |
| void *handle; |
| jk_log(l, JK_LOG_DEBUG, |
| "Into load_jvm_dll, load %s\n", p->jvm_dll_path); |
| |
| handle = dlopen(p->jvm_dll_path, RTLD_NOW | RTLD_GLOBAL); |
| |
| if(!handle) { |
| jk_log(l, JK_LOG_EMERG, |
| "Can't load native library %s : %s\n", p->jvm_dll_path, |
| dlerror()); |
| } else { |
| jni_create_java_vm = dlsym(handle, "JNI_CreateJavaVM"); |
| jni_get_default_java_vm_init_args = dlsym(handle, "JNI_GetDefaultJavaVMInitArgs"); |
| jni_get_created_java_vms = dlsym(handle, "JNI_GetCreatedJavaVMs"); |
| |
| if(jni_create_java_vm && jni_get_default_java_vm_init_args && |
| jni_get_created_java_vms) { |
| jk_log(l, JK_LOG_DEBUG, |
| "In load_jvm_dll, symbols resolved, done\n"); |
| return JK_TRUE; |
| } |
| jk_log(l, JK_LOG_EMERG, |
| "Can't resolve JNI_CreateJavaVM or JNI_GetDefaultJavaVMInitArgs\n"); |
| dlclose(handle); |
| } |
| #endif |
| return JK_FALSE; |
| } |
| |
| static int open_jvm(jni_worker_t *p, |
| JNIEnv **env, |
| jk_logger_t *l) |
| { |
| #ifdef JNI_VERSION_1_2 |
| int jvm_version = detect_jvm_version(l); |
| |
| switch(jvm_version) { |
| case JNI_VERSION_1_1: |
| return open_jvm1(p, env, l); |
| case JNI_VERSION_1_2: |
| return open_jvm2(p, env, l); |
| default: |
| return JK_FALSE; |
| } |
| #else |
| /* [V] Make sure this is _really_ visible */ |
| #warning ------------------------------------------------------- |
| #warning NO JAVA 2 HEADERS! SUPPORT FOR JAVA 2 FEATURES DISABLED |
| #warning ------------------------------------------------------- |
| return open_jvm1(p, env, l); |
| #endif |
| } |
| |
| static int open_jvm1(jni_worker_t *p, |
| JNIEnv **env, |
| jk_logger_t *l) |
| { |
| JDK1_1InitArgs vm_args; |
| JNIEnv *penv; |
| int err; |
| *env = NULL; |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Into open_jvm1\n"); |
| |
| vm_args.version = JNI_VERSION_1_1; |
| |
| if(0 != jni_get_default_java_vm_init_args(&vm_args)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> can't get default vm init args\n"); |
| return JK_FALSE; |
| } |
| jk_log(l, JK_LOG_DEBUG, |
| "In open_jvm_dll, got default jvm args\n"); |
| |
| if(vm_args.classpath) { |
| unsigned len = strlen(vm_args.classpath) + |
| strlen(p->tomcat_classpath) + |
| 3; |
| char *tmp = jk_pool_alloc(&p->p, len); |
| if(tmp) { |
| sprintf(tmp, "%s%c%s", |
| strdup_ascii(&p->p, p->tomcat_classpath), |
| PATH_SEPERATOR, |
| vm_args.classpath); |
| p->tomcat_classpath = tmp; |
| } else { |
| jk_log(l, JK_LOG_EMERG, |
| "Fail-> allocation error for classpath\n"); |
| return JK_FALSE; |
| } |
| } |
| vm_args.classpath = p->tomcat_classpath; |
| |
| if(p->tomcat_mx) { |
| vm_args.maxHeapSize = p->tomcat_mx; |
| } |
| |
| if(p->tomcat_ms) { |
| vm_args.minHeapSize = p->tomcat_ms; |
| } |
| |
| if(p->sysprops) { |
| /* No EBCDIC to ASCII conversion here for AS/400 - later */ |
| vm_args.properties = p->sysprops; |
| } |
| |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm1, about to create JVM...\n"); |
| if((err=jni_create_java_vm(&(p->jvm), |
| &penv, |
| &vm_args)) != 0) { |
| jk_log(l, JK_LOG_EMERG, |
| "Fail-> could not create JVM, code: %d \n", err); |
| return JK_FALSE; |
| } |
| jk_log(l, JK_LOG_DEBUG, |
| "In open_jvm1, JVM created, done\n"); |
| |
| *env = penv; |
| |
| return JK_TRUE; |
| } |
| |
| #ifdef JNI_VERSION_1_2 |
| static int detect_jvm_version(jk_logger_t *l) |
| { |
| JNIEnv *env = NULL; |
| JDK1_1InitArgs vm_args; |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Into detect_jvm_version\n"); |
| |
| /* [V] Idea: ask for 1.2. If the JVM is 1.1 it will return 1.1 instead */ |
| /* Note: asking for 1.1 won't work, 'cause 1.2 JVMs will return 1.1 */ |
| vm_args.version = JNI_VERSION_1_2; |
| |
| if(0 != jni_get_default_java_vm_init_args(&vm_args)) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> can't get default vm init args\n"); |
| return JK_FALSE; |
| } |
| jk_log(l, JK_LOG_DEBUG, |
| "In detect_jvm_version, found: %X, done\n", vm_args.version); |
| |
| return vm_args.version; |
| } |
| |
| static char* build_opt_str(jk_pool_t *p, |
| char* opt_name, |
| char* opt_value, |
| jk_logger_t *l) |
| { |
| unsigned len = strlen(opt_name) + strlen(opt_value) + 2; |
| |
| /* [V] IMHO, these should not be deallocated as long as the JVM runs */ |
| char *tmp = jk_pool_alloc(p, len); |
| |
| if(tmp) { |
| sprintf(tmp, "%s%s", opt_name, opt_value); |
| return tmp; |
| } else { |
| jk_log(l, JK_LOG_EMERG, |
| "Fail-> build_opt_str allocation error for %s\n", opt_name); |
| return NULL; |
| } |
| } |
| |
| static char* build_opt_int(jk_pool_t *p, |
| char* opt_name, |
| int opt_value, |
| jk_logger_t *l) |
| { |
| /* [V] this should suffice even for 64-bit int */ |
| unsigned len = strlen(opt_name) + 20 + 2; |
| /* [V] IMHO, these should not be deallocated as long as the JVM runs */ |
| char *tmp = jk_pool_alloc(p, len); |
| |
| if(tmp) { |
| sprintf(tmp, "%s%d", opt_name, opt_value); |
| return tmp; |
| } else { |
| jk_log(l, JK_LOG_EMERG, |
| "Fail-> build_opt_int allocation error for %s\n", opt_name); |
| return NULL; |
| } |
| } |
| |
| static int open_jvm2(jni_worker_t *p, |
| JNIEnv **env, |
| jk_logger_t *l) |
| { |
| JavaVMInitArgs vm_args; |
| JNIEnv *penv = NULL; |
| JavaVMOption options[100]; |
| int optn = 0, err; |
| char* tmp; |
| |
| *env = NULL; |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Into open_jvm2\n"); |
| |
| vm_args.version = JNI_VERSION_1_2; |
| vm_args.options = options; |
| |
| /* AS/400 need EBCDIC to ASCII conversion to parameters passed to JNI */ |
| /* No conversion for ASCII based systems (what about BS2000 ?) */ |
| |
| if(p->tomcat_classpath) { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting classpath to %s\n", p->tomcat_classpath); |
| tmp = build_opt_str(&p->p, "-Djava.class.path=", p->tomcat_classpath, l); |
| null_check(tmp); |
| options[optn++].optionString = strdup_ascii(&p->p, tmp); |
| } |
| |
| if(p->tomcat_mx) { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting max heap to %d\n", p->tomcat_mx); |
| tmp = build_opt_int(&p->p, "-Xmx", p->tomcat_mx, l); |
| null_check(tmp); |
| options[optn++].optionString = strdup_ascii(&p->p, tmp); |
| } |
| |
| if(p->tomcat_ms) { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting start heap to %d\n", p->tomcat_ms); |
| tmp = build_opt_int(&p->p, "-Xms", p->tomcat_ms, l); |
| null_check(tmp); |
| options[optn++].optionString = strdup_ascii(&p->p, tmp); |
| } |
| |
| if(p->sysprops) { |
| int i = 0; |
| while(p->sysprops[i]) { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting %s\n", p->sysprops[i]); |
| tmp = build_opt_str(&p->p, "-D", p->sysprops[i], l); |
| null_check(tmp); |
| options[optn++].optionString = strdup_ascii(&p->p, tmp); |
| i++; |
| } |
| } |
| |
| if(p->java2opts) { |
| int i=0; |
| |
| while(p->java2opts[i]) { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, using option: %s\n", p->java2opts[i]); |
| /* Pass it "as is" */ |
| options[optn++].optionString = strdup_ascii(&p->p, p->java2opts[i++]); |
| } |
| } |
| |
| vm_args.nOptions = optn; |
| |
| if(p->java2lax) { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, the JVM will ignore unknown options\n"); |
| vm_args.ignoreUnrecognized = JNI_TRUE; |
| } else { |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, the JVM will FAIL if it finds unknown options\n"); |
| vm_args.ignoreUnrecognized = JNI_FALSE; |
| } |
| |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, about to create JVM...\n"); |
| |
| err=jni_create_java_vm(&(p->jvm), &penv, &vm_args); |
| |
| if (JNI_EEXIST == err) { |
| #ifdef AS400 |
| long vmCount; |
| #else |
| int vmCount; |
| #endif |
| jk_log(l, JK_LOG_DEBUG, "JVM alread instantiated." |
| "Trying to attach instead.\n"); |
| |
| jni_get_created_java_vms(&(p->jvm), 1, &vmCount); |
| if (NULL != p->jvm) |
| penv = attach_to_jvm(p, l); |
| |
| if (NULL != penv) |
| err = 0; |
| } |
| |
| if(err != 0) { |
| jk_log(l, JK_LOG_EMERG, "Fail-> could not create JVM, code: %d \n", err); |
| return JK_FALSE; |
| } |
| jk_log(l, JK_LOG_DEBUG, "In open_jvm2, JVM created, done\n"); |
| |
| *env = penv; |
| |
| return JK_TRUE; |
| } |
| #endif |
| |
| static int get_bridge_object(jni_worker_t *p, |
| JNIEnv *env, |
| jk_logger_t *l) |
| { |
| char * btype; |
| char * ctype; |
| |
| jmethodID constructor_method_id; |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "Into get_bridge_object\n"); |
| |
| switch (p->bridge_type) |
| { |
| case TC32_BRIDGE_TYPE : |
| btype = TC32_JAVA_BRIDGE_CLASS_NAME; |
| break; |
| |
| case TC33_BRIDGE_TYPE : |
| btype = TC33_JAVA_BRIDGE_CLASS_NAME; |
| break; |
| |
| case TC40_BRIDGE_TYPE : |
| case TC41_BRIDGE_TYPE : |
| case TC50_BRIDGE_TYPE : |
| jk_log(l, JK_LOG_EMERG, "Bridge type %d not supported\n", p->bridge_type); |
| return JK_FALSE; |
| } |
| |
| /* AS400/BS2000 need conversion from EBCDIC to ASCII before passing to JNI */ |
| /* for others, strdup_ascii is just jk_pool_strdup */ |
| |
| ctype = strdup_ascii(&p->p, btype); |
| |
| p->jk_java_bridge_class = (*env)->FindClass(env, ctype); |
| |
| if(!p->jk_java_bridge_class) { |
| jk_log(l, JK_LOG_EMERG, "Can't find class %s\n", btype); |
| return JK_FALSE; |
| } |
| jk_log(l, JK_LOG_DEBUG, |
| "In get_bridge_object, loaded %s bridge class\n", btype); |
| |
| constructor_method_id = (*env)->GetMethodID(env, |
| p->jk_java_bridge_class, |
| strdup_ascii(&p->p, "<init>"), /* method name */ |
| strdup_ascii(&p->p, "()V")); /* method sign */ |
| |
| if(!constructor_method_id) { |
| p->jk_java_bridge_class = (jclass)NULL; |
| jk_log(l, JK_LOG_EMERG, |
| "Can't find constructor\n"); |
| return JK_FALSE; |
| } |
| |
| p->jk_java_bridge_object = (*env)->NewObject(env, |
| p->jk_java_bridge_class, |
| constructor_method_id); |
| if(! p->jk_java_bridge_object) { |
| p->jk_java_bridge_class = (jclass)NULL; |
| jk_log(l, JK_LOG_EMERG, |
| "Can't create new bridge object\n"); |
| return JK_FALSE; |
| } |
| |
| p->jk_java_bridge_object = (jobject)(*env)->NewGlobalRef(env, p->jk_java_bridge_object); |
| if(! p->jk_java_bridge_object) { |
| jk_log(l, JK_LOG_EMERG, "Can't create global ref to bridge object\n"); |
| p->jk_java_bridge_class = (jclass)NULL; |
| p->jk_java_bridge_object = (jobject)NULL; |
| return JK_FALSE; |
| } |
| |
| jk_log(l, JK_LOG_DEBUG, |
| "In get_bridge_object, bridge built, done\n"); |
| return JK_TRUE; |
| } |
| |
| static int get_method_ids(jni_worker_t *p, |
| JNIEnv *env, |
| jk_logger_t *l) |
| { |
| |
| /* AS400/BS2000 need conversion from EBCDIC to ASCII before passing to JNI */ |
| |
| p->jk_startup_method = (*env)->GetMethodID(env, |
| p->jk_java_bridge_class, |
| strdup_ascii(&p->p, "startup"), |
| strdup_ascii(&p->p, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I")); |
| |
| if(!p->jk_startup_method) { |
| jk_log(l, JK_LOG_EMERG, "Can't find startup()\n"); |
| return JK_FALSE; |
| } |
| |
| p->jk_service_method = (*env)->GetMethodID(env, |
| p->jk_java_bridge_class, |
| strdup_ascii(&p->p, "service"), |
| strdup_ascii(&p->p, "(JJ)I")); |
| |
| if(!p->jk_service_method) { |
| jk_log(l, JK_LOG_EMERG, "Can't find service()\n"); |
| return JK_FALSE; |
| } |
| |
| p->jk_shutdown_method = (*env)->GetMethodID(env, |
| p->jk_java_bridge_class, |
| strdup_ascii(&p->p, "shutdown"), |
| strdup_ascii(&p->p, "()V")); |
| |
| if(!p->jk_shutdown_method) { |
| jk_log(l, JK_LOG_EMERG, "Can't find shutdown()\n"); |
| return JK_FALSE; |
| } |
| |
| return JK_TRUE; |
| } |
| |
| static JNIEnv *attach_to_jvm(jni_worker_t *p, jk_logger_t *l) |
| { |
| JNIEnv *rc = NULL; |
| /* [V] This message is important. If there are signal mask issues, * |
| * the JVM usually hangs when a new thread tries to attach to it */ |
| jk_log(l, JK_LOG_DEBUG, |
| "Into attach_to_jvm\n"); |
| |
| #if defined LINUX && defined APACHE2_SIGHACK |
| linux_signal_hack(); |
| #endif |
| |
| if(0 == (*(p->jvm))->AttachCurrentThread(p->jvm, |
| #ifdef JNI_VERSION_1_2 |
| (void **) |
| #endif |
| &rc, |
| NULL)) { |
| jk_log(l, JK_LOG_DEBUG, |
| "In attach_to_jvm, attached ok\n"); |
| return rc; |
| } |
| jk_log(l, JK_LOG_ERROR, |
| "In attach_to_jvm, cannot attach thread to JVM.\n"); |
| return NULL; |
| } |
| |
| /* |
| static JNIEnv *attach_to_jvm(jni_worker_t *p) |
| { |
| JNIEnv *rc = NULL; |
| |
| #ifdef LINUX |
| linux_signal_hack(); |
| #endif |
| |
| if(0 == (*(p->jvm))->AttachCurrentThread(p->jvm, |
| #ifdef JNI_VERSION_1_2 |
| (void **) |
| #endif |
| &rc, |
| NULL)) { |
| return rc; |
| } |
| |
| return NULL; |
| } |
| */ |
| static void detach_from_jvm(jni_worker_t *p, |
| jk_logger_t *l) |
| { |
| if(!p->jvm || !(*(p->jvm))) { |
| jk_log(l, JK_LOG_ERROR, |
| "In detach_from_jvm, cannot detach from NULL JVM.\n"); |
| } |
| |
| if(0 == (*(p->jvm))->DetachCurrentThread(p->jvm)) { |
| jk_log(l, JK_LOG_DEBUG, |
| "In detach_from_jvm, detached ok\n"); |
| } else { |
| jk_log(l, JK_LOG_ERROR, |
| "In detach_from_jvm, cannot detach from JVM.\n"); |
| } |
| } |