为什么Informix JDBC驱动程序处理不相关的连接字符串?

时间:2016-01-15 19:21:20

标签: jdbc informix

当我的类路径中存在Informix JDBC驱动程序时,它似乎在适当的驱动程序获得机会之前拦截并拒绝所有连接字符串。

例如,像jdbc:ghmghmghm这样完全无意义的连接字符串将导致以下堆栈跟踪:

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)

我的理解是,表现良好的JDBC驱动程序将自己限制为以其魔术前缀开头的连接字符串。 Informix驱动程序是否已损坏,或者我是否有不合理的期望?

更新

如果我只删除Informix驱动程序,但保留所有其他驱动程序,则异常翻转到更加理智

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)

此外,如果Informix驱动程序被删除,则会有特定的有效连接字符串jdbc:sybase:Tds:leeta:5001/leeta_ase1,但如果存在Informix,则会失败(Informix无效的子协议堆栈跟踪)。

我的结论是Informix并没有正确地拒绝完全不匹配的连接字符串,并且Informix在Sybase连接字符串上获得了第一次破解(但并非我尝试过的所有其他连接字符串类型......)

我的Linux JDK是

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)

但是我发现错误的连接字符串也导致Windows上的官方Java 8上的Infx跟踪。从未见过Sybase在Windows上被截获,但也许这是一个类路径排序问题。

更新2

我无法重申我的说法,即完全有效的连接字符串被Informix拦截和拒绝。我一定在尝试巧妙的坏字符串,看到Informix堆栈,在响应中删除Informix驱动程序,然后从正确的驱动程序中考虑堆栈是一个胜利(因为它允许快速修复连接字符串)。

我看到了一些改进方法:

  • 每当知道驱动程序类时停止使用DriverManager(直接实例化驱动程序并在其上调用getConnection()
  • 编写DriverManager.getConnection()的替代品,至少通过Throwable.addSuppressed()
  • 报告所有拒绝堆栈
  • 使用Java类路径尝试使Informix(以及其他不良演员)稍后出现在驱动程序列表中(@jonathan-leffler
  • 使用DriverManager.(de)registerDriver()将错误的actor(静态列表或在运行时动态测试)移动到驱动程序列表的末尾

感谢所有反馈!

3 个答案:

答案 0 :(得分:4)

这听起来像Informix驱动程序中的一个错误(但最好是一个小错误)。一个表现良好的JDBC驱动程序需要遵循java.sql.Driver.connect(String url, Properties properties)(强调我的)中定义的期望:

  

尝试建立与给定URL的数据库连接。司机   如果它意识到它是错误的驱动程序,则应该返回“null”   将连接到给定的网址。这将是常见的,就像JDBC驱动程序一样   管理员被要求连接到给定的URL,它将URL传递给每个URL   依次装载司机。

     

如果驱动程序是正确的驱动程序,则应该抛出SQLException   将连接到给定的URL,但无法连接到数据库。

java.sql.DriverManager.getConnection将逐个查询所有已注册驱动程序的connect方法。如果驱动程序返回null,它将继续使用下一个驱动程序。如果驱动程序返回连接,则该连接将返回给调用者。如果所有驱动程序都返回null,则会在“找不到[url]”消息的合适驱动程序时抛出SQLException

如果驱动程序抛出SQLException,则保留最后抛出的异常,并且驱动程序管理器将继续使用下一个驱动程序。如果所有其他驱动程序拒绝与null的连接尝试,则将抛出最后一个异常而不是“No suitable driver ...”异常。据我所知,在较旧的Java版本中,它实际上会停止尝试其他驱动程序。但是DriverManager的代码(至少从Oracle / Sun Java 5开始)可以防止这种情况发生,并防止行为不当的驱动程序独占JDBC,并允许尝试使用同一数据库(和协议)的多个驱动程序。

因此,除非你的Java版本有一个不同的DriverManager实现(在第一个例外停止),它应该继续使用其他注册的驱动程序,如果其中任何一个接受了URL,那么应该没有问题

答案 1 :(得分:1)

最新的Informix JDBC驱动程序版本(JDBC.4.10.JC8DE)修复了该问题。

答案 2 :(得分:0)

根据DriverManager doc:

  

作为初始化的一部分,DriverManager类将尝试加载“jdbc.drivers”系统属性中引用的驱动程序类。这允许用户自定义其应用程序使用的JDBC驱动程序。例如,在〜/ .hotjava / properties文件中,您可以指定:

     

jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver

所以,假设您有3个JDBC驱动程序,这样您的jdbc.drivers属性如下所示:

jdbc.drivers=com.DriverA:com.DriverB:com.DriverC

如果您致电DriverManager.getConnection("jdbc:driverA:blahblah");,则DriverManager不知道要使用jdbc.drivers属性中的哪个驱动程序,因此必须遍历所有驱动程序。

DriverManager.getConnection()可能与此类似:

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);
}

<强>更新 (代码示例已更新)

OpenJDK似乎可以容忍驱动程序为其无法识别的协议返回null的情况,并且它还容忍驱动程序为其无法识别的协议抛出异常的情况。

我认为OpenJDK永远不会抛出直接来自Driver的异常,并且如果没有找到该URL的驱动程序,则总是抛出新的SQLException。