blob: a37da1ce529a368238d4856b849f42b3f4b4e583 [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.
*/
/**
* Implementation for org.apache.jk.apr.AprImpl
*
* @author Costin Manolache
*/
#ifdef HAVE_JNI
#include <jni.h>
#include "apr.h"
#include "apr_pools.h"
#include "apr_network_io.h"
#include "apr_errno.h"
#include "apr_general.h"
#include "apr_strings.h"
#include "apr_portable.h"
#include "apr_lib.h"
#include "org_apache_jk_apr_AprImpl.h"
#include "jk_global.h"
#include "jk_map.h"
#include "jk_pool.h"
#include "jk_logger.h"
#if APR_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#define P2J(jk_pointer) ((jlong)(long)(void *)jk_pointer)
#define J2P(p, jktype) ((jktype)(void *)(long)p)
/** Access to the jk workerEnv. This field and jk_env_globalEnv are used
to interact with the jk components
*/
static jk_workerEnv_t *workerEnv;
static int jniDebug=0;
int jk_jni_status_code=0;
#define JK_GET_REGION 1
#define JK_GET_BYTE_ARRAY_ELEMENTS 2
#define JK_DIRECT_BUFFER_NIO 3
#define JNI_TOMCAT_STARTING 1
#define JNI_TOMCAT_STARTED 2
static int arrayAccessMethod=JK_GET_REGION;
void JK_METHOD jk2_env_setAprPool( jk_env_t *env, void *aprPool );
JNIEXPORT void JNICALL
Java_org_apache_jk_apr_AprImpl_setArrayAccessMode(JNIEnv *jniEnv, jobject _jthis, jint mode)
{
arrayAccessMethod=mode;
}
/* -------------------- Apr initialization and pools -------------------- */
/** Initialize APR and jk, for standalone use. If we use in-process mode,
i.e. an application using jk to launch an in-process JVM - this function
will not do anything, since the setup is already done.
The code is identical with what mod_jk is doing.
*/
JNIEXPORT jint JNICALL
Java_org_apache_jk_apr_AprImpl_initialize(JNIEnv *jniEnv, jobject _jthis)
{
jk_env_t *env;
/* For in-process the env is initialized already */
if( jk_env_globalEnv == NULL ) {
apr_pool_t *jniAprPool=NULL;
jk_pool_t *globalPool;
apr_initialize();
apr_pool_create( &jniAprPool, NULL );
if( jniAprPool==NULL ) {
return JK_ERR;
}
jk2_env_setAprPool( NULL, jniAprPool );
jk2_pool_apr_create( NULL, &globalPool, NULL, jniAprPool );
/* Create the global env */
env=jk2_env_getEnv( NULL, globalPool );
}
env=jk_env_globalEnv;
workerEnv=env->getByName( env, "workerEnv" );
if( workerEnv==NULL ) {
jk_bean_t *jkb;
jkb=env->createBean2( env, env->globalPool, "logger.file", "");
if( jkb==NULL ) {
fprintf(stderr, "Error creating logger ");
return JK_ERR;
}
env->l=jkb->object;
env->l->name="stderr";
env->l->level=JK_LOG_INFO_LEVEL;
env->alias( env, "logger.file:", "logger");
jkb=env->createBean2( env, env->globalPool,"workerEnv", "");
env->alias( env, "workerEnv:", "workerEnv");
if( jkb==NULL ) {
fprintf(stderr, "Error creating workerEnv ");
return JK_ERR;
}
workerEnv=jkb->object;
workerEnv->init( env, workerEnv );
}
return JK_OK;
}
JNIEXPORT jint JNICALL
Java_org_apache_jk_apr_AprImpl_terminate(JNIEnv *jniEnv, jobject _jthis)
{
apr_pool_t *jniAprPool=jk_env_globalEnv->getAprPool(jk_env_globalEnv);
if ( jniAprPool!=NULL ) {
apr_pool_destroy(jniAprPool);
jniAprPool = NULL;
/* apr_terminate(); */
}
return 0;
}
/* -------------------- Access jk components -------------------- */
/*
* Get a jk_env_t * from the pool
*
* XXX We should use per thread data or per jniEnv data ( the jniEnv and jk_env are
* serving the same purpose )
*/
JNIEXPORT jlong JNICALL
Java_org_apache_jk_apr_AprImpl_getJkEnv
(JNIEnv *jniEnv, jobject o )
{
jk_env_t *env;
if( jk_env_globalEnv == NULL )
return 0;
env=jk_env_globalEnv->getEnv( jk_env_globalEnv );
return P2J(env);
}
/*
Release the jk env
*/
JNIEXPORT void JNICALL
Java_org_apache_jk_apr_AprImpl_releaseJkEnv
(JNIEnv *jniEnv, jobject o, jlong xEnv )
{
jk_env_t *env=J2P( xEnv, jk_env_t *);
if( jk_env_globalEnv != NULL )
jk_env_globalEnv->releaseEnv( jk_env_globalEnv, env );
if( jniDebug > 0 )
env->l->jkLog(env, env->l, JK_LOG_INFO,
"aprImpl.releaseJkEnv() %#lx\n", env);
}
/*
* Recycle the jk endpoint. Will reset the tmp pool and clean error
* state.
*/
JNIEXPORT void JNICALL
Java_org_apache_jk_apr_AprImpl_jkRecycle
(JNIEnv *jniEnv, jobject o, jlong xEnv, jlong endpointP )
{
jk_env_t *env= J2P( xEnv, jk_env_t *);
jk_bean_t *compCtx= J2P( endpointP, jk_bean_t *);
jk_endpoint_t *ep = (compCtx==NULL ) ? NULL : compCtx->object;
if( env == NULL )
return;
if( ep!=NULL ) {
ep->reply->reset( env, ep->reply );
}
env->recycleEnv( env );
}
/*
* Find a jk component.
*/
JNIEXPORT jlong JNICALL
Java_org_apache_jk_apr_AprImpl_getJkHandler
(JNIEnv *jniEnv, jobject o, jlong xEnv, jstring compNameJ)
{
jk_env_t *env= J2P(xEnv, jk_env_t *);
jk_bean_t *component;
char *cname=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, compNameJ, 0);
component=env->getBean( env, cname );
(*jniEnv)->ReleaseStringUTFChars(jniEnv, compNameJ, cname);
return P2J(component);
}
/*
Create a jk handler XXX It should be createJkBean
*/
JNIEXPORT jlong JNICALL
Java_org_apache_jk_apr_AprImpl_createJkHandler
(JNIEnv *jniEnv, jobject o, jlong xEnv, jstring compNameJ)
{
jk_env_t *env= J2P(xEnv, jk_env_t *);
jk_bean_t *component;
char *cname=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, compNameJ, 0);
component=env->createBean( env, NULL, cname );
(*jniEnv)->ReleaseStringUTFChars(jniEnv, compNameJ, cname);
return P2J(component);
}
/*
*/
JNIEXPORT jint JNICALL
Java_org_apache_jk_apr_AprImpl_jkSetAttribute
(JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP, jstring nameJ, jstring valueJ )
{
jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
char *name=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, nameJ, 0);
char *value=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, valueJ, 0);
int rc=JK_OK;
/* XXX need to find a way how to set this to channel:jni component
* instead of global variable.
*/
if(env == NULL || component == NULL) {
if (strcmp(name, "channel:jni") == 0) {
if (strcmp(value, "starting") == 0)
jk_jni_status_code = JNI_TOMCAT_STARTING;
else if (strcmp(value, "done") == 0)
jk_jni_status_code = JNI_TOMCAT_STARTED;
}
} else {
if( component->setAttribute!=NULL ) {
rc=component->setAttribute( env, component, name,
component->pool->pstrdup( env, component->pool, value ) );
}
}
(*jniEnv)->ReleaseStringUTFChars(jniEnv, nameJ, name);
(*jniEnv)->ReleaseStringUTFChars(jniEnv, valueJ, value);
return rc;
}
/*
*/
JNIEXPORT jint JNICALL
Java_org_apache_jk_apr_AprImpl_jkInit
(JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP )
{
jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
int rc;
if( component->init ==NULL )
return JK_OK;
rc=component->init( env, component );
return rc;
}
/*
*/
JNIEXPORT jint JNICALL
Java_org_apache_jk_apr_AprImpl_jkDestroy
(JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP )
{
jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
int rc;
if( component->destroy ==NULL )
return JK_OK;
rc=component->destroy( env, component );
/* XXX component->pool->reset( env, component->pool ); */
return rc;
}
/*
*/
JNIEXPORT jstring JNICALL
Java_org_apache_jk_apr_AprImpl_jkGetAttribute
(JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP, jstring nameJ)
{
jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
char *name=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, nameJ, 0);
char *value;
jstring valueJ=NULL;
if( component->getAttribute !=NULL ){
value=component->getAttribute( env, component, name );
if( value!=NULL ) {
valueJ=(*jniEnv)->NewStringUTF(jniEnv, value);
}
}
(*jniEnv)->ReleaseStringUTFChars(jniEnv, nameJ, name);
return valueJ;
}
/*
*/
JNIEXPORT jint JNICALL
Java_org_apache_jk_apr_AprImpl_jkInvoke
(JNIEnv *jniEnv, jobject o, jlong envJ, jlong componentP, jlong endpointP, jint code,
jbyteArray data, jint off, jint len,
jint raw)
{
jk_env_t *env = (jk_env_t *)(void *)(long)envJ;
jk_bean_t *compCtx=(jk_bean_t *)(void *)(long)endpointP;
void *target=(void *)(long)componentP;
jk_bean_t *bean=(jk_bean_t *)target;
jk_endpoint_t *ep;
jbyte *nbuf=NULL;
jboolean iscopy;
int cnt=0;
jint rc = 0;
unsigned acc = 0;
unsigned char *oldBuf;
if( compCtx==NULL || data==NULL ) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,"jni.jkInvoke() NPE\n");
return JK_ERR;
}
ep = compCtx->object;
if( ep==NULL || ep->reply==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,"jni.jkInvoke() NPE ep==null\n");
return JK_ERR;
}
if( arrayAccessMethod == JK_GET_BYTE_ARRAY_ELEMENTS ) {
nbuf = (*jniEnv)->GetByteArrayElements(jniEnv, data, &iscopy);
if( iscopy )
env->l->jkLog(env, env->l, JK_LOG_INFO,
"aprImpl.jkInvoke() get java bytes iscopy %d\n", iscopy);
if(nbuf==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"jkInvoke() NullPointerException 2\n");
return -1;
}
if( raw==0 ) {
ep->reply->reset(env, ep->reply);
}
oldBuf=ep->reply->buf;
ep->reply->buf = (unsigned char *)nbuf;
} else if ( arrayAccessMethod == JK_GET_REGION ) {
(*jniEnv)->GetByteArrayRegion( jniEnv, data, off, len, (jbyte *)ep->reply->buf );
}
if( raw == 0 ) {
rc=ep->reply->checkHeader( env, ep->reply, ep );
} else {
ep->reply->len = len;
ep->reply->pos= off;
}
/* ep->reply->dump( env, ep->reply ,"MESSAGE"); */
if( rc < 0 ) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"jkInvoke() invalid data\n");
/* we just can't recover, unset recover flag */
if( arrayAccessMethod == JK_GET_BYTE_ARRAY_ELEMENTS ) {
(*jniEnv)->ReleaseByteArrayElements(jniEnv, data, ep->reply->buf, 0);
ep->reply->buf=oldBuf;
}
return JK_ERR;
}
if( bean->debug > 0 )
env->l->jkLog(env, env->l, JK_LOG_INFO,
"jkInvoke() component dispatch %d %d \n", rc, code );
if( bean->invoke != NULL ) {
env->l->jkLog(env, env->l, JK_LOG_INFO,
"jkInvoke() invoke %#lx \n", bean->invoke );
rc=bean->invoke( env, bean, ep, code, ep->reply, raw );
} else {
/* NOT USED. Backward compat for AJP13 messages, where the code is used to
locate a handler. Deprecated, use the invoke() method ! */
env->l->jkLog(env, env->l, JK_LOG_INFO,
"jkInvoke() component dispatch2 %d %d %#lx\n", rc, code, bean->invoke);
rc=workerEnv->dispatch( env, workerEnv, target, ep, code, ep->reply );
}
/* Copy back the response, if any */
if( arrayAccessMethod == JK_GET_BYTE_ARRAY_ELEMENTS ) {
if( rc == JK_INVOKE_WITH_RESPONSE ) {
/* env->l->jkLog(env, env->l, JK_LOG_INFO, */
/* "jkInvoke() release byte array elements %d %d %#lx\n", */
/* ep->reply->pos, ep->reply->len , ep->reply->buf ); */
ep->reply->end( env, ep->reply );
(*jniEnv)->ReleaseByteArrayElements(jniEnv, data, nbuf, JNI_ABORT );
rc=JK_OK;
} else {
(*jniEnv)->ReleaseByteArrayElements(jniEnv, data, nbuf, 0);
}
ep->reply->buf=oldBuf;
} else if ( arrayAccessMethod == JK_GET_REGION ) {
if( rc == JK_INVOKE_WITH_RESPONSE ) {
/*env->l->jkLog(env, env->l, JK_LOG_INFO, */
/* "jkInvoke() release %d %d %#lx\n", */
/* ep->reply->pos, ep->reply->len , ep->reply->buf ); */
ep->reply->end( env, ep->reply );
(*jniEnv)->SetByteArrayRegion( jniEnv, data, 0, ep->reply->len, (jbyte *)ep->reply->buf );
rc=JK_OK;
}
}
if( (*jniEnv)->ExceptionCheck( jniEnv ) ) {
env->l->jkLog(env, env->l, JK_LOG_INFO,
"jkInvoke() component dispatch %d %d %#lx\n", rc, code, bean->invoke);
(*jniEnv)->ExceptionDescribe( jniEnv );
/* Not needed if Describe is used.
(*jniEnv)->ExceptionClear( jniEnv ) */
}
return rc;
}
static JNINativeMethod org_apache_jk_apr_AprImpl_native_methods[] = {
{
"initialize", "()I",
Java_org_apache_jk_apr_AprImpl_initialize
},
{
"terminate", "()I",
Java_org_apache_jk_apr_AprImpl_terminate
},
{
"getJkEnv", "()J",
Java_org_apache_jk_apr_AprImpl_getJkEnv
},
{
"releaseJkEnv", "(J)V",
Java_org_apache_jk_apr_AprImpl_releaseJkEnv
},
{
"getJkHandler", "(JLjava/lang/String;)J",
Java_org_apache_jk_apr_AprImpl_getJkHandler
},
{
"createJkHandler", "(JLjava/lang/String;)J",
Java_org_apache_jk_apr_AprImpl_createJkHandler
},
{
"jkSetAttribute", "(JJLjava/lang/String;Ljava/lang/String;)I",
Java_org_apache_jk_apr_AprImpl_jkSetAttribute
},
{
"jkGetAttribute", "(JJLjava/lang/String;)Ljava/lang/String;",
Java_org_apache_jk_apr_AprImpl_jkGetAttribute
},
{
"jkInit", "(JJ)I",
Java_org_apache_jk_apr_AprImpl_jkInit
},
{
"jkDestroy", "(JJ)I",
Java_org_apache_jk_apr_AprImpl_jkDestroy
},
{
"jkInvoke", "(JJJI[BIII)I",
Java_org_apache_jk_apr_AprImpl_jkInvoke
},
{
"jkRecycle", "(JJ)V",
Java_org_apache_jk_apr_AprImpl_jkRecycle
},
};
/*
Register Native methods returning the total number of
native functions
*/
jint jk_jni_aprImpl_registerNatives(JNIEnv *jniEnv, jclass bridgeClass)
{
return (*jniEnv)->RegisterNatives(jniEnv, bridgeClass,
org_apache_jk_apr_AprImpl_native_methods,
sizeof(org_apache_jk_apr_AprImpl_native_methods) /
sizeof(JNINativeMethod));
}
#endif /* HAVE_JNI */