org.jboss.dashboard.database.NonPooledDataSource.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.dashboard.database.NonPooledDataSource.java

Source

/**
 * Copyright (C) 2012 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.dashboard.database;

import org.jboss.dashboard.commons.misc.ReflectionUtils;
import org.jboss.dashboard.error.ErrorManager;
import org.jboss.dashboard.profiler.CodeBlockTrace;
import org.jboss.dashboard.profiler.CodeBlockType;
import org.jboss.dashboard.profiler.CoreCodeBlockTypes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

/**
 * A data source implementation that bounds every connection to the underlying thread.
 */
public class NonPooledDataSource implements DataSource {

    private static transient Log log = LogFactory.getLog(NonPooledDataSource.class.getName());
    protected PrintWriter printWriter;
    protected int loginTimeOut;

    // Data source properties
    protected String url;
    protected String user;
    protected String password;
    protected String driver;
    protected int isolation;
    protected boolean autoCommit;

    public NonPooledDataSource() {
        this.loginTimeOut = 0;
        this.printWriter = new PrintWriter(System.out);
        this.autoCommit = false;
        this.isolation = Connection.TRANSACTION_SERIALIZABLE;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public int getIsolation() {
        return isolation;
    }

    public void setIsolation(int isolation) {
        this.isolation = isolation;
    }

    public boolean isAutoCommit() {
        return autoCommit;
    }

    public void setAutoCommit(boolean autoCommit) {
        this.autoCommit = autoCommit;
    }

    // javax.sql.DataSource implementation

    public int getLoginTimeout() throws SQLException {
        return loginTimeOut;
    }

    public void setLoginTimeout(int seconds) throws SQLException {
        this.loginTimeOut = seconds;
    }

    public PrintWriter getLogWriter() throws SQLException {
        return printWriter;
    }

    public void setLogWriter(PrintWriter out) throws SQLException {
        this.printWriter = out;
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return getConnection();
    }

    public Connection getConnection() throws SQLException {
        try {
            Class.forName(driver);
            Connection conn = DriverManager.getConnection(url, user, password);
            setAutoCommit(conn, autoCommit);
            setIsolation(conn, isolation);
            return createConnectionProxy(conn);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public Logger getParentLogger() {
        return null;
    }

    protected boolean getAutoCommit(Connection conn) {
        try {
            return conn.getAutoCommit();
        } catch (SQLException e) {
            // Ignore problems when trying to get autocommit.
            // In some environments (Presidencia - Informix) when trying to get autocommit an exception is thrown.
            log.debug("Can not get autocommit.", e);
            return true;
        }
    }

    protected void setAutoCommit(Connection conn, boolean autocommit) {
        try {
            if (getAutoCommit(conn) != autocommit) {
                conn.setAutoCommit(autocommit);
            }
        } catch (SQLException e) {
            // Ignore problems when trying to change autocommit.
            // In some environments (Presidencia - Informix) when trying to set autocommit=true an exception is thrown.
            log.debug("Can not set autocommit.", e);
        }
    }

    protected void setIsolation(Connection conn, int isolation) {
        try {
            if (conn.getTransactionIsolation() != isolation) {
                conn.setTransactionIsolation(isolation);
            }
        } catch (SQLException e) {
            log.debug("Can not set connection isolation.", e);
        }
    }

    // java.sql.Connection proxy

    public Connection createConnectionProxy(Connection conn) throws SQLException {
        return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),
                getClassInterfaces(conn.getClass()), new ConnectionInvocationHandler(conn));
    }

    private class ConnectionInvocationHandler implements InvocationHandler {

        private Connection conn = null;

        public ConnectionInvocationHandler(Connection conn) {
            this.conn = conn;
        }

        public Object invoke(Object proxy, final Method m, final Object[] args) throws Throwable {
            // Capture commit.
            if (m.getName().equals("commit")) {
                CodeBlockTrace trace = new SQLStatementTrace("commit").begin();
                try {
                    return m.invoke(conn, args);
                } catch (Throwable e) {
                    ErrorManager.lookup().notifyError(e, true);
                    throw e;
                } finally {
                    trace.end();
                }
            }
            // Capture rollback.
            if (m.getName().equals("rollback")) {
                CodeBlockTrace trace = new SQLStatementTrace("rollback").begin();
                try {
                    return m.invoke(conn, args);
                } catch (Throwable e) {
                    ErrorManager.lookup().notifyError(e, true);
                    throw e;
                } finally {
                    trace.end();
                }
            }
            // Capture Statement creation.
            Object result = m.invoke(conn, args);
            if (m.getReturnType() != null) {
                if (m.getReturnType().equals(PreparedStatement.class)) {
                    String sql = (String) args[0];
                    return createPreparedStatementProxy((PreparedStatement) result, sql);
                }
                if (m.getReturnType().equals(Statement.class)) {
                    return createStatementProxy((Statement) result);
                }
            }
            return result;
        }
    }

    // java.sql.Statement proxy

    /** A cache of java class interfaces */
    protected transient Map<Class, Class[]> _classInterfacesMap = new HashMap<Class, Class[]>();

    protected Class[] getClassInterfaces(Class clazz) {
        Class[] result = _classInterfacesMap.get(clazz);
        if (result != null)
            return result;

        _classInterfacesMap.put(clazz, result = ReflectionUtils.getClassHierarchyInterfaces(clazz));
        return result;
    }

    protected Statement createStatementProxy(Statement stmt) throws SQLException {
        return (Statement) Proxy.newProxyInstance(stmt.getClass().getClassLoader(),
                getClassInterfaces(stmt.getClass()), new StatementInvocationHandler(stmt));
    }

    protected Statement createPreparedStatementProxy(PreparedStatement stmt, String sql) throws SQLException {
        return (Statement) Proxy.newProxyInstance(stmt.getClass().getClassLoader(),
                getClassInterfaces(stmt.getClass()), new PreparedStatementInvocationHandler(stmt, sql));
    }

    static class StatementInvocationHandler implements InvocationHandler {

        protected Statement stmt;

        public StatementInvocationHandler(Statement stmt) {
            this.stmt = stmt;
        }

        public Object invoke(Object proxy, final Method m, final Object[] args) throws Throwable {
            if (m.getName().startsWith("execute") && args != null && args.length > 0) {
                String sqlToExec = (String) args[0];
                CodeBlockTrace trace = new SQLStatementTrace(sqlToExec).begin();
                try {
                    if (log.isDebugEnabled())
                        log.debug(sqlToExec);
                    return m.invoke(stmt, args);
                } catch (Throwable e) {
                    ErrorManager.lookup().notifyError(e, true);
                    throw e;
                } finally {
                    trace.end();
                }
            } else {
                return m.invoke(stmt, args);
            }
        }
    }

    static class PreparedStatementInvocationHandler extends StatementInvocationHandler {

        protected String sql;

        public PreparedStatementInvocationHandler(Statement stmt, String sql) {
            super(stmt);
            this.sql = sql;
        }

        public Object invoke(Object proxy, final Method m, final Object[] args) throws Throwable {
            if (m.getName().startsWith("execute")) {
                String sqlToExec = args != null && args.length > 0 ? (String) args[0] : sql;
                CodeBlockTrace trace = new SQLStatementTrace(sqlToExec).begin();
                try {
                    if (log.isDebugEnabled())
                        log.debug(sqlToExec);
                    return m.invoke(stmt, args);
                } catch (Throwable e) {
                    ErrorManager.lookup().notifyError(e, true);
                    throw e;
                } finally {
                    trace.end();
                }
            } else {
                return m.invoke(stmt, args);
            }
        }
    }

    static class SQLStatementTrace extends CodeBlockTrace {

        protected Map<String, Object> context;

        public SQLStatementTrace(String sql) {
            super(stripAfterWhere(sql));
            context = new HashMap<String, Object>();
            context.put("SQL", sql);
        }

        public CodeBlockType getType() {
            return CoreCodeBlockTypes.SQL;
        }

        public String getDescription() {
            return (String) context.get("SQL");
        }

        public Map<String, Object> getContext() {
            return context;
        }

        /**
         * To group sensibly and to avoid recording sensitive data, Don't record the where clause
         * (only used for dynamic SQL since parameters aren't included in prepared statements)
         * @return subset of passed SQL up to the where clause.
         */
        public static String stripAfterWhere(String sql) {
            for (int i = 0; i < sql.length() - 4; i++) {
                if (sql.charAt(i) == 'w' || sql.charAt(i) == 'W') {
                    if (sql.substring(i + 1, i + 5).equalsIgnoreCase("here")) {
                        return sql.substring(0, i);
                    }
                }
            }
            return sql;
        }
    }
}