User: ihi Date: 2007-11-21 15:03:17+0000 Modified: dba/connectivity/source/drivers/jdbc/JConnection.cxx
Log: INTEGRATION: CWS dba24c (1.6.2); FILE MERGED 2007/10/29 22:12:33 fs 1.6.2.6: merging changes from CWS dba24b herein, to not wait for later resync 2007/10/29 10:59:19 fs 1.6.2.5: #i83097# additional setting specifying Java methods allowed to call from inside HSQL 2007/10/29 09:44:13 fs 1.6.2.4: adjusted some log levels 2007/10/15 13:33:15 fs 1.6.2.3: #i82222# reworked SBs patch to use the driver classes class loader when constructing the connection, not when actually loading the system driver 2007/10/10 20:40:46 fs 1.6.2.2: #i10000# 2007/10/10 14:41:36 sb 1.6.2.1: #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.7&r2=1.8 Delta lines: +280 -105 ----------------------- --- JConnection.cxx 2007-11-01 14:50:54+0000 1.7 +++ JConnection.cxx 2007-11-21 15:03:14+0000 1.8 @@ -35,13 +35,12 @@ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_connectivity.hxx" -#ifndef _CONNECTIVITY_JAVA_SQL_CONNECTION_HXX_ + #include "java/sql/Connection.hxx" -#endif #include "java/lang/Class.hxx" -#ifndef _CONNECTIVITY_JAVA_TOOLS_HXX_ #include "java/tools.hxx" -#endif +#include "java/ContextClassLoader.hxx" + #ifndef _CONNECTIVITY_JAVA_SQL_DATABASEMETADATA_HXX_ #include "java/sql/DatabaseMetaData.hxx" #endif @@ -66,6 +65,12 @@ #ifndef _COM_SUN_STAR_SDBC_SQLWARNING_HPP_ #include <com/sun/star/sdbc/SQLWarning.hpp> #endif +#ifndef _COM_SUN_STAR_SDBC_SQLWARNING_HPP_ +#include <com/sun/star/sdbc/SQLWarning.hpp> +#endif +#ifndef _COM_SUN_STAR_BEANS_NAMEDVALUE_HPP_ +#include <com/sun/star/beans/NamedValue.hpp> +#endif #ifndef _CONNECTIVITY_SQLPARSE_HXX #include "connectivity/sqlparse.hxx" #endif @@ -73,6 +78,7 @@ #include "connectivity/dbexception.hxx" #endif #include "java/util/Property.hxx" +#include "java/LocalRef.hxx" #include "resource/jdbc_log.hrc" #include "com/sun/star/uno/XComponentContext.hpp" #include "jvmaccess/classpath.hxx" @@ -83,6 +89,7 @@ #include <memory> using namespace connectivity; +using namespace connectivity::jdbc; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::sdbc; @@ -91,29 +98,15 @@ namespace { -struct JObject { - JObject(JNIEnv * environment): object(NULL), m_environment(environment) {} - ~JObject() { if (object != NULL) m_environment->DeleteLocalRef(object); } - - jobject release() { - jobject t = object; - object = NULL; - return t; - } - - jobject object; - -private: - 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 +125,51 @@ } }; -// 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( - Reference< XComponentContext > const & context, JNIEnv * environment, - rtl::OUString const & classPath, rtl::OUString const & name) +template < typename T > +bool getLocalFromWeakRef( jweak& _weak, LocalRef< T >& _inout_local ) +{ + _inout_local.set( static_cast< T >( _inout_local.env().NewLocalRef( _weak ) ) ); + + if ( !_inout_local.is() ) + { + if ( _inout_local.env().ExceptionCheck()) + { + return false; + } + else if ( _weak != NULL ) + { + _inout_local.env().DeleteWeakGlobalRef( _weak ); + _weak = NULL; + } + } + return true; +} + +// 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, + LocalRef< jobject > * classLoaderPtr, LocalRef< jclass > * classPtr) { + OSL_ASSERT(classLoaderPtr != NULL); // For any jweak entries still present in the map upon destruction, // DeleteWeakGlobalRef is not called (which is a leak): ClassMapData * d = @@ -147,47 +177,114 @@ 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) { - if (environment->ExceptionCheck()) { - return NULL; - } - if (i->classObject != NULL) { - environment->DeleteWeakGlobalRef(i->classObject); - } + for (; i != d->map.end();) + { + LocalRef< jobject > classLoader( environment ); + if ( !getLocalFromWeakRef( i->classLoader, classLoader ) ) + return false; + + LocalRef< jclass > classObject( environment ); + if ( !getLocalFromWeakRef( i->classObject, classObject ) ) + return false; + + if ( !classLoader.is() && !classObject.is() ) + { i = d->map.erase(i); - } else { - if (i->classPath == classPath && i->className == name) { - cl.object = o.release(); } + else if ( i->classPath == classPath && i->className == name ) + { + cloader.set( classLoader.release() ); + cl.set( classObject.release() ); + break; + } + else + { ++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: - d->map.push_front(ClassMapEntry(classPath, name)); - cl.object = jvmaccess::ClassPath::loadClass( - context, environment, classPath, name); - if (cl.object == NULL) { - return NULL; - } - jweak w = environment->NewWeakGlobalRef(cl.object); - if (w == NULL) { - return NULL; + if ( !cloader.is() || !cl.is() ) + { + 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 ) ); + i = d->map.begin(); + } + + LocalRef< jclass > clClass( environment ); + clClass.set( environment.FindClass( "java/net/URLClassLoader" ) ); + if ( !clClass.is() ) + return false; + + jweak wcloader = NULL; + if (!cloader.is()) + { + jmethodID ctorLoader( environment.GetMethodID( clClass.get(), "<init>", "([Ljava/net/URL;)V" ) ); + if (ctorLoader == NULL) + return false; + + LocalRef< jobjectArray > arr( environment ); + arr.set( jvmaccess::ClassPath::translateToUrls( context, &environment, classPath ) ); + if ( !arr.is() ) + return false; + + jvalue arg; + arg.l = arr.get(); + cloader.set( environment.NewObjectA( clClass.get(), ctorLoader, &arg ) ); + if ( !cloader.is() ) + return false; + + wcloader = environment.NewWeakGlobalRef( cloader.get() ); + if ( wcloader == NULL ) + return false; + } + + jweak wcl = NULL; + if ( !cl.is() ) + { + jmethodID methLoadClass( environment.GetMethodID( clClass.get(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;" ) ); + if ( methLoadClass == NULL ) + return false; + + LocalRef< jstring > str( environment ); + str.set( convertwchar_tToJavaString( &environment, name ) ); + if ( !str.is() ) + return false; + + jvalue arg; + arg.l = str.get(); + cl.set( static_cast< jclass >( environment.CallObjectMethodA( cloader.get(), methLoadClass, &arg ) ) ); + if ( !cl.is() ) + return false; + + wcl = environment.NewWeakGlobalRef( cl.get() ); + if ( wcl == NULL ) + return false; + } + + if ( wcloader != NULL) + { + i->classLoader = wcloader; + } + if ( wcl != NULL ) + { + i->classObject = wcl; } - d->map.front().classObject = w; } - return static_cast< jclass >(cl.release()); + + classLoaderPtr->set( cloader.release() ); + classPtr->set( cl.release() ); + return true; } } @@ -206,6 +303,7 @@ ,m_xMetaData(NULL) ,m_pDriver( &_rDriver ) ,m_pDriverobject(NULL) + ,m_pDriverClassLoader() ,m_Driver_theClass(NULL) ,m_aLogger( _rDriver.getLogger() ) ,m_bParameterSubstitution(sal_False) @@ -816,14 +914,64 @@ return Any(); } // ----------------------------------------------------------------------------- -void java_sql_Connection::loadDriverFromProperties( - const Sequence< PropertyValue >& info, ::rtl::OUString& _rsGeneratedValueStatement, - sal_Bool& _rbAutoRetrievingEnabled, sal_Bool& _bParameterSubstitution, sal_Bool& _bIgnoreDriverPrivileges ) +namespace { + bool lcl_setSystemProperties_nothrow( const java::sql::ConnectionLog& _rLogger, + JNIEnv& _rEnv, const Sequence< NamedValue >& _rSystemProperties ) + { + if ( _rSystemProperties.getLength() == 0 ) + // nothing to do + return true; + + LocalRef< jclass > systemClass( _rEnv ); + jmethodID nSetPropertyMethodID = 0; + // retrieve the java.lang.System class + systemClass.set( _rEnv.FindClass( "java/lang/System" ) ); + if ( systemClass.is() ) + { + nSetPropertyMethodID = _rEnv.GetStaticMethodID( + systemClass.get(), "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" ); + } + + if ( nSetPropertyMethodID == 0 ) + return false; + + for ( const NamedValue* pSystemProp = _rSystemProperties.getConstArray(); + pSystemProp != _rSystemProperties.getConstArray() + _rSystemProperties.getLength(); + ++pSystemProp + ) + { + ::rtl::OUString sValue; + OSL_VERIFY( pSystemProp->Value >>= sValue ); + + _rLogger.log( LogLevel::FINER, STR_LOG_SETTING_SYSTEM_PROPERTY, pSystemProp->Name, sValue ); + + LocalRef< jstring > jName( _rEnv, convertwchar_tToJavaString( &_rEnv, pSystemProp->Name ) ); + LocalRef< jstring > jValue( _rEnv, convertwchar_tToJavaString( &_rEnv, sValue ) ); + + _rEnv.CallStaticObjectMethod( systemClass.get(), nSetPropertyMethodID, jName.get(), jValue.get() ); + LocalRef< jthrowable > throwable( _rEnv, _rEnv.ExceptionOccurred() ); + if ( throwable.is() ) + return false; + } + + return true; + } +} + +// ----------------------------------------------------------------------------- +void java_sql_Connection::loadDriverFromProperties( const Sequence< PropertyValue >& info ) +{ + // contains the statement which should be used when query for automatically generated values + ::rtl::OUString sGeneratedValueStatement; + // set to <TRUE/> when we should allow to query for generated values + sal_Bool bAutoRetrievingEnabled = sal_False; + // first try if the jdbc driver is alraedy registered at the driver manager - SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java Enviroment geloescht worden!"); + SDBThreadAttach t; try { + Sequence< NamedValue > aSystemProperties; const PropertyValue* pJavaDriverClass = 0; const PropertyValue* pJavaDriverClassPath = 0; const PropertyValue* pBegin = info.getConstArray(); @@ -840,23 +988,32 @@ } else if(!pBegin->Name.compareToAscii("IsAutoRetrievingEnabled")) { - OSL_VERIFY( pBegin->Value >>= _rbAutoRetrievingEnabled ); + OSL_VERIFY( pBegin->Value >>= bAutoRetrievingEnabled ); } else if(!pBegin->Name.compareToAscii("AutoRetrievingStatement")) { - OSL_VERIFY( pBegin->Value >>= _rsGeneratedValueStatement ); + OSL_VERIFY( pBegin->Value >>= sGeneratedValueStatement ); } else if(!pBegin->Name.compareToAscii("ParameterNameSubstitution")) { - OSL_VERIFY( pBegin->Value >>= _bParameterSubstitution ); + OSL_VERIFY( pBegin->Value >>= m_bParameterSubstitution ); } else if(!pBegin->Name.compareToAscii("IgnoreDriverPrivileges")) { - OSL_VERIFY( pBegin->Value >>= _bIgnoreDriverPrivileges ); + OSL_VERIFY( pBegin->Value >>= m_bIgnoreDriverPrivileges ); + } + else if(!pBegin->Name.compareToAscii("SystemProperties")) + { + OSL_VERIFY( pBegin->Value >>= aSystemProperties ); } } if ( !object && pJavaDriverClass != 0 ) { + if ( !lcl_setSystemProperties_nothrow( getLogger(), *t.pEnv, aSystemProperties ) ) + ThrowLoggedSQLException( getLogger(), t.pEnv, *this ); + + m_pDriverClassLoader.reset(); + // here I try to find the class for jdbc driver java_sql_SQLException_BASE::getMyClass(); java_lang_Throwable::getMyClass(); @@ -884,20 +1041,25 @@ ::rtl::OUString classpath; OSL_VERIFY( pJavaDriverClassPath->Value >>= classpath ); - pDrvClass.reset( - new java_lang_Class( - t.pEnv, + LocalRef< jclass > driverClass(t.env()); + LocalRef< jobject > driverClassLoader(t.env()); + loadClass( m_pDriver->getContext().getUNOContext(), - t.pEnv, classpath, aStr - ) - ) - ); + t.env(), classpath, aStr, &driverClassLoader, &driverClass ); + + m_pDriverClassLoader.set( driverClassLoader ); + pDrvClass.reset( new java_lang_Class( t.pEnv, driverClass.release() ) ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); } if ( pDrvClass.get() ) { - m_pDriverobject = pDrvClass->newInstanceObject(); + LocalRef< jobject > driverObject( t.env() ); + driverObject.set( pDrvClass->newInstanceObject() ); + ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + m_pDriverobject = driverObject.release(); + if( t.pEnv && m_pDriverobject ) m_pDriverobject = t.pEnv->NewGlobalRef( m_pDriverobject ); if( t.pEnv ) @@ -916,13 +1078,24 @@ } catch(SQLException& e) { - throw SQLException(::rtl::OUString::createFromAscii("The specified driver could not be loaded!"),*this,::rtl::OUString(),1000,makeAny(e)); + throw SQLException( + ::rtl::OUString::createFromAscii( "The specified driver could not be loaded." ), + // TODO: resource + *this, + ::rtl::OUString(), + 1000, + makeAny(e) + ); } catch(Exception&) { ::dbtools::throwGenericSQLException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("The specified driver could not be loaded!")) ,*this); } + + enableAutoRetrievingEnabled( bAutoRetrievingEnabled ); + setAutoRetrievingStatement( sGeneratedValueStatement ); } + // ----------------------------------------------------------------------------- sal_Bool java_sql_Connection::construct(const ::rtl::OUString& url, const Sequence< PropertyValue >& info) @@ -937,12 +1110,7 @@ if ( !t.pEnv ) throw SQLException(::rtl::OUString::createFromAscii("No Java installation could be found. Please check your installation!"),*this,::rtl::OUString::createFromAscii("S1000"),1000 ,Any()); - ::rtl::OUString sGeneratedValueStatement; // contains the statement which should be used when query for automatically generated values - sal_Bool bAutoRetrievingEnabled = sal_False; // set to <TRUE/> when we should allow to query for generated values - loadDriverFromProperties(info,sGeneratedValueStatement,bAutoRetrievingEnabled,m_bParameterSubstitution,m_bIgnoreDriverPrivileges); - - enableAutoRetrievingEnabled(bAutoRetrievingEnabled); - setAutoRetrievingStatement(sGeneratedValueStatement); + loadDriverFromProperties( info ); if ( t.pEnv && m_Driver_theClass && m_pDriverobject ) { @@ -962,30 +1130,37 @@ java_util_Properties* pProps = createStringPropertyArray(info); args[1].l = pProps->getJavaObject(); - jobject out = t.pEnv->CallObjectMethod( m_pDriverobject, mID, args[0].l,args[1].l ); - if ( !out ) - m_aLogger.log( LogLevel::SEVERE, STR_LOG_NO_SYSTEM_CONNECTION ); + LocalRef< jobject > ensureDelete( t.env(), args[0].l ); - try - { + jobject out = NULL; + // 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: + // #i82222# / 2007-10-15 + { + ContextClassLoaderScope ccl( t.env(), getDriverClassLoader(), getLogger(), *this ); + out = t.pEnv->CallObjectMethod( m_pDriverobject, mID, args[0].l,args[1].l ); + delete pProps, pProps = NULL; ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); } - catch(const SQLException& ) - { - t.pEnv->DeleteLocalRef((jstring)args[0].l); - delete pProps; - throw; - } - // und aufraeumen - t.pEnv->DeleteLocalRef((jstring)args[0].l); - delete pProps; - ThrowLoggedSQLException( m_aLogger, t.pEnv, *this ); + + if ( !out ) + m_aLogger.log( LogLevel::SEVERE, STR_LOG_NO_SYSTEM_CONNECTION ); if ( out ) object = t.pEnv->NewGlobalRef( out ); if ( object ) - m_aLogger.log( LogLevel::FINE, STR_LOG_GOT_JDBC_CONNECTION, url ); + m_aLogger.log( LogLevel::INFO, STR_LOG_GOT_JDBC_CONNECTION, url ); m_aConnectionInfo = info; } //mID --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
