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

ru.curs.celesta.Celesta Maven / Gradle / Ivy

There is a newer version: 8.1.0
Show newest version
package ru.curs.celesta;

import org.h2.tools.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.curs.celesta.dbutils.DbUpdaterBuilder;
import ru.curs.celesta.dbutils.DbUpdaterImpl;
import ru.curs.celesta.dbutils.ILoggingManager;
import ru.curs.celesta.dbutils.IPermissionManager;
import ru.curs.celesta.dbutils.IProfiler;
import ru.curs.celesta.dbutils.LoggingManager;
import ru.curs.celesta.dbutils.PermissionManager;
import ru.curs.celesta.dbutils.ProfilingManager;
import ru.curs.celesta.dbutils.adaptors.DBAdaptor;
import ru.curs.celesta.dbutils.adaptors.configuration.DbAdaptorFactory;
import ru.curs.celesta.dbutils.adaptors.ddl.JdbcDdlConsumer;
import ru.curs.celesta.event.TriggerDispatcher;
import ru.curs.celesta.score.ParseException;
import ru.curs.celesta.score.Score;
import ru.curs.celesta.score.discovery.ScoreByScorePathDiscovery;
import ru.curs.celesta.score.discovery.ScoreByScoreResourceDiscovery;
import ru.curs.celesta.score.discovery.ScoreDiscovery;
import ru.curs.celesta.ver.CelestaVersion;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Optional;
import java.util.Properties;

/**
 * Celesta instance.
 */
public final class Celesta implements ICelesta {

    /**
     * Celesta version.
     *
     * @see CelestaVersion
     */
    public static final String VERSION = CelestaVersion.VERSION;

    protected static final String FILE_PROPERTIES = "celesta.properties";

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

    private final BaseAppSettings appSettings;
    private final Score score;
    private final ConnectionPool connectionPool;
    private final DBAdaptor dbAdaptor;
    private final TriggerDispatcher triggerDispatcher = new TriggerDispatcher();

    private Optional server;
    private final LoggingManager loggingManager;
    private final PermissionManager permissionManager;
    private final ProfilingManager profiler;

    Celesta(BaseAppSettings appSettings, ConnectionPool connectionPool) {
        this.appSettings = appSettings;
        this.connectionPool = connectionPool;
        manageH2Server();

        // CELESTA STARTUP SEQUENCE
        // 1. Parsing of grains description.
        LOGGER.info("Celesta initialization: score parsing...");

        try {
            ScoreDiscovery scoreDiscovery = this.appSettings.getScorePath().isEmpty()
                    ? new ScoreByScoreResourceDiscovery()
                    : new ScoreByScorePathDiscovery(appSettings.getScorePath());
            this.score = new Score.ScoreBuilder<>(Score.class)
                    .scoreDiscovery(scoreDiscovery)
                    .build();
        } catch (ParseException e) {
            throw new CelestaException(e);
        }
        CurrentScore.set(this.score);
        LOGGER.info("done.");

        LOGGER.info(this.score.describeGrains());

        // 2. Updating database structure.
        // Since at this stage meta information is already in use, theCelesta and ConnectionPool
        // have to be initialized.
        DbAdaptorFactory dac = new DbAdaptorFactory()
                .setDbType(appSettings.getDBType())
                .setDdlConsumer(new JdbcDdlConsumer())
                .setConnectionPool(connectionPool)
                .setH2ReferentialIntegrity(appSettings.isH2ReferentialIntegrity());

        dbAdaptor = dac.createDbAdaptor();

        this.loggingManager = new LoggingManager(this);
        this.permissionManager = new PermissionManager(this);
        this.profiler = new ProfilingManager(this);

        if (!appSettings.getSkipDBUpdate()) {
            LOGGER.info("Celesta initialization: database {} upgrade...",
                    PasswordHider.maskPassword(appSettings.getDatabaseConnection()));

            DbUpdaterImpl dbUpdater = new DbUpdaterBuilder()
                    .dbAdaptor(dbAdaptor)
                    .connectionPool(connectionPool)
                    .score(score)
                    .forceDdInitialize(appSettings.getForceDBInitialize())
                    .setCelesta(this)
                    .build();

            dbUpdater.updateDb();
            LOGGER.info("done.");
        } else {
            LOGGER.info("Celesta initialization: database upgrade...skipped.");
        }

    }

    @Override
    public Properties getSetupProperties() {
        return appSettings.getSetupProperties();
    }

    @Override
    public IPermissionManager getPermissionManager() {
        return permissionManager;
    }

    @Override
    public ILoggingManager getLoggingManager() {
        return loggingManager;
    }

    @Override
    public ConnectionPool getConnectionPool() {
        return connectionPool;
    }

    @Override
    public IProfiler getProfiler() {
        return profiler;
    }

    @Override
    public DBAdaptor getDBAdaptor() {
        return dbAdaptor;
    }

