You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Romain Manni-Bucau <rm...@gmail.com> on 2019/08/02 08:46:27 UTC

Re: PooledConnection#connectUsingDriver, Thread.currentThread().getContextClassLoader() is null

Finally managed to reproduce it in a test:
https://github.com/apache/tomcat/pull/183

Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> |  Blog
<https://rmannibucau.metawerx.net/> | Old Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book
<https://www.packtpub.com/application-development/java-ee-8-high-performance>


Le mar. 30 juil. 2019 à 14:21, Romain Manni-Bucau <rm...@gmail.com> a
écrit :

> Yes, got caught by PooledConnection.class.getClassLoader() which tries to
> load the driver from tomcat/lib first (common.loader actually) so if you
> put your driver there it will work in your case. Stays the bug when the
> driver is only in the webapp.
>
>
> Romain Manni-Bucau
> @rmannibucau <https://twitter.com/rmannibucau> |  Blog
> <https://rmannibucau.metawerx.net/> | Old Blog
> <http://rmannibucau.wordpress.com> | Github
> <https://github.com/rmannibucau> | LinkedIn
> <https://www.linkedin.com/in/rmannibucau> | Book
> <https://www.packtpub.com/application-development/java-ee-8-high-performance>
>
>
> Le mar. 30 juil. 2019 à 13:30, Clemens Wyss DEV <cl...@mysign.ch> a
> écrit :
>
>> > recent tomcat version
>>
>> 9.0.22 has the same class loading code as 8.5.35
>>
>>
>> https://jar-download.com/artifacts/org.apache.tomcat/tomcat-jdbc/9.0.22/source-code/org/apache/tomcat/jdbc/pool/PooledConnection.java
>>
>>                     driver = (java.sql.Driver)
>>
>>                         ClassLoaderUtil.*loadClass*(
>>
>>                             poolProperties.getDriverClassName(),
>>
>>                             PooledConnection.*class*.getClassLoader(),
>>
>>                             Thread.*currentThread*
>> ().getContextClassLoader()
>>
>>                         ).getConstructor().newInstance();
>>
>>
>>
>>
>>
>> *Von:* Romain Manni-Bucau <rm...@gmail.com>
>> *Gesendet:* Montag, 29. Juli 2019 09:35
>> *An:* Tomcat Developers List <de...@tomcat.apache.org>
>> *Betreff:* Re: PooledConnection#connectUsingDriver,
>> Thread.currentThread().getContextClassLoader() is null
>>
>>
>>
>> Can you give a try on a more recent tomcat version? Seems it works with
>> master version since the tomcat-jdbc module classloader is tried first
>> before the tccl?
>>
>> Tested with:
>>
>>
>>
>> *public class *PooledConnectionTest {
>>     @Test
>>     *public void *avoidNPEWhenTcclIsNull() *throws *SQLException {
>>         *final *PoolProperties poolProperties = *new *PoolProperties();
>>         poolProperties.setDriverClassName(*"org.h2.Driver"*);
>>         poolProperties.setUsername(*"sa"*);
>>         poolProperties.setPassword(*""*);
>>         poolProperties.setUrl(*"jdbc:h2:mem:PooledConnectionTest_avoidNPEWhenTcclIsNull"*);
>>         poolProperties.setMaxIdle(1);
>>         poolProperties.setMinIdle(1);
>>         poolProperties.setInitialSize(1);
>>         poolProperties.setMaxActive(1);
>>
>>         ensureDataSourceIsValid(poolProperties);
>>
>>         *final *Thread thread = Thread.*currentThread*();
>>         *final *ClassLoader testLoader = thread.getContextClassLoader();
>>         thread.setContextClassLoader(*null*);
>>         *try *{
>>             ensureDataSourceIsValid(poolProperties);
>>         } *finally *{
>>             thread.setContextClassLoader(testLoader);
>>         }
>>     }
>>
>>     *private void *ensureDataSourceIsValid(*final *PoolProperties poolProperties) *throws *SQLException {
>>         *final *DataSource dataSource = *new *DataSource(poolProperties);
>>         *try *(*final *Connection connection = dataSource.getConnection()) {
>>             *assertTrue*(connection.isValid(5));
>>         } *finally *{
>>             dataSource.close();
>>         }
>>     }
>> }
>>
>>
>> Romain Manni-Bucau
>> @rmannibucau <https://twitter.com/rmannibucau> |  Blog
>> <https://rmannibucau.metawerx.net/> | Old Blog
>> <http://rmannibucau.wordpress.com> | Github
>> <https://github.com/rmannibucau> | LinkedIn
>> <https://www.linkedin.com/in/rmannibucau> | Book
>> <https://www.packtpub.com/application-development/java-ee-8-high-performance>
>>
>>
>>
>>
>>
>> Le lun. 29 juil. 2019 à 07:36, Clemens Wyss DEV <cl...@mysign.ch> a
>> écrit :
>>
>> https://bz.apache.org/bugzilla/show_bug.cgi?id=63612
>>
>>
>> > with a small project reproducing it?
>>
>> Unfortunately not easily extractable/reproducible
>>
>> Maybe :
>>
>> th = new Thread() {
>> @Override
>>
>> public void run() {
>> « do something that requires a connection from the pool »
>>
>> }
>> } ;
>> th.set getContextClassLoader( null ) ;
>> th.run() ;
>>
>>
>>
>> *Von:* Romain Manni-Bucau <rm...@gmail.com>
>> *Gesendet:* Donnerstag, 25. Juli 2019 08:06
>> *An:* Tomcat Developers List <de...@tomcat.apache.org>
>> *Betreff:* Re: PooledConnection#connectUsingDriver,
>> Thread.currentThread().getContextClassLoader() is null
>>
>>
>>
>>
>>
>> Le jeu. 25 juil. 2019 à 07:46, Clemens Wyss DEV <cl...@mysign.ch> a
>> écrit :
>>
>> < mais c'était rapide 😉 >
>> >+1
>> should/can I file a bug?
>>
>>
>>
>> Guess so, with a small project reproducing it?
>>
>>
>>
>>
>> > init at bootstrap the pool (initial size) to ensure it is in one tomcat
>> classloader
>> how would you achieve this? By setting «initialSize» to «maxSize» in
>> PoolProperties?
>>
>>
>>
>> 1 should be sufficient, however tomcat-jdbc (vs dbcp2 for instance) loads
>> it per connection and not once for all the pool (
>> https://github.com/apache/tomcat/blob/master/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java#L196)
>> which is likely 1. Bad for perf but moreover 2. Buggy if the classloader
>> changes between usages of a connection in apps. Loader, driver should be
>> loaded once and potentially the datasource be duplicated per app if needed
>> (driver in the app) IMHO. This would also fix your NPE transitively ;).
>>
>>
>>
>>
>>
>>
>>
>>
>> ----------------------------------------------------------
>> Von: Romain Manni-Bucau <rm...@gmail.com>
>> Gesendet: Donnerstag, 25. Juli 2019 07:30
>> An: Tomcat Developers List <de...@tomcat.apache.org>
>> Betreff: Re: PooledConnection#connectUsingDriver,
>> Thread.currentThread().getContextClassLoader() is null
>>
>> +1, there is no real other option AFAIK until you init at bootstrap the
>> pool (initial size) to ensure it is in one tomcat classloader.
>>
>> Le jeu. 25 juil. 2019 à 07:08, Clemens Wyss DEV <mailto:
>> clemensdev@mysign.ch> a écrit :
>> I tried posting this in the tomcat-users-ml, but I guess it rather fits
>> here:
>> ----------------------------------------------------------
>> Context:
>> Debian GNU/Linux 9 \n \l
>> java version 1.8.0_162
>> Tomcat 8.5.35
>>
>> From time to time we are facing the follwing exception (call stack):
>> ...
>> Caused by: java.sql.SQLException: Unable to load class:
>> org.mariadb.jdbc.Driver from ClassLoader:http://java.net
>> .URLClassLoader@4c873330;ClassLoader:null
>>         at
>> org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:292)
>>         at
>> org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:212)
>>         at
>> org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:736)
>>         at
>> org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:668)
>>         at
>> org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:198)
>>         at
>> org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:132)
>>         at org.apache.torque.Torque.getConnection(Torque.java:924)
>>         ... 53 common frames omitted
>> Caused by: java.lang.ClassNotFoundException: Unable to load class:
>> org.mariadb.jdbc.Driver from ClassLoader:http://java.net
>> .URLClassLoader@4c873330;ClassLoader:null
>>         at
>> org.apache.tomcat.jdbc.pool.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:56)
>>         at
>> org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:280)
>>         ... 59 common frames omitted
>> Caused by: java.lang.ClassNotFoundException: Classloader is null
>>         at
>> org.apache.tomcat.jdbc.pool.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:40)
>>         ... 60 common frames omitted
>>
>> According to the code (in PooledConnection# connectUsingDriver)
>> Thread.currentThread().getContextClassLoader() returns null
>>
>> Googling for " Thread.currentThread().getContextClassLoader() is null"
>> the common demoniator seems to be `getContextClassLoader can be null`. If
>> this is true there should be
>> a) a null-check in PooledConnection# connectUsingDriver
>> b) if null, then there should be a fallback-Classloader (the system class
>> laoder?)
>>
>> WDYT ?
>>
>> Or any ideas why the given exception pops up from time to time
>>
>> Thx
>> Clemens
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: mailto:dev-unsubscribe@tomcat.apache.org
>> For additional commands, e-mail: mailto:dev-help@tomcat.apache.org
>>
>>