Question to pax-jdbc devs: how do you handle DataSourceFactory bundle restart?

12 views
Skip to first unread message

Steinar Bang

unread,
Apr 29, 2019, 12:58:57 PM4/29/19
to op...@googlegroups.com
I ran into a problem with the PostgreSQL JDBC driver running as an OSGi
bundle in karaf:
https://github.com/pgjdbc/pgjdbc/issues/1476

In short: the bundle works before restart, but after restart
DataSouce.getConnection() is unable to find the JDBC driver.

I've debugged through what happens, and the PGBundleActivator() seems to
do the right thing:
1. Call DriverManager.registerDriver() on bundle start
2. Call DriverManagerderegisterDriver() on bundle stop
3. Call DriverManager.registerDriver() again when the bundle restarts
(all of these in rt.jar)

However, after the second DriverManager.registerDriver() the
DriverManager.getConnection(String url, java.util.Properties info, Class<?> caller)
method fails because DriverManager.isDriverAllowed(Driver driver, ClassLoader classLoader)
fails (all of these in rt.jar).

And what fails in the isDriverAllowed() method is the comparison between
driver.getClass() and Class.forName(driver.getClass().getName()) fails.

Sounds like an OSGi classloader issue...?

How does the pax-jdbc bundles providing a DataSourceFactory handle JDBC
driver registration and unregistration?

Thanks!


- Steinar

Christian Schneider

unread,
Apr 30, 2019, 11:03:40 AM4/30/19
to op...@googlegroups.com

It only registers and unregisters the DataSourceFactory service which looks correct.
Not sure about the call to Driver.deregister though.
This might cause the issue.

Christian


--
--
------------------
OPS4J - http://www.ops4j.org - op...@googlegroups.com

---
You received this message because you are subscribed to the Google Groups "OPS4J" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ops4j+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
--
Christian Schneider

http://www.liquid-reality.de

Computer Scientist

Steinar Bang

unread,
Apr 30, 2019, 1:43:53 PM4/30/19
to op...@googlegroups.com
>>>>> Christian Schneider <chris-2JE8+nH+GngJmMKNX/1fiV6hY...@public.gmane.org>:
The DriverManager isn't called directly from the PGBundleActivator.

1. DriverManager.registerDriver() is called from Driver.register()
https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/Driver.java#L720
2. Driver.register() is called from a static initializer in the Driver class
https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/Driver.java#L72
3. DriverManager.deregisterDriver() is called from Driver.deregister()
https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/Driver.java#L737
4. Driver.deregister() is called from the PGBundleActivator.stop() method
https://github.com/pgjdbc/pgjdbc/blob/master/pgjdbc/src/main/java/org/postgresql/osgi/PGBundleActivator.java#L56

> It only registers and unregisters the DataSourceFactory service which looks
> correct.

Yes, that bit is correct, and populates to my DS components that has
@Reference to DatasourceFactory (the old service is unbound and the
activate methods are called using the new service).

> Not sure about the call to Driver.deregister though.
> This might cause the issue.

See comment in the issue
https://github.com/pgjdbc/pgjdbc/issues/1476#issuecomment-487370634

Christoph Läubrich

unread,
May 1, 2019, 7:01:59 AM5/1/19
to op...@googlegroups.com
DriverManger.register/unregister can't work reliable in OSGi (especially
with static insitilizer), thus always the DataSourceFactory should be
used instead!

Am 30.04.19 um 19:43 schrieb Steinar Bang:

Steinar Bang

unread,
May 1, 2019, 10:49:54 AM5/1/19
to op...@googlegroups.com
>>>>> 'Christoph Läubrich' via OPS4J <ops4j-/JYPxA39Uh5...@public.gmane.org>:

> DriverManger.register/unregister can't work reliable in OSGi
> (especially with static insitilizer), thus always the
> DataSourceFactory should be used instead!

PGDataSourceFactory is currently implemented using the regular PG JDBC
driver, ie. the Driver class, which in turn uses the DriverManager in
rt.jar.

So the fix should be to change the implementation of PGDataSourceFactory
to not use the Driver class via DriverManager? Maybe
PGDataSourceFactory should create and own a Driver instance?

Christoph Läubrich

unread,
May 2, 2019, 1:15:07 AM5/2/19
to op...@googlegroups.com
Correctly, the PGDataSourceFactory should simply instatiate the required
class directly, no need for using the DriverManager at all!
See [1] line 131 or [2] line 40, 47, 54 for example.

The problem with DataSourceFactory is that it does not support dynmics
or different class-loader spaces very well.

