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