jTDS SQL driver in Glassfish

jtds in glassfish v3

cd /opt/servers/glassfish-3.1.1/glassfish/domains/domain1/lib
ln -s /opt/tools/maven-repo/net/sourceforge/jtds/jtds/1.2.5/jtds-1.2.5.jar

let’s setup the connection pool in the admin console. Ping works, seems to work?

[#|2011-10-01T20:27:09.078+0200|SEVERE|glassfish3.1.1|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=17;_ThreadName=Thread-3;|WebModule[/RTSuiteWeb]StandardWrapper.Throwable
java.lang.AbstractMethodError
	at com.sun.gjc.spi.jdbc40.ConnectionHolder40.init(ConnectionHolder40.java:96)
	at com.sun.gjc.spi.jdbc40.ConnectionHolder40.<init>(ConnectionHolder40.java:87)
	at com.sun.gjc.spi.jdbc40.ConnectionWrapper40.<init>(ConnectionWrapper40.java:64)
	at com.sun.gjc.spi.jdbc40.Jdbc40ObjectsFactory.getConnection(Jdbc40ObjectsFactory.java:90)
	at com.sun.gjc.spi.ManagedConnection.getConnection(ManagedConnection.java:462)
	at com.sun.enterprise.resource.allocator.LocalTxConnectorAllocator.fillInResourceObjects(LocalTxConnectorAllocator.java:119)
	at com.sun.enterprise.resource.pool.ConnectionPool.getResource(ConnectionPool.java:488)
	at com.sun.enterprise.resource.pool.PoolManagerImpl.getResourceFromPool(PoolManagerImpl.java:242)
	at com.sun.enterprise.resource.pool.PoolManagerImpl.getResource(PoolManagerImpl.java:167)
	at com.sun.enterprise.connectors.ConnectionManagerImpl.getResource(ConnectionManagerImpl.java:338)
	at com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:301)
	at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:236)
	at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:165)
	at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:160)
	at com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:113)

Bazinga!

Let’s see some source code:

protected void init() {
    try {
        defaultClientInfo = getClientInfo();
    } catch (SQLException e) {
        _logger.log(Level.INFO, "jdbc.unable_to_get_client_info", e.getMessage());
        if(_logger.isLoggable(Level.FINEST)) {
            _logger.log(Level.FINEST, "jdbc.unable_to_get_client_info", e);
        }
    }
}

com.sun.gjc.spi.jdbc40.ConnectionHolder40.java

    public Properties getClientInfo() throws SQLException {
        checkValidity();
        return con.getClientInfo();
    }

com.sun.gjc.spi.base.ConnectionHolder.java

protected void checkValidity() throws SQLException {
    if (isClosed) throw new SQLException("Connection closed");
    if (!valid) throw new SQLException("Invalid Connection");
    if (active == false) {
        mc.checkIfActive(this);
    }
}

Ok, so it’s the jdbc driver. Glassfish fails with an ConnectionJDBC3 object So.. let’s check the code

net.sourceforge.jtds.jdbcx.proxy.ConnectionProxy.java

public Properties getClientInfo() throws SQLException {
    // TODO Auto-generated method stub
    throw new AbstractMethodError();
}

Ant this indeed throws AbstractMethodError 😐

There is no newer version than 1.2.5. The thing is that jtds used to be compatible with jdk 1.3 and 1.4. JDK 5 brought JDBC 4.0, which brought many new methods in the java.sql.Connection. JTDS doesn’t claim to be implementing these, just wants to be compilable against jdk5/6 – hence AbstractMethodErrors. Some history about this

JDBC 4.0 API is supposed to be backwards compatible, there is no problem with using Java SE 6 with JDBC 3.0 drivers, as long as you do not use the new methods or classes there were introduced in JDBC 4.0 API. That’s the theory at least, since if you want to compile against newer api, you have to include some implementation of new methods.

How exactly does it happen that Glassfish decides a driver is JDBC 3.0 or 4.0 (or 4.1, introduced by JDK 7)? That’s pretty simple:

public void detectJDBC30Connection(Connection con, com.sun.gjc.spi.ManagedConnection mcObject) {

    String dataSourceProperty = mcObject.getManagedConnectionFactory().getJdbc30DataSource();
    if (dataSourceProperty != null) {
        setJdbc30Connection(Boolean.valueOf(dataSourceProperty));
        initJDBC30Connection = true;
    } else {
        try {
            Class paramClasses[] = new Class[]{Class.class};

            Method isWrapperMethod = con.getClass().getMethod("isWrapperFor", paramClasses);
            int modifiers = isWrapperMethod.getModifiers();
            setJdbc30Connection(Modifier.isAbstract(modifiers));
        } catch (NoSuchMethodException e) {
            setJdbc30Connection(true);
        } catch (AbstractMethodError e) {
            setJdbc30Connection(true);
        } catch (Throwable t) {
            setJdbc30Connection(true);
            _logger.log(Level.WARNING, "jdbc.unexpected_exception_on_detecting_jdbc_version", t);
        } finally {
            initJDBC30Connection = true;
        }
    }
}

And by mixture of the above, we have a problem, since jTDS claims to have a method which doesn’t work.
Solutions? There are 3:

  • get a proper JTDS version or something that will be detected as a proper JDBC3 driver. Latest version still using jdk4 was 1.2.2. And it works perfectly.
  • Use a different driver. I don’t have much experience with Microsoft SQL and JDBC yet, but there is a quite new driver from Microsoft itself, probably worth giving a spin.
  • Complex solution: fix JTDS driver.

Author: ags

bio

1 thought on “jTDS SQL driver in Glassfish”

  1. Thanks for your tips,

    Looks like creating the property jdbc30DataSource = true in Data Source additional properties also works!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.