Reputation: 1331
When the Informix JDBC driver is present in my classpath, it seems to intercept and reject all connection strings before the appropriate driver gets the chance.
For example, a completely nonsense connection string like jdbc:ghmghmghm
will result in the following stack trace:
java.sql.SQLException: Invalid sub-protocol Invalid sub-protocol: 'ghmghmghm'
at com.informix.util.IfxErrMsg.getLocSQLException(IfxErrMsg.java:493)
at com.informix.jdbc.IfxDriver.checkURL(IfxDriver.java:560)
at com.informix.jdbc.IfxDriver.connect(IfxDriver.java:208)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:208)
My understanding is that well behaved JDBC drivers restrict themselves to connection strings that start with their magic prefix. Is the Informix driver broken, or do I have unreasonable expectations?
Update
If I delete just the Informix driver, but leave all other drivers in place, the exception flips to the much more sane
java.sql.SQLException: No suitable driver found for jdbc:ghmghmghm
at java.sql.DriverManager.getConnection(DriverManager.java:596)
at java.sql.DriverManager.getConnection(DriverManager.java:187)
Furthermore, there is specific valid connection string jdbc:sybase:Tds:leeta:5001/leeta_ase1
that will work if the Informix driver is deleted, but will fail (Informix invalid sub-protocol stack trace) if Informix is present.
My conclusion is that Informix is not rejecting completely non-matching connection strings correctly, and that Informix is getting first crack at Sybase connection strings (but not every other connection string type I've tried...)
My Linux JDK is
java version "1.7.0_91"
OpenJDK Runtime Environment (IcedTea 2.6.3) (7u91-2.6.3-0ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.91-b01, mixed mode)
but I've seen bad connection strings result in an Infx trace on the official Java 8 on Windows as well. Have never seen Sybase intercepted on Windows yet, but perhaps that is a classpath ordering issue.
Update 2
I can't repro my claim that completely valid connection strings were being intercepted and rejected by Informix. I must have been trying subtly bad strings, seeing the Informix stack, deleting the Informix driver in response, then considering the stack from the correct driver to be a win (as it allowed quick repair of the connection string).
I see a few ways to improve things:
DriverManager
whenever the driver class is known (instantiate the driver directly and call getConnection()
on it)DriverManager.getConnection()
that at least reports all the rejection stacks via Throwable.addSuppressed()
DriverManager.(de)registerDriver()
to move bad actors (either a static list or dynamically tested at runtime) to the end of the driver listThanks for all the feedback!
Upvotes: 3
Views: 1964
Reputation: 11
The problem is fixed in the latest Informix JDBC Driver Version (JDBC.4.10.JC8DE).
Upvotes: 1
Reputation: 42926
According to the DriverManager
doc:
As part of its initialization, the DriverManager class will attempt to load the driver classes referenced in the "jdbc.drivers" system property. This allows a user to customize the JDBC Drivers used by their applications. For example in your ~/.hotjava/properties file you might specify:
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver
So, say you have 3 JDBC Drivers, such that your jdbc.drivers
property looks like this:
jdbc.drivers=com.DriverA:com.DriverB:com.DriverC
If you call DriverManager.getConnection("jdbc:driverA:blahblah");
the DriverManager doesn't know which driver in the jdbc.drivers
property to use, so it has to iterate over all of them.
DriverManager.getConnection() probably does something similar to this:
public Connection getConnection(String url) {
Set<Driver> drivers = // drivers in 'jdbc.drivers' prop
SQLException failure = null;
for(Driver driver : drivers) {
try {
Connection conn = driver.connect(url);
if(conn != null)
return conn;
} catch (SQLException sqle) {
// potentially not trying to connect to the right driver
if(failure == null)
failure = sqle;
}
}
// If we get here, no drivers could connect
if(failure != null)
throw failure;
else // no connection obtained, but no drivers complained
throw new SQLException("No driver found for URL " + url);
}
UPDATE: (Code sample has been updated)
It appears that OpenJDK tolerates the scenario where a Driver returns null for a protocol that it doesn't recognize, and it also tolerates the scenario where a Driver throws an Exception for a protocol that it doesn't recognize.
I'd argue that OpenJDK should never throw an exception that came directly from a Driver, and always throw a new SQLException if no driver was found for the URL.
Upvotes: 0
Reputation: 109001
It sounds like a bug in the Informix driver (but a minor one at best). A well-behaved JDBC driver is required to follow the expectations defined in java.sql.Driver.connect(String url, Properties properties)
(emphasis mine):
Attempts to make a database connection to the given URL. The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. This will be common, as when the JDBC driver manager is asked to connect to a given URL it passes the URL to each loaded driver in turn.
The driver should throw an SQLException if it is the right driver to connect to the given URL but has trouble connecting to the database.
The java.sql.DriverManager.getConnection
will query the connect
method of all registered drivers one by one. If the driver returns null
it will continue with the next driver. If a driver returns a connection, that connection is returned to the caller. If all drivers return null
, then an SQLException
is thrown with the "No suitable driver found for [url]" message.
If a driver throws SQLException
, the last thrown exception is kept, and driver manager will continue with the next driver. If all other drivers reject the connection attempt with null
, then the last exception will be thrown instead of the "No suitable driver..." exception. As far as I know in older Java versions it would actual stop trying other drivers. But the code of DriverManager
(at least since Oracle/Sun Java 5) protects against this, and prevents misbehaving drivers from monopolizing JDBC, and allows multiple drivers for the same database (and protocol) to be tried.
So, unless your Java version has a different DriverManager
implementation (one that stops at the first exception), it should continue with the other registered drivers and if any of them accept the URL then there should be no problem.
Upvotes: 4