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