From 8a4d9708024cba8a0089c39f041ac145d07cbc40 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 18 Apr 2014 12:15:13 +0200 Subject: [PATCH] When using H2, database migration is now automatically done --- .../org/sonar/server/db/EmbeddedDatabase.java | 5 +- .../db/migrations/DatabaseMigrator.java | 61 ++++++++++++------- .../server/platform/ServerComponents.java | 3 +- .../java/org/sonar/server/ui/JRubyFacade.java | 14 ++--- .../webapp/WEB-INF/lib/database_version.rb | 15 ----- .../db/migrations/DatabaseMigratorTest.java | 18 ++++-- .../org/sonar/server/tester/ServerTester.java | 2 - 7 files changed, 62 insertions(+), 56 deletions(-) diff --git a/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java b/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java index ced29614549..2ed48e023e9 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java +++ b/sonar-server/src/main/java/org/sonar/server/db/EmbeddedDatabase.java @@ -23,6 +23,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; import org.h2.Driver; import org.h2.tools.Server; +import org.picocontainer.Startable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -34,7 +35,7 @@ import java.io.File; import java.sql.DriverManager; import java.sql.SQLException; -public class EmbeddedDatabase { +public class EmbeddedDatabase implements Startable { private static final Logger LOG = LoggerFactory.getLogger(EmbeddedDatabase.class); private final Settings settings; private Server server; @@ -43,6 +44,7 @@ public class EmbeddedDatabase { this.settings = settings; } + @Override public void start() { File dbHome = getDataDirectory(settings); if (!dbHome.exists()) { @@ -71,6 +73,7 @@ public class EmbeddedDatabase { } } + @Override public void stop() { if (server != null) { server.stop(); diff --git a/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java b/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java index 833389ad924..a5e3d44cd49 100644 --- a/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java +++ b/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrator.java @@ -22,11 +22,14 @@ package org.sonar.server.db.migrations; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.dbutils.DbUtils; import org.apache.ibatis.session.SqlSession; +import org.picocontainer.Startable; import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; +import org.sonar.api.platform.ServerUpgradeStatus; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.DdlUtils; import org.sonar.core.persistence.MyBatis; +import org.sonar.server.plugins.ServerPluginRepository; import java.sql.Connection; @@ -36,41 +39,57 @@ import java.sql.Connection; * * @since 2.12 */ -public class DatabaseMigrator implements ServerComponent { +public class DatabaseMigrator implements ServerComponent, Startable { private final MyBatis myBatis; private final Database database; private final DatabaseMigration[] migrations; + private final ServerUpgradeStatus serverUpgradeStatus; - public DatabaseMigrator(MyBatis myBatis, Database database, DatabaseMigration[] migrations) { + /** + * ServerPluginRepository is used to ensure H2 schema creation is done only after copy of bundle plugins have been done + */ + public DatabaseMigrator(MyBatis myBatis, Database database, DatabaseMigration[] migrations, ServerUpgradeStatus serverUpgradeStatus, + ServerPluginRepository serverPluginRepository) { this.myBatis = myBatis; this.database = database; this.migrations = migrations; + this.serverUpgradeStatus = serverUpgradeStatus; + } + + @Override + public void start(){ + createDatabase(); + } + + @Override + public void stop(){ + // Nothing to do } /** - * @return true if the database has been created, false if this database is not supported + * @return true if the database has been created, false if this database is not supported or if database has already been created */ - public boolean createDatabase() { - if (!DdlUtils.supportsDialect(database.getDialect().getId())) { - return false; - } - - LoggerFactory.getLogger(getClass()).info("Create database"); - SqlSession session = null; - Connection connection = null; - try { - session = myBatis.openSession(); - connection = session.getConnection(); - createSchema(connection, database.getDialect().getId()); - return true; - } finally { - MyBatis.closeQuietly(session); + @VisibleForTesting + boolean createDatabase() { + if (DdlUtils.supportsDialect(database.getDialect().getId()) && serverUpgradeStatus.isFreshInstall()) { + LoggerFactory.getLogger(getClass()).info("Create database"); + SqlSession session = null; + Connection connection = null; + try { + session = myBatis.openSession(); + connection = session.getConnection(); + createSchema(connection, database.getDialect().getId()); + return true; + } finally { + MyBatis.closeQuietly(session); - // The connection is probably already closed by session.close() - // but it's not documented in mybatis javadoc. - DbUtils.closeQuietly(connection); + // The connection is probably already closed by session.close() + // but it's not documented in mybatis javadoc. + DbUtils.closeQuietly(connection); + } } + return false; } public void executeMigration(String className) { diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index e7f9d4d9385..333305896a5 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -152,7 +152,6 @@ class ServerComponents { DefaultDatabase.class, MyBatis.class, DatabaseServerCompatibility.class, - DatabaseMigrator.class, DatabaseVersion.class, PurgeProfiler.class, DefaultServerFileSystem.class, @@ -176,6 +175,8 @@ class ServerComponents { Collection level2Components() { return Lists.newArrayList( DefaultServerUpgradeStatus.class, + DatabaseMigrator.class, + // plugins ServerPluginJarsInstaller.class, ServerPluginJarInstaller.class, diff --git a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java index 222fbe111db..b3f764ebc58 100644 --- a/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java +++ b/sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java @@ -35,11 +35,7 @@ import org.sonar.api.test.MutableTestPlan; import org.sonar.api.test.MutableTestable; import org.sonar.api.test.TestPlan; import org.sonar.api.test.Testable; -import org.sonar.api.web.Footer; -import org.sonar.api.web.NavigationSection; -import org.sonar.api.web.Page; -import org.sonar.api.web.RubyRailsWebservice; -import org.sonar.api.web.Widget; +import org.sonar.api.web.*; import org.sonar.core.component.SnapshotPerspectives; import org.sonar.core.measure.MeasureFilterEngine; import org.sonar.core.measure.MeasureFilterResult; @@ -54,11 +50,7 @@ import org.sonar.server.platform.Platform; import org.sonar.server.platform.ServerIdGenerator; import org.sonar.server.platform.ServerSettings; import org.sonar.server.platform.SettingsChangeNotifier; -import org.sonar.server.plugins.InstalledPluginReferentialFactory; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerPluginJarsInstaller; -import org.sonar.server.plugins.ServerPluginRepository; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.plugins.*; import org.sonar.server.rule.RuleRepositories; import org.sonar.server.source.CodeColorizers; import org.sonar.server.user.NewUserNotifier; @@ -68,6 +60,7 @@ import org.sonar.updatecenter.common.Version; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.net.InetAddress; import java.sql.Connection; import java.util.Collection; @@ -243,6 +236,7 @@ public final class JRubyFacade { return get(Database.class); } + // Only used by Java migration public DatabaseMigrator databaseMigrator() { return get(DatabaseMigrator.class); } diff --git a/sonar-server/src/main/webapp/WEB-INF/lib/database_version.rb b/sonar-server/src/main/webapp/WEB-INF/lib/database_version.rb index eec43b43741..24415ce1c0e 100644 --- a/sonar-server/src/main/webapp/WEB-INF/lib/database_version.rb +++ b/sonar-server/src/main/webapp/WEB-INF/lib/database_version.rb @@ -70,7 +70,6 @@ class DatabaseVersion def self.automatic_setup if current_version<=0 - try_restore_structure_dump() upgrade_and_start() elsif uptodate? load_java_web_services @@ -84,20 +83,6 @@ class DatabaseVersion ActiveRecord::Base.connected? end - def self.try_restore_structure_dump() - ::Java::OrgSonarServerUi::JRubyFacade.getInstance().databaseMigrator().createDatabase() - end - - def self.execute_sql_requests(requests) - requests.each do |request| - unless request.blank? || request.start_with?('--') - request.chomp! - request.chop! if request.end_with?(';') - ActiveRecord::Base.connection.execute(request) - end - end - end - def self.dialect ::Java::OrgSonarServerUi::JRubyFacade.getInstance().getDatabase().getDialect().getActiveRecordDialectCode() end diff --git a/sonar-server/src/test/java/org/sonar/server/db/migrations/DatabaseMigratorTest.java b/sonar-server/src/test/java/org/sonar/server/db/migrations/DatabaseMigratorTest.java index 84ebdd355b7..423e54a100a 100644 --- a/sonar-server/src/test/java/org/sonar/server/db/migrations/DatabaseMigratorTest.java +++ b/sonar-server/src/test/java/org/sonar/server/db/migrations/DatabaseMigratorTest.java @@ -20,9 +20,11 @@ package org.sonar.server.db.migrations; import org.apache.ibatis.session.SqlSession; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.platform.ServerUpgradeStatus; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.MyBatis; @@ -43,13 +45,19 @@ public class DatabaseMigratorTest extends AbstractDaoTestCase { MyBatis mybatis = mock(MyBatis.class); Database database = mock(Database.class); DatabaseMigration[] migrations = new DatabaseMigration[]{new FakeMigration()}; + ServerUpgradeStatus serverUpgradeStatus = mock(ServerUpgradeStatus.class); + + DatabaseMigrator migrator; + + @Before + public void setUp() throws Exception { + migrator = new DatabaseMigrator(mybatis, database, migrations, serverUpgradeStatus, null); + } @Test public void should_support_only_creation_of_h2_database() throws Exception { when(database.getDialect()).thenReturn(new MySql()); - DatabaseMigrator migrator = new DatabaseMigrator(mybatis, database, migrations); - assertThat(migrator.createDatabase()).isFalse(); verifyZeroInteractions(mybatis); } @@ -59,13 +67,11 @@ public class DatabaseMigratorTest extends AbstractDaoTestCase { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Database migration not found: org.xxx.UnknownMigration"); - DatabaseMigrator migrator = new DatabaseMigrator(mybatis, database, migrations); migrator.executeMigration("org.xxx.UnknownMigration"); } @Test public void execute_migration() throws Exception { - DatabaseMigrator migrator = new DatabaseMigrator(mybatis, database, migrations); assertThat(FakeMigration.executed).isFalse(); migrator.executeMigration(FakeMigration.class.getName()); assertThat(FakeMigration.executed).isTrue(); @@ -73,15 +79,15 @@ public class DatabaseMigratorTest extends AbstractDaoTestCase { @Test public void should_create_schema_on_h2() throws Exception { - Dialect supportedDialect = new H2(); when(database.getDialect()).thenReturn(supportedDialect); Connection connection = mock(Connection.class); SqlSession session = mock(SqlSession.class); when(session.getConnection()).thenReturn(connection); when(mybatis.openSession()).thenReturn(session); + when(serverUpgradeStatus.isFreshInstall()).thenReturn(true); - DatabaseMigrator databaseMigrator = new DatabaseMigrator(mybatis, database, migrations) { + DatabaseMigrator databaseMigrator = new DatabaseMigrator(mybatis, database, migrations, serverUpgradeStatus, null) { @Override protected void createSchema(Connection connection, String dialectId) { } diff --git a/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java b/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java index 5f2f1f26f1e..4a92e0a79b3 100644 --- a/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java +++ b/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java @@ -22,7 +22,6 @@ package org.sonar.server.tester; import org.apache.commons.io.FileUtils; import org.sonar.api.CoreProperties; -import org.sonar.server.db.migrations.DatabaseMigrator; import org.sonar.server.platform.Platform; import java.io.File; @@ -49,7 +48,6 @@ public class ServerTester { properties.setProperty("sonar.jdbc.url", "jdbc:h2:" + temp.getAbsolutePath() + "/h2"); platform.init(properties); - ((DatabaseMigrator) platform.getComponent(DatabaseMigrator.class)).createDatabase(); platform.doStart(); } -- 2.39.5