From 080a0fb172ad6178fef529480b5ace5d0a66f06a Mon Sep 17 00:00:00 2001 From: Sébastien Lesaint Date: Wed, 7 Dec 2016 15:04:10 +0100 Subject: SONAR-8445 handle creation of table SCHEMA_MIGRATIONS in Java --- server/sonar-db-migration/pom.xml | 35 ++++++++++ .../migration/history/MigrationHistoryTable.java | 48 +++++++++++++ .../history/MigrationHistoryTableImpl.java | 80 ++++++++++++++++++++++ .../db/migration/history/package-info.java | 24 +++++++ .../history/MigrationHistoryTableImplTest.java | 77 +++++++++++++++++++++ .../MigrationHistoryTableImplTest/empty.sql | 0 .../platform/db/migrations/DatabaseMigrator.java | 2 +- .../platform/platformlevel/PlatformLevel2.java | 12 +++- 8 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTable.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImpl.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/package-info.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest/empty.sql (limited to 'server') diff --git a/server/sonar-db-migration/pom.xml b/server/sonar-db-migration/pom.xml index 9ece9a01e95..21f004d69eb 100644 --- a/server/sonar-db-migration/pom.xml +++ b/server/sonar-db-migration/pom.xml @@ -14,8 +14,43 @@ Create SonarQube schema + + org.sonarsource.sonarqube + sonar-core + + + org.sonarsource.sonarqube + sonar-db + + + com.google.code.findbugs + jsr305 + provided + + + ${project.groupId} + sonar-testing-harness + test + + + ${project.groupId} + sonar-db + ${project.version} + test-jar + test + + + org.dbunit + dbunit + test + + + com.h2database + h2 + test + diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTable.java new file mode 100644 index 00000000000..a74fd0866fb --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTable.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.history; + +import org.sonar.api.Startable; + +/** + * This class is responsible for ensuring at startup that the persistence of migration history is possible. + *

+ * Therefor, it will create the migration history table if it does not exist yet, update it if necessary and fail + * if any of the two previous operations fails. + *

+ *

+ * This class is intended to be present only in the WebServer and only the web server is the startup leader. + *

+ */ +public interface MigrationHistoryTable extends Startable { + String NAME = "schema_migrations"; + + /** + * Ensures that the history of db migrations can be persisted to database: + * + * + * @throws IllegalStateException if we can not ensure that table {@code SCHEMA_MIGRATIONS} can be accessed correctly + */ + @Override + void start(); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImpl.java new file mode 100644 index 00000000000..68043682b38 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImpl.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.history; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.DatabaseUtils; +import org.sonar.db.DbClient; +import org.sonar.db.version.CreateTableBuilder; +import org.sonar.db.version.VarcharColumnDef; + +public class MigrationHistoryTableImpl implements MigrationHistoryTable { + private static final String VERSION_COLUMN_NAME = "version"; + + private final DbClient dbClient; + + public MigrationHistoryTableImpl(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public void start() { + try (Connection connection = createDdlConnection(dbClient)) { + if (!DatabaseUtils.tableExists(NAME, connection)) { + createTable(connection); + } + } catch (SQLException e) { + throw new IllegalStateException("Failed to create table " + NAME, e); + } + } + + private void createTable(Connection connection) throws SQLException { + List sqls = new CreateTableBuilder(dbClient.getDatabase().getDialect(), NAME) + .addColumn(VarcharColumnDef.newVarcharColumnDefBuilder().setColumnName(VERSION_COLUMN_NAME).setIsNullable(false).setLimit(255).build()) + .build(); + + Loggers.get(MigrationHistoryTableImpl.class).info("Creating table " + NAME); + for (String sql : sqls) { + execute(connection, sql); + } + } + + private static Connection createDdlConnection(DbClient dbClient) throws SQLException { + Connection res = dbClient.getDatabase().getDataSource().getConnection(); + res.setAutoCommit(false); + return res; + } + + private static void execute(Connection connection, String sql) throws SQLException { + try (Statement stmt = connection.createStatement()) { + stmt.execute(sql); + connection.commit(); + } + } + + @Override + public void stop() { + // nothing to do + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/package-info.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/package-info.java new file mode 100644 index 00000000000..894d41a5e42 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.server.platform.db.migration.history; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest.java new file mode 100644 index 00000000000..958728c17f3 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.history; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationHistoryTableImplTest { + private static final String TABLE_SCHEMA_MIGRATIONS = "schema_migrations"; + + @Rule + public DbTester dbTester = DbTester.createForSchema(System2.INSTANCE, MigrationHistoryTableImplTest.class, "empty.sql"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private MigrationHistoryTableImpl underTest = new MigrationHistoryTableImpl(dbTester.getDbClient()); + + @Test + public void start_creates_table_on_empty_schema() { + underTest.start(); + + verifyTable(); + } + + @Test + public void start_does_not_fail_if_table_exists() throws SQLException { + executeDdl("create table " + TABLE_SCHEMA_MIGRATIONS + " (version varchar(255) not null)"); + verifyTable(); + + underTest.start(); + + verifyTable(); + } + + private void executeDdl(String sql) throws SQLException { + try (DbSession dbSession = dbTester.getDbClient().openSession(false); + Connection connection = dbSession.getConnection()) { + connection.setAutoCommit(false); + try (Statement statement = connection.createStatement()) { + statement.execute(sql); + connection.commit(); + } + } + } + + private void verifyTable() { + assertThat(dbTester.countRowsOfTable(TABLE_SCHEMA_MIGRATIONS)).isEqualTo(0); + dbTester.assertColumnDefinition(TABLE_SCHEMA_MIGRATIONS, "version", Types.VARCHAR, 255, false); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/history/MigrationHistoryTableImplTest/empty.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/db/migrations/DatabaseMigrator.java b/server/sonar-server/src/main/java/org/sonar/server/platform/db/migrations/DatabaseMigrator.java index 39a9bbcfe58..1435468e39a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/db/migrations/DatabaseMigrator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/db/migrations/DatabaseMigrator.java @@ -113,6 +113,6 @@ public class DatabaseMigrator implements Startable { @VisibleForTesting protected void createSchema(Connection connection, String dialectId) { - DdlUtils.createSchema(connection, dialectId); + DdlUtils.createSchema(connection, dialectId, false); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel2.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel2.java index b3786b8dc7d..c4356cec0d6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel2.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel2.java @@ -29,6 +29,7 @@ import org.sonar.db.version.MigrationStepModule; import org.sonar.server.platform.DefaultServerUpgradeStatus; import org.sonar.server.platform.StartupMetadataProvider; import org.sonar.server.platform.db.CheckDatabaseCharsetAtStartup; +import org.sonar.server.platform.db.migration.history.MigrationHistoryTableImpl; import org.sonar.server.platform.db.migrations.DatabaseMigrator; import org.sonar.server.platform.db.migrations.PlatformDatabaseMigration; import org.sonar.server.platform.db.migrations.PlatformDatabaseMigrationExecutorServiceImpl; @@ -67,11 +68,16 @@ public class PlatformLevel2 extends PlatformLevel { // depends on plugins RailsAppsDeployer.class, DefaultI18n.class, - RuleI18nManager.class, + RuleI18nManager.class); - // DB migration + // Full Java DB Migration framework and configuration + addIfStartupLeader(MigrationHistoryTableImpl.class); + // platform DB migration (TODO remove call to Ruby's Active Record and use DbMigrationEngine instead) + add( PlatformDatabaseMigrationExecutorServiceImpl.class, - PlatformDatabaseMigration.class, + PlatformDatabaseMigration.class); + // Ruby DB Migration + add( DatabaseMigrator.class, MigrationStepModule.class); -- cgit v1.2.3