Search code examples
javajdbcjettyjndiembedded-jetty

JNDI lookup failing with Jetty for JDBC connection pooling with MySQL?


I'm using Jetty 9.2 (embedded) with the standard MySQL connector API and I'm very confused by how this is supposed to be setup. Currently, I have this in my web.xml file:

<webapp ...

    <resource-ref>
        <description>JDBC Data Source</description>
        <res-ref-name>jdbc/DataSource</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
</webapp>

...and this in my jetty-env.xml:

<Configure class="org.eclipse.jetty.webapp.WebAppContext">

   <New id="DatabaseConnector" class="org.eclipse.jetty.plus.jndi.Resource">
        <Arg></Arg>
        <Arg>jdbc/DataSource</Arg>
        <Arg>
            <New class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource">
                <Set name="Url">jdbc:mysql://localhost:3306/DBName</Set>
                <Set name="User">user</Set>
                <Set name="Password">pass</Set>
            </New>
        </Arg>
    </New>

 </Configure>

...and this code to initialize:

Context envCtx = (Context) new InitialContext().lookup("java:comp/env");
DataSource datasource = (DataSource) envCtx.lookup("jdbc/DataSource");

When I try to fire up the server, I get the error javax.naming.NameNotFoundException; remaining name 'jdbc/DataSource'. I've tried lots of different variations of the strings in the code initialization, like removing the lookup call on the InitialContext object, but I just keep getting variations of the same error with a different name value;

Both of the xml files are located in my /WAR/WEB-INF directory. I've looked at loads of previous questions and tutorials, blogs etc. but I'm getting nowhere.


Solution

  • It was a combination of problems specific to embedded Jetty.

    First, my launcher code that was configuring and launching the web server was doing the JNDI lookup before I actually started the web server i.e. before calling server.start(), so the JNDI configuration was not initialized at that stage.

    But even making this change didn't work, because the envCtx.lookup("jdbc/DataSource") needs to be called from a thread that's associated with the WebApp. So I moved that code to a static block that gets called the first time a database connection is requested by a web server request.

    In the end, I ended up with something like this for my launcher code:

    public static void main(String[] args) {
        Server server = new Server();
    
        //Enable parsing of jndi-related parts of web.xml and jetty-env.xml
        ClassList classlist = ClassList.setServerDefault(server);
        classlist.addAfter(
                "org.eclipse.jetty.webapp.FragmentConfiguration", 
                "org.eclipse.jetty.plus.webapp.EnvConfiguration", 
                "org.eclipse.jetty.plus.webapp.PlusConfiguration");
    ...
    ...
    server.start();
    

    The JNDI lookup cannot be made by this main thread, so put it somewhere like the init method of a servlet request, or like I did, a synchronized method of a static database accessor class that gets used by servlets e.g.

    public class DatabaseUtils {
    
        private static DataSource datasource;
    
        private static synchronized Connection getDBConnection() throws SQLException {
            if (datasource == null) {
                initDataSource();
            }
            return datasource.getConnection();
        }
    
        public static void initDataSource() {
            try {
                 datasource = (DataSource) new InitialContext().lookup("java:comp/env/jdbc/DataSource");
                 LOG.info("Database connection pool initalized successfully");
            } catch (Exception e) {
                LOG.error("Error while initialising the database connection pool", e);
            }
        }