I have a simple wrapper over JDBC Driver class and I would like my Driver to be registered with the DriverManager automatically. I followed the How is driver class located in JDBC4 to set up the META-INF/services/java.sql.Driver file and mentioned my wrapped driver.
My jdbc url is configured to append "sample:" with other standard driver's url.
For example, for MySQL the url will be
sample:jdbc:mysql://localhost:3306/test
My SampleDriver:
package com.trial
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collections;
import java.util.Optional;
import java.util.Properties;
import java.util.logging.Logger;
public class SampleDriver implements Driver {
private static final Driver INSTANCE = new SampleDriver();
private Driver driver;
public SampleDriver() {
}
public SampleDriver(Driver driver) {
this.driver = driver;
}
static {
try {
DriverManager.registerDriver(INSTANCE);
} catch (SQLException e) {
throw new IllegalStateException("Could not register Sample Driver with DriverManager", e);
}
}
/**
* Get a Connection to the database from the real driver that this
* wrapper is tracing on.
*
* @param url JDBC connection URL .
* @param info a list of arbitrary string tag/value pairs as connection
* arguments. Normally at least a "user" and "password" property should
* be included.
* @return a <code>Connection</code> object that represents a connection to
* the URL.
* @throws SQLException if a database access error occurs
*/
@Override
public Connection connect(String url, Properties info) throws SQLException {
Optional<SampleDriver> realDriver = getRealDriver(url);
String realUrl = extractRealURL(url);
if (!realDriver.isPresent()) {
throw new SQLException("Unable to find a driver that accepts the url " + realUrl);
}
Connection conn = realDriver.get().driver.connect(realUrl, info);
if (null == conn) {
throw new SQLException("Invalid or unknown driver url : " + realUrl);
}
return conn;
}
/**
* Given a sample: type URL, find the underlying real driver
* that accepts the URL.
*
* @param url JDBC connection URL.
* @return Real driver for the given URL. Optional.empty is returned
* if the URL is not a sample: type URL or there is
* no underlying driver that accepts the URL.
* @throws SQLException if a database access error occurs.
*/
private Optional<SampleDriver> getRealDriver(String url) throws SQLException {
if (null == url || url.trim().isEmpty()) {
throw new IllegalArgumentException("url is required");
}
Optional<Driver> realDriver;
int instanceId;
if (url.startsWith(getURLPrefix())) {
String realUrl = extractRealURL(url);
try {
realDriver = Collections.list(DriverManager.getDrivers()).stream().
filter(driver -> {
try {
return driver.acceptsURL(realUrl);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}).findFirst();
return Optional.of(new SampleDriver(realDriver.orElse(null)));
} catch (Exception e) {
throw new SQLException(e);
}
}
return Optional.empty();
}
/**
* Given a <code>sample:</code> type URL, extract and return the real URL
*
* @return Real URL client wanted to connect with.
* if the URL is not a <code>sample:</code> return the given input
*/
private String extractRealURL(String url) {
return url.replaceFirst(getURLPrefix(), "");
}
/**
* Get the String used to prefix real URL
*
* @return the URL prefix
*/
private String getURLPrefix() {
return "sample:";
}
/**
* Returns true if this is a <code>sample:</code> URL
*
* @param url JDBC URL.
* @return true if this Driver can handle the URL.
* @throws SQLException if a database access error occurs
*/
@Override
public boolean acceptsURL(String url) throws SQLException {
return url != null && url.startsWith(getURLPrefix());
}
/**
* Gets information about the possible properties for the real driver.
*
* @param url the URL of the database to which to connect
* @param info a proposed list of tag/value pairs that will be sent on connect
* open
* @return an array of <code>DriverPropertyInfo</code> objects describing
* possible properties. This array may be an empty array if no
* properties are required.
* @throws SQLException if a database access error occurs
*/
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
Optional<SampleDriver> realDriver = getRealDriver(url);
DriverPropertyInfo[] propertyInfo;
if (realDriver.isEmpty()) {
propertyInfo = new DriverPropertyInfo[0];
} else {
propertyInfo = realDriver.get().driver.getPropertyInfo(url, info);
}
return propertyInfo;
}
/**
* Returns 1 for the major version of the wrapped driver.
*
* @return Major version of the wrapped driver
*/
@Override
public int getMajorVersion() {
return 1;
}
/**
* Returns 0 for the minor version of the wrapped driver.
*
* @return Minor version of the wrapped driver
*/
@Override
public int getMinorVersion() {
return 0;
}
/**
* Returns true for the wrapped driver. Only to support jdbc compliant drivers.
*
* @return <code>true</code>
*/
@Override
public boolean jdbcCompliant() {
return true;
}
/**
* @return the parent Logger for the wrapped driver. Not implemented.
* @throws SQLFeatureNotSupportedException if the driver does not use
* {@code java.util.logging}.
*/
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException("getParentLogger is not yet implemented for " + this.getClass().getName());
}
}
Contents of my java.sql.Driver file:
com.trial.SampleDriver
But the Driver Manager does not load the driver class automatically. I get the
java.sql.SQLException: No suitable driver found for sample:jdbc:mysql://localhost:3306/test
On debugging I found that the DriverManager.getDrivers() return empty list. Even mysql driver is not registered automatically. I use the following mysql version:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
Is it required to have some mapping between the URL and the Driver? I thought implementing the acceptsURL should resolve that matching.
I use Jdk-11.
EDIT:
The calling code:
GenericObjectPool gPool = new GenericObjectPool();
...
ConnectionFactory cf = new DriverManagerConnectionFactory(uri, props);
PoolableConnectionFactory pcf = new PoolableConnectionFactory(cf, gPool, null, null, false, true);
Connection conn = (Connection) gPool.borrowObject();
Connection conn = (Connection) gPool.borrowObject(); --> This causes the excecption. The exception stack trace shows that DriverManager.getConnection() call causes the exception.