org.apereo.portal.jdbc.RDBMServices.java Source code

Java tutorial

Introduction

Here is the source code for org.apereo.portal.jdbc.RDBMServices.java

Source

/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you 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 the following location:
 *
 *   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.apereo.portal.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.atomic.AtomicInteger;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.portal.jpa.BasePortalJpaDao;
import org.apereo.portal.jdbc.IDatabaseMetadata;
import org.apereo.portal.utils.PortalApplicationContextLocator;
import org.apereo.portal.utils.MovingAverage;
import org.apereo.portal.utils.MovingAverageSample;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;

/**
 * Provides relational database access and helper methods.
 * A static routine determines if the database/driver supports
 * prepared statements and/or outer joins.
 *
 * <p>This class provides database access as a service.  Via the class, uPortal
 * code can obtain a connection to the core uPortal database as well as to other
 * databases available via JNDI.  (Doing that JNDI lookup directly allows your
 * code to avoid dependence upon this class.)  This class provides
 * traditional getConnection() methods as well as static covers for getting a
 * reference to the backing DataSource.</p>
 *
 * <p>This class also provides helper methods for manipulating connections.
 * Mostof the methods are wrappers around methods on the underlying Connection
 * that handle (log and swallow) the SQLExceptions that the underlying methods
 * declare to be thrown (these helpers also catch
 * and log RuntimeExceptions encountered).  They provide an alternative to trying
 * and catching those methods using the JDBC APIs directly.</p>
 *
 * @author Ken Weiner, kweiner@unicon.net
 * @author George Lindholm, george.lindholm@ubc.ca
 * @author Eric Dalquist <a href="mailto:edalquist@unicon.net">edalquist@unicon.net</a>
 * @author Susan Bramhall <a href="mailto:susan.bramhall@yale.edu">susan.bramhall@yale.edu</a>
 * @version $Revision$ $Date$
 */
public class RDBMServices {
    /**
     * Name of the default portal database expected in the Spring application context
     */
    public static final String PORTAL_DB = BasePortalJpaDao.PERSISTENCE_UNIT_NAME;

    /**
     * Name of the {@link IDatabaseMetadata} expected in the Spring application context
     */
    public static final String PORTAL_DB_METADATA = "PortalDB.metadata";

    private static final Log LOG = LogFactory.getLog(RDBMServices.class);

    //DBFlag constants
    private static final String FLAG_TRUE = "Y";
    private static final String FLAG_TRUE_OTHER = "T";
    private static final String FLAG_FALSE = "N";

    // Metric counters
    private static final MovingAverage databaseTimes = new MovingAverage();
    private static MovingAverageSample lastDatabase = new MovingAverageSample();
    private static AtomicInteger activeConnections = new AtomicInteger();
    private static int maxConnections = 0;

    /**
     * Gets the default DataSource. If no server is found
     * a runtime exception will be thrown.  This method will never return null.
     * @return the core uPortal DataSource
     * @throws RuntimeException on failure
     * @deprecated Where possible code should be injected with a {@link DataSource} object via the Spring application context
     */
    @Deprecated
    public static DataSource getDataSource() {
        return getDataSource(PORTAL_DB);
    }

