Raven
Raven

Reputation: 1331

Why does the Informix JDBC driver handle unrelated connection strings?

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:

Thanks for all the feedback!

Upvotes: 3

Views: 1964

Answers (3)

Antonio
Antonio

Reputation: 11

The problem is fixed in the latest Informix JDBC Driver Version (JDBC.4.10.JC8DE).

Upvotes: 1

Andy Guibert
Andy Guibert

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

Mark Rotteveel
Mark Rotteveel

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

Related Questions