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

org.marid.db.hsqldb.HsqldbDatabase Maven / Gradle / Ivy

There is a newer version: 0.9.8.10
Show newest version
/*
 * Copyright (c) 2015 Dmitry Ovchinnikov
 * Marid, the free data acquisition and visualization software
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */

package org.marid.db.hsqldb;

import org.hsqldb.Database;
import org.hsqldb.DatabaseManager;
import org.hsqldb.jdbc.JDBCSessionDataSource;
import org.hsqldb.server.Server;
import org.hsqldb.server.ServerConstants;
import org.marid.logging.LogSupport;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;

import static java.lang.System.currentTimeMillis;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * @author Dmitry Ovchinnikov.
 */
public final class HsqldbDatabase implements Closeable, LogSupport {

    private final Server server;
    private final File directory;
    private final long shutdownTimeout;
    private final Map databaseNameToIndex = new LinkedHashMap<>();

    private PrintWriter outWriter;
    private PrintWriter errWriter;

    public HsqldbDatabase(HsqldbProperties properties) throws MalformedURLException {
        log(INFO, "{0}", properties);
        directory = properties.getDirectory();
        shutdownTimeout = SECONDS.toMillis(properties.getShutdownTimeoutSeconds());
        server = new Server();
        server.setNoSystemExit(true);
        server.setRestartOnShutdown(false);
        server.setPort(properties.getPort());
        server.setSilent(properties.isSilent());
        if (properties.getDatabases() == null) {
            setDatabase("NUMERICS", getClass().getResource("default.sql"));
        } else {
            for (final String database : properties.getDatabases().stringPropertyNames()) {
                final String urlText = properties.getDatabases().getProperty(database);
                if (!urlText.contains("://")) {
                    setDatabase(database, getClass().getClassLoader().getResource(urlText));
                } else {
                    setDatabase(database, new URL(urlText));
                }
            }
        }
    }

    private void setDatabase(String name, URL url) {
        final int index = databaseNameToIndex.size();
        server.setDatabaseName(index, name);
        server.setDatabasePath(index, new File(directory, name).getAbsolutePath());
        databaseNameToIndex.put(name, url);
    }

    @PostConstruct
    public void init() throws IOException {
        outWriter = new PrintWriter(new File(directory, "output.log"));
        errWriter = new PrintWriter(new File(directory, "errors.log"));
        server.start();
        for (final Map.Entry e : databaseNameToIndex.entrySet()) {
            try {
                initDatabase(e.getKey(), e.getValue());
            } catch (IOException | SQLException x) {
                log(WARNING, "Unable to init DB {0}", x, e.getKey());
            }
        }
    }

    @PreDestroy
    public void destroy() throws IOException {
        close();
    }

    private void initDatabase(String name, URL url) throws SQLException, IOException {
        try (final Connection c = dataSource(name).getConnection()) {
            c.setAutoCommit(true);
            final boolean tableExists;
            try (final ResultSet rs = c.getMetaData().getTables(null, null, name, new String[]{"TABLE"})) {
                tableExists = rs.next();
            }
            if (tableExists) {
                log(INFO, "Table {0} already exists", name);
                return;
            }
            try (final Statement s = c.createStatement()) {
                try (final Scanner scanner = new Scanner(url.openStream())) {
                    while (scanner.hasNextLine()) {
                        final String sql = scanner.nextLine().trim();
                        if (sql.startsWith("--") || sql.isEmpty()) {
                            continue;
                        }
                        try {
                            log(INFO, "Executing {0}", sql);
                            s.execute(sql);
                        } catch (SQLException x) {
                            log(WARNING, "Unable to execute '{0}'", x, sql);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void close() throws IOException {
        try (final PrintWriter out = outWriter; final PrintWriter err = errWriter) {
            server.shutdown();
            for (final long startTime = currentTimeMillis(); currentTimeMillis() - startTime < shutdownTimeout; ) {
                if (server.getState() == ServerConstants.SERVER_STATE_SHUTDOWN) {
                    return;
                }
                LockSupport.parkNanos(MILLISECONDS.toNanos(10L));
            }
            throw new InterruptedIOException("Server shutdown timeout exceeded");
        }
    }

    public DataSource dataSource(String name) {
        final int dbIndex = databaseNameToIndex.keySet().stream().collect(Collectors.toList()).indexOf(name);
        final Database database = DatabaseManager.getDatabase(dbIndex);
        return new JDBCSessionDataSource(database, "PUBLIC");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy