JDK-8060068/JDK-8067904 DriverManager clean-up introduces bootstrap problem that breaks production drivers

Lance Andersen lance.andersen at oracle.com
Tue Apr 14 21:12:26 UTC 2015


Hi Robert,

I had a chance to look at the code you provided and part of the problem is that MyDriver is not adhering to the JDBC specification as the call to registerDriver *must* be in the static block of the java.sql.Driver
implementation and *not* in the constructor.   The registerWithDriverManager() logic and the fact the call to this method is in the constructor is the primary culprit.

I will probably put back the static block at least in the short term but please work with the vendor to address their issue

Best,
Lance

On Apr 3, 2015, at 5:33 AM, Robert Gibson <robbiexgibson at yahoo.com> wrote:

> Hi there,
> 
> We are doing some early testing with JDK 9 and have discovered that the changes made to java.sql.DriverManager back in November/December have introduced an incompatibility with our JDBC driver (that we have used unchanged since Java 6) when it is pre-loaded with Class.forName (as recommended by Tomcat and specified as being harmless in the JavaDoc). We filed a bug report but it got an incident ID JI-9019539 and then disappeared into a black hole.
> 
> In the hope of the problem getting a bit more attention I attach a reproducible test case that is representative of the commercially-available JDBC driver that we use (hint: the name rhymes with MyFace and it's used in a lot of banks). I guess the problem is that the driver is calling DriverManager.getDrivers() as part of its registration process, which is now causing the stack overflow given at the end of this mail, after the code.
> 
> If you compile the attached files into test.jar and run
> java -cp test.jar JDBCTest n
> java -cp test.jar JDBCTest y
> then the test should print
> Trying to connect to jdbc:mydriver
> OK
> in both cases (as it does with Java 8).
> When running with Java 9, the second command prints
> Trying to connect to jdbc:mydriver
> FAIL
> No suitable driver found for jdbc:mydriver
> 
> Hope this is helpful.
> Regards,
> Robert
> 
> JDBCTest.java
> 
> import java.io.*;
> import java.sql.*;
> import java.util.*;
> 
> public class JDBCTest
> {
>    public static void main(String... argv) throws Exception
>    {
>        if (argv.length > 1) DriverManager.setLogWriter(new PrintWriter(System.out));
>        String[] urls = new String[] {"jdbc:mydriver"};
>        if (argv.length > 0 && argv[0].equals("y")) {
>            loadClass("MyDriver");
>        }
>        for (String url : urls) {
>            System.out.println("Trying to connect to " + url);
>                    try {
>                        Connection c = DriverManager.getConnection (url);
>                        if (c == null) System.out.println("FAIL");
>                        else System.out.println("OK");
>                    }catch(SQLException e) {
>                        System.out.println("FAIL");
>                        System.out.println(e.getMessage());
>                    }
>        }
>    }
> 
>    public static void loadClass(String classname) {
>        try { Class.forName(classname); } catch(ClassNotFoundException e) { System.out.println("Class not found: " + classname); }
>    }
> }
> 
> MyDriver.java
> 
> import java.sql.*;
> import java.util.Enumeration;
> import java.util.Properties;
> import java.util.logging.Logger;
> 
> public class MyDriver implements java.sql.Driver {
>    static {
>        new MyDriver();
>    }
>    public MyDriver() {
>        this.registerWithDriverManager();
>    }
>    public final Connection connect(String url, Properties props) throws SQLException {
>        return acceptsURL(url) ? new MyConnection() : null;
>    }
>    public boolean acceptsURL(String url) throws SQLException {
>        return url.startsWith("jdbc:mydriver");
>    }
>    public DriverPropertyInfo[] getPropertyInfo(String var1, Properties props) throws SQLException {
>        return null;
>    }
>    public int getMajorVersion() {
>        return 0;
>    }
>    public int getMinorVersion() {
>        return 1;
>    }
>    public boolean jdbcCompliant() {
>        return false;
>    }
>    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
>        throw new SQLFeatureNotSupportedException("No logging for you");
>    }
>    protected void registerWithDriverManager() {
>        try {
>            synchronized(DriverManager.class) {
>                DriverManager.registerDriver(this);
>                Enumeration e = DriverManager.getDrivers();
>                while(e.hasMoreElements()) {
>                    Driver d = (Driver)e.nextElement();
>                    if(d instanceof MyDriver && d != this) {
>                        DriverManager.deregisterDriver(d);
>                    }
>                }
>            }
>        } catch (SQLException ex) {
>        }
>    }
> }
> 
> MyConnection.java
> 
> import java.sql.*;
> import java.util.Map;
> import java.util.Properties;
> import java.util.concurrent.Executor;
> 
> public class MyConnection implements Connection {
>    @Override
>    public Statement createStatement() throws SQLException {
>        return null;
>    }
>    @Override
>    public PreparedStatement prepareStatement(String sql) throws SQLException {
>        return null;
>    }
>    @Override
>    public CallableStatement prepareCall(String sql) throws SQLException {
>        return null;
>    }
>    @Override
>    public String nativeSQL(String sql) throws SQLException {
>        return null;
>    }
>    @Override
>    public void setAutoCommit(boolean autoCommit) throws SQLException {
>    }
>    @Override
>    public boolean getAutoCommit() throws SQLException {
>        return false;
>    }
>    @Override
>    public void commit() throws SQLException {
>    }
>    @Override
>    public void rollback() throws SQLException {
>    }
>    @Override
>    public void close() throws SQLException {
>    }
>    @Override
>    public boolean isClosed() throws SQLException {
>        return false;
>    }
>    @Override
>    public DatabaseMetaData getMetaData() throws SQLException {
>        return null;
>    }
>    @Override
>    public void setReadOnly(boolean readOnly) throws SQLException {
>    }
>    @Override
>    public boolean isReadOnly() throws SQLException {
>        return false;
>    }
>    @Override
>    public void setCatalog(String catalog) throws SQLException {
>    }
>    @Override
>    public String getCatalog() throws SQLException {
>        return null;
>    }
>    @Override
>    public void setTransactionIsolation(int level) throws SQLException {
>    }
>    @Override
>    public int getTransactionIsolation() throws SQLException {
>        return 0;
>    }
>    @Override
>    public SQLWarning getWarnings() throws SQLException {
>        return null;
>    }
>    @Override
>    public void clearWarnings() throws SQLException {
>    }
>    @Override
>    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
>        return null;
>    }
>    @Override
>    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
>        return null;
>    }
>    @Override
>    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
>        return null;
>    }
>    @Override
>    public Map<String, Class<?>> getTypeMap() throws SQLException {
>        return null;
>    }
>    @Override
>    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
>    }
>    @Override
>    public void setHoldability(int holdability) throws SQLException {
>    }
>    @Override
>    public int getHoldability() throws SQLException {
>        return 0;
>    }
>    @Override
>    public Savepoint setSavepoint() throws SQLException {
>        return null;
>    }
>    @Override
>    public Savepoint setSavepoint(String name) throws SQLException {
>        return null;
>    }
>    @Override
>    public void rollback(Savepoint savepoint) throws SQLException {
>    }
>    @Override
>    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
>    }
>    @Override
>    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
>        return null;
>    }
>    @Override
>    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
>        return null;
>    }
>    @Override
>    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
>        return null;
>    }
>    @Override
>    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
>        return null;
>    }
>    @Override
>    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
>        return null;
>    }
>    @Override
>    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
>        return null;
>    }
>    @Override
>    public Clob createClob() throws SQLException {
>        return null;
>    }
>    @Override
>    public Blob createBlob() throws SQLException {
>        return null;
>    }
>    @Override
>    public NClob createNClob() throws SQLException {
>        return null;
>    }
>    @Override
>    public SQLXML createSQLXML() throws SQLException {
>        return null;
>    }
>    @Override
>    public boolean isValid(int timeout) throws SQLException {
>        return false;
>    }
>    @Override
>    public void setClientInfo(String name, String value) throws SQLClientInfoException {
>    }
>    @Override
>    public void setClientInfo(Properties properties) throws SQLClientInfoException {
>    }
>    @Override
>    public String getClientInfo(String name) throws SQLException {
>        return null;
>    }
>    @Override
>    public Properties getClientInfo() throws SQLException {
>        return null;
>    }
>    @Override
>    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
>        return null;
>    }
>    @Override
>    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
>        return null;
>    }
>    @Override
>    public void setSchema(String schema) throws SQLException {
>    }
>    @Override
>    public String getSchema() throws SQLException {
>        return null;
>    }
>    @Override
>    public void abort(Executor executor) throws SQLException {
>    }
>    @Override
>    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
>    }
>    @Override
>    public int getNetworkTimeout() throws SQLException {
>        return 0;
>    }
>    @Override
>    public <T> T unwrap(Class<T> iface) throws SQLException {
>        return null;
>    }
>    @Override
>    public boolean isWrapperFor(Class<?> iface) throws SQLException {
>        return false;
>    }
> }
> 
> Stack overflow (recursive call to new MyDriver())
> 
>  [1] MyDriver.registerWithDriverManager (MyDriver.java:47)
>  [2] MyDriver.<init> (MyDriver.java:12)
>  [3] sun.reflect.NativeConstructorAccessorImpl.newInstance0 (native method)
>  [4] sun.reflect.NativeConstructorAccessorImpl.newInstance (NativeConstructorAccessorImpl.java:62)
>  [5] sun.reflect.DelegatingConstructorAccessorImpl.newInstance (DelegatingConstructorAccessorImpl.java:45)
>  [6] java.lang.reflect.Constructor.newInstance (Constructor.java:425)
>  [7] java.lang.Class.newInstance (Class.java:464)
>  [8] java.util.ServiceLoader$LazyIterator.nextService (ServiceLoader.java:378)
>  [9] java.util.ServiceLoader$LazyIterator.next (ServiceLoader.java:402)
>  [10] java.util.ServiceLoader$1.next (ServiceLoader.java:478)
>  [11] java.sql.DriverManager$2.run (DriverManager.java:614)
>  [12] java.sql.DriverManager$2.run (DriverManager.java:594)
>  [13] java.security.AccessController.doPrivileged (native method)
>  [14] java.sql.DriverManager.ensureDriversInitialized (DriverManager.java:594)
>  [15] java.sql.DriverManager.getDrivers (DriverManager.java:437)
>  [16] MyDriver.registerWithDriverManager (MyDriver.java:47)
>  [17] MyDriver.<init> (MyDriver.java:12)
>  [18] MyDriver.<clinit> (MyDriver.java:8)
>  [19] java.lang.Class.forName0 (native method)
>  [20] java.lang.Class.forName (Class.java:286)
>  [21] JDBCTest.loadClass (JDBCTest.java:28)
>  [22] JDBCTest.main (JDBCTest.java:12)



Lance Andersen| Principal Member of Technical Staff | +1.781.442.2037
Oracle Java Engineering 
1 Network Drive 
Burlington, MA 01803
Lance.Andersen at oracle.com






More information about the core-libs-dev mailing list