Tag: cws_src680_dba24c User: sb Date: 2007-10-10 14:41:39+0000 Modified: dba/connectivity/source/drivers/jdbc/JConnection.cxx
Log: #i82222# Use JavaDriverClassPath class loader as context class loader around JavaDriverClass instance creation. File Changes: Directory: /dba/connectivity/source/drivers/jdbc/ ================================================= File [changed]: JConnection.cxx Url: http://dba.openoffice.org/source/browse/dba/connectivity/source/drivers/jdbc/JConnection.cxx?r1=1.6&r2=1.6.2.1 Delta lines: +270 -52 ---------------------- --- JConnection.cxx 2007-09-26 14:29:37+0000 1.6 +++ JConnection.cxx 2007-10-10 14:41:36+0000 1.6.2.1 @@ -4,9 +4,9 @@ * * $RCSfile: JConnection.cxx,v $ * - * $Revision: 1.6 $ + * $Revision: 1.6.2.1 $ * - * last change: $Author: hr $ $Date: 2007/09/26 14:29:37 $ + * last change: $Author: sb $ $Date: 2007/10/10 14:41:36 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. @@ -91,29 +91,36 @@ namespace { -struct JObject { - JObject(JNIEnv * environment): object(NULL), m_environment(environment) {} - ~JObject() { if (object != NULL) m_environment->DeleteLocalRef(object); } +template< typename T > struct LocalRef { + explicit LocalRef(JNIEnv * environment): + object(NULL), m_environment(environment) {} - jobject release() { - jobject t = object; + ~LocalRef() { if (object != NULL) m_environment->DeleteLocalRef(object); } + + T release() { + T t = object; object = NULL; return t; } - jobject object; + T object; private: + LocalRef(LocalRef &); // not defined + void operator =(LocalRef &); // not defined + JNIEnv *& m_environment; }; struct ClassMapEntry { ClassMapEntry( rtl::OUString const & theClassPath, rtl::OUString const & theClassName): - classPath(theClassPath), className(theClassName), classObject(NULL) {} + classPath(theClassPath), className(theClassName), classLoader(NULL), + classObject(NULL) {} rtl::OUString classPath; rtl::OUString className; + jweak classLoader; jweak classObject; }; @@ -132,14 +139,31 @@ } }; -// Load a class (see jvmaccess::ClassPath::loadClass in jvmaccess/classpath.hxx -// for details). A map from (classPath, name) pairs to weak Java class -// references is maintained, so that a class is only loaded once. If null is -// returned, a (still pending) JNI exception occurred. -jclass loadClass( +// Load a class. A map from pairs of (classPath, name) to pairs of weak Java +// references to (ClassLoader, Class) is maintained, so that a class is only +// loaded once. +// +// It may happen that the weak reference to the ClassLoader becomes null while +// the reference to the Class remains non-null (in case the Class was actually +// loaded by some parent of the ClassLoader), in which case the ClassLoader is +// resurrected (which cannot cause any classes to be loaded multiple times, as +// the ClassLoader is no longer reachable, so no classes it has ever loaded are +// still reachable). +// +// Similarly, it may happen that the weak reference to the Class becomes null +// while the reference to the ClassLoader remains non-null, in which case the +// Class is simply re-loaded. +// +// This code is close to the implementation of jvmaccess::ClassPath::loadClass +// in jvmaccess/classpath.hxx, but not close enough to avoid the duplication. +// +// If false is returned, a (still pending) JNI exception occurred. +bool loadClass( Reference< XComponentContext > const & context, JNIEnv * environment, - rtl::OUString const & classPath, rtl::OUString const & name) + rtl::OUString const & classPath, rtl::OUString const & name, + LocalRef< jobject > * classLoaderPtr, LocalRef< jclass > * classPtr) { + OSL_ASSERT(classLoader != NULL); // For any jweak entries still present in the map upon destruction, // DeleteWeakGlobalRef is not called (which is a leak): ClassMapData * d = @@ -147,49 +171,169 @@ osl::GetGlobalMutex >::create( ClassMapDataInit(), osl::GetGlobalMutex()); osl::MutexGuard g(d->mutex); - JObject cl(environment); + ClassMap::iterator i(d->map.begin()); + LocalRef< jobject > cloader(environment); + LocalRef< jclass > cl(environment); // Prune dangling weak references from the list while searching for a match, // so that the list cannot grow unbounded: - for (ClassMap::iterator i(d->map.begin()); i != d->map.end();) { - JObject o(environment); - o.object = environment->NewLocalRef(i->classObject); - if (o.object == NULL) { + for (; i != d->map.end();) { + LocalRef< jobject > ocloader(environment); + ocloader.object = environment->NewLocalRef(i->classLoader); + if (ocloader.object == NULL) { if (environment->ExceptionCheck()) { - return NULL; + return false; + } else if (i->classLoader != NULL) { + environment->DeleteWeakGlobalRef(i->classLoader); + i->classLoader = NULL; } - if (i->classObject != NULL) { + } + LocalRef< jclass > ocl(environment); + ocl.object = static_cast< jclass >( + environment->NewLocalRef(i->classObject)); + if (ocl.object == NULL) { + if (environment->ExceptionCheck()) { + return false; + } else if (i->classObject != NULL) { environment->DeleteWeakGlobalRef(i->classObject); + i->classObject = NULL; } + } + if (ocloader.object == NULL && ocl.object == NULL) { i = d->map.erase(i); + } else if (i->classPath == classPath && i->className == name) { + cloader.object = ocloader.release(); + cl.object = ocl.release(); + break; } else { - if (i->classPath == classPath && i->className == name) { - cl.object = o.release(); - } ++i; } } - if (cl.object == NULL) { - // Push a new ClassMapEntry (which can potentially fail) before loading - // the class, so that it never happens that a class is loaded but not - // added to the map (which could have effects on the JVM that are not - // easily undone). If the pushed ClassMapEntry is not used after all - // (jvmaccess::ClassPath::loadClass throws, return NULL, etc.) it will - // be pruned on next call because its classObject is null: + if (cloader.object == NULL || cl.object == NULL) { + if (i == d->map.end()) { + // Push a new ClassMapEntry (which can potentially fail) before + // loading the class, so that it never happens that a class is + // loaded but not added to the map (which could have effects on the + // JVM that are not easily undone). If the pushed ClassMapEntry is + // not used after all (return false, etc.) it will be pruned on next + // call because its classLoader/classObject are null: d->map.push_front(ClassMapEntry(classPath, name)); - cl.object = jvmaccess::ClassPath::loadClass( - context, environment, classPath, name); + i = d->map.begin(); + } + LocalRef< jclass > clClass(environment); + clClass.object = environment->FindClass("java/net/URLClassLoader"); + if (clClass.object == NULL) { + return false; + } + jweak wcloader = NULL; + if (cloader.object == NULL) { + jmethodID ctorLoader( + environment->GetMethodID( + clClass.object, "<init>", "([Ljava/net/URL;)V")); + if (ctorLoader == NULL) { + return false; + } + LocalRef< jobjectArray > arr(environment); + arr.object = jvmaccess::ClassPath::translateToUrls( + context, environment, classPath); + if (arr.object == NULL) { + return false; + } + jvalue arg; + arg.l = arr.object; + cloader.object = environment->NewObjectA( + clClass.object, ctorLoader, &arg); + if (cloader.object == NULL) { + return false; + } + wcloader = environment->NewWeakGlobalRef(cloader.object); + if (wcloader == NULL) { + return false; + } + } + jweak wcl = NULL; if (cl.object == NULL) { - return NULL; + jmethodID methLoadClass( + environment->GetMethodID( + clClass.object, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;")); + if (methLoadClass == NULL) { + return false; + } + LocalRef< jstring > str(environment); + str.object = environment->NewString( + static_cast< jchar const * >(name.getStr()), + static_cast< jsize >(name.getLength())); + if (str.object == NULL) { + return false; + } + jvalue arg; + arg.l = str.object; + cl.object = static_cast< jclass > ( + environment->CallObjectMethodA( + cloader.object, methLoadClass, &arg)); + if (cl.object == NULL) { + return false; + } + wcl = environment->NewWeakGlobalRef(cl.object); + if (wcl == NULL) { + return false; } - jweak w = environment->NewWeakGlobalRef(cl.object); - if (w == NULL) { - return NULL; } - d->map.front().classObject = w; + if (wcloader != NULL) { + i->classLoader = wcloader; } - return static_cast< jclass >(cl.release()); + if (wcl != NULL) { + i->classObject = wcl; + } + } + classLoaderPtr->object = cloader.release(); + classPtr->object = cl.release(); + return true; } +// simulate a Java +// finally { +// Thread.currentThread.setContextClassLoader(oldContextClassLoader); } +class ContextClassLoaderScope { +public: + ContextClassLoaderScope( + JNIEnv * environment, LocalRef< jobject > & currentThread, + LocalRef< jobject > & oldContextClassLoader): + m_environment(environment), m_currentThread(currentThread), + m_oldContextClassLoader(oldContextClassLoader), m_active(false) {} + + ~ContextClassLoaderScope() { pop(true); } + + void activate(jmethodID setContextClassLoaderMethod) { + m_setContextClassLoaderMethod = setContextClassLoaderMethod; + m_active = true; + } + + void pop() { pop(false); } + +private: + ContextClassLoaderScope(ContextClassLoaderScope &); // not defined + void operator =(ContextClassLoaderScope &); // not defined + + void pop(bool clearExceptions) { + if (m_active) { + m_active = false; + m_environment->CallObjectMethod( + m_currentThread.object, m_setContextClassLoaderMethod, + m_oldContextClassLoader.object); + if (clearExceptions) { + m_environment->ExceptionClear(); + } + } + } + + JNIEnv *& m_environment; + LocalRef< jobject > & m_currentThread; + LocalRef< jobject > & m_oldContextClassLoader; + jmethodID m_setContextClassLoaderMethod; + bool m_active; +}; + } //------------------------------------------------------------------------------ @@ -873,6 +1017,7 @@ m_aLogger.log( LogLevel::INFO, STR_LOG_LOADING_DRIVER, aStr ); // the driver manager holds the class of the driver for later use ::std::auto_ptr< java_lang_Class > pDrvClass; + LocalRef< jobject > classLoader(t.pEnv); if ( pJavaDriverClassPath == 0 ) { // if forName didn't find the class it will throw an exception @@ -882,21 +1027,94 @@ { ::rtl::OUString classpath; OSL_VERIFY( pJavaDriverClassPath->Value >>= classpath ); - - pDrvClass.reset( - new java_lang_Class( - t.pEnv, + LocalRef< jclass > cl(t.pEnv); loadClass( m_pDriver->getContext().getUNOContext(), - t.pEnv, classpath, aStr - ) - ) + t.pEnv, classpath, aStr, &classLoader, &cl ); + pDrvClass.reset( + new java_lang_Class( t.pEnv, cl.release() ) ); ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); } if ( pDrvClass.get() ) { - m_pDriverobject = pDrvClass->newInstanceObject(); + // In some cases (e.g., + // connectivity/source/drivers/hsqldb/HDriver.cxx:1.24 + // l. 249) the JavaDriverClassPath contains multiple jars, + // as creating the JavaDriverClass instance requires + // (reflective) access to those other jars. Now, if the + // JavaDriverClass is actually loaded by some parent class + // loader (e.g., because its jar is also on the global + // class path), it would still not have access to the + // additional jars on the JavaDriverClassPath. Hence, the + // JavaDriverClassPath class loader is pushed as context + // class loader around the JavaDriverClass instance + // creation: + { + LocalRef< jclass > threadClass(t.pEnv); + LocalRef< jobject > currentThread(t.pEnv); + LocalRef< jobject > oldContextClassLoader(t.pEnv); + ContextClassLoaderScope ccl( + t.pEnv, currentThread, oldContextClassLoader); + if (classLoader.object != NULL) { + threadClass.object = t.pEnv->FindClass( + "java/lang/Thread"); + if (threadClass.object == NULL) { + ThrowLoggedSQLException( + m_aLogger, t.pEnv, *this); + OSL_ASSERT(false); // unreachable + } + jmethodID currentThreadMethod( + t.pEnv->GetStaticMethodID( + threadClass.object, "currentThread", + "()Ljava/lang/Thread;")); + if (currentThreadMethod == NULL) { + ThrowLoggedSQLException( + m_aLogger, t.pEnv, *this); + OSL_ASSERT(false); // unreachable + } + currentThread.object = + t.pEnv->CallStaticObjectMethod( + threadClass.object, currentThreadMethod); + ThrowLoggedSQLException(m_aLogger, t.pEnv, *this); + OSL_ASSERT(currentThread.object != NULL); + jmethodID getContextClassLoaderMethod( + t.pEnv->GetMethodID( + threadClass.object, + "getContextClassLoader", + "()Ljava/lang/ClassLoader;")); + if (getContextClassLoaderMethod == NULL) { + ThrowLoggedSQLException( + m_aLogger, t.pEnv, *this); + OSL_ASSERT(false); // unreachable + } + oldContextClassLoader.object = + t.pEnv->CallObjectMethod( + currentThread.object, + getContextClassLoaderMethod); + ThrowLoggedSQLException(m_aLogger, t.pEnv, *this); + jmethodID setContextClassLoaderMethod( + t.pEnv->GetMethodID( + threadClass.object, + "setContextClassLoader", + "(Ljava/lang/ClassLoader;)V")); + if (setContextClassLoaderMethod == NULL) { + ThrowLoggedSQLException( + m_aLogger, t.pEnv, *this); + OSL_ASSERT(false); // unreachable + } + ccl.activate(setContextClassLoaderMethod); + t.pEnv->CallObjectMethod( + currentThread.object, + setContextClassLoaderMethod, + classLoader.object); + } + LocalRef< jobject > o(t.pEnv); + o.object = pDrvClass->newInstanceObject(); + ccl.pop(); + ThrowLoggedSQLException(m_aLogger, t.pEnv, *this); + m_pDriverobject = o.release(); + } if( t.pEnv && m_pDriverobject ) m_pDriverobject = t.pEnv->NewGlobalRef( m_pDriverobject ); if( t.pEnv ) --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
