Monday, February 3, 2014

Jetty JDBC: PostgreSQL and JNDI

I thought switching to one-off connections in Jetty to a JDBC pool would be easy. Not so fast. Following examples given in the documentation, I first attempted a basic configuration with a resource-ref in my WEB-INF/web.xml and a Resource in the jetty-env.xml for my postgres DB.  When attempting to lookup the stored JNDI variable, I would get this inexplicable error:
java.lang.ClassCastException: java.lang.String cannot be cast to javax.sql.DataSource
After searching Google fruitlessly and even asking in #jetty on FreeNode I was no closer to an answer.  Days later, however, I opened the link to the DTD at the top of jetty-env.xml which contained this gem:
An Arg element can contain value text and/or value elements such as Call,
New, SystemProperty, etc. If no value type is specified, then white
space is trimmed out of the value. If it contains multiple value
elements they are added as strings before being converted to any
specified type.
I immediately knew why the examples weren't working, which to be fair may be caused by a recent Fedora or OpenJDK change in XML parsing.  The jetty-env.xml Resource was pretty-printed and contained whitespace between the Arg and New elements like so:
<Configure id="wac" class="org.eclipse.jetty.webapp.WebAppContext">
    <New id="TestDS" class="org.eclipse.jetty.plus.jndi.Resource">
        <Arg><Ref refid="wac"/></Arg>
        <Arg>jdbc/TestDS</Arg>
        <Arg>
            <New class="org.postgresql.ds.PGConnectionPoolDataSource">
                <Set name="User">user</Set>
                <Set name="Password">pass</Set>
                <Set name="DatabaseName">postgres</Set>
                <Set name="ServerName">localhost</Set>
                <Set name="PortNumber">5432</Set>
            </New>
        </Arg>
    </New>
</Configure>
Simply removing the whitespace resolved the ClassCastException:
<Configure id="wac" class="org.eclipse.jetty.webapp.WebAppContext">
    <New id="TestDS" class="org.eclipse.jetty.plus.jndi.Resource">
        <Arg><Ref refid="wac"/></Arg>
        <Arg>jdbc/TestDS</Arg>
        <Arg><New class="org.postgresql.ds.PGConnectionPoolDataSource">
            <Set name="User">user</Set>
            <Set name="Password">pass</Set>
            <Set name="DatabaseName">postgres</Set>
            <Set name="ServerName">localhost</Set>
            <Set name="PortNumber">5432</Set>
        </New></Arg>
    </New>
</Configure>
This still did not result in a working connection pool, however.  I think it was another ClassCastException between ConnectionPoolDataSource and DataSource.  After some research, I decided to use the new HikariCP JDBC connection pool library, resulting in this jetty-env.xml:
<Configure id="wac" class="org.eclipse.jetty.webapp.WebAppContext">
    <New id="FormsDS" class="org.eclipse.jetty.plus.jndi.Resource">
        <Arg><Ref refid="wac"/></Arg>
        <Arg>jdbc/FormsDS</Arg>
        <Arg><New class="com.zaxxer.hikari.HikariDataSource">
            <Arg><New class="com.zaxxer.hikari.HikariConfig">
                <Set name="dataSourceClassName">org.postgresql.ds.PGSimpleDataSource</Set>
                <Call name="addDataSourceProperty">
                    <Arg>User</Arg>
                    <Arg>user</Arg>
                </Call>
                <Call name="addDataSourceProperty">
                    <Arg>Password</Arg>
                    <Arg>pass</Arg>
                </Call>
                <Call name="addDataSourceProperty">
                    <Arg>DatabaseName</Arg>
                    <Arg>postgres</Arg>
                </Call>
                <Call name="addDataSourceProperty">
                    <Arg>ServerName</Arg>
                    <Arg>localhost</Arg>
                </Call>
                <Call name="addDataSourceProperty">
                    <Arg>PortNumber</Arg>
                    <Arg>5432</Arg>
                </Call>
            </New></Arg>
        </New></Arg>
    </New>
</Configure>
Success at last.