package com.enioka.jqm.providers; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.tomcat.jdbc.pool.ConnectionPool; import org.apache.tomcat.jdbc.pool.JdbcInterceptor; import org.apache.tomcat.jdbc.pool.PooledConnection; /** * Intercepter for Tomcat JDBC pool connections used by JQM payloads.<br> * It tracks all connections opened by a specific payload Thread so as to be able to forcefully close them at the end of its run.<br> * <br> * Limitation: if the payload has created sub-threads, they will not be cleaned up. */ public class PayloadInterceptor extends JdbcInterceptor { private class ConnPair { public Thread thread; public PooledConnection conn; } private static Map<ConnectionPool, Set<ConnPair>> conns = new ConcurrentHashMap<ConnectionPool, Set<ConnPair>>(); private PooledConnection trackedPooledCon; private ConnectionPool trackedConnPool; @Override public void reset(ConnectionPool parent, PooledConnection con) { trackedConnPool = parent; trackedPooledCon = con; if (parent != null && con != null) { ConnPair cp = new ConnPair(); cp.conn = con; cp.thread = Thread.currentThread(); Set<ConnPair> pairs = conns.get(parent); if (pairs != null) { conns.get(parent).add(cp); } } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (this.trackedConnPool != null && this.trackedPooledCon != null && CLOSE_VAL.equals(method.getName())) { for (ConnPair cp : conns.get(trackedConnPool)) { if (cp.conn.equals(trackedPooledCon)) { conns.get(trackedConnPool).remove(cp); } } } return super.invoke(proxy, method, args); } @Override public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing) { for (ConnPair cp : conns.get(parent)) { if (cp.conn.equals(con)) { conns.get(parent).remove(cp); } } super.disconnected(parent, con, finalizing); } @Override public void poolStarted(ConnectionPool pool) { conns.put(pool, Collections.newSetFromMap(new ConcurrentHashMap<ConnPair, Boolean>())); super.poolStarted(pool); } @Override public void poolClosed(ConnectionPool pool) { conns.remove(pool); super.poolClosed(pool); } /** * Called by the engine to trigger the cleanup at the end of a payload thread. */ public static int forceCleanup(Thread t) { int i = 0; for (Map.Entry<ConnectionPool, Set<ConnPair>> e : conns.entrySet()) { for (ConnPair c : e.getValue()) { if (c.thread.equals(t)) { try { // This will in turn remove it from the static Map. c.conn.getHandler().invoke(c.conn, Connection.class.getMethod("close"), null); } catch (Throwable e1) { e1.printStackTrace(); } i++; } } } return i; } }