--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.db.migrations.v50;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.internal.Uuids;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.migration.v50.Component;
+import org.sonar.core.persistence.migration.v50.Migration50Mapper;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.db.migrations.MigrationStep;
+import org.sonar.server.util.ProgressLogger;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+
+/**
+ * Used in the Active Record Migration 705
+ *
+ * @since 5.0
+ */
+public class PopulateProjectsUuidColumnsMigrationStep implements MigrationStep {
+
+ private static final Logger LOG = Loggers.get(PopulateProjectsUuidColumnsMigrationStep.class);
+
++ private static final Logger LOG = Loggers.get(PopulateProjectsUuidColumnsMigration.class);
++
+ private final DbClient db;
+ private final AtomicLong counter = new AtomicLong(0L);
+
+ public PopulateProjectsUuidColumnsMigrationStep(DbClient db) {
+ this.db = db;
+ }
+
+ @Override
+ public void execute() {
+ ProgressLogger progress = ProgressLogger.create(getClass(), counter);
+ progress.start();
+
+ final DbSession readSession = db.openSession(false);
+ final DbSession writeSession = db.openSession(true);
+ try {
+ readSession.select("org.sonar.core.persistence.migration.v50.Migration50Mapper.selectRootProjects", new ResultHandler() {
+ @Override
+ public void handleResult(ResultContext context) {
+ Component project = (Component) context.getResultObject();
+ List<Component> components = readSession.getMapper(Migration50Mapper.class).selectComponentChildrenForProjects(project.getId());
+ MigrationContext migrationContext = new MigrationContext(readSession, writeSession, project, components);
+ migrateEnabledComponents(migrationContext);
+ migrateDisabledComponents(migrationContext);
+ }
+ });
+ writeSession.commit(true);
+ readSession.commit(true);
+
+ migrateComponentsWithoutUuid(readSession, writeSession);
+ writeSession.commit(true);
+
+ // log the total number of process rows
+ progress.log();
+ } finally {
+ readSession.close();
+ writeSession.close();
+ progress.stop();
+ }
+ }
+
+ private void migrateEnabledComponents(MigrationContext migrationContext) {
+ saveComponent(migrationContext.writeSession, migrationContext.project);
+ for (Component component : migrationContext.componentsToMigrate) {
+ migrationContext.updateComponent(component);
+ if (Strings.isNullOrEmpty(component.getModuleUuidPath())) {
+ LOG.warn(String.format("Ignoring component id '%s' because the module uuid path could not be created", component.getId()));
+ } else {
+ migrationContext.updateComponent(component);
+ saveComponent(migrationContext.writeSession, component);
+ }
+ }
+ }
+
+ private void migrateDisabledComponents(MigrationContext migrationContext) {
+ for (Component component : migrationContext.readSession.getMapper(Migration50Mapper.class).selectDisabledDirectComponentChildrenForProjects(migrationContext.project.getId())) {
+ migrationContext.updateComponent(component);
+ saveComponent(migrationContext.writeSession, component);
+ }
+ for (Component component : migrationContext.readSession.getMapper(Migration50Mapper.class).selectDisabledNoneDirectComponentChildrenForProjects(
+ migrationContext.project.getId())) {
+ migrationContext.updateComponent(component);
+ saveComponent(migrationContext.writeSession, component);
+ }
+ }
+
+ private void migrateComponentsWithoutUuid(DbSession readSession, DbSession writeSession) {
+ for (Component component : readSession.getMapper(Migration50Mapper.class).selectComponentsWithoutUuid()) {
+ String uuid = Uuids.create();
+ component.setUuid(uuid);
+ component.setProjectUuid(uuid);
+ saveComponent(writeSession, component);
+ }
+ }
+
+ private void saveComponent(DbSession writeSession, Component component) {
+ writeSession.getMapper(Migration50Mapper.class).updateComponentUuids(component);
+ counter.getAndIncrement();
+ }
+
+ private static class MigrationContext {
+ private final DbSession readSession;
+ private final DbSession writeSession;
+ private final Component project;
+ private final Map<Long, Component> componentsBySnapshotId = newHashMap();
+ private final Map<Long, String> uuidByComponentId = newHashMap();
+ private final List<Component> componentsToMigrate = newArrayList();
+
+ private MigrationContext(DbSession readSession, DbSession writeSession, Component project, List<Component> components) {
+ this.readSession = readSession;
+ this.writeSession = writeSession;
+ this.project = project;
+
+ project.setUuid(getOrCreateUuid(project));
+ project.setProjectUuid(project.getUuid());
+
+ componentsBySnapshotId.put(project.getSnapshotId(), project);
+ for (Component component : components) {
+ componentsBySnapshotId.put(component.getSnapshotId(), component);
+ if (component.getUuid() == null) {
+ componentsToMigrate.add(component);
+ }
+ }
+ }
+
+ public void updateComponent(Component component) {
+ component.setUuid(getOrCreateUuid(component));
+ component.setProjectUuid(getOrCreateUuid(project));
+
+ String snapshotPath = component.getSnapshotPath();
+ StringBuilder moduleUuidPath = new StringBuilder();
+ String lastModuleUuid = null;
+ if (!Strings.isNullOrEmpty(snapshotPath)) {
+ for (String s : Splitter.on(".").omitEmptyStrings().split(snapshotPath)) {
+ Long snapshotId = Long.valueOf(s);
+ Component currentComponent = componentsBySnapshotId.get(snapshotId);
+ if (currentComponent != null && currentComponent.getScope().equals(Scopes.PROJECT)) {
+ lastModuleUuid = getOrCreateUuid(currentComponent);
+ moduleUuidPath.append(lastModuleUuid).append(".");
+ }
+ }
+ }
+
+ if (moduleUuidPath.length() > 0 && lastModuleUuid != null) {
+ // Remove last '.'
+ moduleUuidPath.deleteCharAt(moduleUuidPath.length() - 1);
+
+ component.setModuleUuidPath(moduleUuidPath.toString());
+ component.setModuleUuid(lastModuleUuid);
+ }
+ }
+
+ private String getOrCreateUuid(Component component) {
+ String existingUuid = component.getUuid();
+ String uuid = existingUuid == null ? uuidByComponentId.get(component.getId()) : existingUuid;
+ if (uuid == null) {
+ String newUuid = Uuids.create();
+ uuidByComponentId.put(component.getId(), newUuid);
+ return newUuid;
+ }
+ return uuid;
+ }
+ }
+
+}