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