All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fcrepo.config.DatabaseConfig Maven / Gradle / Ivy

/*
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree.
 */

package org.fcrepo.config;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import com.zaxxer.hikari.HikariConfig;
import org.flywaydb.core.Flyway;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

import com.zaxxer.hikari.HikariDataSource;

/**
 * @author pwinckles
 */
@EnableTransactionManagement
@Configuration
public class DatabaseConfig extends BasePropsConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseConfig.class);

    private static final String H2_FILE = "fcrepo-h2";

    @Value("${flyway.cleanDisabled:true}")
    private boolean flywayCleanDisabled;

    @Value("${fcrepo.db.url:#{'jdbc:h2:'" +
            " + fedoraPropsConfig.fedoraData.resolve('" + H2_FILE + "').toAbsolutePath().toString()" +
            " + ';FILE_LOCK=SOCKET'}}")
    private String dbUrl;

    @Value("${fcrepo.db.user:}")
    private String dbUser;

    @Value("${fcrepo.db.password:}")
    private String dbPassword;

    @Value("${fcrepo.db.max.pool.size:#{null}}")
    private Integer maxPoolSize;

    @Value("${fcrepo.db.connection.checkout.timeout:#{null}}")
    private Integer checkoutTimeout;

    @Value("${fcrepo.db.custom.properties:#{null}}")
    private String customDbProperties;

    private static final Map DB_DRIVER_MAP = Map.of(
            "h2", "org.h2.Driver",
            "postgresql", "org.postgresql.Driver",
            "mariadb", "org.mariadb.jdbc.Driver",
            "mysql", "com.mysql.cj.jdbc.Driver"
    );

    @PostConstruct
    public void setup() {
        ((ConverterRegistry) DefaultConversionService.getSharedInstance())
                // Adds a converter for mapping local datetimes to instants. This is dubious and not supported
                // by default because you must make an assumption about the timezone
                .addConverter(new Converter() {
                    @Override
                    public Instant convert(final LocalDateTime source) {
                        return source.toInstant(ZoneOffset.UTC);
                    }
                });

    }

    @Bean
    public DataSource dataSource() throws Exception {
        final var driver = identifyDbDriver();

        LOGGER.info("JDBC URL: {}", dbUrl);
        LOGGER.info("JDBC User: {}", dbUser);
        LOGGER.info("JDBC Password length: {}", dbPassword == null ? 0 : dbPassword.length());
        LOGGER.info("Using database driver: {}", driver);

        final HikariConfig config;
        if (customDbProperties != null) {
            config = new HikariConfig(customDbProperties);
            LOGGER.info("Using an external configuration file for Hikari: {}", customDbProperties);
        } else {
            config = new HikariConfig();
        }
        if (checkoutTimeout != null) {
            if (config.getConnectionTimeout() != 30000) {
                LOGGER.warn(
                        "Overriding HikariCP connectionTimeout setting in file from {} to {} ",
                        config.getConnectionTimeout(),
                        checkoutTimeout
                );
            }
            config.setConnectionTimeout(checkoutTimeout);
        }
        if (maxPoolSize != null) {
            if (config.getMaximumPoolSize() != 10) {
                LOGGER.warn(
                        "Overriding HikariCP maximumPoolSize setting in file from {} to {} ",
                        config.getMaximumPoolSize(),
                        maxPoolSize
                );
            }
            config.setMaximumPoolSize(maxPoolSize);
        }
        config.setDriverClassName(driver);
        config.setJdbcUrl(dbUrl);
        config.setUsername(dbUser);
        config.setPassword(dbPassword);

        final var dataSource = new HikariDataSource(config);

        flyway(dataSource);

        return dataSource;
    }

    /**
     * Get the database type in use
     * @return database type from the connect url.
     */
    private String getDbType() {
        final var parts = dbUrl.split(":");

        if (parts.length < 2) {
            throw new IllegalArgumentException("Invalid DB url: " + dbUrl);
        }
        return parts[1].toLowerCase();
    }

    private String identifyDbDriver() {
        final var driver = DB_DRIVER_MAP.get(getDbType());

        if (driver == null) {
            throw new IllegalStateException("No database driver found for: " + dbUrl);
        }

        return driver;
    }

    @Bean
    public DataSourceTransactionManager txManager(final DataSource dataSource) {
        final var txManager = new DataSourceTransactionManager();
        txManager.setDataSource(dataSource);
        return txManager;
    }

    @Bean
    public TransactionTemplate txTemplate(final PlatformTransactionManager txManager) {
        final var txDefinition = new DefaultTransactionDefinition();
        txDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        return new TransactionTemplate(txManager, txDefinition);
    }

    @Bean
    public Flyway flyway(final DataSource source) throws Exception {
        LOGGER.debug("Instantiating a new flyway bean");
        return FlywayFactory.create().setDataSource(source).setDatabaseType(getDbType())
                .setCleanDisabled(flywayCleanDisabled).getObject();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy