AbstractMethodError with jTDS JDBC Driver on Tomcat 8

14.3k views Asked by At

I am deploying a web app (WAR) to a Tomcat 8 web container.

The WAR includes in the '/WEB-INF/lib' directory the following jTDS JDBC driver:

<dependency org="net.sourceforge.jtds" name="jtds" rev="1.3.1" />

(file is: jtds-1.3.1.jar).

This is how the resource is defined in META-INF/context.xml:

<Resource name="jdbc/jtds/sybase/somedb"
          auth="Container"
          type="javax.sql.DataSource"
          driverClassName="net.sourceforge.jtds.jdbc.Driver"
          url="jdbc:jtds:sybase://localhost:2501/somedb"
          username="someuser" password="somepassword"
/>

In my code I obtain the javax.sql.DataSource the normal way:

InitialContext cxt = new InitialContext();
if ( cxt == null ) {
    throw new RuntimeException("Uh oh -- no context!");
}
DataSource ds = (DataSource) cxt.lookup( lookupName );

I further verify (by printing) that the DataSource object ds is of the expected type:

org.apache.tomcat.dbcp.dbcp2.BasicDataSource

… but when I try to get a connection out of it:

Connection conn = ds.getConnection();

… I get the following trace:

java.lang.AbstractMethodError
net.sourceforge.jtds.jdbc.JtdsConnection.isValid(JtdsConnection.java:2833)
org.apache.tomcat.dbcp.dbcp2.DelegatingConnection.isValid(DelegatingConnection.java:924)
org.apache.tomcat.dbcp.dbcp2.PoolableConnection.validate(PoolableConnection.java:282)
org.apache.tomcat.dbcp.dbcp2.PoolableConnectionFactory.validateConnection(PoolableConnectionFactory.java:359)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.validateConnectionFactory(BasicDataSource.java:2316)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:2299)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.createDataSource(BasicDataSource.java:2043)
org.apache.tomcat.dbcp.dbcp2.BasicDataSource.getConnection(BasicDataSource.java:1543)

What gives?

3

There are 3 answers

1
Marcus Junius Brutus On BEST ANSWER

Turns out I had to add:

validationQuery="select 1"

in the Resource declaration in context.xml.

This is mentioned here (although mispelled as validateQuery).

Digging into the implementation of JtdsConnection one sees:

/* (non-Javadoc)
 * @see java.sql.Connection#isValid(int)
 */
public boolean isValid(int timeout) throws SQLException {
    // TODO Auto-generated method stub
    throw new AbstractMethodError();
}

This is really weird, I think AbstractMethodError is supposedly thrown by the compiler only, unimplemented methods ought to throw UnsupportedOperationException. At any rate, the following code from PoolableConnection shows why the presence or not of validationQuery in context.xml can change things. Your validationQuery is passed as the value of the sql String parameter in the below method (or null if you don't define a validationQuery):

public void More ...validate(String sql, int timeout) throws SQLException {
    ...
    if (sql == null || sql.length() == 0) {
        ...
        if (!isValid(timeout)) {
            throw new SQLException("isValid() returned false");
        }
        return;
    }
...
}

So basically if no validationQuery is present, then the connection's own implementation of isValid is consulted which in the case of JtdsConnection weirdly throws AbstractMethodError.

0
Gobinath Mani On

The above answer works. If you are setting it up for standalone Java application, set the validation query in the datasource.

 BasicDataSource ds = new BasicDataSource(); 

 ds.setUsername(user);
 ds.setPassword(getPassword());
 ds.setUrl(jdbcUrl);
 ds.setDriverClassName(driver);
 ds.setMaxTotal(10);
 ds.setValidationQuery("select 1"); //DBCP throws error without this query
0
Stephen On

The answer mentioned above by Marcus worked for me when I encountered this problem. To give a specific example of how the validationQuery setting looks in the context.xml file:

    <Resource name="jdbc/myDB" auth="Container" type="javax.sql.DataSource"
            driverClassName="net.sourceforge.jtds.jdbc.Driver"
            url="jdbc:jtds:sqlserver://SQLSERVER01:1433/mydbname;instance=MYDBINSTANCE"
            username="dbuserid" password="dbpassword"
            validationQuery="select 1"
            />

The validationQuery setting goes in with each driver setting for your db connections. So each time you add another db entry to your context.xml file, you will need to include this setting with the driver settings.