/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.datamanagement.backend.metadata.derby.internal; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.BINARY_REFERENCE_KEY; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.COMPONENT_RUN_ID; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.DATA_REFERENCE_KEY; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.DB_VERSION; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.REL_COMPONENTINSTANCE_DATAREFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.REL_COMPONENTRUN_DATAREFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.REL_DATAREFERENCE_BINARYREFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.REL_WORKFLOWRUN_DATAREFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_BINARY_REFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_COMPONENT_INSTANCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_COMPONENT_INSTANCE_PROPERTIES; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_COMPONENT_RUN; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_COMPONENT_RUN_PROPERTIES; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_DATA_REFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_DB_VERSION_INFO; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_ENDPOINT_DATA; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_ENDPOINT_INSTANCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_ENDPOINT_INSTANCE_PROPERTIES; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_TIMELINE_INTERVAL; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_TYPED_DATUM; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_WORKFLOW_RUN; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.TABLE_WORKFLOW_RUN_PROPERTIES; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_COMPONENT_RUNS; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_COMPONENT_TIMELINE_INTERVALS; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_ENDPOINT_DATA; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_ENDPOINT_INSTANCE_PROPERTIES; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_WORKFLOWRUN_COMPONENTRUN; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_WORKFLOWRUN_DATAREFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.VIEW_WORKFLOWRUN_TYPEDDATUM; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.WORKFLOW_FILE_REFERENCE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.COMPONENT_RUN_FINAL_STATE; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.NODE_ID; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.CONTROLLER_NODE_ID; import static de.rcenvironment.core.datamanagement.commons.MetaDataConstants.DATAMANAGEMENT_NODE_ID; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLTransientConnectionException; import java.sql.Statement; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Queue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.datamanagement.commons.MetaDataConstants; import de.rcenvironment.core.utils.common.StringUtils; /** * Derby meta data db setup. * * @author Jan Flink * @author Brigitte Boden */ public abstract class DerbyDatabaseSetup { private static final String VERSION_6_1 = "6.1"; private static final String VERSION_ZERO = "0"; private static final String VERSION_7_0 = "7.0"; /** * For release versions, the version is the release number (e.g. "7.0") For versions during development phases, the release number is * followed by "dev[number]", e.g. "7.0dev1". */ //private static final String CURRENT_DB_VERSION = "8.0dev2"; private static final String CURRENT_DB_VERSION = "8.0"; /** * This array contains all former database versions which can be updated to the current version, in ascending order. For each of those * versions, an update path must be defined in the update method below. */ private static final String[] UPDATABLE_VERSIONS = { VERSION_ZERO, VERSION_6_1, VERSION_7_0 }; /** * This array contains all former database versions which are equivalent to the current version. No update is required, just the db * version in the database will be set to the current version. */ private static final String[] VERSIONS_REQUIRING_NO_UPDATE = {"8.0dev2"}; private static final int QUERY_EXECUTION_TIMEOUT = 600000; private static final int MAX_RETRIES = 3; private static final Log LOGGER = LogFactory.getLog(DerbyMetaDataBackendServiceImpl.class); private static final String WHERE = " WHERE "; private static final String APO = "'"; private static final String AND = " AND "; private static final String SELECT = " SELECT "; protected static void setupDatabase(final Connection connection) throws SQLException { Statement statement = connection.createStatement(); final List<String> updatableVersions = Arrays.asList(UPDATABLE_VERSIONS); final List<String> versionsRequiringNoUpdate = Arrays.asList(VERSIONS_REQUIRING_NO_UPDATE); if (!tableExists(statement, MetaDataConstants.TABLE_DB_VERSION_INFO)) { if (tableExists(statement, MetaDataConstants.TABLE_WORKFLOW_RUN)) { // if DB version is 0 - table version info does not exist - update to current version deleteViews(connection); updateDatabaseToCurrentVersion(connection, new LinkedList<String>(updatableVersions)); createViews(connection); } else { // set up a fresh database LOGGER.debug("Setting up database."); createTableDBVersionInfo(connection, CURRENT_DB_VERSION); createTables(connection); createIndexes(connection); createViews(connection); } } else if (!getDBVersion(connection).equals(CURRENT_DB_VERSION)) { String formerVersion = getDBVersion(connection); if (versionsRequiringNoUpdate.contains(formerVersion)) { LOGGER.debug("Updating database version: v " + formerVersion + " --> " + CURRENT_DB_VERSION + " . No change required."); setDBVersion(CURRENT_DB_VERSION, connection); } else if (updatableVersions.contains(formerVersion)) { // A queue representing the necessary update steps (contains the startVersion for each update step). Queue<String> requiredUpdates = new LinkedList<String>(updatableVersions.subList(updatableVersions.indexOf(formerVersion), updatableVersions.size())); deleteViews(connection); updateDatabaseToCurrentVersion(connection, requiredUpdates); createViews(connection); } else { // Database has an unknown (probably future) version. throw new RuntimeException(StringUtils.format("Failed to update the database from version %s to %s" + ". Most likely reason: It was used with a newer version of RCE before. Use a newer version " + "of RCE or choose another profile directory. (See the user guide for more information " + "about the profile directory.)", getDBVersion(connection), CURRENT_DB_VERSION)); } } else if (!tableExists(statement, MetaDataConstants.TABLE_WORKFLOW_RUN) || !tableExists(statement, MetaDataConstants.TABLE_WORKFLOW_RUN_PROPERTIES) || !tableExists(statement, MetaDataConstants.TABLE_TIMELINE_INTERVAL) || !tableExists(statement, MetaDataConstants.TABLE_COMPONENT_INSTANCE) || !tableExists(statement, MetaDataConstants.TABLE_COMPONENT_INSTANCE_PROPERTIES) || !tableExists(statement, MetaDataConstants.TABLE_COMPONENT_RUN) || !tableExists(statement, MetaDataConstants.TABLE_COMPONENT_RUN_PROPERTIES) || !tableExists(statement, MetaDataConstants.TABLE_DATA_REFERENCE) || !tableExists(statement, MetaDataConstants.TABLE_BINARY_REFERENCE) || !tableExists(statement, MetaDataConstants.TABLE_ENDPOINT_INSTANCE) || !tableExists(statement, MetaDataConstants.TABLE_TYPED_DATUM) || !tableExists(statement, MetaDataConstants.TABLE_ENDPOINT_DATA) || !tableExists(statement, MetaDataConstants.TABLE_ENDPOINT_INSTANCE_PROPERTIES) || !tableExists(statement, MetaDataConstants.REL_COMPONENTINSTANCE_DATAREFERENCE) || !tableExists(statement, MetaDataConstants.REL_WORKFLOWRUN_DATAREFERENCE) || !tableExists(statement, MetaDataConstants.REL_COMPONENTRUN_DATAREFERENCE) || !tableExists(statement, MetaDataConstants.REL_DATAREFERENCE_BINARYREFERENCE) || !viewExists(statement, MetaDataConstants.VIEW_COMPONENT_RUNS) || !viewExists(statement, MetaDataConstants.VIEW_COMPONENT_TIMELINE_INTERVALS) || !viewExists(statement, MetaDataConstants.VIEW_ENDPOINT_DATA) || !viewExists(statement, MetaDataConstants.VIEW_ENDPOINT_INSTANCE_PROPERTIES) || !viewExists(statement, MetaDataConstants.VIEW_WORKFLOWRUN_COMPONENTRUN) || !viewExists(statement, MetaDataConstants.VIEW_WORKFLOWRUN_DATAREFERENCE) || !viewExists(statement, MetaDataConstants.VIEW_WORKFLOWRUN_TYPEDDATUM)) { throw new RuntimeException("Unknown DB state!"); } statement.close(); LOGGER.debug(StringUtils.format("Database version is %s", getDBVersion(connection))); } private static void deleteViews(final Connection connection) { final Runnable task = new SQLRunnable(MAX_RETRIES) { @Override protected void sqlRun() throws SQLTransientConnectionException { try { deleteView(VIEW_COMPONENT_RUNS, connection); deleteView(VIEW_COMPONENT_TIMELINE_INTERVALS, connection); deleteView(VIEW_ENDPOINT_DATA, connection); deleteView(VIEW_ENDPOINT_INSTANCE_PROPERTIES, connection); deleteView(VIEW_WORKFLOWRUN_DATAREFERENCE, connection); deleteView(VIEW_WORKFLOWRUN_TYPEDDATUM, connection); deleteView(VIEW_WORKFLOWRUN_COMPONENTRUN, connection); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to delete views in data management meta data db.", e); } } @Override protected void handleSQLException(SQLException sqlException) { throw new RuntimeException("Failed to delete views.", sqlException); } }; task.run(); } private static void deleteView(String viewName, Connection connection) throws SQLException { String sql = "DROP VIEW %s"; Statement stmt = connection.createStatement(); if (viewExists(stmt, viewName)) { stmt.execute(StringUtils.format(sql, viewName)); LOGGER.debug(StringUtils.format("Deleted view %s", viewName)); } stmt.close(); } /** * Updates the database to the current version, if possible. This method has to be updated on each new database version to include the * new updates! * * @param startVersion The version to start with. * @param connection The database connection. * @throws SQLTransientConnectionException */ private static void updateDatabaseToCurrentVersion(Connection connection, Queue<String> requiredUpdates) throws SQLTransientConnectionException { String startVersion = requiredUpdates.poll(); if (startVersion == null) { // Queue is empty, no updates necessary any more. setDBVersion(CURRENT_DB_VERSION, connection); return; } else if (startVersion.equals(VERSION_ZERO)) { updateFrom0To61(connection); } else if (startVersion.equals(VERSION_6_1)) { updateFrom61To70(connection); } else if (startVersion.equals(VERSION_7_0)) { updateFrom70To80(connection); } // Recursively call update method do perform the next update in the queue. updateDatabaseToCurrentVersion(connection, requiredUpdates); } // Methods performing the individual version updates. private static void updateFrom0To61(Connection connection) { LOGGER.debug("Updating database: v 0 --> v 6.1"); createTableDBVersionInfo(connection, VERSION_6_1); } private static void updateFrom61To70(Connection connection) throws SQLTransientConnectionException { LOGGER.debug("Updating database: v 6.1 --> v 7.0"); try { DatabaseMetaData dbm = connection.getMetaData(); ResultSet rs = dbm.getTables(null, null, TABLE_ENDPOINT_INSTANCE_PROPERTIES, null); if (!rs.next()) { createTable(TABLE_ENDPOINT_INSTANCE_PROPERTIES, connection); } rs = dbm.getColumns(null, null, TABLE_WORKFLOW_RUN, WORKFLOW_FILE_REFERENCE); if (!rs.next()) { // Add colums for wfFile to WorkflowRun table Statement stmt = connection.createStatement(); String sql = "ALTER TABLE %s ADD %s LONG VARCHAR"; stmt.execute(StringUtils.format(sql, TABLE_WORKFLOW_RUN, WORKFLOW_FILE_REFERENCE)); LOGGER.debug("Added column WORKFLOW_FILE_DATA_REFERENCE_ID to WORKFLOW_RUN table."); } } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to update data management meta data db.", e); } } private static void updateFrom70To80(Connection connection) throws SQLTransientConnectionException { LOGGER.debug("Updating database: v 7.0 --> v 8.0"); try { DatabaseMetaData dbm = connection.getMetaData(); ResultSet rs = dbm.getColumns(null, null, TABLE_COMPONENT_RUN, COMPONENT_RUN_FINAL_STATE); if (!rs.next()) { // Add colums for wfFile to WorkflowRun table Statement stmt = connection.createStatement(); String sql = "ALTER TABLE %s ADD %s LONG VARCHAR"; stmt.execute(StringUtils.format(sql, TABLE_COMPONENT_RUN, COMPONENT_RUN_FINAL_STATE)); LOGGER.debug("Added column COMPONENT_RUN_FINAL_STATE to COMPONENT_RUN table."); stmt.close(); } // Modify datatype of columns containing node ids to varchar(100) to allow storing new logical node ids modifyColumnToVarchar100(connection, TABLE_COMPONENT_RUN, NODE_ID); modifyColumnToVarchar100(connection, TABLE_DATA_REFERENCE, NODE_ID); modifyColumnToVarchar100(connection, TABLE_WORKFLOW_RUN, CONTROLLER_NODE_ID); modifyColumnToVarchar100(connection, TABLE_WORKFLOW_RUN, DATAMANAGEMENT_NODE_ID); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to update data management meta data db.", e); } } private static void modifyColumnToVarchar100(Connection connection, String table, String column) throws SQLTransientConnectionException { //It is not possible to change the datatype of "CHAR" columns in a derby DB. //Thus, we create a new column, copy the data and rename it to the old name. String tempColumn = "TEMP_NODE_ID"; try { Statement stmt = connection.createStatement(); String sql1 = "ALTER TABLE %s ADD COLUMN %s VARCHAR(100)"; String sql2 = "UPDATE %s SET %s = %s"; String sql3 = "ALTER TABLE %s DROP COLUMN %s"; String sql4 = "RENAME COLUMN %s.%s TO %s"; stmt.execute(StringUtils.format(sql1, table, tempColumn)); stmt.execute(StringUtils.format(sql2, table, tempColumn, column)); stmt.execute(StringUtils.format(sql3, table, column)); stmt.execute(StringUtils.format(sql4, table, tempColumn, column)); stmt.close(); LOGGER.debug(StringUtils.format("Replaced column NODE_ID of table COMPNENT_RUN with column of type VARCHAR(100).", column, table)); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to change column data type.", e); } } private static void createTableDBVersionInfo(final Connection connection, final String dbVersion) { final Runnable task = new SQLRunnable(MAX_RETRIES) { @Override protected void sqlRun() throws SQLTransientConnectionException { try { createTable(TABLE_DB_VERSION_INFO, connection); setDBVersion(dbVersion, connection); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to create table in data management meta data db.", e); } } @Override protected void handleSQLException(SQLException sqlException) { throw new RuntimeException("Failed to create version information table.", sqlException); } }; task.run(); } private static void setDBVersion(String dbVersion, Connection connection) { try { String sql = "DELETE FROM " + TABLE_DB_VERSION_INFO; Statement stmt = connection.createStatement(); stmt.executeUpdate(StringUtils.format(sql, dbVersion)); stmt.close(); sql = "INSERT INTO " + TABLE_DB_VERSION_INFO + " VALUES('%s')"; stmt = connection.createStatement(); stmt.executeUpdate(StringUtils.format(sql, dbVersion)); stmt.close(); LOGGER.info(StringUtils.format("Set database version to %s", dbVersion)); } catch (SQLException e) { throw new RuntimeException("Failed to set database version.", e); } } private static String getDBVersion(Connection connection) { try { String version = "unknown"; String sql = SELECT + DB_VERSION + " FROM " + TABLE_DB_VERSION_INFO; Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(StringUtils.format(sql)); if (rs != null && rs.next()) { version = rs.getString(DB_VERSION); rs.close(); } stmt.close(); return version; } catch (SQLException e) { throw new RuntimeException("Failed to get database version.", e); } } private static boolean tableExists(Statement statement, String tablename) throws SQLException { boolean isExistentTable = false; ResultSet rs = null; try { final String sql = SELECT + " tablename FROM SYS.SYSTABLES" // + WHERE + "tablename = " + APO + tablename + APO + AND + "tabletype = " + APO + "T" + APO; statement.setQueryTimeout(QUERY_EXECUTION_TIMEOUT); rs = statement.executeQuery(sql); isExistentTable = rs.next(); } finally { try { if (rs != null) { rs.close(); } } catch (SQLException e) { e = null; } } return isExistentTable; } private static boolean viewExists(Statement statement, String viewname) throws SQLException { boolean isExistentView = false; ResultSet rs = null; try { final String sql = SELECT + " tablename FROM SYS.SYSTABLES" // + WHERE + "tablename = " + APO + viewname + APO + AND + "tabletype = " + APO + "V" + APO; statement.setQueryTimeout(QUERY_EXECUTION_TIMEOUT); rs = statement.executeQuery(sql); isExistentView = rs.next(); } finally { try { if (rs != null) { rs.close(); } } catch (SQLException e) { e = null; } } return isExistentView; } private static void createIndexes(final Connection connection) { final Runnable task = new SQLRunnable(MAX_RETRIES) { @Override protected void sqlRun() throws SQLTransientConnectionException { createIndexesTrial(connection); } @Override protected void handleSQLException(SQLException sqlException) { throw new RuntimeException("Failed to create indexes.", sqlException); } }; task.run(); } private static void createIndexesTrial(Connection connection) throws SQLTransientConnectionException { try { createIndex(TABLE_DATA_REFERENCE, DATA_REFERENCE_KEY, connection); createIndex(TABLE_TIMELINE_INTERVAL, COMPONENT_RUN_ID, connection); createIndex(TABLE_BINARY_REFERENCE, BINARY_REFERENCE_KEY, connection); createIndex(TABLE_ENDPOINT_DATA, COMPONENT_RUN_ID, connection); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to create indexes in meta data db.", e); } } private static void createIndex(String tableName, String columnName, Connection connection) throws SQLException { Statement stmt = connection.createStatement(); String sql = StringUtils.format("CREATE INDEX INDEX_%s_%s ON %s (%s)", tableName, columnName, tableName, columnName); LOGGER.debug(StringUtils.format("Creating index 'INDEX_%s_%s'", tableName, columnName)); stmt.executeUpdate(sql); stmt.close(); } private static void createTables(final Connection connection) { final Runnable task = new SQLRunnable(MAX_RETRIES) { @Override protected void sqlRun() throws SQLTransientConnectionException { createTablesTrial(connection); } @Override protected void handleSQLException(SQLException sqlException) { throw new RuntimeException("Failed to create tables.", sqlException); } }; task.run(); } private static void createTablesTrial(final Connection connection) throws SQLTransientConnectionException { try { createTable(TABLE_WORKFLOW_RUN, connection); createTable(TABLE_WORKFLOW_RUN_PROPERTIES, connection); createTable(TABLE_COMPONENT_INSTANCE, connection); createTable(TABLE_COMPONENT_INSTANCE_PROPERTIES, connection); createTable(TABLE_COMPONENT_RUN, connection); createTable(TABLE_COMPONENT_RUN_PROPERTIES, connection); createTable(TABLE_TIMELINE_INTERVAL, connection); createTable(TABLE_DATA_REFERENCE, connection); createTable(TABLE_BINARY_REFERENCE, connection); createTable(TABLE_ENDPOINT_INSTANCE, connection); createTable(TABLE_ENDPOINT_INSTANCE_PROPERTIES, connection); createTable(TABLE_TYPED_DATUM, connection); createTable(TABLE_ENDPOINT_DATA, connection); createTable(REL_COMPONENTRUN_DATAREFERENCE, connection); createTable(REL_COMPONENTINSTANCE_DATAREFERENCE, connection); createTable(REL_WORKFLOWRUN_DATAREFERENCE, connection); createTable(REL_DATAREFERENCE_BINARYREFERENCE, connection); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to create tables in data management meta data db.", e); } } private static void createTable(final String tableName, final Connection connection) throws SQLException { Statement stmt = connection.createStatement(); String sql = null; LOGGER.debug(StringUtils.format("Creating table '%s'", tableName)); if (!tableExists(stmt, tableName)) { switch (tableName) { case TABLE_DB_VERSION_INFO: sql = DerbyDatabaseSetupSqlStatements.getSqlTableDbVersionInfo(); break; case TABLE_WORKFLOW_RUN: sql = DerbyDatabaseSetupSqlStatements.getSqlTableWorkflowRun(); break; case TABLE_WORKFLOW_RUN_PROPERTIES: sql = DerbyDatabaseSetupSqlStatements.getSqlTableWorkflowRunProperties(); break; case TABLE_TIMELINE_INTERVAL: sql = DerbyDatabaseSetupSqlStatements.getSqlTableTimelineInterval(); break; case TABLE_COMPONENT_INSTANCE: sql = DerbyDatabaseSetupSqlStatements.getSqlTableComponentInstance(); break; case TABLE_COMPONENT_INSTANCE_PROPERTIES: sql = DerbyDatabaseSetupSqlStatements.getSqlTableComponentInstanceProperties(); break; case TABLE_COMPONENT_RUN: sql = DerbyDatabaseSetupSqlStatements.getSqlTableComponentRun(); break; case TABLE_COMPONENT_RUN_PROPERTIES: sql = DerbyDatabaseSetupSqlStatements.getSqlTableComponentRunProperties(); break; case TABLE_TYPED_DATUM: sql = DerbyDatabaseSetupSqlStatements.getSqlTableTypedDatum(); break; case TABLE_ENDPOINT_INSTANCE: sql = DerbyDatabaseSetupSqlStatements.getSqlTableEndpointInstance(); break; case TABLE_ENDPOINT_INSTANCE_PROPERTIES: sql = DerbyDatabaseSetupSqlStatements.getSqlTableEndpointInstanceProperties(); break; case TABLE_ENDPOINT_DATA: sql = DerbyDatabaseSetupSqlStatements.getSqlTableEndpointData(); break; case TABLE_DATA_REFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlTableDataReference(); break; case TABLE_BINARY_REFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlTableBinaryReference(); break; case REL_COMPONENTRUN_DATAREFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlRelationComponentRunDataReference(); break; case REL_DATAREFERENCE_BINARYREFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlRelationDataReferenceBinaryReference(); break; case REL_COMPONENTINSTANCE_DATAREFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlRelationComponentInstanceDataReference(); break; case REL_WORKFLOWRUN_DATAREFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlRelationWorkflowRunDataReference(); break; default: break; } } if (sql != null) { stmt.executeUpdate(sql); } stmt.close(); } private static void createViews(final Connection connection) { final Runnable task = new SQLRunnable(MAX_RETRIES) { @Override protected void sqlRun() throws SQLTransientConnectionException { createViewsTrial(connection); } @Override protected void handleSQLException(SQLException sqlException) { throw new RuntimeException("Failed to create views.", sqlException); } }; task.run(); } private static void createViewsTrial(final Connection connection) throws SQLTransientConnectionException { try { createView(VIEW_COMPONENT_RUNS, connection); createView(VIEW_ENDPOINT_DATA, connection); createView(VIEW_ENDPOINT_INSTANCE_PROPERTIES, connection); createView(VIEW_COMPONENT_TIMELINE_INTERVALS, connection); createView(VIEW_WORKFLOWRUN_COMPONENTRUN, connection); createView(VIEW_WORKFLOWRUN_DATAREFERENCE, connection); createView(VIEW_WORKFLOWRUN_TYPEDDATUM, connection); } catch (SQLException e) { if (e instanceof SQLTransientConnectionException) { throw (SQLTransientConnectionException) e; } throw new RuntimeException("Failed to create views in data management meta data db.", e); } } private static void createView(final String viewName, final Connection connection) throws SQLException { Statement stmt = connection.createStatement(); String sql = null; LOGGER.debug(StringUtils.format("Creating view '%s'", viewName)); if (!viewExists(stmt, viewName)) { switch (viewName) { case VIEW_COMPONENT_RUNS: sql = DerbyDatabaseSetupSqlStatements.getSqlViewComponentRuns(); break; case VIEW_ENDPOINT_DATA: sql = DerbyDatabaseSetupSqlStatements.getSqlViewEndpointData(); break; case VIEW_ENDPOINT_INSTANCE_PROPERTIES: sql = DerbyDatabaseSetupSqlStatements.getSqlViewEndpointInstanceProperties(); break; case VIEW_COMPONENT_TIMELINE_INTERVALS: sql = DerbyDatabaseSetupSqlStatements.getSqlViewComponentTimelineIntervals(); break; case VIEW_WORKFLOWRUN_COMPONENTRUN: sql = DerbyDatabaseSetupSqlStatements.getSqlViewWorkflowRunComponentRun(); break; case VIEW_WORKFLOWRUN_DATAREFERENCE: sql = DerbyDatabaseSetupSqlStatements.getSqlViewWorkflowRunDataReference(); break; case VIEW_WORKFLOWRUN_TYPEDDATUM: sql = DerbyDatabaseSetupSqlStatements.getSqlViewWorkflowRunTypedDatum(); break; default: break; } } if (sql != null) { stmt.executeUpdate(sql); } stmt.close(); } /** * Manages the retries for sql statements. * * @author Christian Weiss */ private abstract static class SQLRunnable implements Runnable { private final int maxAttempts; private SQLRunnable() { this(3); } private SQLRunnable(final int maxAttempts) { this.maxAttempts = maxAttempts; } @Override public final void run() { SQLException exception; int attemptCount = 0; do { ++attemptCount; exception = null; try { sqlRun(); break; } catch (SQLTransientConnectionException e) { exception = e; waitForRetry(); } } while (attemptCount <= maxAttempts); if (exception != null) { handleSQLException(exception); } } private void waitForRetry() { LOGGER.info("Waiting half o a second to retry SQL statement execution"); final int halfOfASecond = 500; try { Thread.sleep(halfOfASecond); } catch (InterruptedException e) { LOGGER.warn("Waiting for retrying a failed SQL statement was interupted"); } } protected abstract void sqlRun() throws SQLTransientConnectionException; protected abstract void handleSQLException(final SQLException sqlException); } }