views:

54

answers:

1

I am writing a driver to act as a wrapper around two separate MySQL connections (to distributed databases). Basically, the goal is to enable interaction with my driver for all applications instead of requiring the application to sort out which database holds the desired data.

Most of the code for this is in place, but I'm having a problem in that when I attempt to create connections via the MySQL Driver, the DriverManager is returning an instance of my driver instead of the MySQL Driver. I'd appreciate any tips on what could be causing this and what could be done to fix it!

Below is a few relevant snippets of code. I can provide more, but there's a lot, so I'd need to know what else you want to see.

First, from MyDriver.java:

public MyDriver() throws SQLException
{
    DriverManager.registerDriver(this);
}

public Connection connect(String url, Properties info)
    throws SQLException
{
    try { return new MyConnection(info); }
    catch (Exception e) { return null; }
}

public boolean acceptsURL(String url) 
    throws SQLException
{
    if (url.contains("jdbc:jgb://"))
    { return true; }
    return false;
}

It is my understanding that this acceptsURL function will dictate whether or not the DriverManager deems my driver a suitable fit for a given URL. Hence it should only be passing connections from my driver if the URL contains "jdbc:jgb://" right?

Here's code from MyConnection.java:

Connection c1 = null;
Connection c2 = null;

/** 
 *Constructors 
 */ 
public DDBSConnection (Properties info) 
    throws SQLException, Exception
{
    info.list(System.out); //included for testing
    Class.forName("com.mysql.jdbc.Driver").newInstance();
    String url1 = "jdbc:mysql://server1.com/jgb";
    String url2 = "jdbc:mysql://server2.com/jgb";
    this.c1 = DriverManager.getConnection(
            url1, info.getProperty("username"), info.getProperty("password"));
    this.c2 = DriverManager.getConnection(
            url2, info.getProperty("username"), info.getProperty("password"));
}

And this tells me two things. First, the info.list() call confirms that the correct user and password are being sent. Second, because we enter an infinite loop, we see that the DriverManager is providing new instances of my connection as matches for the mysql URLs instead of the desired mysql driver/connection.

FWIW, I have separately tested implementations that go straight to the mysql driver using this exact syntax (al beit only one at a time), and was able to successfully interact with each database individually from a test application outside of my driver.

+3  A: 

IMO, the main problem with this code is that it uses DriverManager. Avoid statics and stick with instances.

The specific problem is the DriverManager.getConnection goes directly to attempting to connect rather than the acceptsURL that getDriver does. Therefore, you connect implementation should do the same check as the acceptsURL implementation (it may even be stricter and possibly fail at runtime).

As a relatively minor point, the acceptsURL implementation is a little odd.

if (url.contains("jdbc:jgb://"))
{ return true; }
return false;

contains should be startsWith. The little dance with if and returns does not help clarity. It can be written as:

return url.startsWith("jdbc:jgb://");
Tom Hawtin - tackline
Correct, the `connect()` method should first test `acceptsURL()`. Also see [API doc](http://java.sun.com/javase/6/docs/api/java/sql/Driver.html#connect%28java.lang.String,%20java.util.Properties%29): *The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL.*
BalusC
Thanks for the suggestion about rewriting acceptsURL. I did this and added a call to DriverManager.getDriver(url); before each of the calls to getConnection. No change from that.I'm basing the code I'm using to call the mysql drivers on 3.1.2 of http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/drivermanager.htmlI'm open to switching it if there's a better suggestion.
JGB146
Ah! The return null was the part I was missing! I've done that, and indeed it is no longer entering the infinite loop. However, DriverManager is failing to find a driver capable of handling the mysql connection.Do I need to require users to include Class.forName("com.mysql.jdbc.Driver").newInstance(); before calling my driver?
JGB146
Yes, or let your driver do it if it is supposedly tight-coupled with MySQL.
BalusC
As above, I was already making that call before trying to connect to the mysql servers. I tried moving the call to my test-client and I'm still getting an error stating that it can't find a compatible driver for the jdbc:mysql connections
JGB146
Your driver could load the MySQL driver (or just construct it directly, which isn't really significantly more coupled the `forName`ing from a literal string.
Tom Hawtin - tackline
Do you mean via DriverManager.registerDriver() ? I'm trying that currently and it is unable to find the com.mysql.jdbc package (despite it being loaded into the library [developing in NetBeans] and being able to use that driver when I call it instead of mine.
JGB146
Cancel that; changed my syntax to DriverManager.registerDriver(new com.mysql.jdbc.Driver()); and it found the package. But tests are still coming up unable to find a suitable driver when I reach the calls to get the mysql connections
JGB146
Thanks for the help! I found the remaining problem and everything appears to be working now! I'd made a typo in the getConnection calls - it's info.getProperty("user")
JGB146