[1]
https://github.com/MariaDB/mariadb-connector-j/blob/1d831664f7d871ccd871fb9b2137ce3cd3421c4d/src/main/java/org/mariadb/jdbc/internal/osgi/MariaDbDataSourceFactory.java

[2]
https://github.com/Microsoft/mssql-jdbc/blob/dev/src/main/java/com/microsoft/sqlserver/jdbc/osgi/SQLServerDataSourceFactory.java

Am 01.05.19 um 16:49 schrieb Steinar Bang:

Grzegorz Grzybek

unread,
May 31, 2019, 4:48:38 AM5/31/19
to op...@googlegroups.com
Hello

Interesting case. I did some work in pax-jdbc and was especially happy with how PostgreSQL driver natively registers org.osgi.service.jdbc.DataSourceFactory OSGi service. But tbh, even if I did lot of pooling/non-pooling, XA/non-XA tests (under or without several TX managers), I never tried (shame!) restarting postgresql driver bundle!.

With this etc/org.ops4j.datasource-postgres.cfg configuration:

osgi.jdbc.driver.class=org.postgresql.Driver
dataSourceName=postgres
dataSourceType=DataSource
jdbc.url=jdbc:postgresql://localhost:5432/reportdb
jdbc.user=xxx
jdbc.password=xxx

pool=dbcp2
# dbcp2 specific configuration of org.apache.commons.pool2.impl.GenericObjectPoolConfig
pool.minIdle = 10
pool.maxTotal = 100
pool.blockWhenExhausted = true
pool.maxWaitMillis = 2000
pool.testOnBorrow = true
pool.testWhileIdle = false
pool.timeBetweenEvictionRunsMillis = 120000
pool.evictionPolicyClassName = org.apache.commons.pool2.impl.DefaultEvictionPolicy

# dbcp2 specific configuration of org.apache.commons.dbcp2.PoolableConnectionFactory
factory.maxConnLifetimeMillis = 30000
factory.validationQuery  = select schema_name from information_schema.schemata
factory.validationQueryTimeout = 2

I also had this problem:

java.sql.SQLException: No suitable driver found for jdbc:postgresql://localhost:5432/reportdb?loglevel[…]
at java.sql.DriverManager.getConnection(DriverManager.java:689) ~[?:?]
at java.sql.DriverManager.getConnection(DriverManager.java:247) ~[?:?]
at org.postgresql.ds.common.BaseDataSource.getConnection(BaseDataSource.java:86) ~[?:?]
at org.postgresql.ds.common.BaseDataSource.getConnection(BaseDataSource.java:71) ~[?:?]
at org.apache.commons.dbcp2.DataSourceConnectionFactory.createConnection(DataSourceConnectionFactory.java:44) ~[?:?]
at org.apache.commons.dbcp2.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:256) ~[?:?]
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:889) ~[?:?]
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:433) ~[?:?]
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:362) ~[?:?]
at org.apache.commons.dbcp2.PoolingDataSource.getConnection(PoolingDataSource.java:134) ~[?:?]
at org.apache.karaf.jdbc.internal.JdbcConnector.connect(JdbcConnector.java:49) ~[?:?]
at org.apache.karaf.jdbc.internal.JdbcConnector.createStatement(JdbcConnector.java:61) ~[?:?]
at org.apache.karaf.jdbc.internal.JdbcServiceImpl.query(JdbcServiceImpl.java:139) ~[?:?]
at org.apache.karaf.jdbc.command.QueryCommand.execute(QueryCommand.java:50) ~[?:?]
...

because, underneath there's always java.sql.DriverManager.getConnection() call. And postgresql driver unregisters the driver when bundle stops, but never registers it again when it starts (because the registration was in static{} block, not in BundleActivator.start().

simply refreshing postgresql driver bundle after restarting did the trick (new classloader → static{} block invoked again).

I'll switch to https://github.com/pgjdbc/pgjdbc/issues/1476 to continue the thread.

regards
Grzegorz Grzybek

Grzegorz Grzybek

unread,
May 31, 2019, 5:07:19 AM5/31/19
to op...@googlegroups.com
Hello


regards
Grzegorz Grzybek

Steinar Bang

unread,
Jun 1, 2019, 3:40:16 PM6/1/19
to op...@googlegroups.com
>>>>> Grzegorz Grzybek <gr.grzybek-Re5JQ...@public.gmane.org>:
Thanks for working on this!

(I've worked around this by using a karaf feature to load the PostgreSQL
JDBC driver, because then I can avoid reloading the PostgreSQL JDBC
bundle when loading a new application needing it... most of the
time...:-) )
Reply all
Reply to author
Forward
0 new messages