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