org.artifactory.storage.db.DbServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.storage.db.DbServiceImpl.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2012 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.storage.db;

import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
import org.artifactory.api.common.BasicStatusHolder;
import org.artifactory.api.context.ContextHelper;
import org.artifactory.common.ArtifactoryHome;
import org.artifactory.descriptor.config.CentralConfigDescriptor;
import org.artifactory.mbean.MBeanRegistrationService;
import org.artifactory.spring.Reloadable;
import org.artifactory.storage.StorageProperties;
import org.artifactory.storage.db.fs.dao.NodesDao;
import org.artifactory.storage.db.mbean.ManagedDataSource;
import org.artifactory.storage.db.properties.model.DbProperties;
import org.artifactory.storage.db.properties.service.ArtifactoryDbPropertiesService;
import org.artifactory.storage.db.spring.ArtifactoryDataSource;
import org.artifactory.storage.db.spring.ArtifactoryTomcatDataSource;
import org.artifactory.storage.db.util.DbUtils;
import org.artifactory.storage.db.util.IdGenerator;
import org.artifactory.storage.db.util.JdbcHelper;
import org.artifactory.storage.db.version.ArtifactoryDBVersion;
import org.artifactory.util.ResourceUtils;
import org.artifactory.version.CompoundVersionDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.Callable;

/**
 * @author Yossi Shaul
 */
@Repository
@Reloadable(beanClass = DbService.class)
public class DbServiceImpl implements DbService {
    private static final Logger log = LoggerFactory.getLogger(DbServiceImpl.class);

    private static final double MYSQL_MIN_VERSION = 5.5;

    @Autowired
    private JdbcHelper jdbcHelper;

    @Autowired
    @Qualifier("storageProperties")
    private StorageProperties storageProperties;

    @Autowired
    private IdGenerator idGenerator;

    @Autowired
    private ArtifactoryDbPropertiesService dbPropertiesService;

    @PostConstruct
    private void initDb() throws Exception {
        printConnectionInfo();

        // check if db tables exist and initialize if not
        if (!isSchemaExist()) {
            try (Connection con = jdbcHelper.getDataSource().getConnection()) {

                // if using mySQL, check version compatibility
                if (storageProperties.getDbType() == DbType.MYSQL) {
                    checkMySqlMinVersion();
                }

                // read ddl from file and execute
                log.info("***Creating database schema***");
                DbUtils.executeSqlStream(con, getDbSchemaSql());
                updateDbProperties();

            }
        }

        // initialize id generator
        initializeIdGenerator();
    }

    private void updateDbProperties() {
        // Update DBProperties
        long installTime = System.currentTimeMillis();
        CompoundVersionDetails versionDetails = ArtifactoryHome.get().readRunningArtifactoryVersion();
        String versionStr = versionDetails.getVersion().getValue();
        long timestamp = versionDetails.getTimestamp();
        int revisionInt = versionDetails.getRevisionInt();
        dbPropertiesService.updateDbProperties(new DbProperties(installTime, versionStr, revisionInt, timestamp));
    }

    @Override
    public void init() {
        registerDataSourceMBean();
    }

    @Override
    public DbType getDatabaseType() {
        return storageProperties.getDbType();
    }

    @Override
    public long nextId() {
        return idGenerator.nextId();
    }

    @Override
    public void compressDerbyDb(BasicStatusHolder statusHolder) {
        DerbyUtils.compress(statusHolder);
    }

    @Override
    public <T> T invokeInTransaction(String transactionName, Callable<T> execute) {
        if (StringUtils.isNotBlank(transactionName)) {
            TransactionSynchronizationManager.setCurrentTransactionName(transactionName);
        }
        try {
            return execute.call();
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        }
    }

    //used via reflection by DbBaseTest
    public void initializeIdGenerator() throws SQLException {
        idGenerator.initializeIdGenerator();
    }

