I looked for a similar question, but only found similar ones such as Godaddy JNDI Problem---Cannot create JDBC driver of class '' for connect URL 'null' which doesn't answer my generic question.
Tomcat 7.0.8 .. the following code
Context initialContext = new InitialContext();
datasource = (DataSource) initialContext.lookup("java:comp/env/" + "开发者_如何学JAVA;blah");
Connection c = null;
c = datasource.getConnection();
throws this error.
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1452)
at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)
at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
...
Caused by: java.lang.NullPointerException
at sun.jdbc.odbc.JdbcOdbcDriver.getProtocol(JdbcOdbcDriver.java:527)
at sun.jdbc.odbc.JdbcOdbcDriver.knownURL(JdbcOdbcDriver.java:496)
at sun.jdbc.odbc.JdbcOdbcDriver.acceptsURL(JdbcOdbcDriver.java:319)
at java.sql.DriverManager.getDriver(DriverManager.java:386)
at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1437)
... 24 more
I'm not asking why it failed or how to fix it.
My question is, why didn't the LOOKUP fail?
Why did lookup create a useless BasicDataSource with a null URL? Is there a better way to detect if a JNDI name 'doesn't exist' than to try using it and see if it blows up? That approach reminds me of how they test bridges.
I finally bit the bullet and moved a bunch of code from raw JDBC code to JNDI, and many mysteries still remain.
I'd like to avoid downcasting the BasicDataSource if possible, to at least preserve an illusion of database independence.
TIA
As I understand JNDI, it is meant for keeping references to objects or object factories.
Object business is straightforward - configurer provides a reference to the object, app uses it, as simple as that. But for more complex or generic cases it's more convenient to provide a reference to an object factory which gives far more freedom on how objects should be created.
As you haven't posted your webapp's web.xml
nor Tomcat's conf/server.xml
, it's hard to elaborate more, but I would guess you have something similar in your web.xml
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/myDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
If you read Tomcat's JNDI howto, you should see this:
Providing that Tomcat is able to identify an appropriate resource factory to use to create the resource and that no further configuration information is required, Tomcat will use the information in /WEB-INF/web.xml to create the resource.
Now, let's look at Tomcat's implementation of the above. On line 112, you can see that if given res-type
equals to javax.sql.DataSource
, it uses org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory
to create a new data source.
That's it. At this point the job of JNDI is over as it has no influence on how objects are created - and rightfully so! Don't forget that JNDI is meant to be storing references to any kind of objects (mail connections, security realms, etc.). It would be both impossible and unreasonable to write a test for every different type of object. And even so, just by having database settings it would be impossible to determine if these are enough to establish a live connection to the real database.
That's why you have a separate call, datasource.getConnection()
, and that's why it throws SQLException
which you are supposed to catch and handle.
p.s. you could possibly argue that BasicDataSourceFactory
should be more smart and have a list of mandatory properties/arguments. Maybe. But this is the subject to another question, as originally you have asked why didn't the LOOKUP fail ;)
When running a war file on tomcat, the web.xml and server.xml will work together to give you the definitions as described here. Sometimes, in Eclipse, you may be using a different web.xml than you think. Make sure the debugger in Eclipse is loading the web and server.xml files that you expect. I found in my case that the web.xml didnt have the definition, so added it there and this worked.
精彩评论