org.unitime.commons.hibernate.connection.LoggingConnectionProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.commons.hibernate.connection.LoggingConnectionProvider.java

Source

/*
 * UniTime 3.4 - 3.5 (University Timetabling Application)
 * Copyright (C) 2013, UniTime LLC, and individual contributors
 * as indicated by the @authors tag.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
*/
package org.unitime.commons.hibernate.connection;

import java.sql.Connection;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cpsolver.ifs.util.ToolBox;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;

/**
 * @author Tomas Muller
 */
public class LoggingConnectionProvider implements ConnectionProvider {
    private static final long serialVersionUID = 1L;
    private List<Lease> iLeases = new ArrayList<Lease>();
    private LeasedConnectionsLogger iLogger = null;
    private ConnectionProvider iConnectionProvider;

    public LoggingConnectionProvider(ConnectionProvider provider) {
        iConnectionProvider = provider;
        iLogger = new LeasedConnectionsLogger();
        iLogger.start();
    }

    @Override
    public Connection getConnection() throws SQLException {
        Connection connection = iConnectionProvider.getConnection();
        synchronized (iLeases) {
            iLeases.add(new Lease(connection));
        }
        return connection;
    }

    @Override
    public void closeConnection(Connection connection) throws SQLException {
        synchronized (iLeases) {
            for (Iterator<Lease> i = iLeases.iterator(); i.hasNext();) {
                Lease lease = i.next();
                if (lease.getConnection().equals(connection))
                    i.remove();
            }
        }
        iConnectionProvider.closeConnection(connection);
    }

    @Override
    public boolean supportsAggressiveRelease() {
        return iConnectionProvider.supportsAggressiveRelease();
    }

    public static class Lease {
        private static final DecimalFormat sDF = new DecimalFormat("#,##0.00");
        private Connection iConnection;
        private Thread iThread;
        private StackTraceElement[] iTrace;
        private long iTimeStamp;

        public Lease(Connection connection) {
            iConnection = connection;
            iThread = Thread.currentThread();
            iTrace = Thread.currentThread().getStackTrace();
            iTimeStamp = System.currentTimeMillis();
        }

        public Connection getConnection() {
            return iConnection;
        }

        public boolean equals(Object o) {
            if (o == null)
                return false;
            if (o instanceof Connection)
                return getConnection().equals(o);
            if (o instanceof Lease)
                return getConnection().equals(((Lease) o).getConnection());
            return false;
        }

        public double getLeaseTime() {
            return (System.currentTimeMillis() - iTimeStamp) / 1000.0;
        }

        public Thread.State getState() {
            return iThread.getState();
        }

        public String getName() {
            return iThread.getName();
        }

        public String getStackTrace() {
            int first = 0;
            for (int i = 3; i < iTrace.length; i++)
                if (iTrace[i].getClassName().startsWith("org.unitime.")
                        && !iTrace[i].getClassName().endsWith("._BaseRootDAO")) {
                    first = i;
                    break;
                }
            StringBuffer ret = new StringBuffer();
            for (int i = first; i < iTrace.length; i++)
                ret.append("\n  " + iTrace[i]);
            return ret.toString();
        }

        public String toString() {
            StackTraceElement trace = null;
            for (int i = 3; i < iTrace.length; i++)
                if (iTrace[i].getClassName().startsWith("org.unitime.")
                        && !iTrace[i].getClassName().endsWith("._BaseRootDAO")) {
                    trace = iTrace[i];
                    break;
                }
            return sDF.format(getLeaseTime()) + " " + getState() + " " + getName() + " " + trace;
        }
    }

    public class LeasedConnectionsLogger extends Thread {
        private Log iLog = LogFactory.getLog(LeasedConnectionsLogger.class);

        private boolean iActive = true;

        public LeasedConnectionsLogger() {
            super("LeasedConnectionsLogger");
            setDaemon(true);
        }

        @Override
        public void run() {
            iLog.info("Database connection pool logging is enabled.");
            while (iActive) {
                try {
                    try {
                        sleep(60000);
                    } catch (InterruptedException e) {
                    }
                    synchronized (iLeases) {
                        List<Lease> suspicious = new ArrayList<Lease>();
                        for (Lease lease : iLeases)
                            if (lease.getLeaseTime() > 60.0 || lease.getState() == State.TERMINATED)
                                suspicious.add(lease);
                        if (!suspicious.isEmpty())
                            iLog.warn("Suspicious leases:" + ToolBox.col2string(iLeases, 2));
                        for (Lease lease : suspicious)
                            if (lease.getState() == State.TERMINATED) {
                                iLog.fatal("Releasing connection of a terminated thread " + lease.getName() + "."
                                        + lease.getStackTrace());
                                closeConnection(lease.getConnection());
                            }
                    }
                } catch (Exception e) {
                    iLog.warn("Logging failed: " + e.getMessage(), e);
                }
            }
        }

        @Override
        public void interrupt() {
            iActive = false;
            super.interrupt();
            try {
                join();
            } catch (InterruptedException e) {
            }
        }
    }

    @Override
    public boolean isUnwrappableAs(Class clazz) {
        return iConnectionProvider.isUnwrappableAs(clazz);
    }

    @Override
    public <T> T unwrap(Class<T> clazz) {
        return iConnectionProvider.unwrap(clazz);
    }
}