    /**
     * Gets a named DataSource from JNDI, with special handling for the PORTAL_DB
     * datasource. Successful lookups
     * are cached and not done again. Lookup failure is remembered and blocks retry
     * for a
     * number of milliseconds specified by {@link #JNDI_RETRY_TIME} to reduce
     * JNDI overhead and log spam.
     *
     * There are two ways in which we handle the core uPortal DataSource
     * specially.
     *
     * We determine and remember metadata in an DbMetaData object for the core
     * uPortal DataSource.  We do not compute this DbMetaData for any other
     * DataSource.
     *
     * We fall back on using rdbm.properties to construct our core uPortal
     * DataSource in the case where we cannot find it from JNDI.  If the portal
     * property org.apereo.portal.jdbc.RDBMServices.getDatasourceFromJNDI is true,
     * we first
     * first try to get the connection by looking in the
     * JNDI context for the name defined by the portal property
     * org.apereo.portal.jdbc.RDBMServices.PortalDatasourceJndiName .
     *
     * If we were not configured to check JNDI or we didn't find it in JNDI having
     * checked, we then fall back on rdbm.properties.
     *
     * @param name The name of the DataSource to get.
     * @return A named DataSource or <code>null</code> if one cannot be found.
     * @deprecated Where possible code should be injected with a {@link DataSource} object via the Spring application context
     */
    @Deprecated
    public static DataSource getDataSource(String name) {
        if (PORTAL_DB.equals(name)) {
            return PortalDbLocator.getPortalDb();
        }

        final ApplicationContext applicationContext = PortalApplicationContextLocator.getApplicationContext();
        final DataSource dataSource = (DataSource) applicationContext.getBean(name, DataSource.class);
        return dataSource;
    }

    /**
     * @return Return the current number of active connections
     */
    public static int getActiveConnectionCount() {
        return activeConnections.intValue();
    }

    /**
     * @return Return the maximum number of connections
     */
    public static int getMaxConnectionCount() {
        return maxConnections;
    }

    public static MovingAverageSample getLastDatabase() {
        return lastDatabase;
    }

    /**
     * Gets a database connection to the portal database.
     * If datasource not available a runtime exception is thrown
     * @return a database Connection object
     * @throws DataAccessException if unable to return a connection
     * @deprecated Where possible code should be injected with a {@link DataSource} object via the Spring application context
     */
    @Deprecated
    public static Connection getConnection() {
        return getConnection(PORTAL_DB);
    }

    /**
     * Returns a connection produced by a DataSource found in the
     * JNDI context.  The DataSource should be configured and
     * loaded into JNDI by the J2EE container or may be the portal
     * default database.
     *
     * @param dbName the database name which will be retrieved from
     *   the JNDI context relative to "jdbc/"
     * @return a database Connection object or <code>null</code> if no Connection
     * @deprecated Where possible code should be injected with a {@link DataSource} object via the Spring application context
     */
    @Deprecated
    public static Connection getConnection(String dbName) {
        final DataSource dataSource = getDataSource(dbName);

        try {
            final long start = System.currentTimeMillis();
            final Connection c = dataSource.getConnection();
            lastDatabase = databaseTimes.add(System.currentTimeMillis() - start); // metric
            final int current = activeConnections.incrementAndGet();
            if (current > maxConnections) {
                maxConnections = current;
            }
            return c;
        } catch (SQLException e) {
            throw new DataAccessResourceFailureException(
                    "RDBMServices sql error trying to get connection to " + dbName, e);
        }
    }

    /**
     * Releases database connection.
     * Unlike the underlying connection.close(), this method does not throw
     * SQLException or any other exception.  It will fail silently from the
     * perspective of calling code, logging errors using Commons Logging.
     * @param con a database Connection object
     * @deprecated Where possible code should be injected with a {@link DataSource} object via the Spring application context
     */
    @Deprecated
    public static void releaseConnection(final Connection con) {
        // If we had failed allocating the connection, insure we don't try to count it or close it. UP-4446
        if (con != null) {
            try {
                activeConnections.decrementAndGet();

                con.close();
            } catch (Exception e) {
                if (LOG.isWarnEnabled())
                    LOG.warn("Error closing Connection: " + con, e);
            }
        }
    }

    //******************************************
    // Utility Methods
    //******************************************

    /**
     * Close a ResultSet
     * @param rs a database ResultSet object
     */
    public static void closeResultSet(final ResultSet rs) {
        if (rs == null) {
            return;
        }

        try {
            rs.close();
        } catch (Exception e) {
            if (LOG.isWarnEnabled())
                LOG.warn("Error closing ResultSet: " + rs, e);
        }
    }

    /**
     * Close a Statement
     * @param st a database Statement object
     */
    public static void closeStatement(final Statement st) {
        if (st == null) {
            return;
        }

        try {
            st.close();
        } catch (Exception e) {
            if (LOG.isWarnEnabled())
                LOG.warn("Error closing Statement: " + st, e);
        }
    }

