blob: 8634b710ab0f5523ff4b31d32786a6a27f9de1c4 [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.
*/
/*
* Module jk2: keeps all servlet/jakarta related ramblings together.
*
* Author: Alexander Leykekh (aolserver@aol.net)
*
*/
#include "ns.h"
#include "jk_ns.h"
#include <jni.h>
#define LOG_ERROR(msg) Ns_Log(Error,"nsjk2: %s:%d:$s",__FILE__,__LINE__,msg)
#define LOG_ERROR2(msg1,msg2) Ns_Log(Error,"nsjk2: %s:%d:$s:%s",__FILE__,__LINE__,msg1,msg2)
/* Context allows request handler extract worker and vhost for the URI quickly */
typedef struct {
char* serverName;
jk_uriEnv_t* uriEnv;
} uriContext;
#ifdef WIN32
static char file_name[_MAX_PATH];
#endif
/* Standard AOLserver module global */
int Ns_ModuleVersion = 1;
/* Globals
*
* There is one instance of JVM worker environment per process, regardless of
* virtual servers in AOLserver's nsd.tcl file or their counterparts in
* Tomcat's server.xml. The only thing that AOLserver virtual servers do separately
* is map URIs for Tomcat to process.
*
*/
static jk_workerEnv_t *workerEnv = NULL; /* JK2 environment */
static JavaVM* jvmGlobal = NULL; /* JVM instance */
static Ns_Tls jkTls; /* TLS destructor detaches request thread from JVM */
static unsigned jkInitCount =0; /* number of running virtual servers using this module */
/** Basic initialization for jk2.
*/
static int jk2_create_workerEnv(apr_pool_t *p, char* serverRoot) {
jk_env_t *env;
jk_logger_t *l;
jk_pool_t *globalPool;
jk_bean_t *jkb;
if (jk2_pool_apr_create( NULL, &globalPool, NULL, p) != JK_OK) {
LOG_ERROR("Cannot create apr pool");
return JK_ERR;
}
/** Create the global environment. This will register the default
factories
*/
env=jk2_env_getEnv( NULL, globalPool );
if (env == NULL) {
LOG_ERROR("jk2_create_workerEnv: Cannot get environment");
return JK_ERR;
}
/* Optional. Register more factories ( or replace existing ones ) */
/* Init the environment. */
/* Create the logger */
env->registerFactory( env, "logger.ns", jk2_logger_ns_factory );
jkb=env->createBean2( env, env->globalPool, "logger.ns", "");
if (jkb == NULL) {
LOG_ERROR("Cannot create logger bean (with factory)");
return JK_ERR;
}
env->alias( env, "logger.ns:", "logger");
l = jkb->object;
env->l=l;
#ifdef WIN32
env->soName=env->globalPool->pstrdup(env, env->globalPool, file_name);
if( env->soName == NULL ){
env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error creating env->soName\n");
return JK_ERR;
}
#else
env->soName=NULL;
#endif
/* Create the workerEnv */
jkb=env->createBean2( env, env->globalPool,"workerEnv", "");
if (jkb == NULL) {
LOG_ERROR("Cannot create worker environment bean");
return JK_ERR;
}
workerEnv= jkb->object;
if( workerEnv==NULL ) {
env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error creating workerEnv\n");
return JK_ERR;
}
env->alias( env, "workerEnv:" , "workerEnv");
/* AOLserver is a single-process environment, but leaving childId as -1 breaks
JNI initialization
*/
workerEnv->childId=0;
workerEnv->initData->add( env, workerEnv->initData, "serverRoot",
workerEnv->pool->pstrdup( env, workerEnv->pool, serverRoot));
env->l->jkLog(env, env->l, JK_LOG_INFO, "Set JK2 serverRoot %s\n", serverRoot);
return JK_OK;
}
/**
* Shutdown callback for AOLserver
*/
void jk2_shutdown_system (void* data)
{
if (--jkInitCount == 0) {
apr_pool_terminate ();
apr_terminate ();
}
}
/**
* JK2 module shutdown callback for APR (Apache Portable Runtime)
*/
static apr_status_t jk2_shutdown(void *data)
{
jk_env_t *env;
if (workerEnv) {
env=workerEnv->globalEnv;
/* bug in APR?? results in SEGV. Not important, the process is going away
workerEnv->close(env, workerEnv);
*/
if( workerEnv->vm != NULL && !workerEnv->vm->mbean->disabled)
workerEnv->vm->destroy( env, workerEnv->vm );
workerEnv = NULL;
}
return APR_SUCCESS;
}
/* ========================================================================= */
/* The JK module handlers */
/* ========================================================================= */
/** Main service method, called to forward a request to tomcat
*/
static int jk2_handler(void* context, Ns_Conn *conn)
{
jk_logger_t *l=NULL;
int rc;
jk_worker_t *worker=NULL;
jk_endpoint_t *end = NULL;
jk_env_t *env;
char* workerName;
jk_ws_service_t *s=NULL;
jk_pool_t *rPool=NULL;
int rc1;
jk_uriEnv_t *uriEnv;
uriContext* uriCtx = (uriContext*)context;
/* must make this call to assure TLS destructor runs when thread exists */
Ns_TlsSet (&jkTls, jvmGlobal);
uriEnv= uriCtx->uriEnv;
/* there is a chance of dynamic reconfiguration, so instead of doing above statement, might map
the URI to its context object on the fly, like this:
uriEnv = workerEnv->uriMap->mapUri (workerEnv->globalEnv, workerEnv->uriMap, conn->request->host, conn->request->url);
*/
/* Get an env instance */
env = workerEnv->globalEnv->getEnv( workerEnv->globalEnv );
worker = uriEnv->worker;
if (worker == NULL) /* use global default */
worker=workerEnv->defaultWorker;
workerName = uriEnv->mbean->getAttribute (env, uriEnv->mbean, "group");
if( worker==NULL && workerName!=NULL ) {
worker=env->getByName( env, workerName);
env->l->jkLog(env, env->l, JK_LOG_INFO,
"finding worker for %#lx %#lx %s\n",
worker, uriEnv, workerName);
uriEnv->worker=worker;
}
if(worker==NULL || worker->mbean==NULL || worker->mbean->localName==NULL ) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"No worker for %s\n", conn->request->url);
workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
return NS_FILTER_RETURN;
}
if( uriEnv->mbean->debug > 0 )
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"serving %s with %#lx %#lx %s\n",
uriEnv->mbean->localName, worker, worker->mbean, worker->mbean->localName );
/* Get a pool for the request */
rPool= worker->rPoolCache->get( env, worker->rPoolCache );
if( rPool == NULL ) {
rPool=worker->mbean->pool->create( env, worker->mbean->pool, HUGE_POOL_SIZE );
if( uriEnv->mbean->debug > 0 )
env->l->jkLog(env, env->l, JK_LOG_DEBUG, "new rpool %#lx\n", rPool );
}
s=(jk_ws_service_t *)rPool->calloc( env, rPool, sizeof( jk_ws_service_t ));
jk2_service_ns_init( env, s, uriCtx->serverName);
s->pool = rPool;
s->init( env, s, worker, conn );
/* reset the reco_status, will be set to INITED in LB mode */
s->reco_status = RECO_NONE;
s->is_recoverable_error = JK_FALSE;
s->uriEnv = uriEnv;
rc = worker->service(env, worker, s);
s->afterRequest(env, s);
rPool->reset(env, rPool);
rc1=worker->rPoolCache->put( env, worker->rPoolCache, rPool );
if( rc1 == JK_OK ) {
rPool=NULL;
}
if( rPool!=NULL ) {
rPool->close(env, rPool);
}
if(rc==JK_OK) {
workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
return NS_OK;
}
env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error connecting to tomcat %d\n", rc);
workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
Ns_ConnReturnInternalError (conn);
return NS_FILTER_RETURN;
}
/*
* TLS destructor. Sole purpose is to detach current thread from JVM before the thread exits
*/
static void jkTlsDtor (void* arg) {
void* env = NULL;
jint err;
/* check if attached 1st */
if (jvmGlobal!=NULL &&
JNI_OK==((*jvmGlobal)->GetEnv(jvmGlobal, &env, JNI_VERSION_1_2 )))
{
(*jvmGlobal)->DetachCurrentThread (jvmGlobal);
}
}
/** Register a URI pattern with AOLserver
*/
static void registerURI (jk_env_t* env, jk_uriEnv_t* uriEnv, char* vserver, char* serverName) {
uriContext* ctx;
char* uriname;
if (uriEnv==NULL || uriEnv->mbean==NULL)
return;
uriname=uriEnv->mbean->getAttribute (env, uriEnv->mbean, "uri");
if (uriname==NULL || strcmp(uriname, "/")==0)
return;
ctx = Ns_Malloc (sizeof (uriContext));
if (ctx == NULL)
return;
ctx->serverName = serverName;
ctx->uriEnv = uriEnv;
if (uriEnv->mbean->debug > 0)
env->l->jkLog (env, env->l, JK_LOG_DEBUG, "registering URI %s", uriname);
Ns_RegisterRequest(vserver, "GET", uriname, jk2_handler, NULL, ctx, 0);
Ns_RegisterRequest(vserver, "HEAD", uriname, jk2_handler, NULL, ctx, 0);
Ns_RegisterRequest(vserver, "POST", uriname, jk2_handler, NULL, ctx, 0);
}
/** Standard AOLserver callback.
* Initialize jk2, unless already done in another server. Register URI mappping for Tomcat
* If multiple virtual servers use this module, calls to Ns_ModuleInit will be made serially
* in order of appearance of those servers in nsd.tcl
*/
int Ns_ModuleInit(char *server, char *module)
{
jk_env_t *env;
/* configuration-related */
char* serverName=NULL;
char* confPath;
static char cwdBuf[PATH_MAX];
static char* serverRoot = NULL;
/* APR-related */
apr_status_t aprrc;
char errbuf[512];
apr_pool_t *jk_globalPool = NULL;
/* URI registration */
char *hosts[2] = {"*", NULL};
jk_map_t *vhosts;
int i, j, k, l, cnt1, cnt2;
jk_map_t *uriMap, *webapps, *uriMaps[3];
jk_uriEnv_t *uriEnv, *hostEnv, *appEnv;
if (jkInitCount++ == 0) {
/* Get Tomcat installation root - this value is same for all virtual servers*/
if (serverRoot == NULL) {
confPath = Ns_ConfigGetPath (NULL, module, NULL);
serverRoot = (confPath? Ns_ConfigGetValue (confPath, "serverRoot") : NULL);
}
/* not configured in nsd.tcl? try env. variable */
if (serverRoot == NULL) {
serverRoot = getenv ("TOMCAT_HOME");
}
/* not in env. variables? get it from CWD */
if (serverRoot == NULL) {
serverRoot = getcwd (cwdBuf, sizeof(cwdBuf));
}
/* Initialize APR */
if ((aprrc=apr_initialize()) != APR_SUCCESS) {
LOG_ERROR2 ("Cannot initialize APR", apr_strerror (aprrc, errbuf, sizeof(errbuf)));
return NS_ERROR;
}
if ((aprrc=apr_pool_create(&jk_globalPool, NULL)) != APR_SUCCESS) {
LOG_ERROR2 ("Cannot create global APR pool", apr_strerror (aprrc, errbuf, sizeof(errbuf)));
return NS_ERROR;
}
/* Initialize JNI */
if (workerEnv==NULL && jk2_create_workerEnv(jk_globalPool, serverRoot)==JK_ERR) {
return NS_ERROR;
}
env=workerEnv->globalEnv;
env->setAprPool(env, jk_globalPool);
/* Initialize JK2 */
if (workerEnv->init(env, workerEnv ) != JK_OK) {
LOG_ERROR("Cannot initialize worker environment");
return NS_ERROR;
}
workerEnv->server_name = apr_pstrcat (jk_globalPool, Ns_InfoServerName(), " ", Ns_InfoServerVersion (), NULL);
apr_pool_cleanup_register(jk_globalPool, NULL, jk2_shutdown, apr_pool_cleanup_null);
workerEnv->was_initialized = JK_TRUE;
if ((aprrc=apr_pool_userdata_set( "INITOK", "Ns_ModuleInit", NULL, jk_globalPool )) != APR_SUCCESS) {
LOG_ERROR2 ("Cannot set APR pool user data", apr_strerror (aprrc, errbuf, sizeof(errbuf)));
return NS_ERROR;
}
if (workerEnv->parentInit( env, workerEnv) != JK_OK) {
LOG_ERROR ("Cannot initialize global environment");
return NS_ERROR;
}
/* Obtain TLS slot - it's destructor will detach threads from JVM */
jvmGlobal = workerEnv->vm->jvm;
Ns_TlsAlloc (&jkTls, jkTlsDtor);
Ns_Log (Notice, "nsjk2: Initialized JK2 environment");
} else {
env = workerEnv->globalEnv;
}
Ns_RegisterShutdown (jk2_shutdown_system, NULL);
/* Register URI patterns from workers2.properties with AOLserver
*
* Worker environment has a list of vhosts, including "*" vhost.
* Each vhost has a list of web applications (contexts) associated with it.
* Each webapp has a list of exact, prefix, suffix and regexp URI patterns.
*
* Will register URIs that are either in vhost "*", or one with name matching
* this AOLserver virtual server. Will ignore regexp patterns. Will register
* exact webapp URIs (context root) as JK2 somehow doesn't include them in URI
* maps, even if specified in workers2.properties.
*
*/
/* virtual server name override if specified */
confPath = Ns_ConfigGetPath (server, module, NULL);
if (confPath != NULL)
serverName = Ns_ConfigGetValue (confPath, "serverName");
if (serverName == NULL)
serverName = server;
vhosts=workerEnv->uriMap->vhosts;
hosts[1]= serverName;
for (i=0; i<sizeof(hosts)/sizeof(*hosts); i++) {
hostEnv=vhosts->get (env, vhosts, hosts[i]);
if (hostEnv==NULL || hostEnv->webapps==NULL)
continue;
webapps=hostEnv->webapps;
cnt1=webapps->size(env, webapps);
for (j=0; j<cnt1; j++) {
appEnv = webapps->valueAt (env, webapps, j);
if (appEnv == NULL)
continue;
/* register webapp root - registerURI checks if it is "/" */
registerURI (env, appEnv, server, serverName);
uriMaps[0] = appEnv->exactMatch;
uriMaps[1] = appEnv->prefixMatch;
uriMaps[2] = appEnv->suffixMatch;
for (k=0; k<sizeof(uriMaps)/sizeof(*uriMaps); k++) {
if (uriMaps[k] == NULL)
continue;
cnt2 = uriMaps[k]->size (env, uriMaps[k]);
for (l=0; l<cnt2; l++) {
registerURI (env, uriMaps[k]->valueAt (env, uriMaps[k], l), server, serverName);
}
}
}
}
Ns_Log (Notice, "nsjk2: Initialized on %s", server);
return NS_OK;
}
#ifdef WIN32
BOOL WINAPI DllMain(HINSTANCE hInst, // Instance Handle of the DLL
ULONG ulReason, // Reason why NT called this DLL
LPVOID lpReserved) // Reserved parameter for future use
{
GetModuleFileName( hInst, file_name, sizeof(file_name));
return TRUE;
}
#endif