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 <robbiexgib...@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.ander...@oracle.com



Reply via email to