Alien-SVN
view release on metacpan or search on metacpan
src/subversion/subversion/bindings/javahl/native/JNIUtil.cpp view on Meta::CPAN
#include "JNIThreadData.h"
#include "JNIStringHolder.h"
#include "Pool.h"
// Static members of JNIUtil are allocated here.
apr_pool_t *JNIUtil::g_pool = NULL;
std::list<SVNBase*> JNIUtil::g_finalizedObjects;
JNIMutex *JNIUtil::g_finalizedObjectsMutex = NULL;
JNIMutex *JNIUtil::g_logMutex = NULL;
bool JNIUtil::g_initException;
bool JNIUtil::g_inInit;
JNIEnv *JNIUtil::g_initEnv;
char JNIUtil::g_initFormatBuffer[formatBufferSize];
int JNIUtil::g_logLevel = JNIUtil::noLog;
std::ofstream JNIUtil::g_logStream;
/**
* Initialize the environment for all requests.
* @param env the JNI environment for this request
*/
bool JNIUtil::JNIInit(JNIEnv *env)
{
// Clear all standing exceptions.
env->ExceptionClear();
// Remember the env parameter for the remainder of the request.
setEnv(env);
// Lock the list of finalized objects.
JNICriticalSection cs(*g_finalizedObjectsMutex) ;
if (isExceptionThrown())
return false;
// Delete all finalized, but not yet deleted objects.
for (std::list<SVNBase*>::iterator it = g_finalizedObjects.begin();
it != g_finalizedObjects.end();
++it)
{
delete *it;
}
g_finalizedObjects.clear();
return true;
}
/**
* Initialize the environment for all requests.
* @param env the JNI environment for this request
*/
bool JNIUtil::JNIGlobalInit(JNIEnv *env)
{
// This method has to be run only once during the run a program.
static bool run = false;
svn_error_t *err;
if (run) // already run
return true;
run = true;
// Do not run this part more than one time. This leaves a small
// time window when two threads create their first SVNClient and
// SVNAdmin at the same time, but I do not see a better option
// without APR already initialized
if (g_inInit)
return false;
g_inInit = true;
g_initEnv = env;
apr_status_t status;
/* Initialize the APR subsystem, and register an atexit() function
* to Uninitialize that subsystem at program exit. */
status = apr_initialize();
if (status)
{
if (stderr)
{
char buf[1024];
apr_strerror(status, buf, sizeof(buf) - 1);
fprintf(stderr,
"%s: error: cannot initialize APR: %s\n",
"svnjavahl", buf);
}
return FALSE;
}
/* This has to happen before any pools are created. */
if ((err = svn_dso_initialize2()))
{
if (stderr && err->message)
fprintf(stderr, "%s", err->message);
svn_error_clear(err);
return FALSE;
}
if (0 > atexit(apr_terminate))
{
if (stderr)
fprintf(stderr,
"%s: error: atexit registration failed\n",
"svnjavahl");
return FALSE;
}
/* Create our top-level pool. */
g_pool = svn_pool_create(NULL);
apr_allocator_t* allocator = apr_pool_allocator_get(g_pool);
if (allocator)
{
/* Keep a maximum of 1 free block, to release memory back to the JVM
(and other modules). */
apr_allocator_max_free_set(allocator, 1);
}
svn_utf_initialize2(FALSE, g_pool); /* Optimize character conversions */
svn_fs_initialize(g_pool); /* Avoid some theoretical issues */
svn_ra_initialize(g_pool);
/* We shouldn't fill the JVMs memory with FS cache data unless explictly
requested. */
{
svn_cache_config_t settings = *svn_cache_config_get();
settings.cache_size = 0;
settings.file_handle_count = 0;
settings.single_threaded = FALSE;
svn_cache_config_set(&settings);
}
#ifdef ENABLE_NLS
#ifdef WIN32
{
WCHAR ucs2_path[MAX_PATH];
char *utf8_path;
const char *internal_path;
apr_pool_t *pool;
apr_status_t apr_err;
apr_size_t inwords, outbytes;
unsigned int outlength;
pool = svn_pool_create(g_pool);
/* get dll name - our locale info will be in '../share/locale' */
inwords = sizeof(ucs2_path) / sizeof(ucs2_path[0]);
HINSTANCE moduleHandle = GetModuleHandle("libsvnjavahl-1");
GetModuleFileNameW(moduleHandle, ucs2_path, inwords);
inwords = lstrlenW(ucs2_path);
outbytes = outlength = 3 * (inwords + 1);
utf8_path = reinterpret_cast<char *>(apr_palloc(pool, outlength));
apr_err = apr_conv_ucs2_to_utf8((const apr_wchar_t *) ucs2_path,
&inwords, utf8_path, &outbytes);
if (!apr_err && (inwords > 0 || outbytes == 0))
apr_err = APR_INCOMPLETE;
if (apr_err)
{
if (stderr)
fprintf(stderr, "Can't convert module path to UTF-8");
return FALSE;
}
utf8_path[outlength - outbytes] = '\0';
internal_path = svn_dirent_internal_style(utf8_path, pool);
/* get base path name */
internal_path = svn_dirent_dirname(internal_path, pool);
internal_path = svn_dirent_join(internal_path, SVN_LOCALE_RELATIVE_PATH,
pool);
bindtextdomain(PACKAGE_NAME, internal_path);
svn_pool_destroy(pool);
}
#else
bindtextdomain(PACKAGE_NAME, SVN_LOCALE_DIR);
#endif
#endif
#if defined(WIN32) || defined(__CYGWIN__)
/* See http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt */
/* ### This code really only needs to be invoked by consumers of
### the libsvn_wc library, which basically means SVNClient. */
if (getenv("SVN_ASP_DOT_NET_HACK"))
{
err = svn_wc_set_adm_dir("_svn", g_pool);
if (err)
{
if (stderr)
{
fprintf(stderr,
"%s: error: SVN_ASP_DOT_NET_HACK failed: %s\n",
"svnjavahl", err->message);
}
svn_error_clear(err);
return FALSE;
}
}
#endif
svn_error_set_malfunction_handler(svn_error_raise_on_malfunction);
// Build all mutexes.
g_finalizedObjectsMutex = new JNIMutex(g_pool);
if (isExceptionThrown())
return false;
g_logMutex = new JNIMutex(g_pool);
if (isExceptionThrown())
return false;
// initialized the thread local storage
if (!JNIThreadData::initThreadData())
return false;
setEnv(env);
if (isExceptionThrown())
return false;
g_initEnv = NULL;
g_inInit = false;
return true;
}
/**
* Returns the global (not request specific) pool.
* @return global pool
*/
apr_pool_t *JNIUtil::getPool()
{
return g_pool;
}
void JNIUtil::raiseThrowable(const char *name, const char *message)
{
if (getLogLevel() >= errorLog)
{
JNICriticalSection cs(*g_logMutex);
g_logStream << "Throwable raised <" << message << ">" << std::endl;
}
JNIEnv *env = getEnv();
jclass clazz = env->FindClass(name);
if (isJavaExceptionThrown())
return;
env->ThrowNew(clazz, message);
setExceptionThrown();
env->DeleteLocalRef(clazz);
}
jstring JNIUtil::makeSVNErrorMessage(svn_error_t *err)
{
if (err == NULL)
return NULL;
std::string buffer;
assembleErrorMessage(err, 0, APR_SUCCESS, buffer);
jstring jmessage = makeJString(buffer.c_str());
return jmessage;
}
void
JNIUtil::throwNativeException(const char *className, const char *msg,
const char *source, int aprErr)
{
JNIEnv *env = getEnv();
jclass clazz = env->FindClass(className);
// Create a local frame for our references
env->PushLocalFrame(LOCAL_FRAME_SIZE);
if (JNIUtil::isJavaExceptionThrown())
return;
src/subversion/subversion/bindings/javahl/native/JNIUtil.cpp view on Meta::CPAN
}
env->CallVoidMethod(nativeException, mid_sst, jStackTrace);
if (isJavaExceptionThrown())
POP_AND_RETURN_NOTHING();
#endif
env->Throw(static_cast<jthrowable>(env->PopLocalFrame(nativeException)));
}
void JNIUtil::handleSVNError(svn_error_t *err)
{
try {
wrappedHandleSVNError(err);
} catch (...) {
svn_error_clear(err);
throw;
}
svn_error_clear(err);
}
void JNIUtil::putFinalizedClient(SVNBase *object)
{
enqueueForDeletion(object);
}
void JNIUtil::enqueueForDeletion(SVNBase *object)
{
JNICriticalSection cs(*g_finalizedObjectsMutex);
if (!isExceptionThrown())
g_finalizedObjects.push_back(object);
}
/**
* Handle an apr error (those are not expected) by throwing an error.
* @param error the apr error number
* @param op the apr function returning the error
*/
void JNIUtil::handleAPRError(int error, const char *op)
{
char *buffer = getFormatBuffer();
if (buffer == NULL)
return;
apr_snprintf(buffer, formatBufferSize,
_("an error occurred in function %s with return value %d"),
op, error);
throwError(buffer);
}
/**
* Return if an exception has been detected.
* @return a exception has been detected
*/
bool JNIUtil::isExceptionThrown()
{
// During init -> look in the global member.
if (g_inInit)
return g_initException;
// Look in the thread local storage.
JNIThreadData *data = JNIThreadData::getThreadData();
return data == NULL || data->m_exceptionThrown;
}
/**
* Store the JNI environment for this request in the thread local
* storage.
* @param env the JNI environment
*/
void JNIUtil::setEnv(JNIEnv *env)
{
JNIThreadData::pushNewThreadData();
JNIThreadData *data = JNIThreadData::getThreadData();
data->m_env = env;
data->m_exceptionThrown = false;
}
/**
* Return the JNI environment to use
* @return the JNI environment
*/
JNIEnv *JNIUtil::getEnv()
{
// During init -> look into the global variable.
if (g_inInit)
return g_initEnv;
// Look in the thread local storage.
JNIThreadData *data = JNIThreadData::getThreadData();
return data->m_env;
}
/**
* Check if a Java exception has been thrown.
* @return is a Java exception has been thrown
*/
bool JNIUtil::isJavaExceptionThrown()
{
JNIEnv *env = getEnv();
if (env->ExceptionCheck())
{
// Retrieving the exception removes it so we rethrow it here.
jthrowable exp = env->ExceptionOccurred();
env->ExceptionDescribe();
env->Throw(exp);
env->DeleteLocalRef(exp);
setExceptionThrown();
return true;
}
return false;
}
const char *
JNIUtil::thrownExceptionToCString(SVN::Pool &in_pool)
{
const char *msg = NULL;
JNIEnv *env = getEnv();
apr_pool_t *pool = in_pool.getPool();
if (env->ExceptionCheck())
{
jthrowable t = env->ExceptionOccurred();
jclass cls = env->GetObjectClass(t);
jstring jclass_name;
{
jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;");
jobject clsobj = env->CallObjectMethod(t, mid);
jclass basecls = env->GetObjectClass(clsobj);
mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;");
jclass_name = (jstring) env->CallObjectMethod(clsobj, mid);
}
jstring jmessage;
{
jmethodID mid = env->GetMethodID(cls, "getMessage",
"()Ljava/lang/String;");
jmessage = (jstring) env->CallObjectMethod(t, mid);
}
JNIStringHolder class_name(jclass_name);
if (jmessage)
{
JNIStringHolder message(jmessage);
msg = apr_pstrcat(pool,
static_cast<const char*>(class_name), ": ",
static_cast<const char*>(message), NULL);
}
else
msg = class_name.pstrdup(pool);
// ### Conditionally add t.printStackTrace() to msg?
}
return msg;
}
/**
* Create a Java string from a native UTF-8 string.
* @param txt native UTF-8 string
* @return the Java string. It is a local reference, which should be deleted
* as soon a possible
*/
jstring JNIUtil::makeJString(const char *txt)
{
if (txt == NULL)
// A NULL pointer is equates to a null java.lang.String.
return NULL;
JNIEnv *env = getEnv();
return env->NewStringUTF(txt);
}
void
JNIUtil::setExceptionThrown(bool flag)
{
if (g_inInit)
{
// During global initialization, store any errors that occur
// in a global variable (since thread-local storage may not
// yet be available).
g_initException = flag;
}
else
{
// When global initialization is complete, thread-local
// storage should be available, so store the error there.
JNIThreadData *data = JNIThreadData::getThreadData();
data->m_exceptionThrown = flag;
}
}
/**
* Initialite the log file.
* @param level the log level
* @param the name of the log file
*/
void JNIUtil::initLogFile(int level, jstring path)
{
// lock this operation
JNICriticalSection cs(*g_logMutex);
if (g_logLevel > noLog) // if the log file has been opened
g_logStream.close();
// remember the log level
g_logLevel = level;
JNIStringHolder myPath(path);
if (g_logLevel > noLog) // if a new log file is needed
{
// open it
g_logStream.open(myPath, std::ios::app);
}
}
/**
* Returns a buffer to format error messages.
* @return a buffer for formating error messages
*/
char *JNIUtil::getFormatBuffer()
{
if (g_inInit) // during init -> use the global buffer
return g_initFormatBuffer;
// use the buffer in the thread local storage
JNIThreadData *data = JNIThreadData::getThreadData();
if (data == NULL) // if that does not exists -> use the global buffer
return g_initFormatBuffer;
return data->m_formatBuffer;
}
/**
* Returns the current log level.
* @return the log level
*/
int JNIUtil::getLogLevel()
{
return g_logLevel;
}
/**
* Write a message to the log file if needed.
* @param the log message
*/
void JNIUtil::logMessage(const char *message)
{
// lock the log file
JNICriticalSection cs(*g_logMutex);
g_logStream << message << std::endl;
}
/**
* Create a java.util.Date object from an apr time.
* @param time the apr time
* @return the java.util.Date. This is a local reference. Delete as
* soon as possible
*/
jobject JNIUtil::createDate(apr_time_t time)
{
jlong javatime = time /1000;
JNIEnv *env = getEnv();
jclass clazz = env->FindClass("java/util/Date");
if (isJavaExceptionThrown())
return NULL;
static jmethodID mid = 0;
if (mid == 0)
{
mid = env->GetMethodID(clazz, "<init>", "(J)V");
if (isJavaExceptionThrown())
return NULL;
}
jobject ret = env->NewObject(clazz, mid, javatime);
if (isJavaExceptionThrown())
return NULL;
env->DeleteLocalRef(clazz);
return ret;
}
/**
* Create a Java byte array from an array of characters.
( run in 0.725 second using v1.01-cache-2.11-cpan-796a6f069b2 )