Reputation: 11486
I am trying to implement jdbc-pool in a standalone web app (self contained - not relying on server.xml) so that it can be moved to tomcat installations that may be earlier than 7.0.
I am connecting to MSSQL Server with the sourceforge driver (net.sourceforge.jtds.jdbc.Driver)
Everything runs fine except for this error:
SEVERE: The web application [/jdbc-pool] appears to have started a thread named [[Pool-Cleaner]:Tomcat Connection Pool[1-12524859]] but has failed to stop it. This is very likely to create a memory leak.
Based on this I determined that I need to close the jdbc-pool datasource. I am having trouble with this last line from that post though:
>> If it is configured in the application context, then this simply means you forgot to call DataSource.close on the connection pool when your web application is stopped.
> This is confusing advice because javax.sql.DataSource doesn't have a close() method.
In order to call close, one has to cast it to what ever the data source you are using.
How do I find out what type of datasource I am using and where is the class for it? Can I extract it from the driver jar somehow?
In addition to a servlet which uses the pool, I am using a ServletContextListener so that I can start out with pooled connections immediately from the contextInitialized method. I started adding the code to kill the connection in the contextDestroyed method of this ServletContextListener but got hung up where the question marks are:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
public class JdbcPoolListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent myServletContextEvent) {
// initialize jdbc-pool datasource to start out with pooled connections
try {
Context myContext = (Context) new InitialContext().lookup("java:comp/env");
DataSource myDataSource = (DataSource) myContext.lookup("jdbc/db");
myServletContextEvent.getServletContext().setAttribute("JdbcPool", myDataSource);
} catch (NamingException e) {
System.out.println("Error initializing jdbc-pool datasource");
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent myServletContextEvent) {
// failed attempt to close the data source
ServletContext myServletContext = myServletContextEvent.getServletContext();
//DataSource myDataSource = (DataSource) myServletContext.getAttribute("JdbcPool");
DataSource dataSource = (DataSource)((???) myServletContext.getAttribute(contextAttribute)).getConfiguration().getEnvironment().getDataSource();
dataSource.close();
myServletContext.removeAttribute("JdbcPool");
// deregister JDBC driver to prevent Tomcat 7 from complaining about memory leaks
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
System.out.println(String.format("Deregistering jdbc driver: %s", driver));
} catch (SQLException e) {
System.out.println(String.format("Error deregistering driver %s", driver));
e.printStackTrace();
}
}
}
}
Upvotes: 3
Views: 3960
Reputation: 11486
Resolved this, I noticed that the tomcat.jdbc.pool.DataSourceProxy
had a close method, so I cast the datasource as a DataSourceProxy
, then called closed on it. I no longer get the tomcat memory leak error in the log now.
SOLUTION:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
public class JdbcPoolListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent myServletContextEvent) {
// initialize jdbc-pool datasource to start out with pooled connections
try {
Context myContext = (Context) new InitialContext().lookup("java:comp/env");
DataSource myDataSource = (DataSource) myContext.lookup("jdbc/cf");
myServletContextEvent.getServletContext().setAttribute("JdbcPool", myDataSource);
} catch (NamingException e) {
System.out.println("Error initializing jdbc-pool datasource");
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent myServletContextEvent) {
// close datasource from proxy?
ServletContext myServletContext = myServletContextEvent.getServletContext();
DataSourceProxy myDataSource = (DataSourceProxy) myServletContext.getAttribute("JdbcPool");
myDataSource.close();
myServletContext.removeAttribute("JdbcPool");
// deregister JDBC driver to prevent Tomcat 7 from complaining about memory leaks
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
System.out.println(String.format("Deregistering jdbc driver: %s", driver));
} catch (SQLException e) {
System.out.println(String.format("Error deregistering driver %s", driver));
e.printStackTrace();
}
}
}
}
Upvotes: 3