blob: 34e610471dc3f02b87fc4e57e8e6f2afa23645db [file] [log] [blame]
/*
* 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");
}
}