/* * Copyright (C) 2014-2015 ULYSSIS VZW * * This file is part of i++. * * i++ is free software: you can redistribute it and/or modify * it under the terms of version 3 of the GNU Affero General Public License * as published by the Free Software Foundation. No other versions apply. * * 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 <http://www.gnu.org/licenses/> */ package org.ulyssis.ipp.processor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.net.URI; import java.sql.*; import java.util.*; public final class Database { private static final Logger LOG = LogManager.getLogger(Database.class); @SuppressWarnings("unused") private Database() {} public enum ConnectionFlags { READ_WRITE, READ_ONLY } private static URI databaseURI = null; public static void setDatabaseURI(URI uri) { databaseURI = uri; } public static Connection createConnection(EnumSet<ConnectionFlags> flags) throws SQLException { assert flags.contains(ConnectionFlags.READ_ONLY) != /* XOR */ flags.contains(ConnectionFlags.READ_WRITE); assert databaseURI != null; boolean readOnly = flags.contains(ConnectionFlags.READ_ONLY); Properties props = new Properties(); props.setProperty("readOnly", readOnly ? "true" : "false"); Connection connection = null; try { if (databaseURI.toString().startsWith("jdbc:h2")) { Class.forName("org.h2.Driver"); connection = DriverManager.getConnection(databaseURI.toString()); } else { Class.forName("org.postgresql.Driver"); connection = DriverManager.getConnection(databaseURI.toString(), props); } connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); connection.setAutoCommit(false); return connection; } catch (ClassNotFoundException e) { LOG.fatal("Couldn't load driver!", e); throw new IllegalStateException(e); } catch (SQLException e) { if (connection != null) connection.rollback(); throw e; } } public static void clearDb(Connection connection) throws SQLException { List<String> statements = Arrays.asList( "DROP TABLE IF EXISTS \"events\" CASCADE", "DROP TABLE IF EXISTS \"tagSeenEvents\" CASCADE", "DROP TABLE IF EXISTS \"snapshots\" CASCADE" ); for (String statement : statements) { try (Statement stmt = connection.createStatement()) { LOG.debug("Executing statement: {}", statement); stmt.execute(statement); } } } private static class TableDescription { String tableName; List<String> createStrings; TableDescription(String tableName, List<String> createStrings) { this.tableName = tableName; this.createStrings = createStrings; } } public static void initDb(Connection connection) throws SQLException { List<TableDescription> descriptions = Arrays.asList( new TableDescription( "events", Arrays.asList( "CREATE TABLE \"events\" (" + "\"id\" bigserial PRIMARY KEY NOT NULL," + "\"type\" VARCHAR(255) NOT NULL," + "\"time\" timestamp NOT NULL," + "\"data\" text NOT NULL," + "\"removed\" boolean NOT NULL" + ")", "CREATE UNIQUE INDEX ON \"events\" (\"time\" DESC, \"id\" DESC)", "CREATE INDEX ON \"events\" (\"type\", \"removed\")" ) ), new TableDescription( "tagSeenEvents", Arrays.asList( "CREATE TABLE \"tagSeenEvents\" (" + "\"id\" bigint PRIMARY KEY NOT NULL," + "\"readerId\" integer NOT NULL," + "\"updateCount\" bigint NOT NULL," + "FOREIGN KEY (\"id\") REFERENCES \"events\" (\"id\")" + ")", "CREATE UNIQUE INDEX ON \"tagSeenEvents\" (\"readerId\", \"updateCount\" DESC)" ) ), new TableDescription( "snapshots", Arrays.asList( "CREATE TABLE \"snapshots\" (" + "\"id\" bigserial PRIMARY KEY NOT NULL," + "\"time\" timestamp NOT NULL," + "\"data\" text NOT NULL," + "\"event\" bigint NOT NULL," + "FOREIGN KEY (\"event\") REFERENCES \"events\" (\"id\")" + ")", "CREATE INDEX ON \"snapshots\" (\"time\" DESC)" ) ) ); for (TableDescription desc : descriptions) { String existsCheck = "SELECT 1 FROM information_schema.tables WHERE table_name = ?"; try (PreparedStatement stmt = connection.prepareStatement(existsCheck)) { stmt.setString(1, desc.tableName); ResultSet rs = stmt.executeQuery(); if (!rs.next()) { // Exists check failed for (String createString : desc.createStrings) { try (Statement createStmt = connection.createStatement()) { LOG.debug("Executing statement: {}", createString); createStmt.execute(createString); } } } } } } }