/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 * * Based on: * * Version: $Revision: 750428 $ * ***************************************************************************/ #if !defined(WIN32) && !defined(NETWARE) && !defined(AS400) #include #endif #include #include "jk_pool.h" #include "jk_jni_worker.h" #include "jk_util.h" #if !defined(JNI_VERSION_1_6) #if defined LINUX && defined APACHE2_SIGHACK #include #include #include #endif #ifdef NETWARE #ifdef __NOVELL_LIBC__ #include #else #include #include #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 (before V5R4 ?) 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 /* * Return values of service() method for jni worker: * return value is_error reason * JK_FALSE JK_HTTP_SERVER_ERROR Invalid parameters (null values) * Error during attach to the JNI backend * Error during JNI call * JK_TRUE JK_HTTP_OK All other cases */ static int JK_METHOD service(jk_endpoint_t *e, jk_ws_service_t *s, jk_logger_t *l, int *is_error) { jni_endpoint_t *p; jint rc; JK_TRACE_ENTER(l); if (!e || !e->endpoint_private || !s || !is_error) { JK_LOG_NULL_PARAMS(l); if (is_error) *is_error = JK_HTTP_SERVER_ERROR; JK_TRACE_EXIT(l); return JK_FALSE; } p = e->endpoint_private; /* Set returned error to OK */ *is_error = JK_HTTP_OK; if (!p->attached) { /* Try to attach */ if (!(p->env = attach_to_jvm(p->worker, l))) { jk_log(l, JK_LOG_EMERG, "Attach failed"); *is_error = JK_HTTP_SERVER_ERROR; JK_TRACE_EXIT(l); 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 !!! */ jk_log(l, JK_LOG_DEBUG, "In service, calling Tomcat..."); 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, "Tomcat returned OK, done"); JK_TRACE_EXIT(l); return JK_TRUE; } else { jk_log(l, JK_LOG_ERROR, "Tomcat FAILED!"); *is_error = JK_HTTP_SERVER_ERROR; JK_TRACE_EXIT(l); return JK_FALSE; } } static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l) { jni_endpoint_t *p; JK_TRACE_ENTER(l); if (!e || !*e || !(*e)->endpoint_private) { JK_LOG_NULL_PARAMS(l); JK_TRACE_EXIT(l); return JK_FALSE; } p = (*e)->endpoint_private; if (p->attached) { detach_from_jvm(p->worker, l); } free(p); *e = NULL; JK_TRACE_EXIT(l); 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; const char *str_config = NULL; JNIEnv *env; JK_TRACE_ENTER(l); if (!pThis || !pThis->worker_private) { JK_LOG_NULL_PARAMS(l); JK_TRACE_EXIT(l); return JK_FALSE; } p = pThis->worker_private; if (p->was_verified) { jk_log(l, JK_LOG_DEBUG, "been here before, done"); JK_TRACE_EXIT(l); 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, "no classpath"); JK_TRACE_EXIT(l); 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, "no jvm_dll_path"); JK_TRACE_EXIT(l); 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", 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, "can't load jvm dll"); /* [V] no detach needed here */ JK_TRACE_EXIT(l); return JK_FALSE; } if (!open_jvm(p, &env, l)) { jk_log(l, JK_LOG_EMERG, "can't open jvm"); /* [V] no detach needed here */ JK_TRACE_EXIT(l); return JK_FALSE; } if (!get_bridge_object(p, env, l)) { jk_log(l, JK_LOG_EMERG, "can't get bridge object"); /* [V] the detach here may segfault on 1.1 JVM... */ detach_from_jvm(p, l); JK_TRACE_EXIT(l); return JK_FALSE; } if (!get_method_ids(p, env, l)) { jk_log(l, JK_LOG_EMERG, "can't get method ids"); /* [V] the detach here may segfault on 1.1 JVM... */ detach_from_jvm(p, l); JK_TRACE_EXIT(l); return JK_FALSE; } p->was_verified = JK_TRUE; p->tmp_env = env; JK_TRACE_EXIT(l); 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_TRACE_ENTER(l); if (!pThis || !pThis->worker_private) { JK_LOG_NULL_PARAMS(l); JK_TRACE_EXIT(l); return JK_FALSE; } p = pThis->worker_private; if (p->was_initialized) { jk_log(l, JK_LOG_DEBUG, "done (been here!)"); JK_TRACE_EXIT(l); 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, "worker not set completely"); JK_TRACE_EXIT(l); 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, "calling Tomcat to intialize itself..."); 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, "Tomcat initialized OK, done"); JK_TRACE_EXIT(l); return JK_TRUE; } else { jk_log(l, JK_LOG_EMERG, "could not initialize Tomcat"); JK_TRACE_EXIT(l); return JK_FALSE; } } else { jk_log(l, JK_LOG_ERROR, "In init, FIXME: init didn't gen env from validate!"); JK_TRACE_EXIT(l); 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; JK_TRACE_ENTER(l); if (!pThis || !pThis->worker_private || !pend) { JK_LOG_NULL_PARAMS(l); JK_TRACE_EXIT(l); return JK_FALSE; } p = (jni_endpoint_t *) calloc(1, sizeof(jni_endpoint_t)); 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; JK_TRACE_EXIT(l); return JK_TRUE; } else { jk_log(l, JK_LOG_ERROR, "could not allocate endpoint"); JK_TRACE_EXIT(l); return JK_FALSE; } } static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l) { jni_worker_t *p; JNIEnv *env; JK_TRACE_ENTER(l); if (!pThis || !*pThis || !(*pThis)->worker_private) { JK_LOG_NULL_PARAMS(l); JK_TRACE_EXIT(l); return JK_FALSE; } p = (*pThis)->worker_private; if (!p->jvm) { jk_log(l, JK_LOG_DEBUG, "JVM not intantiated"); JK_TRACE_EXIT(l); return JK_FALSE; } if (!p->jk_java_bridge_object || !p->jk_shutdown_method) { jk_log(l, JK_LOG_DEBUG, "Tomcat not intantiated"); JK_TRACE_EXIT(l); return JK_FALSE; } if ((env = attach_to_jvm(p, l))) { jk_log(l, JK_LOG_DEBUG, "shutting down Tomcat..."); (*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, "destroyed"); JK_TRACE_EXIT(l); 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_TRACE_ENTER(l); jk_log(l, JK_LOG_WARNING, "Worker '%s' is of type jni, which is deprecated " "and will be removed in a future release.", name ? name : "(null)"); if (!name || !w) { JK_LOG_NULL_PARAMS(l); JK_TRACE_EXIT(l); return 0; } if (the_singleton_jni_worker) { jk_log(l, JK_LOG_DEBUG, "instance already created"); *w = the_singleton_jni_worker; JK_TRACE_EXIT(l); return JK_JNI_WORKER_TYPE; } private_data = (jni_worker_t *) malloc(sizeof(jni_worker_t)); if (!private_data) { jk_log(l, JK_LOG_ERROR, "memory allocation error"); JK_TRACE_EXIT(l); return 0; } 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, "memory allocation error"); jk_close_pool(&private_data->p); free(private_data); JK_TRACE_EXIT(l); return 0; } 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_TRACE_EXIT(l); return JK_JNI_WORKER_TYPE; } 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"); 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)"); 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, "loading JVM %s", 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", 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"); return JK_TRUE; } jk_log(l, JK_LOG_EMERG, "Can't resolve JNI_CreateJavaVM or JNI_GetDefaultJavaVMInitArgs"); 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_TRACE_ENTER(l); vm_args.version = JNI_VERSION_1_1; if (0 != jni_get_default_java_vm_init_args(&vm_args)) { jk_log(l, JK_LOG_EMERG, "can't get default vm init args"); JK_TRACE_EXIT(l); return JK_FALSE; } jk_log(l, JK_LOG_DEBUG, "got default jvm args"); if (vm_args.classpath) { size_t 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, "allocation error for classpath"); JK_TRACE_EXIT(l); 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..."); if ((err = jni_create_java_vm(&(p->jvm), &penv, &vm_args)) != 0) { jk_log(l, JK_LOG_EMERG, "could not create JVM, code: %d ", err); JK_TRACE_EXIT(l); return JK_FALSE; } jk_log(l, JK_LOG_DEBUG, "JVM created, done"); *env = penv; JK_TRACE_EXIT(l); 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_TRACE_ENTER(l); /* [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, "can't get default vm init args"); JK_TRACE_EXIT(l); return JK_FALSE; } jk_log(l, JK_LOG_DEBUG, "found version: %X, done", vm_args.version); JK_TRACE_EXIT(l); return vm_args.version; } static char *build_opt_str(jk_pool_t *p, char *opt_name, char *opt_value, jk_logger_t *l) { size_t 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, "allocation error for %s", 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 */ size_t 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, "allocation error for %s", 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_TRACE_ENTER(l); 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, "setting classpath to %s", 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, "setting max heap to %d", 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, "setting start heap to %d", 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, "setting %s", 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, "using option: %s", 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, "the JVM will ignore unknown options"); vm_args.ignoreUnrecognized = JNI_TRUE; } else { jk_log(l, JK_LOG_DEBUG, "the JVM will FAIL if it finds unknown options"); vm_args.ignoreUnrecognized = JNI_FALSE; } jk_log(l, JK_LOG_DEBUG, "about to create JVM..."); 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."); 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 ", err); JK_TRACE_EXIT(l); return JK_FALSE; } *env = penv; jk_log(l, JK_LOG_DEBUG, "JVM created"); JK_TRACE_EXIT(l); 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_TRACE_ENTER(l); 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", p->bridge_type); JK_TRACE_EXIT(l); 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", btype); JK_TRACE_EXIT(l); return JK_FALSE; } jk_log(l, JK_LOG_DEBUG, "In get_bridge_object, loaded %s bridge class", btype); constructor_method_id = (*env)->GetMethodID(env, p->jk_java_bridge_class, strdup_ascii(&p->p, ""), /* 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"); JK_TRACE_EXIT(l); 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"); JK_TRACE_EXIT(l); 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"); p->jk_java_bridge_class = (jclass) NULL; p->jk_java_bridge_object = (jobject) NULL; JK_TRACE_EXIT(l); return JK_FALSE; } JK_TRACE_EXIT(l); 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()"); 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()"); 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()"); 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_TRACE_ENTER(l); #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"); JK_TRACE_EXIT(l); return rc; } jk_log(l, JK_LOG_ERROR, "In attach_to_jvm, cannot attach thread to JVM."); JK_TRACE_EXIT(l); 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) { JK_TRACE_ENTER(l); if (!p->jvm || !(*(p->jvm))) { jk_log(l, JK_LOG_ERROR, "cannot detach from NULL JVM."); } if (0 == (*(p->jvm))->DetachCurrentThread(p->jvm)) { jk_log(l, JK_LOG_DEBUG, "detached ok"); } else { jk_log(l, JK_LOG_ERROR, "cannot detach from JVM."); } JK_TRACE_EXIT(l); } #else int JK_METHOD jni_worker_factory(jk_worker_t **w, const char *name, jk_logger_t *l) { jk_log(l, JK_LOG_WARNING, "Worker '%s' is of type jni, which is deprecated " "and will be removed in a future release.", name ? name : "(null)"); if (w) *w = NULL; return 0; } #endif /* JNI_VERSION_1_6 */