1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <java/sql/Connection.hxx>
23 #include <java/lang/Class.hxx>
24 #include <java/tools.hxx>
25 #include <java/ContextClassLoader.hxx>
26 #include <java/sql/DatabaseMetaData.hxx>
27 #include <java/sql/JStatement.hxx>
28 #include <java/sql/Driver.hxx>
29 #include <java/sql/PreparedStatement.hxx>
30 #include <java/sql/CallableStatement.hxx>
31 #include <java/sql/SQLWarning.hxx>
32 #include <com/sun/star/lang/DisposedException.hpp>
33 #include <com/sun/star/sdbc/SQLWarning.hpp>
34 #include <com/sun/star/beans/NamedValue.hpp>
35 #include <connectivity/sqlparse.hxx>
36 #include <connectivity/dbexception.hxx>
37 #include <java/util/Property.hxx>
38 #include <java/LocalRef.hxx>
39 #include <com/sun/star/uno/XComponentContext.hpp>
40 #include <jvmaccess/classpath.hxx>
41 #include <comphelper/namedvaluecollection.hxx>
42 #include <cppuhelper/exc_hlp.hxx>
43 #include <rtl/ustrbuf.hxx>
44 #include <jni.h>
45 #include <strings.hrc>
46 #include <unotools/confignode.hxx>
47 #include <strings.hxx>
48 
49 #include <vector>
50 #include <memory>
51 
52 using namespace connectivity;
53 using namespace connectivity::jdbc;
54 using namespace ::com::sun::star::uno;
55 using namespace ::com::sun::star::beans;
56 using namespace ::com::sun::star::sdbc;
57 using namespace ::com::sun::star::container;
58 using namespace ::com::sun::star::lang;
59 
60 namespace {
61 
62 struct ClassMapEntry {
ClassMapEntry__anon165ce71e0111::ClassMapEntry63     ClassMapEntry(
64         OUString const & theClassPath, OUString const & theClassName):
65         classPath(theClassPath), className(theClassName), classLoader(nullptr),
66         classObject(nullptr) {}
67 
68     OUString classPath;
69     OUString className;
70     jweak classLoader;
71     jweak classObject;
72 };
73 
74 typedef std::vector< ClassMapEntry > ClassMap;
75 
76 struct ClassMapData {
77     osl::Mutex mutex;
78 
79     ClassMap map;
80 };
81 
82 struct ClassMapDataInit {
operator ()__anon165ce71e0111::ClassMapDataInit83     ClassMapData * operator()() {
84         static ClassMapData instance;
85         return &instance;
86     }
87 };
88 
89 template < typename T >
getLocalFromWeakRef(jweak & _weak,LocalRef<T> & _inout_local)90 bool getLocalFromWeakRef( jweak& _weak, LocalRef< T >& _inout_local )
91 {
92     _inout_local.set( static_cast< T >( _inout_local.env().NewLocalRef( _weak ) ) );
93 
94     if ( !_inout_local.is() )
95     {
96         if ( _inout_local.env().ExceptionCheck())
97         {
98             return false;
99         }
100         else if ( _weak != nullptr )
101         {
102             _inout_local.env().DeleteWeakGlobalRef( _weak );
103             _weak = nullptr;
104         }
105     }
106     return true;
107 }
108 
109 // Load a class.  A map from pairs of (classPath, name) to pairs of weak Java
110 // references to (ClassLoader, Class) is maintained, so that a class is only
111 // loaded once.
112 //
113 // It may happen that the weak reference to the ClassLoader becomes null while
114 // the reference to the Class remains non-null (in case the Class was actually
115 // loaded by some parent of the ClassLoader), in which case the ClassLoader is
116 // resurrected (which cannot cause any classes to be loaded multiple times, as
117 // the ClassLoader is no longer reachable, so no classes it has ever loaded are
118 // still reachable).
119 //
120 // Similarly, it may happen that the weak reference to the Class becomes null
121 // while the reference to the ClassLoader remains non-null, in which case the
122 // Class is simply re-loaded.
123 //
124 // This code is close to the implementation of jvmaccess::ClassPath::loadClass
125 // in jvmaccess/classpath.hxx, but not close enough to avoid the duplication.
126 //
127 // If false is returned, a (still pending) JNI exception occurred.
loadClass(Reference<XComponentContext> const & context,JNIEnv & environment,OUString const & classPath,OUString const & name,LocalRef<jobject> * classLoaderPtr,LocalRef<jclass> * classPtr)128 bool loadClass(
129     Reference< XComponentContext > const & context, JNIEnv& environment,
130     OUString const & classPath, OUString const & name,
131     LocalRef< jobject > * classLoaderPtr, LocalRef< jclass > * classPtr)
132 {
133     OSL_ASSERT(classLoaderPtr != nullptr);
134     // For any jweak entries still present in the map upon destruction,
135     // DeleteWeakGlobalRef is not called (which is a leak):
136     ClassMapData * d =
137         rtl_Instance< ClassMapData, ClassMapDataInit, osl::MutexGuard,
138         osl::GetGlobalMutex >::create(
139             ClassMapDataInit(), osl::GetGlobalMutex());
140     osl::MutexGuard g(d->mutex);
141     ClassMap::iterator i(d->map.begin());
142     LocalRef< jobject > cloader(environment);
143     LocalRef< jclass > cl(environment);
144     // Prune dangling weak references from the list while searching for a match,
145     // so that the list cannot grow unbounded:
146     for (; i != d->map.end();)
147     {
148         LocalRef< jobject > classLoader( environment );
149         if ( !getLocalFromWeakRef( i->classLoader, classLoader ) )
150             return false;
151 
152         LocalRef< jclass > classObject( environment );
153         if ( !getLocalFromWeakRef( i->classObject, classObject ) )
154             return false;
155 
156         if ( !classLoader.is() && !classObject.is() )
157         {
158             i = d->map.erase(i);
159         }
160         else if ( i->classPath == classPath && i->className == name )
161         {
162             cloader.set( classLoader.release() );
163             cl.set( classObject.release() );
164             break;
165         }
166         else
167         {
168             ++i;
169         }
170     }
171     if ( !cloader.is() || !cl.is() )
172     {
173         if ( i == d->map.end() )
174         {
175             // Push a new ClassMapEntry (which can potentially fail) before
176             // loading the class, so that it never happens that a class is
177             // loaded but not added to the map (which could have effects on the
178             // JVM that are not easily undone).  If the pushed ClassMapEntry is
179             // not used after all (return false, etc.) it will be pruned on next
180             // call because its classLoader/classObject are null:
181             d->map.push_back( ClassMapEntry( classPath, name ) );
182             i = std::prev(d->map.end());
183         }
184 
185         LocalRef< jclass > clClass( environment );
186         clClass.set( environment.FindClass( "java/net/URLClassLoader" ) );
187         if ( !clClass.is() )
188             return false;
189 
190         jweak wcloader = nullptr;
191         if (!cloader.is())
192         {
193             jmethodID ctorLoader( environment.GetMethodID( clClass.get(), "<init>", "([Ljava/net/URL;)V" ) );
194             if (ctorLoader == nullptr)
195                 return false;
196 
197             LocalRef< jobjectArray > arr( environment );
198             arr.set( jvmaccess::ClassPath::translateToUrls( context, &environment, classPath ) );
199             if ( !arr.is() )
200                 return false;
201 
202             jvalue arg;
203             arg.l = arr.get();
204             cloader.set( environment.NewObjectA( clClass.get(), ctorLoader, &arg ) );
205             if ( !cloader.is() )
206                 return false;
207 
208             wcloader = environment.NewWeakGlobalRef( cloader.get() );
209             if ( wcloader == nullptr )
210                 return false;
211         }
212 
213         jweak wcl = nullptr;
214         if ( !cl.is() )
215         {
216             jmethodID methLoadClass( environment.GetMethodID( clClass.get(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;" ) );
217             if ( methLoadClass == nullptr )
218                 return false;
219 
220             LocalRef< jstring > str( environment );
221             str.set( convertwchar_tToJavaString( &environment, name ) );
222             if ( !str.is() )
223                 return false;
224 
225             jvalue arg;
226             arg.l = str.get();
227             cl.set( static_cast< jclass >( environment.CallObjectMethodA( cloader.get(), methLoadClass, &arg ) ) );
228             if ( !cl.is() )
229                 return false;
230 
231             wcl = environment.NewWeakGlobalRef( cl.get() );
232             if ( wcl == nullptr )
233                 return false;
234         }
235 
236         if ( wcloader != nullptr)
237         {
238             i->classLoader = wcloader;
239         }
240         if ( wcl != nullptr )
241         {
242             i->classObject = wcl;
243         }
244     }
245 
246     classLoaderPtr->set( cloader.release() );
247     classPtr->set( cl.release() );
248     return true;
249 }
250 
251 }
252 
253 
254 IMPLEMENT_SERVICE_INFO(java_sql_Connection,"com.sun.star.sdbcx.JConnection","com.sun.star.sdbc.Connection");
255 
256 
257 //************ Class: java.sql.Connection
258 
259 jclass java_sql_Connection::theClass = nullptr;
260 
java_sql_Connection(const java_sql_Driver & _rDriver)261 java_sql_Connection::java_sql_Connection( const java_sql_Driver& _rDriver )
262     :java_lang_Object()
263     ,m_xContext( _rDriver.getContext() )
264     ,m_pDriver( &_rDriver )
265     ,m_pDriverobject(nullptr)
266     ,m_pDriverClassLoader()
267     ,m_Driver_theClass(nullptr)
268     ,m_aLogger( _rDriver.getLogger() )
269     ,m_bIgnoreDriverPrivileges(true)
270     ,m_bIgnoreCurrency(false)
271 {
272 }
273 
~java_sql_Connection()274 java_sql_Connection::~java_sql_Connection()
275 {
276     ::rtl::Reference< jvmaccess::VirtualMachine > xTest = java_lang_Object::getVM();
277     if ( xTest.is() )
278     {
279         SDBThreadAttach t;
280         clearObject(*t.pEnv);
281 
282         {
283             if ( m_pDriverobject )
284                 t.pEnv->DeleteGlobalRef( m_pDriverobject );
285             m_pDriverobject = nullptr;
286             if ( m_Driver_theClass )
287                 t.pEnv->DeleteGlobalRef( m_Driver_theClass );
288             m_Driver_theClass = nullptr;
289         }
290         SDBThreadAttach::releaseRef();
291     }
292 }
293 
disposing()294 void java_sql_Connection::disposing()
295 {
296     ::osl::MutexGuard aGuard(m_aMutex);
297 
298     m_aLogger.log( LogLevel::INFO, STR_LOG_SHUTDOWN_CONNECTION );
299 
300     java_sql_Connection_BASE::disposing();
301 
302     if ( object )
303     {
304         static jmethodID mID(nullptr);
305         callVoidMethod_ThrowSQL("close", mID);
306     }
307 }
308 
getMyClass() const309 jclass java_sql_Connection::getMyClass() const
310 {
311     // the class must be fetched only once, therefore static
312     if( !theClass )
313         theClass = findMyClass("java/sql/Connection");
314     return theClass;
315 }
316 
317 
getCatalog()318 OUString SAL_CALL java_sql_Connection::getCatalog(  )
319 {
320     ::osl::MutexGuard aGuard( m_aMutex );
321     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
322 
323     static jmethodID mID(nullptr);
324     return callStringMethod("getCatalog",mID);
325 }
326 
getMetaData()327 Reference< XDatabaseMetaData > SAL_CALL java_sql_Connection::getMetaData(  )
328 {
329     ::osl::MutexGuard aGuard( m_aMutex );
330     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
331 
332 
333     Reference< XDatabaseMetaData > xMetaData = m_xMetaData;
334     if(!xMetaData.is())
335     {
336         SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!");
337         static jmethodID mID(nullptr);
338         jobject out = callObjectMethod(t.pEnv,"getMetaData","()Ljava/sql/DatabaseMetaData;", mID);
339         if(out)
340         {
341             xMetaData = new java_sql_DatabaseMetaData( t.pEnv, out, *this );
342             m_xMetaData = xMetaData;
343         }
344     }
345 
346     return xMetaData;
347 }
348 
close()349 void SAL_CALL java_sql_Connection::close(  )
350 {
351     dispose();
352 }
353 
commit()354 void SAL_CALL java_sql_Connection::commit(  )
355 {
356     static jmethodID mID(nullptr);
357     callVoidMethod_ThrowSQL("commit", mID);
358 }
359 
isClosed()360 sal_Bool SAL_CALL java_sql_Connection::isClosed(  )
361 {
362     ::osl::MutexGuard aGuard( m_aMutex );
363 
364     static jmethodID mID(nullptr);
365     return callBooleanMethod( "isClosed", mID ) && java_sql_Connection_BASE::rBHelper.bDisposed;
366 }
367 
isReadOnly()368 sal_Bool SAL_CALL java_sql_Connection::isReadOnly(  )
369 {
370     ::osl::MutexGuard aGuard( m_aMutex );
371     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
372     static jmethodID mID(nullptr);
373     return callBooleanMethod( "isReadOnly", mID );
374 }
375 
setCatalog(const OUString & catalog)376 void SAL_CALL java_sql_Connection::setCatalog( const OUString& catalog )
377 {
378     static jmethodID mID(nullptr);
379     callVoidMethodWithStringArg("setCatalog",mID,catalog);
380 }
381 
rollback()382 void SAL_CALL java_sql_Connection::rollback(  )
383 {
384     static jmethodID mID(nullptr);
385     callVoidMethod_ThrowSQL("rollback", mID);
386 }
387 
getAutoCommit()388 sal_Bool SAL_CALL java_sql_Connection::getAutoCommit(  )
389 {
390     static jmethodID mID(nullptr);
391     return callBooleanMethod( "getAutoCommit", mID );
392 }
393 
setReadOnly(sal_Bool readOnly)394 void SAL_CALL java_sql_Connection::setReadOnly( sal_Bool readOnly )
395 {
396     static jmethodID mID(nullptr);
397     callVoidMethodWithBoolArg_ThrowSQL("setReadOnly", mID, readOnly);
398 }
399 
setAutoCommit(sal_Bool autoCommit)400 void SAL_CALL java_sql_Connection::setAutoCommit( sal_Bool autoCommit )
401 {
402     static jmethodID mID(nullptr);
403     callVoidMethodWithBoolArg_ThrowSQL("setAutoCommit", mID, autoCommit);
404 }
405 
getTypeMap()406 Reference< css::container::XNameAccess > SAL_CALL java_sql_Connection::getTypeMap(  )
407 {
408     ::osl::MutexGuard aGuard( m_aMutex );
409     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
410 
411     SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!");
412     static jmethodID mID(nullptr);
413     callObjectMethod(t.pEnv,"getTypeMap","()Ljava/util/Map;", mID);
414     // WARNING: the caller becomes the owner of the returned pointer
415     return nullptr;
416 }
417 
setTypeMap(const Reference<css::container::XNameAccess> &)418 void SAL_CALL java_sql_Connection::setTypeMap( const Reference< css::container::XNameAccess >& /*typeMap*/ )
419 {
420     ::osl::MutexGuard aGuard( m_aMutex );
421     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
422 
423     ::dbtools::throwFeatureNotImplementedSQLException( "XConnection::setTypeMap", *this );
424 }
425 
426 
getTransactionIsolation()427 sal_Int32 SAL_CALL java_sql_Connection::getTransactionIsolation(  )
428 {
429     ::osl::MutexGuard aGuard( m_aMutex );
430     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
431 
432     static jmethodID mID(nullptr);
433     return callIntMethod_ThrowSQL("getTransactionIsolation", mID);
434 }
435 
setTransactionIsolation(sal_Int32 level)436 void SAL_CALL java_sql_Connection::setTransactionIsolation( sal_Int32 level )
437 {
438     ::osl::MutexGuard aGuard( m_aMutex );
439     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
440 
441     static jmethodID mID(nullptr);
442     callVoidMethodWithIntArg_ThrowSQL("setTransactionIsolation", mID, level);
443 }
444 
createStatement()445 Reference< XStatement > SAL_CALL java_sql_Connection::createStatement(  )
446 {
447     ::osl::MutexGuard aGuard( m_aMutex );
448     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
449     m_aLogger.log( LogLevel::FINE, STR_LOG_CREATE_STATEMENT );
450 
451     SDBThreadAttach t;
452     java_sql_Statement* pStatement = new java_sql_Statement( t.pEnv, *this );
453     Reference< XStatement > xStmt = pStatement;
454     m_aStatements.push_back( WeakReferenceHelper( xStmt ) );
455 
456     m_aLogger.log( LogLevel::FINE, STR_LOG_CREATED_STATEMENT_ID, pStatement->getStatementObjectID() );
457     return xStmt;
458 }
459 
prepareStatement(const OUString & sql)460 Reference< XPreparedStatement > SAL_CALL java_sql_Connection::prepareStatement( const OUString& sql )
461 {
462     ::osl::MutexGuard aGuard( m_aMutex );
463     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
464     m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARE_STATEMENT, sql );
465 
466     SDBThreadAttach t;
467 
468     java_sql_PreparedStatement* pStatement = new java_sql_PreparedStatement( t.pEnv, *this, sql );
469     Reference< XPreparedStatement > xReturn( pStatement );
470     m_aStatements.push_back(WeakReferenceHelper(xReturn));
471 
472     m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARED_STATEMENT_ID, pStatement->getStatementObjectID() );
473     return xReturn;
474 }
475 
prepareCall(const OUString & sql)476 Reference< XPreparedStatement > SAL_CALL java_sql_Connection::prepareCall( const OUString& sql )
477 {
478     ::osl::MutexGuard aGuard( m_aMutex );
479     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
480     m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARE_CALL, sql );
481 
482     SDBThreadAttach t;
483 
484     java_sql_CallableStatement* pStatement = new java_sql_CallableStatement( t.pEnv, *this, sql );
485     Reference< XPreparedStatement > xStmt( pStatement );
486     m_aStatements.push_back(WeakReferenceHelper(xStmt));
487 
488     m_aLogger.log( LogLevel::FINE, STR_LOG_PREPARED_CALL_ID, pStatement->getStatementObjectID() );
489     return xStmt;
490 }
491 
nativeSQL(const OUString & sql)492 OUString SAL_CALL java_sql_Connection::nativeSQL( const OUString& sql )
493 {
494     ::osl::MutexGuard aGuard( m_aMutex );
495     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
496 
497     OUString aStr;
498     SDBThreadAttach t; OSL_ENSURE(t.pEnv,"Java environment has been deleted!");
499     {
500 
501         // initialize temporary Variable
502         static const char * const cSignature = "(Ljava/lang/String;)Ljava/lang/String;";
503         static const char * const cMethodName = "nativeSQL";
504         // Java-Call
505         static jmethodID mID(nullptr);
506         obtainMethodId_throwSQL(t.pEnv, cMethodName,cSignature, mID);
507         // Convert Parameter
508         jdbc::LocalRef< jstring > str( t.env(),convertwchar_tToJavaString(t.pEnv,sql));
509 
510         jobject out = t.pEnv->CallObjectMethod( object, mID, str.get() );
511         aStr = JavaString2String(t.pEnv, static_cast<jstring>(out) );
512         ThrowLoggedSQLException( m_aLogger, t.pEnv, *this );
513     } //t.pEnv
514 
515     m_aLogger.log( LogLevel::FINER, STR_LOG_NATIVE_SQL, sql, aStr );
516 
517     return aStr;
518 }
519 
clearWarnings()520 void SAL_CALL java_sql_Connection::clearWarnings(  )
521 {
522     static jmethodID mID(nullptr);
523     callVoidMethod_ThrowSQL("clearWarnings", mID);
524 }
525 
getWarnings()526 Any SAL_CALL java_sql_Connection::getWarnings(  )
527 {
528     ::osl::MutexGuard aGuard( m_aMutex );
529     checkDisposed(java_sql_Connection_BASE::rBHelper.bDisposed);
530 
531     SDBThreadAttach t;
532     static jmethodID mID(nullptr);
533     jobject out = callObjectMethod(t.pEnv,"getWarnings","()Ljava/sql/SQLWarning;", mID);
534     // WARNING: the caller becomes the owner of the returned pointer
535     if( out )
536     {
537         java_sql_SQLWarning_BASE        warn_base(t.pEnv, out);
538         SQLException aAsException( java_sql_SQLWarning( warn_base, *this ) );
539 
540         // translate to warning
541         SQLWarning aWarning;
542         aWarning.Context = aAsException.Context;
543         aWarning.Message = aAsException.Message;
544         aWarning.SQLState = aAsException.SQLState;
545         aWarning.ErrorCode = aAsException.ErrorCode;
546         aWarning.NextException = aAsException.NextException;
547 
548         return makeAny( aWarning );
549     }
550 
551     return Any();
552 }
553 
554 
555 namespace
556 {
lcl_getDriverLoadErrorMessage(const::connectivity::SharedResources & _aResource,const OUString & _rDriverClass,const OUString & _rDriverClassPath)557     OUString lcl_getDriverLoadErrorMessage( const ::connectivity::SharedResources& _aResource,const OUString& _rDriverClass, const OUString& _rDriverClassPath )
558     {
559         OUString sError1( _aResource.getResourceStringWithSubstitution(
560                 STR_NO_CLASSNAME,
561                 "$classname$", _rDriverClass
562              ) );
563         if ( !_rDriverClassPath.isEmpty() )
564         {
565             const OUString sError2( _aResource.getResourceStringWithSubstitution(
566                 STR_NO_CLASSNAME_PATH,
567                 "$classpath$", _rDriverClassPath
568              ) );
569             sError1 += sError2;
570         } // if ( _rDriverClassPath.getLength() )
571         return sError1;
572     }
573 }
574 
575 
576 namespace
577 {
lcl_setSystemProperties_nothrow(const java::sql::ConnectionLog & _rLogger,JNIEnv & _rEnv,const Sequence<NamedValue> & _rSystemProperties)578     bool lcl_setSystemProperties_nothrow( const java::sql::ConnectionLog& _rLogger,
579         JNIEnv& _rEnv, const Sequence< NamedValue >& _rSystemProperties )
580     {
581         if ( !_rSystemProperties.hasElements() )
582             // nothing to do
583             return true;
584 
585         LocalRef< jclass > systemClass( _rEnv );
586         jmethodID nSetPropertyMethodID = nullptr;
587         // retrieve the java.lang.System class
588         systemClass.set( _rEnv.FindClass( "java/lang/System" ) );
589         if ( systemClass.is() )
590         {
591             nSetPropertyMethodID = _rEnv.GetStaticMethodID(
592                 systemClass.get(), "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;" );
593         }
594 
595         if ( nSetPropertyMethodID == nullptr )
596             return false;
597 
598         for ( auto const & systemProp : _rSystemProperties )
599         {
600             OUString sValue;
601             OSL_VERIFY( systemProp.Value >>= sValue );
602 
603             _rLogger.log( LogLevel::FINER, STR_LOG_SETTING_SYSTEM_PROPERTY, systemProp.Name, sValue );
604 
605             LocalRef< jstring > jName( _rEnv, convertwchar_tToJavaString( &_rEnv, systemProp.Name ) );
606             LocalRef< jstring > jValue( _rEnv, convertwchar_tToJavaString( &_rEnv, sValue ) );
607 
608             _rEnv.CallStaticObjectMethod( systemClass.get(), nSetPropertyMethodID, jName.get(), jValue.get() );
609             LocalRef< jthrowable > throwable( _rEnv, _rEnv.ExceptionOccurred() );
610             if ( throwable.is() )
611                 return false;
612         }
613 
614         return true;
615     }
616 }
617 
618 
loadDriverFromProperties(const OUString & _sDriverClass,const OUString & _sDriverClassPath,const Sequence<NamedValue> & _rSystemProperties)619 void java_sql_Connection::loadDriverFromProperties( const OUString& _sDriverClass, const OUString& _sDriverClassPath,
620     const Sequence< NamedValue >& _rSystemProperties )
621 {
622     // first try if the jdbc driver is already registered at the driver manager
623     SDBThreadAttach t;
624     try
625     {
626         if ( !object )
627         {
628             if ( !lcl_setSystemProperties_nothrow( getLogger(), *t.pEnv, _rSystemProperties ) )
629                 ThrowLoggedSQLException( getLogger(), t.pEnv, *this );
630 
631             m_pDriverClassLoader.reset();
632 
633             // here I try to find the class for jdbc driver
634             java_sql_SQLException_BASE::st_getMyClass();
635             java_lang_Throwable::st_getMyClass();
636 
637             if ( _sDriverClass.isEmpty() )
638             {
639                 m_aLogger.log( LogLevel::SEVERE, STR_LOG_NO_DRIVER_CLASS );
640                 ::dbtools::throwGenericSQLException(
641                     lcl_getDriverLoadErrorMessage( getResources(),_sDriverClass, _sDriverClassPath ),
642                     *this
643                 );
644             }
645             else
646             {
647                 m_aLogger.log( LogLevel::INFO, STR_LOG_LOADING_DRIVER, _sDriverClass );
648                 // the driver manager holds the class of the driver for later use
649                 std::unique_ptr< java_lang_Class > pDrvClass;
650                 if ( _sDriverClassPath.isEmpty() )
651                 {
652                     // if forName didn't find the class it will throw an exception
653                     pDrvClass.reset(java_lang_Class::forName(_sDriverClass));
654                 }
655                 else
656                 {
657                     LocalRef< jclass > driverClass(t.env());
658                     LocalRef< jobject > driverClassLoader(t.env());
659 
660                     loadClass(
661                         m_pDriver->getContext(),
662                         t.env(), _sDriverClassPath, _sDriverClass, &driverClassLoader, &driverClass );
663 
664                     m_pDriverClassLoader.set( driverClassLoader );
665                     pDrvClass.reset( new java_lang_Class( t.pEnv, driverClass.release() ) );
666 
667                     ThrowLoggedSQLException( m_aLogger, t.pEnv, *this );
668                 }
669                 if (pDrvClass)
670                 {
671                     LocalRef< jobject > driverObject( t.env() );
672                     driverObject.set( pDrvClass->newInstanceObject() );
673                     ThrowLoggedSQLException( m_aLogger, t.pEnv, *this );
674                     m_pDriverobject = driverObject.release();
675 
676                     if( m_pDriverobject )
677                         m_pDriverobject = t.pEnv->NewGlobalRef( m_pDriverobject );
678 
679                     {
680                         jclass tempClass = t.pEnv->GetObjectClass(m_pDriverobject);
681                         if ( m_pDriverobject )
682                         {
683                             m_Driver_theClass = static_cast<jclass>(t.pEnv->NewGlobalRef( tempClass ));
684                             t.pEnv->DeleteLocalRef( tempClass );
685                         }
686                     }
687                 }
688                 m_aLogger.log( LogLevel::INFO, STR_LOG_CONN_SUCCESS );
689             }
690         }
691     }
692     catch( const SQLException& )
693     {
694         css::uno::Any anyEx = cppu::getCaughtException();
695         throw SQLException(
696             lcl_getDriverLoadErrorMessage( getResources(),_sDriverClass, _sDriverClassPath ),
697             *this,
698             OUString(),
699             1000,
700             anyEx);
701     }
702     catch( Exception& )
703     {
704         css::uno::Any anyEx = cppu::getCaughtException();
705         ::dbtools::throwGenericSQLException(
706             lcl_getDriverLoadErrorMessage( getResources(),_sDriverClass, _sDriverClassPath ),
707             *this,
708             anyEx
709         );
710     }
711 }
712 
impl_getJavaDriverClassPath_nothrow(const OUString & _sDriverClass)713 OUString java_sql_Connection::impl_getJavaDriverClassPath_nothrow(const OUString& _sDriverClass)
714 {
715     static const char s_sNodeName[] = "org.openoffice.Office.DataAccess/JDBC/DriverClassPaths";
716     ::utl::OConfigurationTreeRoot aNamesRoot = ::utl::OConfigurationTreeRoot::createWithComponentContext(
717         m_pDriver->getContext(), s_sNodeName, -1, ::utl::OConfigurationTreeRoot::CM_READONLY);
718     OUString sURL;
719     if ( aNamesRoot.isValid() && aNamesRoot.hasByName( _sDriverClass ) )
720     {
721         ::utl::OConfigurationNode aRegisterObj = aNamesRoot.openNode( _sDriverClass );
722         OSL_VERIFY( aRegisterObj.getNodeValue( "Path" ) >>= sURL );
723     }
724     return sURL;
725 }
726 
construct(const OUString & url,const Sequence<PropertyValue> & info)727 bool java_sql_Connection::construct(const OUString& url,
728                                     const Sequence< PropertyValue >& info)
729 {
730     { // initialize the java vm
731         ::rtl::Reference< jvmaccess::VirtualMachine > xTest = java_lang_Object::getVM(m_xContext);
732         if ( !xTest.is() )
733             throwGenericSQLException(STR_NO_JAVA,*this);
734     }
735     SDBThreadAttach t;
736     SDBThreadAttach::addRef();      // will be released in dtor
737     if ( !t.pEnv )
738         throwGenericSQLException(STR_NO_JAVA,*this);
739 
740     OUString     sGeneratedValueStatement; // contains the statement which should be used when query for automatically generated values
741     bool            bAutoRetrievingEnabled = false; // set to <TRUE/> when we should allow to query for generated values
742     OUString sDriverClassPath,sDriverClass;
743     Sequence< NamedValue > aSystemProperties;
744 
745     ::comphelper::NamedValueCollection aSettings( info );
746     sDriverClass = aSettings.getOrDefault( "JavaDriverClass", sDriverClass );
747     sDriverClassPath = aSettings.getOrDefault( "JavaDriverClassPath", sDriverClassPath);
748     if ( sDriverClassPath.isEmpty() )
749         sDriverClassPath = impl_getJavaDriverClassPath_nothrow(sDriverClass);
750     bAutoRetrievingEnabled = aSettings.getOrDefault( "IsAutoRetrievingEnabled", bAutoRetrievingEnabled );
751     sGeneratedValueStatement = aSettings.getOrDefault( "AutoRetrievingStatement", sGeneratedValueStatement );
752     m_bIgnoreDriverPrivileges = aSettings.getOrDefault( "IgnoreDriverPrivileges", m_bIgnoreDriverPrivileges );
753     m_bIgnoreCurrency = aSettings.getOrDefault( "IgnoreCurrency", m_bIgnoreCurrency );
754     aSystemProperties = aSettings.getOrDefault( "SystemProperties", aSystemProperties );
755     m_aCatalogRestriction = aSettings.getOrDefault( "ImplicitCatalogRestriction", Any() );
756     m_aSchemaRestriction = aSettings.getOrDefault( "ImplicitSchemaRestriction", Any() );
757 
758     loadDriverFromProperties( sDriverClass, sDriverClassPath, aSystemProperties );
759 
760     enableAutoRetrievingEnabled(bAutoRetrievingEnabled);
761     setAutoRetrievingStatement(sGeneratedValueStatement);
762 
763     if ( t.pEnv && m_Driver_theClass && m_pDriverobject )
764     {
765         // Java-Call
766         static const char * const cSignature = "(Ljava/lang/String;Ljava/util/Properties;)Ljava/sql/Connection;";
767         static const char * const cMethodName = "connect";
768         jmethodID mID  = t.pEnv->GetMethodID( m_Driver_theClass, cMethodName, cSignature );
769 
770         if ( mID )
771         {
772             jvalue args[2];
773             // convert Parameter
774             args[0].l = convertwchar_tToJavaString(t.pEnv,url);
775             std::unique_ptr<java_util_Properties> pProps = createStringPropertyArray(info);
776             args[1].l = pProps->getJavaObject();
777 
778             LocalRef< jobject > ensureDelete( t.env(), args[0].l );
779 
780             jobject out = nullptr;
781             // In some cases (e.g.,
782             // connectivity/source/drivers/hsqldb/HDriver.cxx:1.24
783             // l. 249) the JavaDriverClassPath contains multiple jars,
784             // as creating the JavaDriverClass instance requires
785             // (reflective) access to those other jars.  Now, if the
786             // JavaDriverClass is actually loaded by some parent class
787             // loader (e.g., because its jar is also on the global
788             // class path), it would still not have access to the
789             // additional jars on the JavaDriverClassPath.  Hence, the
790             // JavaDriverClassPath class loader is pushed as context
791             // class loader around the JavaDriverClass instance
792             // creation:
793             // #i82222# / 2007-10-15
794             {
795                 ContextClassLoaderScope ccl( t.env(), getDriverClassLoader(), getLogger(), *this );
796                 out = t.pEnv->CallObjectMethod( m_pDriverobject, mID, args[0].l,args[1].l );
797                 pProps.reset();
798                 ThrowLoggedSQLException( m_aLogger, t.pEnv, *this );
799             }
800 
801             if ( !out )
802                 m_aLogger.log( LogLevel::SEVERE, STR_LOG_NO_SYSTEM_CONNECTION );
803 
804             if ( out )
805                 object = t.pEnv->NewGlobalRef( out );
806 
807             if ( object )
808                 m_aLogger.log( LogLevel::INFO, STR_LOG_GOT_JDBC_CONNECTION, url );
809 
810             m_aConnectionInfo = info;
811         } //mID
812     } //t.pEnv
813     return object != nullptr;
814 }
815 
816 
817 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
818