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 <clemens...@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 <rmannibu...@gmail.com> > *Gesendet:* Montag, 29. Juli 2019 09:35 > *An:* Tomcat Developers List <dev@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 <clemens...@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 <rmannibu...@gmail.com> > *Gesendet:* Donnerstag, 25. Juli 2019 08:06 > *An:* Tomcat Developers List <dev@tomcat.apache.org> > *Betreff:* Re: PooledConnection#connectUsingDriver, > Thread.currentThread().getContextClassLoader() is null > > > > > > Le jeu. 25 juil. 2019 à 07:46, Clemens Wyss DEV <clemens...@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 <rmannibu...@gmail.com> > Gesendet: Donnerstag, 25. Juli 2019 07:30 > An: Tomcat Developers List <dev@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: > clemens...@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-unsubscr...@tomcat.apache.org > For additional commands, e-mail: mailto:dev-h...@tomcat.apache.org > >