You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/12/01 23:37:29 UTC

cvs commit: jakarta-tomcat-connectors/jk/native2/common jk_ajp14_worker.c jk_jni_worker.c jk_lb_worker.c

costin      01/12/01 14:37:29

  Added:       jk/native2/common jk_ajp14_worker.c jk_jni_worker.c
                        jk_lb_worker.c
  Log:
  Workers - updated to use the new factory style.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat-connectors/jk/native2/common/jk_ajp14_worker.c
  
  Index: jk_ajp14_worker.c
  ===================================================================
  /* ========================================================================= *
   *                                                                           *
   *                 The Apache Software License,  Version 1.1                 *
   *                                                                           *
   *          Copyright (c) 1999-2001 The Apache Software Foundation.          *
   *                           All rights reserved.                            *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * Redistribution and use in source and binary forms,  with or without modi- *
   * fication, are permitted provided that the following conditions are met:   *
   *                                                                           *
   * 1. Redistributions of source code  must retain the above copyright notice *
   *    notice, this list of conditions and the following disclaimer.          *
   *                                                                           *
   * 2. Redistributions  in binary  form  must  reproduce the  above copyright *
   *    notice,  this list of conditions  and the following  disclaimer in the *
   *    documentation and/or other materials provided with the distribution.   *
   *                                                                           *
   * 3. The end-user documentation  included with the redistribution,  if any, *
   *    must include the following acknowlegement:                             *
   *                                                                           *
   *       "This product includes  software developed  by the Apache  Software *
   *        Foundation <http://www.apache.org/>."                              *
   *                                                                           *
   *    Alternately, this acknowlegement may appear in the software itself, if *
   *    and wherever such third-party acknowlegements normally appear.         *
   *                                                                           *
   * 4. The names  "The  Jakarta  Project",  "Jk",  and  "Apache  Software     *
   *    Foundation"  must not be used  to endorse or promote  products derived *
   *    from this  software without  prior  written  permission.  For  written *
   *    permission, please contact <ap...@apache.org>.                        *
   *                                                                           *
   * 5. Products derived from this software may not be called "Apache" nor may *
   *    "Apache" appear in their names without prior written permission of the *
   *    Apache Software Foundation.                                            *
   *                                                                           *
   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES *
   * INCLUDING, BUT NOT LIMITED TO,  THE IMPLIED WARRANTIES OF MERCHANTABILITY *
   * AND FITNESS FOR  A PARTICULAR PURPOSE  ARE DISCLAIMED.  IN NO EVENT SHALL *
   * THE APACHE  SOFTWARE  FOUNDATION OR  ITS CONTRIBUTORS  BE LIABLE  FOR ANY *
   * DIRECT,  INDIRECT,   INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL *
   * DAMAGES (INCLUDING,  BUT NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE GOODS *
   * OR SERVICES;  LOSS OF USE,  DATA,  OR PROFITS;  OR BUSINESS INTERRUPTION) *
   * HOWEVER CAUSED AND  ON ANY  THEORY  OF  LIABILITY,  WHETHER IN  CONTRACT, *
   * STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
   * ANY  WAY  OUT OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF  ADVISED  OF THE *
   * POSSIBILITY OF SUCH DAMAGE.                                               *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * This software  consists of voluntary  contributions made  by many indivi- *
   * duals on behalf of the  Apache Software Foundation.  For more information *
   * on the Apache Software Foundation, please see <http://www.apache.org/>.   *
   *                                                                           *
   * ========================================================================= */
  
  /**
   * Description: AJP14 next generation Bi-directional protocol.
   *              Backward compatible with Ajp13
   * Author:      Henri Gomez <hg...@slib.fr>
   * Author:      Costin <co...@costin.dnt.ro>                              
   * Author:      Gal Shachor <sh...@il.ibm.com>                           
   */
  
  #include "jk_global.h"
  #include "jk_context.h"
  #include "jk_pool.h"
  #include "jk_util.h"
  #include "jk_msg_buff.h"
  #include "jk_ajp_common.h"
  #include "jk_ajp14.h" 
  #include "jk_logger.h"
  #include "jk_service.h"
  #include "jk_env.h"
  
  /* Ajp14 methods - XXX move to handler abstraction */
  int logon(ajp_endpoint_t *ae,
            jk_logger_t    *l);
  
  int discovery(ajp_endpoint_t *ae,
                jk_workerEnv_t *we,
                jk_logger_t    *l);
  
  /* -------------------- Method -------------------- */
  static int JK_METHOD validate(jk_worker_t *pThis,
                                jk_map_t    *props,
                                jk_workerEnv_t *we,
                                jk_logger_t *l)
  {   
      ajp_worker_t *aw;
      char * secret_key;
      int proto=AJP14_PROTO;
  
      aw = pThis->worker_private;
      secret_key = jk_get_worker_secret_key(props, aw->name);
      
      if ((!secret_key) || (!strlen(secret_key))) {
          jk_log(l, JK_LOG_ERROR,
                 "No secretkey, defaulting to unauthenticated AJP13\n");
          proto=AJP13_PROTO;
          aw->proto= AJP13_PROTO;
          aw->logon= NULL; 
      }
      
      if (ajp_validate(pThis, props, we, l, proto) == JK_FALSE)
          return JK_FALSE;
      
      /* jk_log(l, JK_LOG_DEBUG,
         "Into ajp14:validate - secret_key=%s\n", secret_key); */
      return JK_TRUE;
  }
  
  static int JK_METHOD get_endpoint(jk_worker_t    *pThis,
                                    jk_endpoint_t **pend,
                                    jk_logger_t    *l)
  {
      ajp_worker_t *aw=pThis->worker_private;
  
      if( aw->login->secret_key ==NULL ) {
          return (ajp_get_endpoint(pThis, pend, l, AJP13_PROTO));
      } else {
          return (ajp_get_endpoint(pThis, pend, l, AJP14_PROTO));
      }
  }
  
  static int JK_METHOD init(jk_worker_t *pThis,
                            jk_map_t    *props, 
                            jk_workerEnv_t *we,
                            jk_logger_t *l)
  {
      ajp_worker_t   *aw=pThis->worker_private;
      ajp_endpoint_t *ae;
      jk_endpoint_t  *je;
      int             rc;
      char * secret_key;
      int proto=AJP14_PROTO;
      
      secret_key = jk_get_worker_secret_key(props, aw->name);
      
      if( secret_key==NULL ) {
          proto=AJP13_PROTO;
          aw->proto= AJP13_PROTO;
          aw->logon= NULL; 
      } else {
          /* Set Secret Key (used at logon time) */	
          aw->login->secret_key = strdup(secret_key);
      }
  
      if (ajp_init(pThis, props, we, l, proto) == JK_FALSE)
          return JK_FALSE;
      
      if (aw->login->secret_key == NULL) {
          /* No extra initialization for AJP13 */
          return JK_TRUE;
      }
  
      /* -------------------- Ajp14 discovery -------------------- */
      
      /* Set WebServerName (used at logon time) */
      aw->login->web_server_name = strdup(we->server_name);
      
      if (aw->login->web_server_name == NULL) {
          jk_log(l, JK_LOG_ERROR, "can't malloc web_server_name\n");
          return JK_FALSE;
      }
      
      if (get_endpoint(pThis, &je, l) == JK_FALSE)
          return JK_FALSE;
      
      ae = je->endpoint_private;
      
      if (ajp_connect_to_endpoint(ae, l) == JK_TRUE) {
          
  	/* connection stage passed - try to get context info
  	 * this is the long awaited autoconf feature :)
  	 */
          rc = discovery(ae, we, l);
          ajp_close_endpoint(ae, l);
          return rc;
      }
      
      return JK_TRUE;
  }
  
  
  static int JK_METHOD destroy(jk_worker_t **pThis,
                               jk_logger_t *l)
  {
      ajp_worker_t *aw = (*pThis)->worker_private;
      
      if (aw->login != NULL &&
          aw->login->secret_key != NULL ) {
          /* Ajp14-specific initialization */
          if (aw->login->web_server_name) {
              free(aw->login->web_server_name);
              aw->login->web_server_name = NULL;
          }
          
          if (aw->login->secret_key) {
              free(aw->login->secret_key);
              aw->login->secret_key = NULL;
          }
          
          free(aw->login);
          aw->login = NULL;
          return (ajp_destroy(pThis, l, AJP14_PROTO));
      } else {
          return (ajp_destroy(pThis, l, AJP13_PROTO));
      }    
  }
  
  int JK_METHOD jk_worker_ajp14_factory( jk_env_t *env, void **result,
                                         char *type, char *name)
  {
      jk_logger_t *l=env->logger;
      jk_worker_t **w=result;
      ajp_worker_t *aw = (ajp_worker_t *)malloc(sizeof(ajp_worker_t));
     
      jk_log(l, JK_LOG_DEBUG, "Into ajp14_worker_factory\n");
  
      if (name == NULL || w == NULL) {
          jk_log(l, JK_LOG_ERROR, "In ajp14_worker_factory, NULL parameters\n");
          return JK_FALSE;
      }
  
      if (! aw) {
          jk_log(l, JK_LOG_ERROR,
                 "In ajp14_worker_factory, malloc of private data failed\n");
          return JK_FALSE;
      }
  
      aw->name = strdup(name);
      
      if (! aw->name) {
          free(aw);
          jk_log(l, JK_LOG_ERROR,
                 "In ajp14_worker_factory, malloc failed for name\n");
          return JK_FALSE;
      }
  
      aw->proto= AJP14_PROTO;
  
      aw->login= (jk_login_service_t *)malloc(sizeof(jk_login_service_t));
  
      if (aw->login == NULL) {
          jk_log(l, JK_LOG_ERROR,
                 "In ajp14_worker_factory, malloc failed for login area\n");
          return JK_FALSE;
      }
  	
      memset(aw->login, 0, sizeof(jk_login_service_t));
      
      aw->login->negociation=
          (AJP14_CONTEXT_INFO_NEG | AJP14_PROTO_SUPPORT_AJP14_NEG);
      aw->login->web_server_name=NULL; /* must be set in init */
      
      aw->ep_cache_sz= 0;
      aw->ep_cache= NULL;
      aw->connect_retry_attempts= AJP_DEF_RETRY_ATTEMPTS;
      aw->worker.worker_private= aw;
     
      aw->worker.validate= validate;
      aw->worker.init= init;
      aw->worker.get_endpoint= get_endpoint;
      aw->worker.destroy=destroy;
      aw->logon= logon; 
  
      *w = &aw->worker;
      return JK_TRUE;
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/native2/common/jk_jni_worker.c
  
  Index: jk_jni_worker.c
  ===================================================================
  /* ========================================================================= *
   *                                                                           *
   *                 The Apache Software License,  Version 1.1                 *
   *                                                                           *
   *          Copyright (c) 1999-2001 The Apache Software Foundation.          *
   *                           All rights reserved.                            *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * Redistribution and use in source and binary forms,  with or without modi- *
   * fication, are permitted provided that the following conditions are met:   *
   *                                                                           *
   * 1. Redistributions of source code  must retain the above copyright notice *
   *    notice, this list of conditions and the following disclaimer.          *
   *                                                                           *
   * 2. Redistributions  in binary  form  must  reproduce the  above copyright *
   *    notice,  this list of conditions  and the following  disclaimer in the *
   *    documentation and/or other materials provided with the distribution.   *
   *                                                                           *
   * 3. The end-user documentation  included with the redistribution,  if any, *
   *    must include the following acknowlegement:                             *
   *                                                                           *
   *       "This product includes  software developed  by the Apache  Software *
   *        Foundation <http://www.apache.org/>."                              *
   *                                                                           *
   *    Alternately, this acknowlegement may appear in the software itself, if *
   *    and wherever such third-party acknowlegements normally appear.         *
   *                                                                           *
   * 4. The names  "The  Jakarta  Project",  "Jk",  and  "Apache  Software     *
   *    Foundation"  must not be used  to endorse or promote  products derived *
   *    from this  software without  prior  written  permission.  For  written *
   *    permission, please contact <ap...@apache.org>.                        *
   *                                                                           *
   * 5. Products derived from this software may not be called "Apache" nor may *
   *    "Apache" appear in their names without prior written permission of the *
   *    Apache Software Foundation.                                            *
   *                                                                           *
   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES *
   * INCLUDING, BUT NOT LIMITED TO,  THE IMPLIED WARRANTIES OF MERCHANTABILITY *
   * AND FITNESS FOR  A PARTICULAR PURPOSE  ARE DISCLAIMED.  IN NO EVENT SHALL *
   * THE APACHE  SOFTWARE  FOUNDATION OR  ITS CONTRIBUTORS  BE LIABLE  FOR ANY *
   * DIRECT,  INDIRECT,   INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL *
   * DAMAGES (INCLUDING,  BUT NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE GOODS *
   * OR SERVICES;  LOSS OF USE,  DATA,  OR PROFITS;  OR BUSINESS INTERRUPTION) *
   * HOWEVER CAUSED AND  ON ANY  THEORY  OF  LIABILITY,  WHETHER IN  CONTRACT, *
   * STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
   * ANY  WAY  OUT OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF  ADVISED  OF THE *
   * POSSIBILITY OF SUCH DAMAGE.                                               *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * This software  consists of voluntary  contributions made  by many indivi- *
   * duals on behalf of the  Apache Software Foundation.  For more information *
   * on the Apache Software Foundation, please see <http://www.apache.org/>.   *
   *                                                                           *
   * ========================================================================= */
  
  /***************************************************************************
   * Description: In process JNI worker                                      *
   * Author:      Gal Shachor <sh...@il.ibm.com>                           *
   * Based on:                                                               *
   * Version:     $Revision: 1.1 $                                           *
   ***************************************************************************/
  
  #if !defined(WIN32) && !defined(NETWARE)
  #include <dlfcn.h>
  #endif
  
  #include <jni.h>
  
  #include "jk_pool.h"
  #include "jk_util.h"
  #include "jk_env.h"
  
  #if defined LINUX && defined APACHE2_SIGHACK
  #include <pthread.h>
  #include <signal.h>
  #include <bits/signum.h>
  #endif
  
  #ifdef NETWARE
  #include <nwthread.h>
  #include <nwadv.h>
  #endif
  #include "jk_logger.h"
  #include "jk_service.h"
  
  #ifndef JNI_VERSION_1_1
  #define JNI_VERSION_1_1 0x00010001
  #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;
  jint (JNICALL *jni_get_created_java_vms)(JavaVM **, int, int *) = NULL;
  
  #define JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/modules/server/JNIEndpoint")
  /* #define 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;
  
      /*
       * 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);
  
  
  #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.  */
                                      (jlong)(int)s,
                                      (jlong)(int)l);
  
      /* [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_workerEnv_t *we,
                                jk_logger_t *l)
  {
      jni_worker_t *p;
      int mem_config = 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_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_workerEnv_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 = NULL;
          jstring stdout_name = NULL;
          jstring stderr_name = NULL;
          jint rc = 0;
  
          if(p->tomcat_cmd_line) {
              cmd_line = (*env)->NewStringUTF(env, p->tomcat_cmd_line);
          }
          if(p->stdout_name) {
              stdout_name = (*env)->NewStringUTF(env, p->stdout_name);
          }
          if(p->stdout_name) {
              stderr_name = (*env)->NewStringUTF(env, 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;
  	p->endpoint.channelData = NULL;
          *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 jk_worker_jni_factory(jk_env_t *env, void **result,
                                      char *type, char *name)
  {
      jk_logger_t *l=env->logger;
      jk_worker_t **w=result;
      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 = NULL;
      private_data->jk_java_bridge_class  = NULL;
      private_data->jk_startup_method     = NULL;
      private_data->jk_service_method     = NULL;
      private_data->jk_shutdown_method    = NULL;
      private_data->tomcat_cmd_line       = NULL;
      private_data->tomcat_classpath      = NULL;
      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)
      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;
      }
  #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");
  
          if(jni_create_java_vm && jni_get_default_java_vm_init_args) {
      	    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", 
                      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) {
          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;
      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;
  
      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 = 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 = 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 = 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 = 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 = 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");
  
      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_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)
  {
      jmethodID  constructor_method_id;
  
      jk_log(l, JK_LOG_DEBUG, 
             "Into get_bridge_object\n");
  
      p->jk_java_bridge_class = (*env)->FindClass(env, JAVA_BRIDGE_CLASS_NAME);
      if(!p->jk_java_bridge_class) {
  	    jk_log(l, JK_LOG_EMERG, "Can't find class %s\n", JAVA_BRIDGE_CLASS_NAME);
  	    return JK_FALSE;
      }
      jk_log(l, JK_LOG_DEBUG, 
             "In get_bridge_object, loaded %s bridge class\n", JAVA_BRIDGE_CLASS_NAME);
  
      constructor_method_id = (*env)->GetMethodID(env,
                                                  p->jk_java_bridge_class,
                                                  "<init>", /* method name */
                                                  "()V");   /* method sign */
      if(!constructor_method_id) {
  	    p->jk_java_bridge_class = 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 = 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 = NULL;
          p->jk_java_bridge_object = 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)
  {
      p->jk_startup_method = (*env)->GetMethodID(env,
                                                 p->jk_java_bridge_class, 
                                                 "startup", 
                                                 "(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, 
                                                 "service", 
                                                 "(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, 
                                                  "shutdown", 
                                                  "()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");
      }
  }
  
  
  
  1.1                  jakarta-tomcat-connectors/jk/native2/common/jk_lb_worker.c
  
  Index: jk_lb_worker.c
  ===================================================================
  /* ========================================================================= *
   *                                                                           *
   *                 The Apache Software License,  Version 1.1                 *
   *                                                                           *
   *          Copyright (c) 1999-2001 The Apache Software Foundation.          *
   *                           All rights reserved.                            *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * Redistribution and use in source and binary forms,  with or without modi- *
   * fication, are permitted provided that the following conditions are met:   *
   *                                                                           *
   * 1. Redistributions of source code  must retain the above copyright notice *
   *    notice, this list of conditions and the following disclaimer.          *
   *                                                                           *
   * 2. Redistributions  in binary  form  must  reproduce the  above copyright *
   *    notice,  this list of conditions  and the following  disclaimer in the *
   *    documentation and/or other materials provided with the distribution.   *
   *                                                                           *
   * 3. The end-user documentation  included with the redistribution,  if any, *
   *    must include the following acknowlegement:                             *
   *                                                                           *
   *       "This product includes  software developed  by the Apache  Software *
   *        Foundation <http://www.apache.org/>."                              *
   *                                                                           *
   *    Alternately, this acknowlegement may appear in the software itself, if *
   *    and wherever such third-party acknowlegements normally appear.         *
   *                                                                           *
   * 4. The names  "The  Jakarta  Project",  "Jk",  and  "Apache  Software     *
   *    Foundation"  must not be used  to endorse or promote  products derived *
   *    from this  software without  prior  written  permission.  For  written *
   *    permission, please contact <ap...@apache.org>.                        *
   *                                                                           *
   * 5. Products derived from this software may not be called "Apache" nor may *
   *    "Apache" appear in their names without prior written permission of the *
   *    Apache Software Foundation.                                            *
   *                                                                           *
   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES *
   * INCLUDING, BUT NOT LIMITED TO,  THE IMPLIED WARRANTIES OF MERCHANTABILITY *
   * AND FITNESS FOR  A PARTICULAR PURPOSE  ARE DISCLAIMED.  IN NO EVENT SHALL *
   * THE APACHE  SOFTWARE  FOUNDATION OR  ITS CONTRIBUTORS  BE LIABLE  FOR ANY *
   * DIRECT,  INDIRECT,   INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL *
   * DAMAGES (INCLUDING,  BUT NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE GOODS *
   * OR SERVICES;  LOSS OF USE,  DATA,  OR PROFITS;  OR BUSINESS INTERRUPTION) *
   * HOWEVER CAUSED AND  ON ANY  THEORY  OF  LIABILITY,  WHETHER IN  CONTRACT, *
   * STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
   * ANY  WAY  OUT OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF  ADVISED  OF THE *
   * POSSIBILITY OF SUCH DAMAGE.                                               *
   *                                                                           *
   * ========================================================================= *
   *                                                                           *
   * This software  consists of voluntary  contributions made  by many indivi- *
   * duals on behalf of the  Apache Software Foundation.  For more information *
   * on the Apache Software Foundation, please see <http://www.apache.org/>.   *
   *                                                                           *
   * ========================================================================= */
  
  /***************************************************************************
   * Description: Load balancer worker, knows how to load balance among      *
   *              several workers.                                           *
   * Author:      Gal Shachor <sh...@il.ibm.com>                           *
   * Based on:                                                               *
   * Version:     $Revision: 1.1 $                                           *
   ***************************************************************************/
  
  #include "jk_pool.h"
  #include "jk_service.h"
  #include "jk_util.h"
  #include "jk_worker.h"
  #include "jk_logger.h"
  #include "jk_env.h"
  
  int JK_METHOD lb_worker_factory(jk_worker_t **w,
                                  const char *name,
                                  jk_logger_t *l);
  
  
  /*
   * The load balancing code in this 
   */
  
  
  /* 
   * Time to wait before retry...
   */
  #define WAIT_BEFORE_RECOVER (60*1) 
  #define ADDITINAL_WAIT_LOAD (20)
  
  struct worker_record {
      char    *name;
      double  lb_factor;
      double  lb_value;
      int     in_error_state;
      int     in_recovering;
      time_t  error_time;
      jk_worker_t *w;
  };
  typedef struct worker_record worker_record_t;
  
  struct lb_worker {
      worker_record_t *lb_workers;
      unsigned num_of_workers;
  
      jk_pool_t p;
      jk_pool_atom_t buf[TINY_POOL_SIZE];
  
      char *name; 
      jk_worker_t worker;
  };
  typedef struct lb_worker lb_worker_t;
  
  struct lb_endpoint {    
      jk_endpoint_t *e;
      lb_worker_t *worker;
      
      jk_endpoint_t endpoint;
  };
  typedef struct lb_endpoint lb_endpoint_t;
  
  
  /* ========================================================================= */
  /* Retrieve the parameter with the given name                                */
  static char *get_path_param(jk_ws_service_t *s,
                              const char *name)
  {
      char *id_start = NULL;
      for(id_start = strstr(s->req_uri, name) ; 
          id_start ; 
          id_start = strstr(id_start + 1, name)) {
          if('=' == id_start[strlen(name)]) {
              /*
               * Session path-cookie was found, get it's value
               */
              id_start += (1 + strlen(name));
              if(strlen(id_start)) {
                  char *id_end;
                  id_start = jk_pool_strdup(s->pool, id_start);
                  /* 
                   * The query string is not part of req_uri, however
                   * to be on the safe side lets remove the trailing query 
                   * string if appended...
                   */
                  if(id_end = strchr(id_start, '?')) { 
                      *id_end = '\0';
                  }
                  return id_start;
              }
          }
      }
    
      return NULL;
  }
  
  /* ========================================================================= */
  /* Retrieve the cookie with the given name                                   */
  static char *get_cookie(jk_ws_service_t *s,
                          const char *name)
  {
      unsigned i;
  
      for(i = 0 ; i < s->num_headers ; i++) {
          if(0 == strcasecmp(s->headers_names[i], "cookie")) {
  
              char *id_start;
              for(id_start = strstr(s->headers_values[i], name) ; 
                  id_start ; 
                  id_start = strstr(id_start + 1, name)) {
                  if('=' == id_start[strlen(name)]) {
                      /*
                       * Session cookie was found, get it's value
                       */
                      id_start += (1 + strlen(name));
                      if(strlen(id_start)) {
                          char *id_end;
                          id_start = jk_pool_strdup(s->pool, id_start);
                          if(id_end = strchr(id_start, ';')) {
                              *id_end = '\0';
                          }
                          return id_start;
                      }
                  }
              }
          }
      }
  
      return NULL;
  }
  
  
  /* ========================================================================= */
  /* Retrieve session id from the cookie or the parameter                      */
  /* (parameter first)                                                         */
  static char *get_sessionid(jk_ws_service_t *s)
  {
      char *val;
      val = get_path_param(s, JK_PATH_SESSION_IDENTIFIER);
      if(!val) {
          val = get_cookie(s, JK_SESSION_IDENTIFIER);
      }
      return val;
  }
  
  static char *get_session_route(jk_ws_service_t *s)
  {
      char *sessionid = get_sessionid(s);
      char *ch;
  
      if(!sessionid) {
          return NULL;
      }
  
      /*
       * Balance parameter is appended to the end
       */  
      ch = strrchr(sessionid, '.');
      if(!ch) {
          return 0;
      }
      ch++;
      if(*ch == '\0') {
          return NULL;
      }
      return ch;
  }
  
  static void close_workers(lb_worker_t *p, 
                            int num_of_workers,
                            jk_logger_t *l)
  {
      int i = 0;
      for(i = 0 ; i < num_of_workers ; i++) {
          p->lb_workers[i].w->destroy(&(p->lb_workers[i].w),
                                      l);
      }
  }
  
  static double get_max_lb(lb_worker_t *p) 
  {
      unsigned i;
      double rc = 0.0;    
  
      for(i = 0 ; i < p->num_of_workers ; i++) {
          if(!p->lb_workers[i].in_error_state) {
              if(p->lb_workers[i].lb_value > rc) {
                  rc = p->lb_workers[i].lb_value;
              }
          }            
      }
  
      return rc;
  
  }
  
  static worker_record_t *get_most_suitable_worker(lb_worker_t *p, 
                                                   jk_ws_service_t *s)
  {
      worker_record_t *rc = NULL;
      double lb_min = 0.0;    
      unsigned i;
      char *session_route = get_session_route(s);
         
      if(session_route) {
          for(i = 0 ; i < p->num_of_workers ; i++) {
              if(0 == strcmp(session_route, p->lb_workers[i].name)) {
                  if(p->lb_workers[i].in_error_state) {
                     break;
                  } else {
                      return &(p->lb_workers[i]);
                  }
              }
          }
      }
  
      for(i = 0 ; i < p->num_of_workers ; i++) {
          if(p->lb_workers[i].in_error_state) {
              if(!p->lb_workers[i].in_recovering) {
                  time_t now = time(0);
                  
                  if((now - p->lb_workers[i].error_time) > WAIT_BEFORE_RECOVER) {
                      
                      p->lb_workers[i].in_recovering  = JK_TRUE;
                      p->lb_workers[i].error_time     = now;
                      rc = &(p->lb_workers[i]);
  
                      break;
                  }
              }
          } else {
              if(p->lb_workers[i].lb_value < lb_min || !rc) {
                  lb_min = p->lb_workers[i].lb_value;
                  rc = &(p->lb_workers[i]);
              }
          }            
      }
  
      if(rc) {
          rc->lb_value += rc->lb_factor;                
      }
  
      return rc;
  }
      
  static int JK_METHOD service(jk_endpoint_t *e, 
                               jk_ws_service_t *s,
                               jk_logger_t *l,
                               int *is_recoverable_error)
  {
      jk_log(l, JK_LOG_DEBUG, 
             "Into jk_endpoint_t::service\n");
  
      if(e && e->endpoint_private && s && is_recoverable_error) {
          lb_endpoint_t *p = e->endpoint_private;
          jk_endpoint_t *end = NULL;
  
          /* you can not recover on another load balancer */
          *is_recoverable_error = JK_FALSE;
  
  
          while(1) {
              worker_record_t *rec = get_most_suitable_worker(p->worker, s);
              int rc;
  
              if(rec) {
                  int is_recoverable = JK_FALSE;
                  
                  s->jvm_route = jk_pool_strdup(s->pool,  rec->name);
  
                  rc = rec->w->get_endpoint(rec->w, &end, l);
                  if(rc && end) {
                      int src = end->service(end, s, l, &is_recoverable);
                      end->done(&end, l);
                      if(src) {                        
                          if(rec->in_recovering) {
                              rec->lb_value = get_max_lb(p->worker) + ADDITINAL_WAIT_LOAD;
                          }
                          rec->in_error_state = JK_FALSE;
                          rec->in_recovering  = JK_FALSE;
                          rec->error_time     = 0;                        
                          return JK_TRUE;
                      } 
                  }
  
                  /*
                   * Service failed !!!
                   *
                   * Time for fault tolerance (if possible)...
                   */
  
                  rec->in_error_state = JK_TRUE;
                  rec->in_recovering  = JK_FALSE;
                  rec->error_time     = time(0);
  
                  if(!is_recoverable) {
                      /*
                       * Error is not recoverable - break with an error.
                       */
                      jk_log(l, JK_LOG_ERROR, 
                             "In jk_endpoint_t::service, none recoverable error...\n");
                      break;
                  }
  
                  /* 
                   * Error is recoverable by submitting the request to
                   * another worker... Lets try to do that.
                   */
                   jk_log(l, JK_LOG_DEBUG, 
                          "In jk_endpoint_t::service, recoverable error... will try to recover on other host\n");
              } else {
                  /* NULL record, no more workers left ... */
                   jk_log(l, JK_LOG_ERROR, 
                          "In jk_endpoint_t::service, No more workers left, can not submit the request\n");
                  break;
              }
          }
      }
  
      jk_log(l, JK_LOG_ERROR, 
             "In jk_endpoint_t::service: NULL Parameters\n");
  
      return JK_FALSE;
  }
  
  static int JK_METHOD done(jk_endpoint_t **e,
                            jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, 
             "Into jk_endpoint_t::done\n");
  
      if(e && *e && (*e)->endpoint_private) {
          lb_endpoint_t *p = (*e)->endpoint_private;
  
          if(p->e) {
              p->e->done(&p->e, l);
          }
  
          free(p);
          *e = NULL;
          return JK_TRUE;
      }
  
      jk_log(l, JK_LOG_ERROR, 
             "In jk_endpoint_t::done: NULL Parameters\n");
  
      return JK_FALSE;
  }
  
  static int JK_METHOD validate(jk_worker_t *pThis,
                                jk_map_t *props,                            
                                jk_workerEnv_t *we,
                                jk_logger_t *l)
  {
      int err;
      jk_log(l, JK_LOG_DEBUG, 
             "Into jk_worker_t::validate\n");
  
      if(pThis && pThis->worker_private) {        
          lb_worker_t *p = pThis->worker_private;
          char **worker_names;
          unsigned num_of_workers;
          
          if(jk_get_lb_worker_list(props,
                                   p->name,
                                   &worker_names, 
                                   &num_of_workers) && num_of_workers) {
              unsigned i = 0;
  
              p->lb_workers = jk_pool_alloc(&p->p, 
                                            num_of_workers * sizeof(worker_record_t));
  
              if(!p->lb_workers) {
                  return JK_FALSE;
              }
  
              for(i = 0 ; i < num_of_workers ; i++) {
                  p->lb_workers[i].name = jk_pool_strdup(&p->p, worker_names[i]);
                  p->lb_workers[i].lb_factor = jk_get_lb_factor(props, 
                                                                 worker_names[i]);
                  p->lb_workers[i].lb_factor = 1/p->lb_workers[i].lb_factor;
                  /* 
                   * Allow using lb in fault-tolerant mode.
                   * Just set lbfactor in worker.properties to 0 to have 
                   * a worker used only when principal is down or session route
                   * point to it. Provided by Paul Frieden <pf...@dchain.com>
                   */
                  p->lb_workers[i].lb_value = p->lb_workers[i].lb_factor;
                  p->lb_workers[i].in_error_state = JK_FALSE;
                  p->lb_workers[i].in_recovering  = JK_FALSE;
  
                  p->lb_workers[i].w=
                      pThis->workerEnv->createWorker( pThis->workerEnv,
                                                      p->lb_workers[i].name,
                                                      props );
  
                  if( p->lb_workers[i].w == NULL ) {
                      break;
                  }
              }
  
              if(i != num_of_workers) {
                  close_workers(p, i, l);
                  jk_log(l, JK_LOG_ERROR, 
                         "In jk_worker_t::validate: Failed to create worker %s\n",
                         p->lb_workers[i].name);
  
              } else {
                  p->num_of_workers = num_of_workers;
                  return JK_TRUE;
              }
          }        
      }
  
      jk_log(l, JK_LOG_ERROR, 
             "In jk_worker_t::validate: NULL Parameters\n");
  
      return JK_FALSE;
  }
  
  static int JK_METHOD init(jk_worker_t *pThis,
                            jk_map_t *props, 
                            jk_workerEnv_t *we,
                            jk_logger_t *log)
  {
      /* Nothing to do for now */
      return JK_TRUE;
  }
  
  static int JK_METHOD get_endpoint(jk_worker_t *pThis,
                                    jk_endpoint_t **pend,
                                    jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, 
             "Into jk_worker_t::get_endpoint\n");
  
      if(pThis && pThis->worker_private && pend) {        
          lb_endpoint_t *p = (lb_endpoint_t *)malloc(sizeof(lb_endpoint_t));
          if(p) {
              p->e = NULL;
              p->worker = pThis->worker_private;
              p->endpoint.endpoint_private = p;
              p->endpoint.service = service;
              p->endpoint.done = done;
  	    p->endpoint.channelData = NULL;
              *pend = &p->endpoint;
  
              return JK_TRUE;
          }
          jk_log(l, JK_LOG_ERROR, 
                 "In jk_worker_t::get_endpoint, malloc failed\n");
      } else {
          jk_log(l, JK_LOG_ERROR, 
                 "In jk_worker_t::get_endpoint, NULL parameters\n");
      }
  
      return JK_FALSE;
  }
  
  static int JK_METHOD destroy(jk_worker_t **pThis,
                               jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, 
             "Into jk_worker_t::destroy\n");
  
      if(pThis && *pThis && (*pThis)->worker_private) {
          lb_worker_t *private_data = (*pThis)->worker_private;
  
          close_workers(private_data, 
                        private_data->num_of_workers,
                        l);
  
          jk_close_pool(&private_data->p);
          free(private_data);
  
          return JK_TRUE;
      }
  
      jk_log(l, JK_LOG_ERROR, 
             "In jk_worker_t::destroy, NULL parameters\n");
      return JK_FALSE;
  }
  
  int JK_METHOD jk_worker_lb_factory(jk_env_t *env,
                                     void **result,
                                     char *type,
                                     char *name)
  {
      jk_logger_t *l=env->logger;
      jk_worker_t **w=result;
      lb_worker_t *private_data;
      
      jk_log(l, JK_LOG_DEBUG, "Into lb_worker_factory\n");
  
      if(NULL != name && NULL != w) {
          jk_log(l, JK_LOG_ERROR,"In lb_worker_factory, NULL parameters\n");
          return JK_FALSE;
      }
  
      private_data = (lb_worker_t *)malloc(sizeof(lb_worker_t));
      if(!private_data) {
          jk_log(l, JK_LOG_ERROR,"In lb_worker_factory, malloc failed\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 lb_worker_factory, malloc failed\n");
          jk_close_pool(&private_data->p);
          free(private_data);
          return JK_FALSE;
      }
  
      private_data->lb_workers = NULL;
      private_data->num_of_workers = 0;
      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;
      return JK_TRUE;
  }
  
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>