    /**
     * Commit pending transactions.
     * Unlike the underlying commit(), this method does not throw SQLException or
     * any other exception.  It will fail silently from the perspective of calling code,
     * logging any errors using Commons Logging.
     * @param connection
     */
    static final public void commit(final Connection connection) {
        if (connection == null) {
            return;
        }

        try {
            connection.commit();
        } catch (Exception e) {
            if (LOG.isWarnEnabled())
                LOG.warn("Error committing Connection: " + connection, e);
        }
    }

    /**
     * Set auto commit state for the connection.
     * Unlike the underlying connection.setAutoCommit(), this method does not
     * throw SQLException or any other Exception.  It fails silently from the
     * perspective of calling code, logging any errors encountered using
     * Commons Logging.
     * @param connection
     * @param autocommit
     */
    public static final void setAutoCommit(final Connection connection, boolean autocommit) {
        try {
            connection.setAutoCommit(autocommit);
        } catch (Exception e) {
            if (LOG.isWarnEnabled())
                LOG.warn("Error committing Connection: " + connection + " to: " + autocommit, e);
        }
    }

    /**
     * rollback unwanted changes to the database
     * @param connection
     */
    public static final void rollback(final Connection connection) {
        try {
            connection.rollback();
        } catch (Exception e) {
            if (LOG.isWarnEnabled())
                LOG.warn("Error rolling back Connection: " + connection, e);
        }
    }

    /**
     * Returns the name of the JDBC driver being used for the default
     * uPortal database connections.
     *
     * @return the name of the JDBC Driver.
     */
    public static String getJdbcDriver() {
        final IDatabaseMetadata dbMetaData = getDbMetaData();
        return dbMetaData.getJdbcDriver();
    }

    /**
     * Gets the JDBC URL of the default uPortal database connections.
     *
     */
    public static String getJdbcUrl() {
        final IDatabaseMetadata dbMetaData = getDbMetaData();
        return dbMetaData.getJdbcUrl();
    }

    /**
     * Get the username under which we are connecting for the default uPortal
     * database connections.
     */
    public static String getJdbcUser() {
        final IDatabaseMetadata dbMetaData = getDbMetaData();
        return dbMetaData.getJdbcUser();
    }

    //******************************************
    // Data Type / Formatting Methods
    //******************************************

    /**
     * Return DB format of a boolean. "Y" for true and "N" for false.
     * @param flag true or false
     * @return either "Y" or "N"
     */
    public static final String dbFlag(final boolean flag) {
        if (flag) {
            return FLAG_TRUE;
        }

        return FLAG_FALSE;
    }

    /**
     * Return boolean value of DB flag, "Y" or "N".
     * @param flag either "Y" or "N"
     * @return boolean true or false
     */
    public static final boolean dbFlag(final String flag) {
        return flag != null && (FLAG_TRUE.equalsIgnoreCase(flag) || FLAG_TRUE_OTHER.equalsIgnoreCase(flag));
    }

    /**
     * Make a string SQL safe
     * @param sql
     * @return SQL safe string
     */
    public static final String sqlEscape(final String sql) {
        if (sql == null) {
            return "";
        }

        int primePos = sql.indexOf("'");

        if (primePos == -1) {
            return sql;
        }

        final StringBuffer sb = new StringBuffer(sql.length() + 4);
        int startPos = 0;

        do {
            sb.append(sql.substring(startPos, primePos + 1));
            sb.append("'");
            startPos = primePos + 1;
            primePos = sql.indexOf("'", startPos);
        } while (primePos != -1);

        sb.append(sql.substring(startPos));
        return sb.toString();
    }

    /**
     * Get metadata about the default DataSource.
     * @return metadata about the default DataSource.
     * @deprecated Use the bean named PortalDB.metadata instead
     */
    @Deprecated
    public static IDatabaseMetadata getDbMetaData() {
        return PortalDbMetadataLocator.getPortalDbMetadata();
    }
}