    private InputStream getDbSchemaSql() throws IOException {
        String dbTypeName = storageProperties.getDbType().toString();
        String resourcePath = "/" + dbTypeName + "/" + dbTypeName + ".sql";
        InputStream resource = ResourceUtils.getResource(resourcePath);
        if (resource == null) {
            throw new IOException("Database DDL resource not found at: '" + resourcePath + "'");
        }
        return resource;
    }

    private boolean isSchemaExist() throws SQLException {
        log.debug("Checking for database schema existence");
        try (Connection con = jdbcHelper.getDataSource().getConnection()) {
            DatabaseMetaData metaData = con.getMetaData();
            return tableExists(metaData, NodesDao.TABLE_NAME);
        }
    }

    public static boolean tableExists(DatabaseMetaData metaData, String tableName) throws SQLException {
        return DbUtils.tableExists(metaData, tableName);
    }

    private void printConnectionInfo() throws SQLException {
        Connection connection = jdbcHelper.getDataSource().getConnection();
        try {
            DatabaseMetaData meta = connection.getMetaData();
            log.info("Database: {} {}. Driver: {} {}", meta.getDatabaseProductName(),
                    meta.getDatabaseProductVersion(), meta.getDriverName(), meta.getDriverVersion());
            log.info("Connection URL: {}", meta.getURL());
        } catch (SQLException e) {
            log.warn("Can not retrieve database and driver name / version", e);
        } finally {
            DbUtils.close(connection);
        }
    }

    private void registerDataSourceMBean() {
        DataSource dataSource = jdbcHelper.getDataSource();
        if (dataSource instanceof ArtifactoryDataSource) {
            ContextHelper.get().beanForType(MBeanRegistrationService.class).register(
                    new ManagedDataSource((ArtifactoryDataSource) dataSource, jdbcHelper), "Storage",
                    "Data Source");
        }
        if (dataSource instanceof ArtifactoryTomcatDataSource) {
            // register the Tomcat JDBC pool JMX if enabled
            ArtifactoryTomcatDataSource tomcatDataSource = (ArtifactoryTomcatDataSource) dataSource;
            if (tomcatDataSource.isJmxEnabled()) {
                ConnectionPool jmxPool = tomcatDataSource.getPool().getJmxPool();
                ContextHelper.get().beanForType(MBeanRegistrationService.class).register(jmxPool, "Storage",
                        "Connection Pool");

            }
        }
    }

    private boolean checkMySqlMinVersion() {
        log.debug("Checking MySQL version compatibility");
        ResultSet rs = null;
        try {
            rs = jdbcHelper.executeSelect("SELECT VERSION();");
            if (rs.next()) {
                String versionString = rs.getString(1);
                int i = StringUtils.ordinalIndexOf(versionString, ".", 2);
                if (i == -1) {
                    i = versionString.length();
                }
                Double mysqlVersion = Double.valueOf(versionString.substring(0, i));
                if (mysqlVersion >= MYSQL_MIN_VERSION) {
                    return true;
                } else {
                    log.error("Unsupported MySQL version found [" + versionString + "]. "
                            + "Minimum version required is " + MYSQL_MIN_VERSION + ". "
                            + "Please follow the requirements on the wiki page.");
                    return false;
                }
            }
        } catch (Exception e) {
            log.error("Could not determine MySQL version due to an exception", e);
        } finally {
            DbUtils.close(rs);
        }
        log.error("Could not determine MySQL version. Minimum version should be " + MYSQL_MIN_VERSION
                + " and above.");
        return false;
    }

    @Override
    public void convert(CompoundVersionDetails source, CompoundVersionDetails target) {
        ArtifactoryDBVersion.convert(source.getVersion(), jdbcHelper, storageProperties.getDbType());
        updateDbProperties();
    }

    @Override
    public void reload(CentralConfigDescriptor oldDescriptor) {
    }

    @Override
    public void destroy() {
        jdbcHelper.destroy();
    }

}