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]

Reply via email to