diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2016-12-08 18:25:20 +0100 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2016-12-14 12:11:52 +0100 |
commit | ff766539d309bbaaa416b2ce05d8d478086b6176 (patch) | |
tree | efdabc29e2931b71429f114090b2e098a0183829 | |
parent | ea8ca620bf1792afe96bdcf2b24b943fa812a415 (diff) | |
download | sonarqube-ff766539d309bbaaa416b2ce05d8d478086b6176.tar.gz sonarqube-ff766539d309bbaaa416b2ce05d8d478086b6176.zip |
SONAR-8445 run DB migration of SQ 6.3 with Java
add Java toolkit to run DB migrations
port last 3 DB migrations (ie. from current SQ 6.3) to this Java toolkit and make it run after Ruby migration
67 files changed, 2572 insertions, 122 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationEngineModule.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationEngineModule.java new file mode 100644 index 00000000000..69ca3930f5f --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationEngineModule.java @@ -0,0 +1,49 @@ +/* + * 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; + +import org.sonar.core.platform.Module; +import org.sonar.server.platform.db.migration.engine.MigrationContainerPopulatorImpl; +import org.sonar.server.platform.db.migration.engine.MigrationEngineImpl; +import org.sonar.server.platform.db.migration.history.MigrationHistoryImpl; +import org.sonar.server.platform.db.migration.step.MigrationStepRegistryImpl; +import org.sonar.server.platform.db.migration.step.MigrationStepsProvider; + +/** + * Defines the components for the migration engine. This does not include the + * {@link org.sonar.server.platform.db.migration.version.DbVersion} classes which are bundled together in + * {@link org.sonar.server.platform.db.migration.version.DbVersionModule}. + */ +public class MigrationEngineModule extends Module { + @Override + protected void configureModule() { + add( + // migration steps + MigrationStepRegistryImpl.class, + new MigrationStepsProvider(), + + // history + MigrationHistoryImpl.class, + + // engine + MigrationContainerPopulatorImpl.class, + MigrationEngineImpl.class); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainer.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainer.java new file mode 100644 index 00000000000..507a124ca1d --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainer.java @@ -0,0 +1,42 @@ +/* + * 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.engine; + +import org.sonar.core.platform.ContainerPopulator; + +/** + * A dedicated container used to run DB migrations where all components are lazily instantiated. + * <p> + * As a new container will be created for each run of DB migrations, components in this container can safely be + * stateful. + * </p> + * <p> + * Lazy instantiation is convenient to instantiate {@link org.sonar.server.platform.db.migration.step.MigrationStep} + * classes only they really are to be executed. + * </p> + */ +public interface MigrationContainer extends ContainerPopulator.Container { + + /** + * Cleans up resources after migration has run. + * <strong>This method must never fail.</strong> + */ + void cleanup(); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerImpl.java new file mode 100644 index 00000000000..b998879eb85 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerImpl.java @@ -0,0 +1,70 @@ +/* + * 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.engine; + +import org.picocontainer.ComponentAdapter; +import org.picocontainer.ComponentMonitor; +import org.picocontainer.DefaultPicoContainer; +import org.picocontainer.MutablePicoContainer; +import org.picocontainer.behaviors.OptInCaching; +import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; +import org.picocontainer.monitors.NullComponentMonitor; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.core.platform.ComponentContainer; + +import static java.util.Objects.requireNonNull; + +public class MigrationContainerImpl extends ComponentContainer implements MigrationContainer { + + public MigrationContainerImpl(ComponentContainer parent, MigrationContainerPopulator populator) { + super(createContainer(requireNonNull(parent)), parent.getComponentByType(PropertyDefinitions.class)); + + populateContainer(requireNonNull(populator)); + startComponents(); + } + + private void populateContainer(MigrationContainerPopulator populator) { + populator.populateContainer(this); + } + + /** + * Creates a PicContainer which extends the specified ComponentContainer <strong>but is not referenced in return</strong>. + */ + private static MutablePicoContainer createContainer(ComponentContainer parent) { + ComponentMonitor componentMonitor = new NullComponentMonitor(); + ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(componentMonitor, "start", "stop", "close") { + @Override + public boolean isLazy(ComponentAdapter<?> adapter) { + return true; + } + }; + return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, parent.getPicoContainer(), componentMonitor); + } + + @Override + public void cleanup() { + stopComponents(true); + } + + @Override + public String toString() { + return "MigrationContainerImpl"; + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulator.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulator.java new file mode 100644 index 00000000000..23de1b24068 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulator.java @@ -0,0 +1,25 @@ +/* + * 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.engine; + +import org.sonar.core.platform.ContainerPopulator; + +public interface MigrationContainerPopulator extends ContainerPopulator<MigrationContainer> { +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulatorImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulatorImpl.java new file mode 100644 index 00000000000..b5e81d24606 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulatorImpl.java @@ -0,0 +1,45 @@ +/* + * 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.engine; + +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.MigrationStepsExecutorImpl; + +/** + * Responsible for: + * <ul> + * <li>adding all the {@link MigrationStep} classes to the container after building it</li> + * <li>adding dependencies for them to the container if there aren't already available in parent container</li> + * <li>adding the {@link MigrationStepsExecutorImpl} to the container</li> + * </ul> + */ +public class MigrationContainerPopulatorImpl implements MigrationContainerPopulator { + @Override + public void populateContainer(MigrationContainer container) { + container.add(MigrationStepsExecutorImpl.class); + populateFromMigrationSteps(container); + } + + private static void populateFromMigrationSteps(MigrationContainer container) { + MigrationSteps migrationSteps = container.getComponentByType(MigrationSteps.class); + migrationSteps.readAll().forEach(step -> container.add(step.getStepClass())); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngine.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngine.java new file mode 100644 index 00000000000..51489a17122 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngine.java @@ -0,0 +1,33 @@ +/* + * 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.engine; + +/** + * This class is responsible for: + * <ul> + * <li>creating a dedicate container to run the migrations, populating it and starting it</li> + * <li>resolving the migration starting point</li> + * <li>starting the db migration execution</li> + * <li>stop the container and dispose of it</li> + * </ul> + */ +public interface MigrationEngine { + void execute(); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java new file mode 100644 index 00000000000..5a0af4a5870 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImpl.java @@ -0,0 +1,57 @@ +/* + * 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.engine; + +import java.util.Optional; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.platform.db.migration.history.MigrationHistory; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.MigrationStepsExecutor; + +public class MigrationEngineImpl implements MigrationEngine { + private final MigrationHistory migrationHistory; + private final ComponentContainer serverContainer; + private final MigrationContainerPopulator populator; + private final MigrationSteps migrationSteps; + + public MigrationEngineImpl(MigrationHistory migrationHistory, ComponentContainer serverContainer, MigrationContainerPopulator populator, MigrationSteps migrationSteps) { + this.migrationHistory = migrationHistory; + this.serverContainer = serverContainer; + this.populator = populator; + this.migrationSteps = migrationSteps; + } + + @Override + public void execute() { + MigrationContainer migrationContainer = new MigrationContainerImpl(serverContainer, populator); + + try { + MigrationStepsExecutor stepsExecutor = migrationContainer.getComponentByType(MigrationStepsExecutor.class); + Optional<Long> lastMigrationNumber = migrationHistory.getLastMigrationNumber(); + if (lastMigrationNumber.isPresent()) { + stepsExecutor.execute(migrationSteps.readFrom(lastMigrationNumber.get() + 1)); + } else { + stepsExecutor.execute(migrationSteps.readAll()); + } + } finally { + migrationContainer.cleanup(); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/package-info.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/package-info.java new file mode 100644 index 00000000000..8b76f6afe22 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/engine/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.engine; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistory.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistory.java new file mode 100644 index 00000000000..cc4edfaf402 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistory.java @@ -0,0 +1,56 @@ +/* + * 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.util.Optional; +import org.sonar.api.Startable; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +/** + * This class is responsible for providing methods to read and write information from the persisted + * history. + * <p> + * This class assumes the Migration History table exists (see {@link MigrationHistoryTable}) and will + * fail at startup (see {@link #start()}) if it doesn't. + * </p> + */ +public interface MigrationHistory extends Startable { + /** + * @throws IllegalStateException if the Migration History table does not exist. + */ + @Override + void start(); + + /** + * Returns the last execute migration number according to the persistence information. + * + * @return a long >= 0. + */ + Optional<Long> getLastMigrationNumber(); + + /** + * Saves in persisted migration history the fact that the specified {@link RegisteredMigrationStep} has + * been successfully executed. + * + * @throws RuntimeException if the information can not be persisted (and committed). + */ + void done(RegisteredMigrationStep dbMigration); + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java new file mode 100644 index 00000000000..9c00b983a8b --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImpl.java @@ -0,0 +1,84 @@ +/* + * 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 com.google.common.base.Throwables; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.sonar.db.DatabaseUtils; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.version.SchemaMigrationMapper; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +import static com.google.common.base.Preconditions.checkState; + +/** + * TODO make {@link org.sonar.db.version.DatabaseVersion} use this class + */ +public class MigrationHistoryImpl implements MigrationHistory { + private final DbClient dbClient; + + public MigrationHistoryImpl(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public void start() { + try (Connection connection = dbClient.getDatabase().getDataSource().getConnection()) { + checkState(DatabaseUtils.tableExists(MigrationHistoryTable.NAME, connection), "Migration history table is missing"); + } catch (SQLException e) { + Throwables.propagate(e); + } + } + + @Override + public void stop() { + // nothing to do + } + + @Override + public Optional<Long> getLastMigrationNumber() { + try (DbSession dbSession = dbClient.openSession(false)) { + List<Integer> versions = getMapper(dbSession).selectVersions(); + + if (!versions.isEmpty()) { + Collections.sort(versions); + return Optional.of(versions.get(versions.size() - 1).longValue()); + } + return Optional.empty(); + } + } + + @Override + public void done(RegisteredMigrationStep dbMigration) { + try (DbSession dbSession = dbClient.openSession(false)) { + getMapper(dbSession).insert(String.valueOf(dbMigration.getMigrationNumber())); + dbSession.commit(); + } + } + + private static SchemaMigrationMapper getMapper(DbSession dbSession) { + return dbSession.getMapper(SchemaMigrationMapper.class); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/InternalMigrationStepRegistry.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/InternalMigrationStepRegistry.java new file mode 100644 index 00000000000..de0c450e2a9 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/InternalMigrationStepRegistry.java @@ -0,0 +1,27 @@ +/* + * 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.step; + +public interface InternalMigrationStepRegistry extends MigrationStepRegistry { + /** + * @throws IllegalStateException if the registry is empty + */ + MigrationSteps build(); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationNumber.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationNumber.java new file mode 100644 index 00000000000..93e5ba9841d --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationNumber.java @@ -0,0 +1,33 @@ +/* + * 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.step; + +import static com.google.common.base.Preconditions.checkArgument; + +public final class MigrationNumber { + private MigrationNumber() { + // prevents instantiation + } + + public static void validate(long migrationNumber) { + checkArgument(migrationNumber >= 0, "Migration number must be >= 0"); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStep.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStep.java new file mode 100644 index 00000000000..c04ed0f6362 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStep.java @@ -0,0 +1,27 @@ +/* + * 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.step; + +import java.sql.SQLException; + +public interface MigrationStep { + + void execute() throws SQLException; +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepExecutionException.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepExecutionException.java new file mode 100644 index 00000000000..6c9cc124485 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepExecutionException.java @@ -0,0 +1,44 @@ +/* + * 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.step; + +import static java.util.Objects.requireNonNull; + +public class MigrationStepExecutionException extends RuntimeException { + private final transient RegisteredMigrationStep failingStep; + + public MigrationStepExecutionException(RegisteredMigrationStep failingStep, Throwable cause) { + super(createMessage(failingStep), requireNonNull(cause, "cause can't be null")); + this.failingStep = failingStep; + } + + private static String createMessage(RegisteredMigrationStep failingStep) { + check(failingStep); + return String.format("Execution of migration step %s failed", failingStep); + } + + private static RegisteredMigrationStep check(RegisteredMigrationStep failingStep) { + return requireNonNull(failingStep, "RegisteredMigrationStep can't be null"); + } + + public RegisteredMigrationStep getFailingStep() { + return failingStep; + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistry.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistry.java new file mode 100644 index 00000000000..8c19a2bd2f4 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistry.java @@ -0,0 +1,29 @@ +/* + * 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.step; + +public interface MigrationStepRegistry { + /** + * + * @throws IllegalArgumentException if migrationNumber is < 0. + * @throws IllegalStateException if a db migration is already registered for the specified migrationNumber + */ + <T extends MigrationStep> MigrationStepRegistry add(long migrationNumber, String description, Class<T> stepClass); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistryImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistryImpl.java new file mode 100644 index 00000000000..9310bcfbb65 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistryImpl.java @@ -0,0 +1,60 @@ +/* + * 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.step; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.sonar.core.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; +import static org.sonar.server.platform.db.migration.step.MigrationNumber.validate; + +public class MigrationStepRegistryImpl implements InternalMigrationStepRegistry { + private final Map<Long, RegisteredMigrationStep> migrations = new HashMap<>(); + + @Override + public <T extends MigrationStep> MigrationStepRegistry add(long migrationNumber, String description, Class<T> stepClass) { + validate(migrationNumber); + requireNonNull(description, "description can't be null"); + checkArgument(!description.isEmpty(), "description can't be empty"); + requireNonNull(stepClass, "MigrationStep class can't be null"); + checkState(!migrations.containsKey(migrationNumber), "A migration is already registered for migration number '%s'", migrationNumber); + this.migrations.put(migrationNumber, new RegisteredMigrationStep(migrationNumber, description, stepClass)); + return this; + } + + @Override + public MigrationSteps build() { + checkState(!migrations.isEmpty(), "Registry is empty"); + return new MigrationStepsImpl(toOrderedList(this.migrations)); + } + + private static List<RegisteredMigrationStep> toOrderedList(Map<Long, RegisteredMigrationStep> migrations) { + return migrations.entrySet().stream() + .sorted(Comparator.comparingLong(Map.Entry::getKey)) + .map(Map.Entry::getValue) + .collect(Collectors.toList(migrations.size())); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationSteps.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationSteps.java new file mode 100644 index 00000000000..94296f4d4cc --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationSteps.java @@ -0,0 +1,39 @@ +/* + * 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.step; + +import java.util.stream.Stream; + +public interface MigrationSteps { + /** + * @return the migration number of the last migration step. + */ + long getMaxMigrationNumber(); + + /** + * Reads all migration steps in order of increasing migration number. + */ + Stream<RegisteredMigrationStep> readAll(); + + /** + * Reads migration steps, in order of increasing migration number, from the specified migration number <strong>included</strong>. + */ + Stream<RegisteredMigrationStep> readFrom(long migrationNumber); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutor.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutor.java new file mode 100644 index 00000000000..c52f9b61ba9 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutor.java @@ -0,0 +1,38 @@ +/* + * 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.step; + +import java.util.stream.Stream; + +/** + * Responsible for: + * <ul> + * <li>looping over all the {@link MigrationStep} to execute</li> + * <li>put INFO log between each {@link MigrationStep} for user information</li> + * <li>handle errors during the execution of {@link MigrationStep}</li> + * <li>update the content of table {@code SCHEMA_MIGRATION}</li> + * </ul> + */ +public interface MigrationStepsExecutor { + /** + * @throws MigrationStepExecutionException at the first failing migration step execution + */ + void execute(Stream<RegisteredMigrationStep> steps); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutorImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutorImpl.java new file mode 100644 index 00000000000..331b9287efa --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutorImpl.java @@ -0,0 +1,88 @@ +/* + * 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.step; + +import java.util.stream.Stream; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.util.logs.Profiler; +import org.sonar.server.platform.db.migration.engine.MigrationContainer; +import org.sonar.server.platform.db.migration.history.MigrationHistory; + +import static com.google.common.base.Preconditions.checkState; + +public class MigrationStepsExecutorImpl implements MigrationStepsExecutor { + private static final Logger LOGGER = Loggers.get("DbMigrations"); + private static final String GLOBAL_START_MESSAGE = "Executing DB migrations..."; + private static final String GLOBAL_END_MESSAGE = "Executed DB migrations: {}"; + private static final String STEP_START_PATTERN = "{}..."; + private static final String STEP_STOP_PATTERN = "{}: {}"; + + private final MigrationContainer migrationContainer; + private final MigrationHistory migrationHistory; + + public MigrationStepsExecutorImpl(MigrationContainer migrationContainer, MigrationHistory migrationHistory) { + this.migrationContainer = migrationContainer; + this.migrationHistory = migrationHistory; + } + + @Override + public void execute(Stream<RegisteredMigrationStep> steps) { + Profiler globalProfiler = Profiler.create(LOGGER); + globalProfiler.startInfo(GLOBAL_START_MESSAGE); + boolean allStepsExecuted = false; + try { + steps.forEachOrdered(this::execute); + allStepsExecuted = true; + } finally { + if (allStepsExecuted) { + globalProfiler.stopInfo(GLOBAL_END_MESSAGE, "success"); + } else { + globalProfiler.stopError(GLOBAL_END_MESSAGE, "failure"); + } + } + } + + private void execute(RegisteredMigrationStep step) { + MigrationStep migrationStep = migrationContainer.getComponentByType(step.getStepClass()); + checkState(migrationStep != null, "Can not find instance of " + step.getStepClass()); + + execute(step, migrationStep); + } + + private void execute(RegisteredMigrationStep step, MigrationStep migrationStep) { + Profiler stepProfiler = Profiler.create(LOGGER); + stepProfiler.startInfo(STEP_START_PATTERN, step); + boolean done = false; + try { + migrationStep.execute(); + migrationHistory.done(step); + done = true; + } catch (Exception e) { + throw new MigrationStepExecutionException(step, e); + } finally { + if (done) { + stepProfiler.stopInfo(STEP_STOP_PATTERN, step, "success"); + } else { + stepProfiler.stopError(STEP_STOP_PATTERN, step, "failure"); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsImpl.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsImpl.java new file mode 100644 index 00000000000..fd07a47a320 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsImpl.java @@ -0,0 +1,69 @@ +/* + * 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.step; + +import java.util.List; +import java.util.stream.Stream; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.copyOf; +import static java.util.Objects.requireNonNull; +import static org.sonar.server.platform.db.migration.step.MigrationNumber.validate; + +class MigrationStepsImpl implements MigrationSteps { + private final List<RegisteredMigrationStep> steps; + + MigrationStepsImpl(List<RegisteredMigrationStep> steps) { + requireNonNull(steps, "steps can't be null"); + checkArgument(!steps.isEmpty(), "steps can't be empty"); + this.steps = copyOf(steps); + } + + @Override + public long getMaxMigrationNumber() { + return steps.get(steps.size() -1).getMigrationNumber(); + } + + @Override + public Stream<RegisteredMigrationStep> readAll() { + return steps.stream(); + } + + @Override + public Stream<RegisteredMigrationStep> readFrom(long migrationNumber) { + validate(migrationNumber); + int startingIndex = lookupIndexOfClosestTo(migrationNumber); + if (startingIndex < 0) { + return Stream.empty(); + } + return steps.subList(startingIndex, steps.size()).stream(); + } + + private int lookupIndexOfClosestTo(long startingPoint) { + int index = 0; + for (RegisteredMigrationStep step : steps) { + if (step.getMigrationNumber() >= startingPoint) { + return index; + } + index++; + } + return -1; + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsProvider.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsProvider.java new file mode 100644 index 00000000000..a599af60917 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/MigrationStepsProvider.java @@ -0,0 +1,44 @@ +/* + * 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.step; + +import java.util.Arrays; +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.server.platform.db.migration.version.DbVersion; + +/** + * This class is responsible for providing the {@link MigrationSteps} to be injected in classes that need it and + * ensures that there's only one such instance. + */ +public class MigrationStepsProvider extends ProviderAdapter { + private MigrationSteps migrationSteps; + + public MigrationSteps provide(InternalMigrationStepRegistry migrationStepRegistry, DbVersion... dbVersions) { + if (migrationSteps == null) { + migrationSteps = buildMigrationSteps(migrationStepRegistry, dbVersions); + } + return migrationSteps; + } + + private static MigrationSteps buildMigrationSteps(InternalMigrationStepRegistry migrationStepRegistry, DbVersion[] dbVersions) { + Arrays.stream(dbVersions).forEach(dbVersion -> dbVersion.addSteps(migrationStepRegistry)); + return migrationStepRegistry.build(); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/RegisteredMigrationStep.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/RegisteredMigrationStep.java new file mode 100644 index 00000000000..67f3d1cafdb --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/RegisteredMigrationStep.java @@ -0,0 +1,51 @@ +/* + * 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.step; + +import static java.util.Objects.requireNonNull; + +public final class RegisteredMigrationStep { + private final long migrationNumber; + private final String description; + private final Class<? extends MigrationStep> stepClass; + + public RegisteredMigrationStep(long migrationNumber, String description, Class<? extends MigrationStep> migration) { + this.migrationNumber = migrationNumber; + this.description = requireNonNull(description, "description can't be null"); + this.stepClass = requireNonNull(migration, "MigrationStep class can't be null"); + } + + public long getMigrationNumber() { + return migrationNumber; + } + + public String getDescription() { + return description; + } + + public Class<? extends MigrationStep> getStepClass() { + return stepClass; + } + + @Override + public String toString() { + return "#" + migrationNumber + " '" + description + "'"; + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v63/package-info.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/package-info.java index 8908b7cd4ca..1e449f74624 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/v63/package-info.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/package-info.java @@ -17,9 +17,8 @@ * 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.db.version.v63; +package org.sonar.server.platform.db.migration.step; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/DbVersion.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/DbVersion.java new file mode 100644 index 00000000000..971d399967e --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/DbVersion.java @@ -0,0 +1,26 @@ +/* + * 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.version; + +import org.sonar.server.platform.db.migration.step.MigrationStepRegistry; + +public interface DbVersion { + void addSteps(MigrationStepRegistry registry); +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/DbVersionModule.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/DbVersionModule.java new file mode 100644 index 00000000000..3047464206d --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/DbVersionModule.java @@ -0,0 +1,31 @@ +/* + * 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.version; + +import org.sonar.core.platform.Module; +import org.sonar.server.platform.db.migration.version.v63.DbVersion63; + +public class DbVersionModule extends Module { + @Override + protected void configureModule() { + add(DbVersion63.class); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/package-info.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/package-info.java new file mode 100644 index 00000000000..c31514b0f5d --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/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.version; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-db/src/main/java/org/sonar/db/version/v63/AddUuidToEvents.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/AddUuidToEvents.java index 220ba52ca54..9f4b53bdcf6 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/v63/AddUuidToEvents.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/AddUuidToEvents.java @@ -18,13 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.db.version.v63; +package org.sonar.server.platform.db.migration.version.v63; import java.sql.SQLException; import org.sonar.db.Database; import org.sonar.db.version.AddColumnsBuilder; -import org.sonar.db.version.DdlChange; import org.sonar.db.version.VarcharColumnDef; +import org.sonar.server.platform.db.migration.step.DdlChange; import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/DbVersion63.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/DbVersion63.java new file mode 100644 index 00000000000..03800cd11dc --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/DbVersion63.java @@ -0,0 +1,33 @@ +/* + * 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.version.v63; + +import org.sonar.server.platform.db.migration.step.MigrationStepRegistry; +import org.sonar.server.platform.db.migration.version.DbVersion; + +public class DbVersion63 implements DbVersion { + @Override + public void addSteps(MigrationStepRegistry registry) { + registry + .add(1500, "Add Events.UUID", AddUuidToEvents.class) + .add(1501, "Populate Events.UUID", PopulateUuidColumnOfEvents.class) + .add(1502, "Make Events.UUID not nullable", MakeUuidNotNullOnEvents.class); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v63/MakeUuidNotNullOnEvents.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/MakeUuidNotNullOnEvents.java index e8e3bc2db47..972c7ef9b0b 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/v63/MakeUuidNotNullOnEvents.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/MakeUuidNotNullOnEvents.java @@ -17,14 +17,14 @@ * 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.db.version.v63; +package org.sonar.server.platform.db.migration.version.v63; import java.sql.SQLException; import org.sonar.db.Database; import org.sonar.db.version.AlterColumnsBuilder; import org.sonar.db.version.CreateIndexBuilder; -import org.sonar.db.version.DdlChange; import org.sonar.db.version.VarcharColumnDef; +import org.sonar.server.platform.db.migration.step.DdlChange; import static org.sonar.db.version.VarcharColumnDef.UUID_SIZE; import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder; diff --git a/sonar-db/src/main/java/org/sonar/db/version/v63/PopulateUuidColumnOfEvents.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/PopulateUuidColumnOfEvents.java index 698d29f188e..f4cecbffbd6 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/v63/PopulateUuidColumnOfEvents.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/PopulateUuidColumnOfEvents.java @@ -18,17 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.db.version.v63; +package org.sonar.server.platform.db.migration.version.v63; import java.sql.SQLException; import org.sonar.core.util.UuidFactory; import org.sonar.db.Database; -import org.sonar.db.version.BaseDataChange; import org.sonar.db.version.MassUpdate; import org.sonar.db.version.Select; import org.sonar.db.version.SqlStatement; +import org.sonar.server.platform.db.migration.step.DataChange; -public class PopulateUuidColumnOfEvents extends BaseDataChange { +public class PopulateUuidColumnOfEvents extends DataChange { private final UuidFactory uuidFactory; @@ -38,7 +38,7 @@ public class PopulateUuidColumnOfEvents extends BaseDataChange { } @Override - public void execute(Context context) throws SQLException { + protected void execute(Context context) throws SQLException { MassUpdate massUpdate = context.prepareMassUpdate(); massUpdate.select("SELECT e.id from events e where e.uuid is null"); massUpdate.update("UPDATE events SET uuid=? WHERE id=?"); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/package-info.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/package-info.java new file mode 100644 index 00000000000..86bf6ad3104 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v63/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.version.v63; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationEngineModuleTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationEngineModuleTest.java new file mode 100644 index 00000000000..fb5bb4451b5 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationEngineModuleTest.java @@ -0,0 +1,41 @@ +/* + * 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; + +import org.junit.Test; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationEngineModuleTest { + private static final int COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER = 2; + + private MigrationEngineModule underTest = new MigrationEngineModule(); + + @Test + public void verify_component_count() { + ComponentContainer container = new ComponentContainer(); + + underTest.configure(container); + + assertThat(container.getPicoContainer().getComponentAdapters()) + .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 5); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationContainerImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationContainerImplTest.java new file mode 100644 index 00000000000..82a9402fd64 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationContainerImplTest.java @@ -0,0 +1,96 @@ +/* + * 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.engine; + +import org.junit.Test; +import org.picocontainer.Startable; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationContainerImplTest { + private ComponentContainer parent = new ComponentContainer(); + private MigrationContainerPopulator populator = new MigrationContainerPopulator() { + @Override + public void populateContainer(MigrationContainer container) { + container.add(StartCallCounter.class); + } + }; + + private MigrationContainerImpl underTest = new MigrationContainerImpl(parent, populator); + + @Test + public void pico_container_of_migration_container_has_pico_container_of_specified_container_as_parent() { + assertThat(underTest.getPicoContainer().getParent()).isEqualTo(parent.getPicoContainer()); + } + + @Test + public void pico_container_of_parent_does_not_have_pico_container_of_migration_container_as_child() { + assertThat(parent.getPicoContainer().removeChildContainer(underTest.getPicoContainer())).isFalse(); + } + + @Test + public void pico_container_of_migration_container_is_started_in_constructor() { + assertThat(underTest.getPicoContainer().getLifecycleState().isStarted()).isTrue(); + } + + @Test + public void migration_container_lazily_instance_components() { + assertThat(StartCallCounter.startCalls).isEqualTo(0); + + StartCallCounter startCallCounter = underTest.getComponentByType(StartCallCounter.class); + + assertThat(startCallCounter).isNotNull(); + assertThat(StartCallCounter.startCalls).isEqualTo(1); + } + + @Test + public void cleanup_does_not_fail_even_if_stop_of_component_fails() { + MigrationContainerImpl underTest = new MigrationContainerImpl(parent, (container -> container.add(StopFailing.class))); + + underTest.cleanup(); + } + + public static final class StartCallCounter implements Startable { + private static int startCalls = 0; + + @Override + public void start() { + startCalls++; + } + + @Override + public void stop() { + // do nothing + } + } + + public static final class StopFailing implements Startable { + @Override + public void start() { + // do nothing + } + + @Override + public void stop() { + throw new RuntimeException("Faking stop call failing"); + } + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulatorImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulatorImplTest.java new file mode 100644 index 00000000000..a0cc4d54bfe --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationContainerPopulatorImplTest.java @@ -0,0 +1,92 @@ +/* + * 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.engine; + +import java.sql.SQLException; +import java.util.stream.Stream; +import org.junit.Before; +import org.junit.Test; +import org.sonar.server.platform.db.migration.history.MigrationHistory; +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.MigrationStepsExecutorImpl; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MigrationContainerPopulatorImplTest { + private MigrationContainer migrationContainer = new SimpleMigrationContainer(); + private MigrationSteps migrationSteps = mock(MigrationSteps.class); + private MigrationContainerPopulatorImpl underTest = new MigrationContainerPopulatorImpl(); + + @Before + public void setUp() throws Exception { + migrationContainer.add(migrationSteps); + } + + @Test + public void populateContainer_adds_MigrationStepsExecutorImpl() { + when(migrationSteps.readAll()).thenReturn(Stream.empty()); + + // add MigrationStepsExecutorImpl's dependencies + migrationContainer.add(mock(MigrationHistory.class)); + + underTest.populateContainer(migrationContainer); + + assertThat(migrationContainer.getComponentByType(MigrationStepsExecutorImpl.class)).isNotNull(); + } + + @Test + public void populateContainer_adds_classes_of_all_steps_defined_in_MigrationSteps() { + when(migrationSteps.readAll()).thenReturn(Stream.of( + new RegisteredMigrationStep(1, "foo", MigrationStep1.class), + new RegisteredMigrationStep(2, "bar", MigrationStep2.class), + new RegisteredMigrationStep(3, "dor", MigrationStep3.class) + )); + + underTest.populateContainer(migrationContainer); + + assertThat(migrationContainer.getComponentsByType(MigrationStep1.class)).isNotNull(); + assertThat(migrationContainer.getComponentsByType(MigrationStep2.class)).isNotNull(); + assertThat(migrationContainer.getComponentsByType(MigrationStep3.class)).isNotNull(); + } + + private static abstract class NoopMigrationStep implements MigrationStep { + @Override + public void execute() throws SQLException { + throw new UnsupportedOperationException("execute not implemented"); + } + } + + public static final class MigrationStep1 extends NoopMigrationStep { + + } + + public static final class MigrationStep2 extends NoopMigrationStep { + + } + + public static final class MigrationStep3 extends NoopMigrationStep { + + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImplTest.java new file mode 100644 index 00000000000..7e334fb18d4 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/MigrationEngineImplTest.java @@ -0,0 +1,74 @@ +/* + * 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.engine; + +import java.util.Optional; +import java.util.stream.Stream; +import org.junit.Test; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.platform.db.migration.history.MigrationHistory; +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.MigrationSteps; +import org.sonar.server.platform.db.migration.step.MigrationStepsExecutor; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class MigrationEngineImplTest { + private MigrationHistory migrationHistory = mock(MigrationHistory.class); + private ComponentContainer serverContainer = new ComponentContainer(); + private MigrationStepsExecutor stepsExecutor = mock(MigrationStepsExecutor.class); + private MigrationContainerPopulator populator = new MigrationContainerPopulator() { + @Override + public void populateContainer(MigrationContainer container) { + container.add(stepsExecutor); + } + }; + private MigrationSteps migrationSteps = mock(MigrationSteps.class); + + private MigrationEngineImpl underTest = new MigrationEngineImpl(migrationHistory, serverContainer, populator, migrationSteps); + + @Test + public void execute_execute_all_steps_of_there_is_no_last_migration_number() { + when(migrationHistory.getLastMigrationNumber()).thenReturn(Optional.empty()); + Stream<RegisteredMigrationStep> steps = Stream.of(new RegisteredMigrationStep(1, "doo", MigrationStep.class)); + when(migrationSteps.readAll()).thenReturn(steps); + + underTest.execute(); + + verify(migrationSteps).readAll(); + verify(stepsExecutor).execute(steps); + } + + @Test + public void execute_execute_steps_from_last_migration_number_plus_1() { + when(migrationHistory.getLastMigrationNumber()).thenReturn(Optional.of(50L)); + Stream<RegisteredMigrationStep> steps = Stream.of(new RegisteredMigrationStep(1, "doo", MigrationStep.class)); + when(migrationSteps.readFrom(51)).thenReturn(steps); + + underTest.execute(); + + verify(migrationSteps).readFrom(51); + verify(stepsExecutor).execute(steps); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/SimpleMigrationContainer.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/SimpleMigrationContainer.java new file mode 100644 index 00000000000..5b242bf9774 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/engine/SimpleMigrationContainer.java @@ -0,0 +1,30 @@ +/* + * 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.engine; + +import org.sonar.core.platform.ComponentContainer; + +public final class SimpleMigrationContainer extends ComponentContainer implements MigrationContainer { + + @Override + public void cleanup() { + stopComponents(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java new file mode 100644 index 00000000000..6ece014caf8 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/MigrationHistoryImplTest.java @@ -0,0 +1,81 @@ +/* + * 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.util.Arrays; +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 org.sonar.db.version.SchemaMigrationMapper; +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.RegisteredMigrationStep; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationHistoryImplTest { + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private DbSession session = dbTester.getSession(); + private SchemaMigrationMapper schemaMigrationMapper = session.getMapper(SchemaMigrationMapper.class); + + private MigrationHistoryImpl underTest = new MigrationHistoryImpl(dbTester.getDbClient()); + + @Test + public void start_does_not_fail_if_table_history_exists() { + underTest.start(); + } + + @Test + public void getLastMigrationNumber_returns_empty_if_history_table_is_empty() { + assertThat(underTest.getLastMigrationNumber()).isEmpty(); + } + + @Test + public void getLastMigrationNumber_returns_last_version_assuming_version_are_only_number() { + insert("12", "5", "30", "8"); + + assertThat(underTest.getLastMigrationNumber()).contains(30L); + } + + @Test + public void done_fails_with_NPE_if_argument_is_null() { + expectedException.expect(NullPointerException.class); + + underTest.done(null); + } + + @Test + public void done_adds_migration_number_to_table() { + underTest.done(new RegisteredMigrationStep(12, "aa", MigrationStep.class)); + + assertThat(underTest.getLastMigrationNumber()).contains(12L); + } + + private void insert(String... versions) { + Arrays.stream(versions).forEach(version -> schemaMigrationMapper.insert(version)); + session.commit(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java new file mode 100644 index 00000000000..b61a737352a --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java @@ -0,0 +1,46 @@ +/* + * 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.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; + +public class NoTableMigrationHistoryImplTest { + @Rule + public DbTester dbTester = DbTester.createForSchema(System2.INSTANCE, NoTableMigrationHistoryImplTest.class, "empty.sql"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private MigrationHistoryImpl underTest = new MigrationHistoryImpl(dbTester.getDbClient()); + + @Test + public void start_fails_with_ISE_if_table_history_does_not_exist() throws SQLException { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Migration history table is missing"); + + underTest.start(); + } + + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationNumberTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationNumberTest.java new file mode 100644 index 00000000000..cb2b391f218 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationNumberTest.java @@ -0,0 +1,54 @@ +/* + * 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.step; + +import java.util.Random; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.test.TestUtils; + +public class MigrationNumberTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void constructor_is_private() { + TestUtils.hasOnlyPrivateConstructors(MigrationNumber.class); + } + + @Test + public void validate_throws_IAE_if_argument_is_less_then_0() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Migration number must be >= 0"); + + MigrationNumber.validate(-(Math.abs(new Random().nextInt()) + 1)); + } + + @Test + public void validate_accepts_0() { + MigrationNumber.validate(0); + } + + @Test + public void validate_accepts_any_positive_long() { + MigrationNumber.validate(Math.abs(new Random().nextInt())); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepExecutionExceptionTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepExecutionExceptionTest.java new file mode 100644 index 00000000000..d77e083fdc4 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepExecutionExceptionTest.java @@ -0,0 +1,66 @@ +/* + * 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.step; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationStepExecutionExceptionTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private RegisteredMigrationStep step = new RegisteredMigrationStep(1, "foo", MigrationStep.class); + private MigrationStepExecutionException underTest = new MigrationStepExecutionException( + step, new IllegalArgumentException("some cause")); + + @Test + public void MigrationStepExecutionException_is_unchecked() { + assertThat(RuntimeException.class.isAssignableFrom(MigrationStepExecutionException.class)).isTrue(); + } + + @Test + public void constructor_throws_NPE_if_step_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("RegisteredMigrationStep can't be null"); + + new MigrationStepExecutionException(null, new NullPointerException("Some cause")); + } + + @Test + public void constructor_throws_NPE_if_cause_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("cause can't be null"); + + new MigrationStepExecutionException(new RegisteredMigrationStep(1, "foo", MigrationStep.class), null); + } + + @Test + public void constructor_sets_exception_message_from_step_argument() { + assertThat(underTest.getMessage()).isEqualTo("Execution of migration step #1 'foo' failed"); + } + + @Test + public void getFailingStep_returns_constructor_argument() { + assertThat(underTest.getFailingStep()).isSameAs(step); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistryImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistryImplTest.java new file mode 100644 index 00000000000..ec016716c19 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepRegistryImplTest.java @@ -0,0 +1,133 @@ +/* + * 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.step; + +import java.sql.SQLException; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationStepRegistryImplTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private MigrationStepRegistryImpl underTest = new MigrationStepRegistryImpl(); + + @Test + public void add_fails_with_IAE_if_migrationNumber_is_less_than_0() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Migration number must be >= 0"); + + underTest.add(-Math.abs(new Random().nextLong() + 1), "sdsd", MigrationStep.class); + } + + @Test + public void add_fails_with_NPE_if_description_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("description can't be null"); + + underTest.add(12, null, MigrationStep.class); + } + + @Test + public void add_fails_with_IAE_if_description_is_empty() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("description can't be empty"); + + underTest.add(12, "", MigrationStep.class); + } + + @Test + public void add_fails_with_NPE_is_migrationstep_class_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("MigrationStep class can't be null"); + + underTest.add(12, "sdsd", null); + } + + @Test + public void add_fails_with_ISE_when_called_twice_with_same_migration_number() { + underTest.add(12, "dsd", MigrationStep.class); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("A migration is already registered for migration number '12'"); + + underTest.add(12, "dfsdf", MigrationStep.class); + } + + @Test + public void build_fails_with_ISE_if_registry_is_empty() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Registry is empty"); + + underTest.build(); + } + + @Test + public void build_returns_a_MigrationStepsImpl_with_all_steps_added_to_registry_ordered_by_migration_number() { + underTest.add(343, "sss", MigrationStep2.class); + underTest.add(5, "aazsa", MigrationStep1.class); + underTest.add(66, "bbb", MigrationStep3.class); + underTest.add(2, "aaaa", MigrationStep4.class); + + MigrationSteps migrationSteps = underTest.build(); + assertThat(migrationSteps).isInstanceOf(MigrationStepsImpl.class); + List<RegisteredMigrationStep> registeredMigrationSteps = migrationSteps.readAll().collect(Collectors.toList()); + assertThat(registeredMigrationSteps).hasSize(4); + verify(registeredMigrationSteps.get(0), 2, "aaaa", MigrationStep4.class); + verify(registeredMigrationSteps.get(1), 5, "aazsa", MigrationStep1.class); + verify(registeredMigrationSteps.get(2), 66, "bbb", MigrationStep3.class); + verify(registeredMigrationSteps.get(3), 343, "sss", MigrationStep2.class); + } + + private static void verify(RegisteredMigrationStep step, int migrationNUmber, String description, Class<? extends MigrationStep> stepClass) { + assertThat(step.getMigrationNumber()).isEqualTo(migrationNUmber); + assertThat(step.getDescription()).isEqualTo(description); + assertThat(step.getStepClass()).isEqualTo(stepClass); + } + + private static abstract class NoopMigrationStep implements MigrationStep { + @Override + public void execute() throws SQLException { + throw new IllegalStateException("execute is not implemented"); + } + } + + private static class MigrationStep1 extends NoopMigrationStep { + + } + + private static class MigrationStep2 extends NoopMigrationStep { + + } + + private static class MigrationStep3 extends NoopMigrationStep { + + } + + private static class MigrationStep4 extends NoopMigrationStep { + + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutorImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutorImplTest.java new file mode 100644 index 00000000000..f5a1a0cba07 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsExecutorImplTest.java @@ -0,0 +1,213 @@ +/* + * 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.step; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; +import org.hamcrest.Matchers; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.server.platform.db.migration.engine.MigrationContainer; +import org.sonar.server.platform.db.migration.engine.SimpleMigrationContainer; +import org.sonar.server.platform.db.migration.history.MigrationHistory; + +import static com.google.common.base.Preconditions.checkState; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mock; + +public class MigrationStepsExecutorImplTest { + @Rule + public LogTester logTester = new LogTester(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private MigrationContainer migrationContainer = new SimpleMigrationContainer(); + private MigrationHistory migrationHistor = mock(MigrationHistory.class); + private MigrationStepsExecutorImpl underTest = new MigrationStepsExecutorImpl(migrationContainer, migrationHistor); + + @Test + public void execute_does_not_fail_when_stream_is_empty_and_log_start_stop_INFO() { + underTest.execute(Stream.empty()); + + assertThat(logTester.logs()).hasSize(2); + assertLogLevel(LoggerLevel.INFO, "Executing DB migrations...", "Executed DB migrations: success | time="); + } + + @Test + public void execute_fails_with_ISE_if_no_instance_of_computation_step_exist_in_container() { + Stream<RegisteredMigrationStep> steps = Stream.of(registeredStepOf(1, MigrationStep1.class)); + + try { + underTest.execute(steps); + fail("execute should have thrown a IllegalStateException"); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("Can not find instance of " + MigrationStep1.class); + } finally { + assertThat(logTester.logs()).hasSize(2); + assertLogLevel(LoggerLevel.INFO, "Executing DB migrations..."); + assertLogLevel(LoggerLevel.ERROR, "Executed DB migrations: failure | time="); + } + } + + private void assertLogLevel(LoggerLevel level, String... expected) { + List<String> logs = logTester.logs(level); + assertThat(logs).hasSize(expected.length); + Iterator<String> iterator = logs.iterator(); + Arrays.stream(expected).forEachOrdered(log -> { + if (log.endsWith(" | time=")) { + assertThat(iterator.next()).startsWith(log); + } else { + assertThat(iterator.next()).isEqualTo(log); + } + }); + } + + @Test + public void execute_execute_the_instance_of_type_specified_in_step_in_stream_order() { + migrationContainer.add(MigrationStep1.class, MigrationStep2.class, MigrationStep3.class); + + underTest.execute(Stream.of( + registeredStepOf(1, MigrationStep2.class), + registeredStepOf(2, MigrationStep1.class), + registeredStepOf(3, MigrationStep3.class))); + + assertThat(SingleCallCheckerMigrationStep.calledSteps) + .containsExactly(MigrationStep2.class, MigrationStep1.class, MigrationStep3.class); + assertThat(logTester.logs()).hasSize(8); + assertLogLevel(LoggerLevel.INFO, + "Executing DB migrations...", + "#1 '1-MigrationStep2'...", + "#1 '1-MigrationStep2': success | time=", + "#2 '2-MigrationStep1'...", + "#2 '2-MigrationStep1': success | time=", + "#3 '3-MigrationStep3'...", + "#3 '3-MigrationStep3': success | time=", + "Executed DB migrations: success | time="); + + assertThat(migrationContainer.getComponentByType(MigrationStep1.class).isCalled()).isTrue(); + assertThat(migrationContainer.getComponentByType(MigrationStep2.class).isCalled()).isTrue(); + assertThat(migrationContainer.getComponentByType(MigrationStep3.class).isCalled()).isTrue(); + } + + @Test + public void execute_throws_MigrationStepExecutionException_on_first_failing_step_execution_throws_SQLException() { + migrationContainer.add(MigrationStep2.class, SqlExceptionFailingMigrationStep.class, MigrationStep3.class); + Stream<RegisteredMigrationStep> steps = Stream.of( + registeredStepOf(1, MigrationStep2.class), + registeredStepOf(2, SqlExceptionFailingMigrationStep.class), + registeredStepOf(3, MigrationStep3.class)); + + try { + underTest.execute(steps); + fail("a MigrationStepExecutionException should have been thrown"); + } catch (MigrationStepExecutionException e) { + assertThat(e).hasMessage("Execution of migration step #2 '2-SqlExceptionFailingMigrationStep' failed"); + assertThat(e).hasCause(SqlExceptionFailingMigrationStep.THROWN_EXCEPTION); + } finally { + assertThat(logTester.logs()).hasSize(6); + assertLogLevel(LoggerLevel.INFO, + "Executing DB migrations...", + "#1 '1-MigrationStep2'...", + "#1 '1-MigrationStep2': success | time=", + "#2 '2-SqlExceptionFailingMigrationStep'..."); + assertLogLevel(LoggerLevel.ERROR, + "#2 '2-SqlExceptionFailingMigrationStep': failure | time=", + "Executed DB migrations: failure | time="); + } + } + + @Test + public void execute_throws_MigrationStepExecutionException_on_first_failing_step_execution_throws_any_exception() { + migrationContainer.add(MigrationStep2.class, RuntimeExceptionFailingMigrationStep.class, MigrationStep3.class); + + Stream<RegisteredMigrationStep> steps = Stream.of( + registeredStepOf(1, MigrationStep2.class), + registeredStepOf(2, RuntimeExceptionFailingMigrationStep.class), + registeredStepOf(3, MigrationStep3.class)); + expectedException.expect(MigrationStepExecutionException.class); + expectedException.expectMessage("Execution of migration step #2 '2-RuntimeExceptionFailingMigrationStep' failed"); + expectedException.expectCause(Matchers.sameInstance(RuntimeExceptionFailingMigrationStep.THROWN_EXCEPTION)); + + underTest.execute(steps); + } + + private static RegisteredMigrationStep registeredStepOf(int migrationNumber, Class<? extends MigrationStep> migrationStep1Class) { + return new RegisteredMigrationStep(migrationNumber, migrationNumber + "-" + migrationStep1Class.getSimpleName(), migrationStep1Class); + } + + private static abstract class SingleCallCheckerMigrationStep implements MigrationStep { + private static List<Class<? extends MigrationStep>> calledSteps = new ArrayList<>(); + private boolean called = false; + + @Override + public void execute() throws SQLException { + checkState(!called, "execute must not be called twice"); + this.called = true; + calledSteps.add(getClass()); + } + + public boolean isCalled() { + return called; + } + + public static List<Class<? extends MigrationStep>> getCalledSteps() { + return calledSteps; + } + } + + public static final class MigrationStep1 extends SingleCallCheckerMigrationStep { + + } + + public static final class MigrationStep2 extends SingleCallCheckerMigrationStep { + + } + + public static final class MigrationStep3 extends SingleCallCheckerMigrationStep { + + } + + public static class SqlExceptionFailingMigrationStep implements MigrationStep { + private static final SQLException THROWN_EXCEPTION = new SQLException("Faking SQL exception in MigrationStep#execute()"); + + @Override + public void execute() throws SQLException { + throw THROWN_EXCEPTION; + } + } + + public static class RuntimeExceptionFailingMigrationStep implements MigrationStep { + private static final RuntimeException THROWN_EXCEPTION = new RuntimeException("Faking failing migration step"); + + @Override + public void execute() throws SQLException { + throw THROWN_EXCEPTION; + } + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsImplTest.java new file mode 100644 index 00000000000..0ac73e7223e --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsImplTest.java @@ -0,0 +1,116 @@ +/* + * 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.step; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MigrationStepsImplTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private MigrationStepsImpl underTest = new MigrationStepsImpl(Arrays.asList( + new RegisteredMigrationStep(1, "mmmmmm", MigrationStep.class), + new RegisteredMigrationStep(2, "sds", MigrationStep.class), + new RegisteredMigrationStep(8, "ss", MigrationStep.class))); + private MigrationStepsImpl unorderedSteps = new MigrationStepsImpl(Arrays.asList( + new RegisteredMigrationStep(2, "sds", MigrationStep.class), + new RegisteredMigrationStep(8, "ss", MigrationStep.class), + new RegisteredMigrationStep(1, "mmmmmm", MigrationStep.class))); + + @Test + public void constructor_fails_with_NPE_if_argument_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("steps can't be null"); + + new MigrationStepsImpl(null); + } + + @Test + public void constructor_fails_with_IAE_if_argument_is_empty() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("steps can't be empty"); + + new MigrationStepsImpl(Collections.emptyList()); + } + + @Test + public void constructor_fails_with_NPE_if_argument_contains_a_null() { + expectedException.expect(NullPointerException.class); + + new MigrationStepsImpl(Arrays.asList( + new RegisteredMigrationStep(12, "sdsd", MigrationStep.class), + null, + new RegisteredMigrationStep(88, "q", MigrationStep.class))); + } + + @Test + public void getMaxMigrationNumber_returns_migration_of_last_step_in_constructor_list_argument() { + assertThat(underTest.getMaxMigrationNumber()).isEqualTo(8L); + assertThat(unorderedSteps.getMaxMigrationNumber()).isEqualTo(1L); + } + + @Test + public void readAll_iterates_over_all_steps_in_constructor_list_argument() { + verifySteamContainsNumbers(underTest.readAll(), 1L, 2L, 8L); + } + + @Test + public void readFrom_throws_IAE_if_number_is_less_than_0() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Migration number must be >= 0"); + + underTest.readFrom(-1); + } + + @Test + public void readFrom_returns_stream_of_sublist_from_the_first_migration_with_number_greater_or_equal_to_argument() { + verifySteamContainsNumbers(underTest.readFrom(1), 1L, 2L, 8L); + verifySteamContainsNumbers(underTest.readFrom(2), 2L, 8L); + verifySteamContainsNumbers(underTest.readFrom(3), 8L); + verifySteamContainsNumbers(underTest.readFrom(4), 8L); + verifySteamContainsNumbers(underTest.readFrom(5), 8L); + verifySteamContainsNumbers(underTest.readFrom(6), 8L); + verifySteamContainsNumbers(underTest.readFrom(7), 8L); + verifySteamContainsNumbers(underTest.readFrom(8), 8L); + } + + @Test + public void readFrom_returns_an_empty_stream_if_argument_is_greater_than_biggest_migration_number() { + verifySteamContainsNumbers(underTest.readFrom(9)); + verifySteamContainsNumbers(unorderedSteps.readFrom(9)); + } + + private static void verifySteamContainsNumbers(Stream<RegisteredMigrationStep> stream, Long... expectedMigrationNumbers) { + List<RegisteredMigrationStep> steps = stream.collect(Collectors.toList()); + assertThat(steps).hasSize(expectedMigrationNumbers.length); + Iterator<RegisteredMigrationStep> iterator = steps.iterator(); + Arrays.stream(expectedMigrationNumbers).forEach(expected -> assertThat(iterator.next().getMigrationNumber()).isEqualTo(expected)); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsProviderTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsProviderTest.java new file mode 100644 index 00000000000..c5e9a61891f --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/MigrationStepsProviderTest.java @@ -0,0 +1,99 @@ +/* + * 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.step; + +import java.util.Random; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.InOrder; +import org.sonar.server.platform.db.migration.version.DbVersion; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class MigrationStepsProviderTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private InternalMigrationStepRegistry internalMigrationStepRegistry = mock(InternalMigrationStepRegistry.class); + private MigrationStepsProvider underTest = new MigrationStepsProvider(); + + @Test + public void provide_throws_ISE_with_registry_build_throws_ISE_because_it_is_empty() { + IllegalStateException expected = new IllegalStateException("faking ISE because registry is empty"); + when(internalMigrationStepRegistry.build()).thenThrow(expected); + + expectedException.expect(expected.getClass()); + expectedException.expectMessage(expected.getMessage()); + + underTest.provide(internalMigrationStepRegistry); + } + + @Test + public void provide_calls_DbVersion_addStep_in_order_and_only_once() { + DbVersion dbVersion1 = newMockFailingOnSecondBuildCall(); + DbVersion dbVersion2 = newMockFailingOnSecondBuildCall(); + DbVersion dbVersion3 = newMockFailingOnSecondBuildCall(); + InOrder inOrder = inOrder(dbVersion1, dbVersion2, dbVersion3); + MigrationSteps expected = mock(MigrationSteps.class); + when(internalMigrationStepRegistry.build()).thenReturn(expected); + + assertThat(underTest.provide(internalMigrationStepRegistry, dbVersion1, dbVersion2, dbVersion3)) + .isSameAs(expected); + + inOrder.verify(dbVersion1).addSteps(internalMigrationStepRegistry); + inOrder.verify(dbVersion2).addSteps(internalMigrationStepRegistry); + inOrder.verify(dbVersion3).addSteps(internalMigrationStepRegistry); + inOrder.verifyNoMoreInteractions(); + + // calling a second time with another argument, it's just ignored + DbVersion dbVersion4 = newMockFailingOnSecondBuildCall(); + assertThat(underTest.provide(internalMigrationStepRegistry, dbVersion4)).isSameAs(expected); + verifyZeroInteractions(dbVersion4); + } + + @Test + public void provide_always_returns_the_same_MigrationSteps_instance_and_calls_registry_build_only_once() { + MigrationSteps migrationSteps = mock(MigrationSteps.class); + when(internalMigrationStepRegistry.build()) + .thenReturn(migrationSteps) + .thenThrow(new RuntimeException("method build should not be called twice")); + + for (int i = 0; i < Math.abs(new Random().nextInt(50)) + 1; i++) { + assertThat(underTest.provide(internalMigrationStepRegistry)).isSameAs(migrationSteps); + } + + } + + private static DbVersion newMockFailingOnSecondBuildCall() { + DbVersion res = mock(DbVersion.class); + doNothing() + .doThrow(new RuntimeException("addStep should not be called twice")) + .when(res) + .addSteps(any(MigrationStepRegistry.class)); + return res; + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/RegisteredMigrationStepTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/RegisteredMigrationStepTest.java new file mode 100644 index 00000000000..8029d7b4bb0 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/RegisteredMigrationStepTest.java @@ -0,0 +1,59 @@ +/* + * 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.step; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RegisteredMigrationStepTest { + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void constructor_throws_NPE_if_description_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("description can't be null"); + + new RegisteredMigrationStep(1, null, MigrationStep.class); + } + + @Test + public void constructor_throws_NPE_if_MigrationStep_class_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("MigrationStep class can't be null"); + + new RegisteredMigrationStep(1, "", null); + } + + @Test + public void verify_getters() { + RegisteredMigrationStep underTest = new RegisteredMigrationStep(3, "foo", MyMigrationStep.class); + assertThat(underTest.getMigrationNumber()).isEqualTo(3L); + assertThat(underTest.getDescription()).isEqualTo("foo"); + assertThat(underTest.getStepClass()).isEqualTo(MyMigrationStep.class); + } + + private static abstract class MyMigrationStep implements MigrationStep { + + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionModuleTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionModuleTest.java new file mode 100644 index 00000000000..0ad182db9f6 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionModuleTest.java @@ -0,0 +1,42 @@ +/* + * 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.version; + +import org.junit.Test; +import org.sonar.core.platform.ComponentContainer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DbVersionModuleTest { + private static final int COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER = 2; + + private DbVersionModule underTest = new DbVersionModule(); + + @Test + public void verify_component_count() { + ComponentContainer container = new ComponentContainer(); + + underTest.configure(container); + + assertThat(container.getPicoContainer().getComponentAdapters()) + .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 1); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionTestUtils.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionTestUtils.java new file mode 100644 index 00000000000..39b1a3b6376 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionTestUtils.java @@ -0,0 +1,67 @@ +/* + * 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.version; + +import java.util.HashSet; +import java.util.Set; +import org.sonar.server.platform.db.migration.step.MigrationStep; +import org.sonar.server.platform.db.migration.step.MigrationStepRegistry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.server.platform.db.migration.step.MigrationNumber.validate; + +public class DbVersionTestUtils { + + public static void verifyMinimumMigrationNumber(DbVersion underTest, int minimumMigrationNumber) { + TestMigrationStepRegistry registry = new TestMigrationStepRegistry() { + @Override + public <T extends MigrationStep> MigrationStepRegistry add(long migrationNumber, String description, Class<T> stepClass) { + super.add(migrationNumber, description, MigrationStep.class); + + assertThat(migrationNumber).isGreaterThanOrEqualTo(minimumMigrationNumber); + return this; + } + }; + + underTest.addSteps(registry); + + assertThat(registry.migrationNumbers).describedAs("No migration added to registry").isNotEmpty(); + assertThat(registry.migrationNumbers.stream().sorted().findFirst().get()).isEqualTo(minimumMigrationNumber); + } + + public static void verifyMigrationCount(DbVersion underTest, int migrationCount) { + TestMigrationStepRegistry registry = new TestMigrationStepRegistry(); + underTest.addSteps(registry); + assertThat(registry.migrationNumbers).hasSize(migrationCount); + } + + private static class TestMigrationStepRegistry implements MigrationStepRegistry { + private Set<Long> migrationNumbers = new HashSet<>(); + + @Override + public <T extends MigrationStep> MigrationStepRegistry add(long migrationNumber, String description, Class<T> stepClass) { + validate(migrationNumber); + assertThat(description).isNotEmpty(); + assertThat(stepClass).isNotNull(); + assertThat(migrationNumbers.add(migrationNumber)).isTrue(); + return this; + } + } +} diff --git a/sonar-db/src/test/java/org/sonar/db/version/v63/AddUuidToEventsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/AddUuidToEventsTest.java index 71b91392ff5..9ccc01c861b 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/v63/AddUuidToEventsTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/AddUuidToEventsTest.java @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.db.version.v63; +package org.sonar.server.platform.db.migration.version.v63; import java.sql.SQLException; import java.sql.Types; diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/DbVersion63Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/DbVersion63Test.java new file mode 100644 index 00000000000..6209c1f2546 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/DbVersion63Test.java @@ -0,0 +1,41 @@ +/* + * 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.version.v63; + +import org.junit.Test; + +import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationCount; +import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber; + +public class DbVersion63Test { + private DbVersion63 underTest = new DbVersion63(); + + @Test + public void migrationNumber_starts_at_1500() { + verifyMinimumMigrationNumber(underTest, 1500); + } + + @Test + public void verify_migration_count() { + verifyMigrationCount(underTest, 3); + } + + +} diff --git a/sonar-db/src/test/java/org/sonar/db/version/v63/MakeUuidNotNullOnEventsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/MakeUuidNotNullOnEventsTest.java index 2813b7b5488..0e1d922f7b7 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/v63/MakeUuidNotNullOnEventsTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/MakeUuidNotNullOnEventsTest.java @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.db.version.v63; +package org.sonar.server.platform.db.migration.version.v63; import java.sql.SQLException; import java.sql.Types; diff --git a/sonar-db/src/test/java/org/sonar/db/version/v63/PopulateUuidColumnOfEventsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/PopulateUuidColumnOfEventsTest.java index 1c67cc9fd20..e9369d46e71 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/v63/PopulateUuidColumnOfEventsTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v63/PopulateUuidColumnOfEventsTest.java @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.db.version.v63; +package org.sonar.server.platform.db.migration.version.v63; import java.sql.SQLException; import java.util.List; diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest/empty.sql new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest/empty.sql diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v63/AddUuidToEventsTest/previous-events.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v63/AddUuidToEventsTest/previous-events.sql index b0712271a84..b0712271a84 100644 --- a/sonar-db/src/test/resources/org/sonar/db/version/v63/AddUuidToEventsTest/previous-events.sql +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v63/AddUuidToEventsTest/previous-events.sql diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v63/MakeUuidNotNullOnEventsTest/in_progress_events.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v63/MakeUuidNotNullOnEventsTest/in_progress_events.sql index d83b16dd817..d83b16dd817 100644 --- a/sonar-db/src/test/resources/org/sonar/db/version/v63/MakeUuidNotNullOnEventsTest/in_progress_events.sql +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v63/MakeUuidNotNullOnEventsTest/in_progress_events.sql diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v63/PopulateUuidColumnOfEventsTest/in_progress_events.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v63/PopulateUuidColumnOfEventsTest/in_progress_events.sql index d83b16dd817..d83b16dd817 100644 --- a/sonar-db/src/test/resources/org/sonar/db/version/v63/PopulateUuidColumnOfEventsTest/in_progress_events.sql +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v63/PopulateUuidColumnOfEventsTest/in_progress_events.sql diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java index 68f4dadbd91..c1980a69c19 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/Platform.java @@ -31,6 +31,7 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.platform.ComponentContainer; import org.sonar.db.version.DatabaseVersion; +import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.platform.platformlevel.PlatformLevel; import org.sonar.server.platform.platformlevel.PlatformLevel1; import org.sonar.server.platform.platformlevel.PlatformLevel2; @@ -39,6 +40,8 @@ import org.sonar.server.platform.platformlevel.PlatformLevel4; import org.sonar.server.platform.platformlevel.PlatformLevelSafeMode; import org.sonar.server.platform.platformlevel.PlatformLevelStartup; +import static com.google.common.base.Preconditions.checkState; + /** * @since 2.2 */ @@ -94,6 +97,12 @@ public class Platform { } } + public void upgradeDb() { + checkState(isInSafeMode(), "Must be in safe mode"); + MigrationEngine migrationEngine = currentLevel.getContainer().getComponentByType(MigrationEngine.class); + migrationEngine.execute(); + } + // Platform is injected in Pico, so do not rename this method "start" public void doStart() { doStart(Startup.ALL); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java b/server/sonar-server/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java index 4770873e53b..193f8fd6143 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/db/migration/DatabaseMigrationImpl.java @@ -26,6 +26,7 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.server.platform.Platform; +import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.ruby.RubyBridge; import static org.sonar.server.platform.db.migration.DatabaseMigrationState.Status; @@ -37,11 +38,12 @@ public class DatabaseMigrationImpl implements DatabaseMigration { private static final Logger LOGGER = Loggers.get(DatabaseMigrationImpl.class); - private final RubyBridge rubyBridge; /** * ExecutorService implements threads management. */ private final DatabaseMigrationExecutorService executorService; + private final RubyBridge rubyBridge; + private final MigrationEngine migrationEngine; private final Platform platform; private final MutableDatabaseMigrationState migrationState; /** @@ -58,12 +60,13 @@ public class DatabaseMigrationImpl implements DatabaseMigration { */ private final AtomicBoolean running = new AtomicBoolean(false); - public DatabaseMigrationImpl(RubyBridge rubyBridge, DatabaseMigrationExecutorService executorService, Platform platform, - MutableDatabaseMigrationState migrationState) { - this.rubyBridge = rubyBridge; + public DatabaseMigrationImpl(DatabaseMigrationExecutorService executorService, MutableDatabaseMigrationState migrationState, + RubyBridge rubyBridge, MigrationEngine migrationEngine, Platform platform) { this.executorService = executorService; - this.platform = platform; this.migrationState = migrationState; + this.rubyBridge = rubyBridge; + this.migrationEngine = migrationEngine; + this.platform = platform; } @Override @@ -120,6 +123,7 @@ public class DatabaseMigrationImpl implements DatabaseMigration { Profiler profiler = Profiler.createIfTrace(LOGGER); profiler.startTrace("Starting DB Migration"); rubyBridge.databaseMigration().trigger(); + migrationEngine.execute(); profiler.stopTrace("DB Migration ended"); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java index 5d86d638fce..c9043e0008d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelSafeMode.java @@ -22,6 +22,8 @@ package org.sonar.server.platform.platformlevel; import org.sonar.server.organization.NoopDefaultOrganizationCache; import org.sonar.server.platform.ServerImpl; import org.sonar.server.platform.db.migration.DatabaseMigrationImpl; +import org.sonar.server.platform.db.migration.MigrationEngineModule; +import org.sonar.server.platform.db.migration.version.DbVersionModule; import org.sonar.server.platform.web.WebPagesFilter; import org.sonar.server.platform.ws.DbMigrationStatusAction; import org.sonar.server.platform.ws.IndexAction; @@ -63,5 +65,7 @@ public class PlatformLevelSafeMode extends PlatformLevel { NoopDefaultOrganizationCache.class); add(DatabaseMigrationImpl.class); + add(DbVersionModule.class); + addIfStartupLeader(MigrationEngineModule.class); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplAsynchronousTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplAsynchronousTest.java index d5904c95389..eea53055a8a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplAsynchronousTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplAsynchronousTest.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration; import org.junit.Test; import org.sonar.server.platform.Platform; +import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.ruby.RubyBridge; import static org.assertj.core.api.Assertions.assertThat; @@ -39,10 +40,11 @@ public class DatabaseMigrationImplAsynchronousTest { taskSuppliedForAsyncProcess = true; } }; + private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class); private RubyBridge rubyBridge = mock(RubyBridge.class); private Platform platform = mock(Platform.class); - private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class); - private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(rubyBridge, executorService, platform, migrationState); + private MigrationEngine migrationEngine = mock(MigrationEngine.class); + private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(executorService, migrationState, rubyBridge, migrationEngine, platform); @Test public void testName() throws Exception { diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplConcurrentAccessTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplConcurrentAccessTest.java index 75fe3d42753..1b27e76ecd1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplConcurrentAccessTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplConcurrentAccessTest.java @@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Test; import org.sonar.server.platform.Platform; +import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.ruby.RubyBridge; import org.sonar.server.ruby.RubyDatabaseMigration; import org.sonar.server.ruby.RubyRailsRoutes; @@ -72,11 +73,12 @@ public class DatabaseMigrationImplConcurrentAccessTest { } } }; + private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class); private RubyBridge rubyBridge = mock(RubyBridge.class); private Platform platform = mock(Platform.class); private RubyRailsRoutes railsRoutes = mock(RubyRailsRoutes.class); - private MutableDatabaseMigrationState migrationState = mock(MutableDatabaseMigrationState.class); - private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(rubyBridge, executorService, platform, migrationState); + private MigrationEngine migrationEngine = mock(MigrationEngine.class); + private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(executorService, migrationState, rubyBridge, migrationEngine, platform); @After public void tearDown() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java index a1a248a074e..3704d8c7761 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/db/migration/DatabaseMigrationImplTest.java @@ -23,6 +23,7 @@ import java.util.Date; import org.junit.Test; import org.mockito.InOrder; import org.sonar.server.platform.Platform; +import org.sonar.server.platform.db.migration.engine.MigrationEngine; import org.sonar.server.ruby.RubyBridge; import org.sonar.server.ruby.RubyDatabaseMigration; import org.sonar.server.ruby.RubyRailsRoutes; @@ -49,14 +50,15 @@ public class DatabaseMigrationImplTest { command.run(); } }; + private MutableDatabaseMigrationState migrationState = new DatabaseMigrationStateImpl(); private RubyBridge rubyBridge = mock(RubyBridge.class); private RubyDatabaseMigration rubyDatabaseMigration = mock(RubyDatabaseMigration.class); private RubyRailsRoutes rubyRailsRoutes = mock(RubyRailsRoutes.class); private Platform platform = mock(Platform.class); - private MutableDatabaseMigrationState migrationState = new DatabaseMigrationStateImpl(); - private InOrder inOrder = inOrder(rubyDatabaseMigration, rubyBridge, rubyRailsRoutes, platform); + private MigrationEngine migrationEngine = mock(MigrationEngine.class); + private InOrder inOrder = inOrder(rubyDatabaseMigration, rubyBridge, rubyRailsRoutes, platform, migrationEngine); - private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(rubyBridge, executorService, platform, migrationState); + private DatabaseMigrationImpl underTest = new DatabaseMigrationImpl(executorService, migrationState, rubyBridge, migrationEngine, platform); @Test public void startit_calls_databasemigration_trigger_in_a_separate_thread() { diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1500_add_uuid_to_events.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1500_add_uuid_to_events.rb deleted file mode 100644 index 2f7fdd22930..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1500_add_uuid_to_events.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2016 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube 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. -# -# SonarQube 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. -# - -# -# SonarQube 6.3 -# -class AddUuidToEvents < ActiveRecord::Migration - - def self.up - execute_java_migration('org.sonar.db.version.v63.AddUuidToEvents') - end -end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1501_populate_uuid_of_events.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1501_populate_uuid_of_events.rb deleted file mode 100644 index b155e253d79..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1501_populate_uuid_of_events.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2016 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube 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. -# -# SonarQube 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. -# - -# -# SonarQube 6.3 -# -class PopulateUuidOfEvents < ActiveRecord::Migration - - def self.up - execute_java_migration('org.sonar.db.version.v63.PopulateUuidColumnOfEvents') - end -end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1502_make_uuid_not_null_on_events.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1502_make_uuid_not_null_on_events.rb deleted file mode 100644 index 8aee750daf7..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1502_make_uuid_not_null_on_events.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2016 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube 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. -# -# SonarQube 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. -# - -# -# SonarQube 6.3 -# -class MakeUuidNotNullOnEvents < ActiveRecord::Migration - - def self.up - execute_java_migration('org.sonar.db.version.v63.MakeUuidNotNullOnEvents') - end -end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/lib/database_version.rb b/server/sonar-web/src/main/webapp/WEB-INF/lib/database_version.rb index ebac4615442..52f032d0441 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/lib/database_version.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/lib/database_version.rb @@ -64,6 +64,7 @@ class DatabaseVersion def self.upgrade_and_start ActiveRecord::Migrator.migrate(migrations_path) + Java::OrgSonarServerPlatform::Platform.getInstance().upgradeDb() Java::OrgSonarServerPlatform::Platform.getInstance().doStart() load_java_web_services end diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java index 96be3391fe7..df4322c14b4 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -141,9 +141,6 @@ import org.sonar.db.version.v62.PopulateOrganizationUuidOfGroups; import org.sonar.db.version.v62.PopulateOrganizationUuidOfPermissionTemplates; import org.sonar.db.version.v62.PopulateOrganizationUuidOfUserRoles; import org.sonar.db.version.v62.UpdateQualityGateConditionsOnCoverage; -import org.sonar.db.version.v63.AddUuidToEvents; -import org.sonar.db.version.v63.MakeUuidNotNullOnEvents; -import org.sonar.db.version.v63.PopulateUuidColumnOfEvents; public class MigrationStepModule extends Module { @Override @@ -293,11 +290,6 @@ public class MigrationStepModule extends Module { DropMeasureFiltersTables.class, DropIssueFiltersTables.class, CreateTableWebhookDeliveries.class, - IncludeOrganizationUuidInUniqueIndexOfGroupRoles.class, - - // 6.3 - AddUuidToEvents.class, - PopulateUuidColumnOfEvents.class, - MakeUuidNotNullOnEvents.class); + IncludeOrganizationUuidInUniqueIndexOfGroupRoles.class); } } diff --git a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java index 579506d38b1..0502ecf2ce8 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java @@ -29,6 +29,6 @@ public class MigrationStepModuleTest { public void verify_count_of_added_MigrationStep_types() { ComponentContainer container = new ComponentContainer(); new MigrationStepModule().configure(container); - assertThat(container.size()).isEqualTo(126); + assertThat(container.size()).isEqualTo(123); } } |