    @Override
    public TriggerDispatcher getTriggerDispatcher() {
        return this.triggerDispatcher;
    }

    @Override
    public Score getScore() {
        return score;
    }

    /**
     * Stops working of Celesta. After the call the instance of Celesta becomes unusable.
     */
    @Override
    public void close() {
        connectionPool.close();
        server.ifPresent(Server::shutdown);
    }

    /**
     * Creates Celesta instance with specified properties and {@link DataSource}.
     *
     * @param properties Celesta initialization properties. All the properties regarding db connection will be ignored,
     *                   but {@code rdbms.connection.url} is still required in order for
     *                   Celesta to define the database type (you may pass only the
     *                   prefix, e. g. {@code jdbc:postgresql})
     * @param dataSource Provided data source.
     * @return
     */
    public static Celesta createInstance(Properties properties, DataSource dataSource) {
        return createInstance(properties, new DatasourceConnectionPool(dataSource));
    }


    /**
     * Creates Celesta instance with specified properties and ConnectionPool.
     *
     * @param properties     Celesta initialization properties. All the properties regarding db connection
     *                       will be ignored, but {@code rdbms.connection.url} is still required in order for
     *                       Celesta to define the database type (you may pass only the
     *                       prefix, e. g. {@code jdbc:postgresql})
     * @param connectionPool Provided connection pool (either {@link DatasourceConnectionPool} or
     *                       {@link InternalConnectionPool}).
     * @return
     */
    public static Celesta createInstance(Properties properties, ConnectionPool connectionPool) {
        AppSettings appSettings = preInit(properties);
        return new Celesta(appSettings, connectionPool);
    }

    /**
     * Creates Celesta instance with the specified properties and internal connection pool.
     *
     * @param properties Celesta initialization properties
     * @return
     */
    public static Celesta createInstance(Properties properties) {
        AppSettings appSettings = preInit(properties);
        ConnectionPoolConfiguration cpc = new ConnectionPoolConfiguration();
        cpc.setJdbcConnectionUrl(appSettings.getDatabaseConnection());
        cpc.setDriverClassName(appSettings.getDbClassName());
        cpc.setLogin(appSettings.getDBLogin());
        cpc.setPassword(appSettings.getDBPassword());
        return new Celesta(appSettings, InternalConnectionPool.create(cpc));
    }

    /**
     * Creates Celesta instance with properties specified in celesta.properties
     * file.
     *
     * @return
     */
    public static Celesta createInstance() {
        Properties properties = loadPropertiesDynamically();
        return createInstance(properties);
    }

    private static AppSettings preInit(Properties properties) {
        LOGGER.info("Celesta ver. {}", VERSION != null ? VERSION : "N/A (invalid build?)");
        LOGGER.info("Celesta pre-initialization: system settings reading...");
        AppSettings appSettings = new AppSettings(properties);
        LOGGER.info("done.");
        return appSettings;
    }

    /**
     * Reads and returns properties from celesta.properties file.
     *
     * @return
     */
    public static Properties loadPropertiesDynamically() {
        // Разбираемся с настроечным файлом: читаем его и превращаем в
        // Properties.
        Properties properties = new Properties();
        try {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            InputStream in = loader.getResourceAsStream(FILE_PROPERTIES);
            if (in == null) {
                throw new CelestaException(
                        String.format("Couldn't find file %s on classpath.", FILE_PROPERTIES)
                );
            }
            try {
                properties.load(in);
            } finally {
                in.close();
            }
        } catch (IOException e) {
            throw new CelestaException(
                    String.format("IOException while reading %s file: %s", FILE_PROPERTIES, e.getMessage())
            );
        }

        return properties;
    }

    private void manageH2Server() {
        if (appSettings.getH2Port() > 0) {
            try {
                LOGGER.info("H2 server starting on port {}...", appSettings.getH2Port());
                server = Optional.of(Server.createTcpServer(
                        "-tcpPort",
                        Integer.toString(appSettings.getH2Port()),
                        "-ifNotExists",
                        "-tcpAllowOthers").start());

                LOGGER.info("done.");

                CurrentScore.global(true);
            } catch (SQLException e) {
                throw new CelestaException(e);
            }
        } else {
            server = Optional.empty();
            CurrentScore.global(false);
        }
    }


    /**
     * Returns if profiling mode is set (whether the time of method calls
     * is written to 'calllog' table).
     *
     * @return
     */
    public boolean isProfilemode() {
        return profiler.isProfilemode();
    }

    /**
     * Returns the behavior {@code NULLS FIRST} of current database.
     *
     * @return
     */
    public boolean nullsFirst() {
        return dbAdaptor.nullsFirst();
    }

    /**
     * Sets profiling mode.
     *
     * @param profilemode profiling mode
     */
    public void setProfilemode(boolean profilemode) {
        profiler.setProfilemode(profilemode);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy