tomcat7jnditomcat-valve

How to acces to a global JNDI resource outside of Context container in Apache Tomcat 7.x?


I am developing a custom Valve for Apache Tomcat 7, the valve is defined at Host container level in the Apache Tomcat server.xml configuration file.

<Engine defaultHost="localhost" name="Catalina">

    <Realm className="org.apache.catalina.realm.DataSourceRealm" dataSourceName="jdbc/qgw" 
    roleNameCol="role_name" userCredCol="user_pass" userNameCol="user_name" userRoleTable="user_roles" userTable="users"/>

    <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
        <Valve className="org.mycompany.valves.CustomValve"/>
        <Valve className="org.apache.catalina.authenticator.SingleSignOn"/>
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" 
        pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>
    </Host>

</Engine>

The valve needs to get a connection to database to do some queries.

I am trying to get a JNDI resource defined as global resource in GlobalNamingResources.

<GlobalNamingResources>
    <Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" 
    logAbandoned="true" maxActive="25" maxIdle="10" name="jdbc/qgw" 
    password="pass" removeAbandoned="true" removeAbandonedTimeout="300" 
    testOnBorrow="true" type="javax.sql.DataSource" 
    url="jdbc:mysql://localhost/qgw?autoReconnect=true" 
    username="username" validationQuery="SELECT 1"/>
</GlobalNamingResources>

The problem is, the resource is accesible only at Context container level because a ResourceLink is defined in the context.xml configuration file.

<ResourceLink global="jdbc/qgw" name="jdbc/qgw" type="javax.sql.DataSource"/> 

Obviously, when the valve trys to get the Datasource via JNDI

InitialContext initCtx = new InitialContext();
DataSource ds = (DataSource)initCtx.lookup("java:comp/env/jdbc/qgw");

obtains a NameNotFoundException

javax.naming.NameNotFoundException: Name comp/env/jdbc/qgw is not bound in this Context

So, is there any way to use the resource at Host Container level to connect to the defined database?


Solution

  • After a long time, this is the only way i found to get the DataSource, getting the Catalina StandardService from the container, I don't know if this is the best way to accomplish this, but it works.

    private static final String RESOURCE = "jdbc/qgw";
    
    private DataSource ds;
    
    @Override
    protected synchronized void startInternal() throws LifecycleException {
        super.startInternal();
    
        StandardService service = (StandardService)((StandardEngine)((StandardHost) container).getParent()).getService();
        try {
            // Accesible via GlobalNamingResources too
            //service.getServer().getGlobalNamingResources().findResource(RESOURCE);
            ds = (DataSource)service.getServer().getGlobalNamingContext().lookup(RESOURCE);
            if (ds==null) {
                throw new LifecycleException("Can't get the datasource to connect to database from the valve.");
            }
        } catch (Exception e) {
            container.getLogger().error(e);
            throw new LifecycleException(e.getMessage());
        }
    }