diff options
Diffstat (limited to 'server')
82 files changed, 2333 insertions, 2187 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWsModule.java index 8357272f318..2d0f594eefe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWsModule.java @@ -20,7 +20,6 @@ package org.sonar.server.batch; import org.sonar.core.platform.Module; -import org.sonar.server.computation.ws.SubmitReportAction; public class BatchWsModule extends Module { @Override @@ -30,7 +29,6 @@ public class BatchWsModule extends Module { GlobalAction.class, ProjectAction.class, ProjectRepositoryLoader.class, - SubmitReportAction.class, IssuesAction.class, UsersAction.class, BatchWs.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java index 1530536274a..8c9b2c7c109 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java @@ -144,7 +144,7 @@ public class ComponentService { } } - public String create(NewComponent newComponent) { + public ComponentDto create(NewComponent newComponent) { userSession.checkGlobalPermission(GlobalPermissions.PROVISIONING); DbSession session = dbClient.openSession(false); @@ -175,9 +175,9 @@ public class ComponentService { dbClient.componentIndexDao().indexResource(session, component.getId()); session.commit(); - return component.key(); + return component; } finally { - session.close(); + dbClient.closeSession(session); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java index b83a3f73819..2fa6e0cfe6f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java @@ -73,12 +73,15 @@ public class DefaultRubyComponentService implements RubyComponentService { public Long createComponent(String key, @Nullable String branch, String name, @Nullable String qualifier) { // Sub view should not be created with provisioning. Will be fixed by http://jira.sonarsource.com/browse/VIEWS-296 if (!Qualifiers.SUBVIEW.equals(qualifier)) { - String createdKey = componentService.create(NewComponent.create(key, name).setQualifier(qualifier).setBranch(branch)); - ComponentDto component = (ComponentDto) resourceDao.selectByKey(createdKey); + ComponentDto componentDto = componentService.create(NewComponent.create(key, name).setQualifier(qualifier).setBranch(branch)); + if (componentDto == null) { + throw new BadRequestException(String.format("Component not created: %s", key)); + } + ComponentDto component = (ComponentDto) resourceDao.selectByKey(componentDto.getKey()); if (component == null) { - throw new BadRequestException(String.format("Component not created: %s", createdKey)); + throw new BadRequestException(String.format("Component not created: %s", key)); } - permissionService.applyDefaultPermissionTemplate(createdKey); + permissionService.applyDefaultPermissionTemplate(component.getKey()); return component.getId(); } return null; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueue.java new file mode 100644 index 00000000000..4dfab3e76f5 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueue.java @@ -0,0 +1,230 @@ +/* + * 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.computation; + +import com.google.common.base.Optional; +import org.sonar.api.server.ServerSide; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.server.computation.monitoring.CEQueueStatus; + +import static java.lang.String.format; + +/** + * Queue of pending Compute Engine tasks. Both producer and consumer actions + * are implemented. + * <p> + * This class is decoupled from the regular task type {@link org.sonar.db.ce.CeTaskTypes#REPORT}. + * </p> + */ +@ServerSide +public class CeQueue { + + private final System2 system2; + private final DbClient dbClient; + private final UuidFactory uuidFactory; + private final CEQueueStatus queueStatus; + private final CeQueueListener[] listeners; + + // state + private boolean submitPaused = false; + private boolean peekPaused = false; + + public CeQueue(System2 system2, DbClient dbClient, UuidFactory uuidFactory, + CEQueueStatus queueStatus, CeQueueListener[] listeners) { + this.system2 = system2; + this.dbClient = dbClient; + this.uuidFactory = uuidFactory; + this.queueStatus = queueStatus; + this.listeners = listeners; + } + + public CeTaskSubmit prepareSubmit() { + return new CeTaskSubmit(uuidFactory.create()); + } + + public CeTask submit(CeTaskSubmit submit) { + if (submitPaused) { + throw new IllegalStateException("Compute Engine does not currently accept new tasks"); + } + CeTask task = new CeTask(submit); + DbSession dbSession = dbClient.openSession(false); + try { + CeQueueDto dto = new CeQueueDto(); + dto.setUuid(task.getUuid()); + dto.setTaskType(task.getType()); + dto.setComponentUuid(task.getComponentUuid()); + dto.setStatus(CeQueueDto.Status.PENDING); + dto.setSubmitterLogin(task.getSubmitterLogin()); + dto.setStartedAt(null); + dbClient.ceQueueDao().insert(dbSession, dto); + dbSession.commit(); + queueStatus.addReceived(); + return task; + } finally { + dbClient.closeSession(dbSession); + } + } + + public Optional<CeTask> peek() { + if (peekPaused) { + return Optional.absent(); + } + DbSession dbSession = dbClient.openSession(false); + try { + Optional<CeQueueDto> dto = dbClient.ceQueueDao().peek(dbSession); + if (!dto.isPresent()) { + return Optional.absent(); + } + queueStatus.addInProgress(); + return Optional.of(new CeTask(dto.get())); + + } finally { + dbClient.closeSession(dbSession); + } + } + + public boolean cancel(String taskUuid) { + DbSession dbSession = dbClient.openSession(false); + try { + Optional<CeQueueDto> queueDto = dbClient.ceQueueDao().selectByUuid(dbSession, taskUuid); + if (queueDto.isPresent()) { + if (!queueDto.get().getStatus().equals(CeQueueDto.Status.PENDING)) { + throw new IllegalStateException(String.format("Task is in progress and can't be cancelled [uuid=%s]", taskUuid)); + } + cancel(dbSession, queueDto.get()); + return true; + } + return false; + } finally { + dbClient.closeSession(dbSession); + } + } + + void cancel(DbSession dbSession, CeQueueDto q) { + CeActivityDto activityDto = new CeActivityDto(q); + activityDto.setStatus(CeActivityDto.Status.CANCELED); + remove(dbSession, new CeTask(q), q, activityDto); + } + + + /** + * Removes all the tasks from the queue, whatever their status. They are marked + * as {@link org.sonar.db.ce.CeActivityDto.Status#CANCELED} in past activity. + * This method can NOT be called when workers are being executed, as in progress + * tasks can't be killed. + * + * @return the number of canceled tasks + */ + public int clear() { + return cancelAll(true); + } + + /** + * Similar as {@link #clear()}, except that the tasks with status + * {@link org.sonar.db.ce.CeQueueDto.Status#IN_PROGRESS} are ignored. This method + * can be called at runtime, even if workers are being executed. + * + * @return the number of canceled tasks + */ + public int cancelAll() { + return cancelAll(false); + } + + private int cancelAll(boolean includeInProgress) { + int count = 0; + DbSession dbSession = dbClient.openSession(false); + try { + for (CeQueueDto queueDto : dbClient.ceQueueDao().selectAllInAscOrder(dbSession)) { + if (includeInProgress || !queueDto.getStatus().equals(CeQueueDto.Status.IN_PROGRESS)) { + cancel(dbSession, queueDto); + count++; + } + } + return count; + } finally { + dbClient.closeSession(dbSession); + } + } + + public void remove(CeTask task, CeActivityDto.Status status) { + DbSession dbSession = dbClient.openSession(false); + try { + Optional<CeQueueDto> queueDto = dbClient.ceQueueDao().selectByUuid(dbSession, task.getUuid()); + if (!queueDto.isPresent()) { + throw new IllegalStateException(format("Task does not exist anymore: %s", task)); + } + CeActivityDto activityDto = new CeActivityDto(queueDto.get()); + activityDto.setStatus(status); + Long startedAt = activityDto.getStartedAt(); + if (startedAt != null) { + activityDto.setFinishedAt(system2.now()); + long executionTime = activityDto.getFinishedAt() - startedAt; + activityDto.setExecutionTimeMs(executionTime); + if (status == CeActivityDto.Status.SUCCESS) { + queueStatus.addSuccess(executionTime); + } else { + queueStatus.addError(executionTime); + } + } + remove(dbSession, task, queueDto.get(), activityDto); + + } finally { + dbClient.closeSession(dbSession); + } + } + + private void remove(DbSession dbSession, CeTask task, CeQueueDto queueDto, CeActivityDto activityDto) { + dbClient.ceActivityDao().insert(dbSession, activityDto); + dbClient.ceQueueDao().deleteByUuid(dbSession, queueDto.getUuid()); + dbSession.commit(); + for (CeQueueListener listener : listeners) { + listener.onRemoved(task, activityDto.getStatus()); + } + } + + public void pauseSubmit() { + this.submitPaused = true; + } + + public void resumeSubmit() { + this.submitPaused = false; + } + + public boolean isSubmitPaused() { + return submitPaused; + } + + public void pausePeek() { + this.peekPaused = true; + } + + public void resumePeek() { + this.peekPaused = false; + } + + public boolean isPeekPaused() { + return peekPaused; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueInitializer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueInitializer.java new file mode 100644 index 00000000000..661e9ba680d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueInitializer.java @@ -0,0 +1,110 @@ +/* + * 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.computation; + +import java.util.HashSet; +import java.util.Set; +import org.sonar.api.platform.ServerUpgradeStatus; +import org.sonar.api.server.ServerSide; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.server.computation.monitoring.CEQueueStatus; + +/** + * Cleans-up the Compute Engine queue and resets the JMX counters. + * CE workers must not be started before execution of this class. + */ +@ServerSide +public class CeQueueInitializer { + + private static final Logger LOGGER = Loggers.get(CeQueueInitializer.class); + + private final DbClient dbClient; + private final ServerUpgradeStatus serverUpgradeStatus; + private final ReportFiles reportFiles; + private final CeQueue queue; + private final CEQueueStatus queueStatus; + + public CeQueueInitializer(DbClient dbClient, ServerUpgradeStatus serverUpgradeStatus, ReportFiles reportFiles, + CeQueue queue, CEQueueStatus queueStatus) { + this.dbClient = dbClient; + this.serverUpgradeStatus = serverUpgradeStatus; + this.reportFiles = reportFiles; + this.queue = queue; + this.queueStatus = queueStatus; + } + + /** + * Do not rename. Used at server startup. + */ + public void start() { + DbSession dbSession = dbClient.openSession(false); + try { + initJmxCounters(dbSession); + + if (serverUpgradeStatus.isUpgraded()) { + cleanOnUpgrade(); + } else { + verifyConsistency(dbSession); + } + + } finally { + dbClient.closeSession(dbSession); + } + } + + private void cleanOnUpgrade() { + // we assume that pending tasks are not compatible with the new version + // and can't be processed + LOGGER.info("Cancel all pending tasks (due to upgrade)"); + queue.clear(); + } + + private void verifyConsistency(DbSession dbSession) { + // server is not being upgraded + dbClient.ceQueueDao().resetAllToPendingStatus(dbSession); + dbSession.commit(); + + // verify that the report files are available for the tasks in queue + Set<String> uuidsInQueue = new HashSet<>(); + for (CeQueueDto queueDto : dbClient.ceQueueDao().selectAllInAscOrder(dbSession)) { + uuidsInQueue.add(queueDto.getUuid()); + if (CeTaskTypes.REPORT.equals(queueDto.getTaskType()) && !reportFiles.fileForUuid(queueDto.getUuid()).exists()) { + // the report is not available on file system + queue.cancel(dbSession, queueDto); + } + } + + // clean-up filesystem + for (String uuid : reportFiles.listUuids()) { + if (!uuidsInQueue.contains(uuid)) { + reportFiles.deleteIfExists(uuid); + } + } + } + + private void initJmxCounters(DbSession dbSession) { + queueStatus.initPendingCount(dbClient.ceQueueDao().countAll(dbSession)); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueListener.java index 84449c529aa..420c660dd70 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/package-info.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueListener.java @@ -17,8 +17,12 @@ * 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.computation; -@ParametersAreNonnullByDefault -package org.sonar.server.computation.activity; +import org.sonar.db.ce.CeActivityDto; -import javax.annotation.ParametersAreNonnullByDefault; +public interface CeQueueListener { + + void onRemoved(CeTask task, CeActivityDto.Status status); + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/CeTask.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeTask.java new file mode 100644 index 00000000000..38284022f10 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeTask.java @@ -0,0 +1,101 @@ +/* + * 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.computation; + +import com.google.common.base.Objects; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import org.sonar.db.ce.CeQueueDto; + +@Immutable +public class CeTask { + + private final String type; + private final String uuid; + private final String componentUuid; + private final String submitterLogin; + + public CeTask(String uuid, String type, @Nullable String componentUuid, @Nullable String submitterLogin) { + this.uuid = uuid; + this.type = type; + this.componentUuid = componentUuid; + this.submitterLogin = submitterLogin; + } + + CeTask(CeTaskSubmit submit) { + this.uuid = submit.getUuid(); + this.type = submit.getType(); + this.componentUuid = submit.getComponentUuid(); + this.submitterLogin = submit.getSubmitterLogin(); + } + + CeTask(CeQueueDto dto) { + this.uuid = dto.getUuid(); + this.type = dto.getTaskType(); + this.componentUuid = dto.getComponentUuid(); + this.submitterLogin = dto.getSubmitterLogin(); + } + + public String getUuid() { + return uuid; + } + + public String getType() { + return type; + } + + @CheckForNull + public String getComponentUuid() { + return componentUuid; + } + + @CheckForNull + public String getSubmitterLogin() { + return submitterLogin; + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("componentUuid", componentUuid) + .add("uuid", uuid) + .add("type", type) + .add("submitterLogin", submitterLogin) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CeTask ceTask = (CeTask) o; + return uuid.equals(ceTask.uuid); + } + + @Override + public int hashCode() { + return uuid.hashCode(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueCleanerTest.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeTaskSubmit.java index 38a587590c3..ab2c2513005 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueCleanerTest.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeTaskSubmit.java @@ -17,32 +17,44 @@ * 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.computation; -import org.junit.Test; -import org.sonar.api.platform.ServerUpgradeStatus; +public class CeTaskSubmit { + + private final String uuid; + private String type; + private String componentUuid; + private String submitterLogin; + + CeTaskSubmit(String uuid) { + this.uuid = uuid; + } -import static org.mockito.Mockito.*; + public String getUuid() { + return uuid; + } -public class ReportQueueCleanerTest { + public String getType() { + return type; + } - ServerUpgradeStatus serverUpgradeStatus = mock(ServerUpgradeStatus.class); - ReportQueue queue = mock(ReportQueue.class); - ReportQueueCleaner underTest = new ReportQueueCleaner(serverUpgradeStatus, queue); + public void setType(String type) { + this.type = type; + } + + public String getComponentUuid() { + return componentUuid; + } + + public void setComponentUuid(String s) { + this.componentUuid = s; + } - @Test - public void reset_reports_on_restart() { - underTest.start(); - verify(queue).resetToPendingStatus(); - underTest.stop(); + public String getSubmitterLogin() { + return submitterLogin; } - @Test - public void delete_all_reports_on_upgrade() { - when(serverUpgradeStatus.isUpgraded()).thenReturn(Boolean.TRUE); - underTest.start(); - verify(queue).clear(); - underTest.stop(); + public void setSubmitterLogin(String s) { + this.submitterLogin = s; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineTask.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeWorker.java index 626c11b207f..f3cf7df966f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineTask.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeWorker.java @@ -20,7 +20,7 @@ package org.sonar.server.computation; /** - * A task to be executed by the Compute Engine. + * Worker that executes the tasks got from the queue */ -public interface ComputeEngineTask extends Runnable { +public interface CeWorker extends Runnable { } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/CeWorkerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeWorkerImpl.java new file mode 100644 index 00000000000..3e2f8d29be1 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeWorkerImpl.java @@ -0,0 +1,66 @@ +/* + * 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.computation; + +import com.google.common.base.Optional; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.util.logs.Profiler; +import org.sonar.db.ce.CeActivityDto; + +public class CeWorkerImpl implements CeWorker { + + private static final Logger LOG = Loggers.get(CeWorkerImpl.class); + + private final CeQueue queue; + private final ReportTaskProcessor reportTaskProcessor; + + public CeWorkerImpl(CeQueue queue, ReportTaskProcessor reportTaskProcessor) { + this.queue = queue; + this.reportTaskProcessor = reportTaskProcessor; + } + + @Override + public void run() { + Profiler profiler = Profiler.create(LOG).start(); + CeTask task; + try { + Optional<CeTask> taskOpt = queue.peek(); + if (!taskOpt.isPresent()) { + return; + } + task = taskOpt.get(); + } catch (Exception e) { + LOG.error("Failed to pop the queue of analysis reports", e); + return; + } + + try { + reportTaskProcessor.process(task); + queue.remove(task, CeActivityDto.Status.SUCCESS); + } catch (Throwable e) { + LOG.error(String.format("Failed to process task %s", task.getUuid()), e); + queue.remove(task, CeActivityDto.Status.FAILED); + } finally { + profiler.stopInfo(String.format("Total thread execution of project %s (report %s)", task.getComponentUuid(), task.getUuid())); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/CleanReportQueueListener.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CleanReportQueueListener.java new file mode 100644 index 00000000000..16a793ea6a1 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CleanReportQueueListener.java @@ -0,0 +1,36 @@ +/* + * 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.computation; + +import org.sonar.db.ce.CeActivityDto; + +public class CleanReportQueueListener implements CeQueueListener { + + private final ReportFiles reportFiles; + + public CleanReportQueueListener(ReportFiles reportFiles) { + this.reportFiles = reportFiles; + } + + @Override + public void onRemoved(CeTask task, CeActivityDto.Status status) { + reportFiles.deleteIfExists(task.getUuid()); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationStepExecutor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationStepExecutor.java index c90f2a75f7f..8fb79cce6e4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationStepExecutor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationStepExecutor.java @@ -20,79 +20,26 @@ package org.sonar.server.computation; 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.computation.monitoring.CEQueueStatus; import org.sonar.server.computation.step.ComputationStep; - -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; +import org.sonar.server.computation.step.ComputationSteps; public final class ComputationStepExecutor { - private final Logger logger; - private final Listener listener; - private final String description; - private final CEQueueStatus queueStatus; - - public ComputationStepExecutor(Logger logger, Listener listener, String description, CEQueueStatus queueStatus) { - this.logger = requireNonNull(logger); - this.listener = requireNonNull(listener); - this.description = requireNonNull(description); - this.queueStatus = queueStatus; - } + private static final Logger LOGGER = Loggers.get(ComputationStepExecutor.class); - public void execute(Iterable<ComputationStep> steps) { - queueStatus.addInProgress(); - listener.onStart(); - Profiler profiler = Profiler.create(logger).startDebug(description); - long timingSum = 0L; - Profiler stepProfiler = Profiler.create(logger); - try { - for (ComputationStep step : steps) { - stepProfiler.start(); - step.execute(); - timingSum += stepProfiler.stopInfo(step.getDescription()); - } - long timing = logProcessingEnd(description, profiler, timingSum); - queueStatus.addSuccess(timing); - listener.onSuccess(timing); - } catch (Throwable e) { - long timing = logProcessingEnd(description, profiler, timingSum); - queueStatus.addError(timing); - listener.onError(e, timing); - } finally { - listener.onEnd(); - } - } + private final ComputationSteps steps; - private static long logProcessingEnd(String message, Profiler profiler, long timingSum) { - return profiler.stopInfo(format("%s total time spent in steps=%sms", message, timingSum)); + public ComputationStepExecutor(ComputationSteps steps) { + this.steps = steps; } - public interface Listener { - - /** - * Called before the first ComputationStep is executed. - */ - void onStart(); - - /** - * Called when on ComputationSteps have been executed and no error occurred. - * - * @param timing the duration of the execution - */ - void onSuccess(long timing); - - /** - * Called when on ComputationSteps have been executed and no error occurred. - * - * @param e the error - * @param timing the duration of the execution - */ - void onError(Throwable e, long timing); - - /** - * Called when all ComputationSteps have been executed, after either {@link #onSuccess(long)} or {@link #onError(Throwable, long)} - */ - void onEnd(); + public void execute() { + Profiler stepProfiler = Profiler.create(LOGGER); + for (ComputationStep step : steps.instances()) { + stepProfiler.start(); + step.execute(); + stepProfiler.stopInfo(step.getDescription()); + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingExecutorService.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingExecutorService.java index 1c3fea8fd8d..f6075d569f8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingExecutorService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingExecutorService.java @@ -22,7 +22,7 @@ package org.sonar.server.computation; import org.sonar.server.util.StoppableScheduledExecutorService; /** - * The {@link java.util.concurrent.ExecutorService} responsible for running {@link ComputeEngineTask}. + * The {@link java.util.concurrent.ExecutorService} responsible for running {@link CeWorker}. */ public interface ComputeEngineProcessingExecutorService extends StoppableScheduledExecutorService { } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingModule.java index c48cb432f7d..ea1d40a72a0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingModule.java @@ -20,11 +20,16 @@ package org.sonar.server.computation; import org.sonar.core.platform.Module; +import org.sonar.server.computation.container.ContainerFactoryImpl; public class ComputeEngineProcessingModule extends Module { @Override protected void configureModule() { add( + CeWorkerImpl.class, + ContainerFactoryImpl.class, + ComputationStepExecutor.class, + ReportTaskProcessor.class, ReportProcessingScheduler.class, ReportProcessingSchedulerExecutorServiceImpl.class, ComputeEngineProcessingExecutorServiceImpl.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueue.java index 687aca20c59..5bef453b115 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueue.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueue.java @@ -23,5 +23,5 @@ public interface ComputeEngineProcessingQueue { /** * Adds a task to the Compute Engine processing queue. */ - void addTask(ComputeEngineTask task); + void addTask(CeWorker task); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueueImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueueImpl.java index b981002ca30..69805c00c4f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueueImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueueImpl.java @@ -33,7 +33,7 @@ public class ComputeEngineProcessingQueueImpl implements ComputeEngineProcessing private static final Logger LOG = Loggers.get(ComputeEngineProcessingQueueImpl.class); private final ComputeEngineProcessingExecutorService processingService; - private final ConcurrentLinkedQueue<ComputeEngineTask> queue = Queues.newConcurrentLinkedQueue(); + private final ConcurrentLinkedQueue<CeWorker> queue = Queues.newConcurrentLinkedQueue(); private final long delayBetweenTasks; private final long delayForFirstStart; @@ -48,7 +48,7 @@ public class ComputeEngineProcessingQueueImpl implements ComputeEngineProcessing } @Override - public void addTask(ComputeEngineTask task) { + public void addTask(CeWorker task) { requireNonNull(task, "a ComputeEngineTask can not be null"); queue.add(task); @@ -62,7 +62,7 @@ public class ComputeEngineProcessingQueueImpl implements ComputeEngineProcessing private class ProcessHeadOfQueueRunnable implements Runnable { @Override public void run() { - ComputeEngineTask task = queue.poll(); + CeWorker task = queue.poll(); if (task != null) { try { task.run(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportFiles.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportFiles.java new file mode 100644 index 00000000000..6b24dafdff9 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportFiles.java @@ -0,0 +1,95 @@ +/* + * 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.computation; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.sonar.api.config.Settings; +import org.sonar.api.server.ServerSide; +import org.sonar.process.ProcessProperties; + +import static java.lang.String.format; + +// TODO deal with temporary unzipped files +@ServerSide +public class ReportFiles { + + private static final String ZIP_EXTENSION = "zip"; + + private final Settings settings; + + public ReportFiles(Settings settings) { + this.settings = settings; + } + + public void save(CeTaskSubmit taskSubmit, InputStream reportInput) { + File file = fileForUuid(taskSubmit.getUuid()); + try { + FileUtils.copyInputStreamToFile(reportInput, file); + } catch (Exception e) { + FileUtils.deleteQuietly(file); + IOUtils.closeQuietly(reportInput); + throw new IllegalStateException(format("Fail to copy report to file: %s", file.getAbsolutePath()), e); + } + } + + public void deleteIfExists(String taskUuid) { + FileUtils.deleteQuietly(fileForUuid(taskUuid)); + } + + public void deleteAll() { + File dir = reportDir(); + try { + FileUtils.deleteDirectory(dir); + } catch (Exception e) { + throw new IllegalStateException(format("Fail to delete directory: %s", dir.getAbsolutePath()), e); + } + } + + private File reportDir() { + return new File(settings.getString(ProcessProperties.PATH_DATA), "ce/reports"); + } + + /** + * The analysis report to be processed. Can't be null + * but may no exist on file system. + */ + public File fileForUuid(String taskUuid) { + return new File(reportDir(), format("%s.%s", taskUuid, ZIP_EXTENSION)); + } + + public List<String> listUuids() { + List<String> uuids = new ArrayList<>(); + File dir = reportDir(); + if (dir.exists()) { + Collection<File> files = FileUtils.listFiles(dir, new String[]{ZIP_EXTENSION}, false); + for (File file : files) { + uuids.add(FilenameUtils.getBaseName(file.getName())); + } + } + return uuids; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingScheduler.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingScheduler.java index 7bc833ff734..89c472b813a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingScheduler.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingScheduler.java @@ -23,23 +23,14 @@ package org.sonar.server.computation; import java.util.concurrent.TimeUnit; import org.sonar.api.platform.Server; import org.sonar.api.platform.ServerStartHandler; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.container.ContainerFactory; -import org.sonar.server.computation.container.ContainerFactoryImpl; /** * Adds tasks to the Compute Engine to process batch reports. */ public class ReportProcessingScheduler implements ServerStartHandler { - private static final Logger LOG = Loggers.get(ReportProcessingScheduler.class); - private final ReportProcessingSchedulerExecutorService reportProcessingSchedulerExecutorService; private final ComputeEngineProcessingQueue processingQueue; - private final ReportQueue queue; - private final ComponentContainer sqContainer; - private final ContainerFactory containerFactory; + private final CeWorker worker; private final long delayBetweenTasks; private final long delayForFirstStart; @@ -47,12 +38,10 @@ public class ReportProcessingScheduler implements ServerStartHandler { public ReportProcessingScheduler(ReportProcessingSchedulerExecutorService reportProcessingSchedulerExecutorService, ComputeEngineProcessingQueue processingQueue, - ReportQueue queue, ComponentContainer sqContainer) { + CeWorker worker) { this.reportProcessingSchedulerExecutorService = reportProcessingSchedulerExecutorService; this.processingQueue = processingQueue; - this.queue = queue; - this.sqContainer = sqContainer; - this.containerFactory = new ContainerFactoryImpl(); + this.worker = worker; this.delayBetweenTasks = 10; this.delayForFirstStart = 0; @@ -71,14 +60,7 @@ public class ReportProcessingScheduler implements ServerStartHandler { private class AddReportProcessingToCEProcessingQueue implements Runnable { @Override public void run() { - try { - ReportQueue.Item item = queue.pop(); - if (item != null) { - processingQueue.addTask(new ReportProcessingTask(queue, item, sqContainer, containerFactory)); - } - } catch (Exception e) { - LOG.error("Failed to pop the queue of analysis reports", e); - } + processingQueue.addTask(worker); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingSchedulerExecutorService.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingSchedulerExecutorService.java index 806cfd9a791..fb9ecd57618 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingSchedulerExecutorService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingSchedulerExecutorService.java @@ -22,7 +22,7 @@ package org.sonar.server.computation; import org.sonar.server.util.StoppableScheduledExecutorService; /** - * ExecutorService responsible for adding {@link ReportProcessingTask} to {@link ReportQueue} on a regular basis. + * ExecutorService responsible for adding {@link CeWorkerImpl} to {@link CeQueue} on a regular basis. */ public interface ReportProcessingSchedulerExecutorService extends StoppableScheduledExecutorService { } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingTask.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingTask.java deleted file mode 100644 index 4b6b68b8312..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingTask.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.computation; - -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.platform.ComponentContainer; -import org.sonar.core.util.logs.Profiler; -import org.sonar.server.computation.container.ComputeEngineContainer; -import org.sonar.server.computation.container.ContainerFactory; - -/** - * This Compute E pops a report from the queue and integrate it. - */ -public class ReportProcessingTask implements ComputeEngineTask { - - private static final Logger LOG = Loggers.get(ReportProcessingTask.class); - - private final ReportQueue queue; - private final ReportQueue.Item item; - private final ComponentContainer sqContainer; - private final ContainerFactory containerFactory; - - public ReportProcessingTask(ReportQueue queue, ReportQueue.Item item, ComponentContainer sqContainer, ContainerFactory containerFactory) { - this.queue = queue; - this.item = item; - this.sqContainer = sqContainer; - this.containerFactory = containerFactory; - } - - @Override - public void run() { - Profiler profiler = Profiler.create(LOG).start(); - - ComputeEngineContainer computeEngineContainer = containerFactory.create(sqContainer, item); - try { - computeEngineContainer.getComponentByType(ReportProcessor.class).process(); - } catch (Throwable e) { - LOG.error(String.format( - "Failed to process analysis report %d of project %s", item.dto.getId(), item.dto.getProjectKey()), e); - } finally { - computeEngineContainer.cleanup(); - - removeSilentlyFromQueue(item); - } - profiler.stopInfo(String.format("Total thread execution of project %s (report %d)", item.dto.getProjectKey(), item.dto.getId())); - } - - private void removeSilentlyFromQueue(ReportQueue.Item item) { - try { - queue.remove(item); - } catch (Exception e) { - LOG.error(String.format("Failed to remove analysis report %d from queue", item.dto.getId()), e); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessor.java deleted file mode 100644 index 958cbae3d2d..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.computation; - -import com.google.common.base.Throwables; -import org.sonar.api.utils.System2; -import org.sonar.api.utils.log.Loggers; -import org.sonar.server.computation.activity.ActivityManager; -import org.sonar.server.computation.monitoring.CEQueueStatus; -import org.sonar.server.computation.step.ComputationSteps; - -import static java.lang.String.format; -import static org.sonar.db.compute.AnalysisReportDto.Status.FAILED; -import static org.sonar.db.compute.AnalysisReportDto.Status.SUCCESS; - -public class ReportProcessor { - - private final ComputationStepExecutor executor; - private final ComputationSteps steps; - - public ReportProcessor(ComputationSteps steps, - ReportQueue.Item item, ActivityManager activityManager, System2 system, CEQueueStatus queueStatus) { - this.executor = new ComputationStepExecutor( - Loggers.get(ReportProcessor.class), - new ReportProcessingStepsExecutorListener(item, system, activityManager, queueStatus), - createDescription(item), queueStatus); - this.steps = steps; - } - - public void process() { - this.executor.execute(this.steps.instances()); - } - - private static String createDescription(ReportQueue.Item item) { - String projectKey = item.dto.getProjectKey(); - return format("Analysis of project %s (report %d)", projectKey, item.dto.getId()); - } - - private static class ReportProcessingStepsExecutorListener implements ComputationStepExecutor.Listener { - private final ReportQueue.Item item; - private final System2 system; - private final ActivityManager activityManager; - - private ReportProcessingStepsExecutorListener(ReportQueue.Item item, System2 system, ActivityManager activityManager, CEQueueStatus queueStatus) { - this.item = item; - this.system = system; - this.activityManager = activityManager; - } - - @Override - public void onStart() { - // nothing to do on start - } - - @Override - public void onSuccess(long timing) { - item.dto.setStatus(SUCCESS); - } - - @Override - public void onError(Throwable e, long timing) { - item.dto.setStatus(FAILED); - throw Throwables.propagate(e); - } - - @Override - public void onEnd() { - item.dto.setFinishedAt(system.now()); - activityManager.saveActivity(item.dto); - } - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueue.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueue.java deleted file mode 100644 index 5a0607b5c50..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueue.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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.computation; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import javax.annotation.CheckForNull; -import org.apache.commons.io.FileUtils; -import org.sonar.api.config.Settings; -import org.sonar.api.server.ServerSide; -import org.sonar.core.util.Uuids; -import org.sonar.api.utils.log.Loggers; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.MyBatis; -import org.sonar.db.compute.AnalysisReportDao; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.process.ProcessProperties; -import org.sonar.server.computation.monitoring.CEQueueStatus; - -import static org.sonar.db.compute.AnalysisReportDto.Status.PENDING; - -@ServerSide -public class ReportQueue { - private final DbClient dbClient; - private final Settings settings; - private final CEQueueStatus queueStatus; - - public ReportQueue(DbClient dbClient, Settings settings, CEQueueStatus queueStatus) { - this.dbClient = dbClient; - this.settings = settings; - this.queueStatus = queueStatus; - } - - public void start() { - DbSession session = dbClient.openSession(false); - try { - queueStatus.initPendingCount(dao().countPending(session)); - } finally { - MyBatis.closeQuietly(session); - } - } - - public Item add(String projectKey, String projectName, InputStream reportData) { - String uuid = Uuids.create(); - File file = reportFileForUuid(uuid); - - DbSession session = dbClient.openSession(false); - try { - saveReportOnDisk(reportData, file); - AnalysisReportDto dto = saveReportMetadataInDatabase(projectKey, projectName, uuid, session); - - return new Item(dto, file); - } catch (Exception e) { - FileUtils.deleteQuietly(file); - throw new IllegalStateException("Fail to store analysis report of project " + projectKey, e); - } finally { - MyBatis.closeQuietly(session); - } - } - - private AnalysisReportDto saveReportMetadataInDatabase(String projectKey, String projectName, String uuid, DbSession session) { - AnalysisReportDto dto = new AnalysisReportDto() - .setProjectKey(projectKey) - .setProjectName(projectName) - .setStatus(PENDING) - .setUuid(uuid); - dao().insert(session, dto); - session.commit(); - return dto; - } - - private AnalysisReportDao dao() { - return dbClient.analysisReportDao(); - } - - private static void saveReportOnDisk(InputStream reportData, File file) throws IOException { - FileUtils.copyInputStreamToFile(reportData, file); - } - - public void remove(Item item) { - DbSession session = dbClient.openSession(false); - try { - FileUtils.deleteQuietly(item.zipFile); - dao().delete(session, item.dto.getId()); - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } - } - - @CheckForNull - public Item pop() { - DbSession session = dbClient.openSession(false); - try { - AnalysisReportDto dto = dao().pop(session); - if (dto != null) { - File file = reportFileForUuid(dto.getUuid()); - if (file.exists()) { - return new Item(dto, file); - } - Loggers.get(getClass()).error("Analysis report not found: " + file.getAbsolutePath()); - dao().delete(session, dto.getId()); - session.commit(); - } - return null; - } finally { - MyBatis.closeQuietly(session); - } - } - - /** - * Truncates table ANALYSIS_REPORTS and delete all files from directory {data}/analysis - */ - public void clear() { - File dir = reportsDir(); - try { - FileUtils.deleteDirectory(dir); - } catch (IOException e) { - throw new IllegalStateException("Fail to delete directory: " + dir.getAbsolutePath(), e); - } - - DbSession session = dbClient.openSession(false); - try { - dao().truncate(session); - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } - } - - public void resetToPendingStatus() { - DbSession session = dbClient.openSession(false); - try { - dao().resetAllToPendingStatus(session); - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } - } - - /** - * All the reports of the queue, whatever the status - */ - public List<AnalysisReportDto> all() { - DbSession session = dbClient.openSession(false); - try { - return dao().selectAll(session); - } finally { - MyBatis.closeQuietly(session); - } - } - - /** - * This directory is a flat list of the reports referenced in table ANALYSIS_REPORTS. - * Never return null but the directory may not exist. - */ - private File reportsDir() { - return new File(settings.getString(ProcessProperties.PATH_DATA), "analysis"); - } - - private File reportFileForUuid(String uuid) { - return new File(reportsDir(), String.format("%s.zip", uuid)); - } - - public static class Item { - public final AnalysisReportDto dto; - public final File zipFile; - - public Item(AnalysisReportDto dto, File zipFile) { - this.dto = dto; - this.zipFile = zipFile; - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueueCleaner.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueueCleaner.java deleted file mode 100644 index fb82118ccd8..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueueCleaner.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.computation; - -import org.picocontainer.Startable; -import org.sonar.api.server.ServerSide; -import org.sonar.api.platform.ServerUpgradeStatus; - -/** - * Clean-up queue of reports at server startup: - * <ul> - * <li>remove all reports if server being upgraded to a new version (we assume that - * format of reports is not forward-compatible)</li> - * <li>reset reports that were in status WORKING while server stopped</li> - * </ul> - */ -@ServerSide -public class ReportQueueCleaner implements Startable { - - private final ServerUpgradeStatus serverUpgradeStatus; - private final ReportQueue queue; - - public ReportQueueCleaner(ServerUpgradeStatus serverUpgradeStatus, ReportQueue queue) { - this.serverUpgradeStatus = serverUpgradeStatus; - this.queue = queue; - } - - @Override - public void start() { - if (serverUpgradeStatus.isUpgraded()) { - queue.clear(); - } else { - queue.resetToPendingStatus(); - } - } - - @Override - public void stop() { - // do nothing - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportSubmitter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportSubmitter.java new file mode 100644 index 00000000000..1fe382e9b51 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportSubmitter.java @@ -0,0 +1,71 @@ +/* + * 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.computation; + +import java.io.InputStream; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.server.ServerSide; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentService; +import org.sonar.server.component.NewComponent; +import org.sonar.server.user.UserSession; + +@ServerSide +public class ReportSubmitter { + + private final CeQueue queue; + private final UserSession userSession; + private final ReportFiles reportFiles; + private final ComponentService componentService; + + public ReportSubmitter(CeQueue queue, UserSession userSession, ReportFiles reportFiles, + ComponentService componentService) { + this.queue = queue; + this.userSession = userSession; + this.reportFiles = reportFiles; + this.componentService = componentService; + } + + public CeTask submit(String projectKey, @Nullable String projectName, InputStream reportInput) { + userSession.checkGlobalPermission(GlobalPermissions.SCAN_EXECUTION); + + ComponentDto project = componentService.getNullableByKey(projectKey); + if (project == null) { + // the project does not exist -> requires to provision it + NewComponent newProject = new NewComponent(projectKey, StringUtils.defaultIfBlank(projectName, projectKey)); + newProject.setQualifier(Qualifiers.PROJECT); + // no need to verify the permission "provisionning" as it's already handled by componentService + project = componentService.create(newProject); + } + + // the report file must be saved before submitting the task + CeTaskSubmit submit = queue.prepareSubmit(); + reportFiles.save(submit, reportInput); + + submit.setType(CeTaskTypes.REPORT); + submit.setComponentUuid(project.uuid()); + submit.setSubmitterLogin(userSession.getLogin()); + return queue.submit(submit); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportTaskProcessor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportTaskProcessor.java new file mode 100644 index 00000000000..e8dc5803bde --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportTaskProcessor.java @@ -0,0 +1,44 @@ +/* + * 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.computation; + +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.container.ComputeEngineContainer; +import org.sonar.server.computation.container.ContainerFactory; + +public class ReportTaskProcessor { + + private final ContainerFactory containerFactory; + private final ComponentContainer serverContainer; + + public ReportTaskProcessor(ContainerFactory containerFactory, ComponentContainer serverContainer) { + this.containerFactory = containerFactory; + this.serverContainer = serverContainer; + } + + public void process(CeTask task) { + ComputeEngineContainer ceContainer = containerFactory.create(serverContainer, task); + try { + ceContainer.getComponentByType(ComputationStepExecutor.class).execute(); + } finally { + ceContainer.cleanup(); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java b/server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java deleted file mode 100644 index 7df6ff1cf80..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.computation.activity; - -import com.google.common.base.Optional; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.MyBatis; -import org.sonar.db.component.ComponentDto; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.activity.Activity; -import org.sonar.server.activity.ActivityService; - -import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe; -import static org.sonar.api.utils.DateUtils.longToDate; - -public class ActivityManager { - private final ActivityService activityService; - private final DbClient dbClient; - - public ActivityManager(ActivityService activityService, DbClient dbClient) { - this.activityService = activityService; - this.dbClient = dbClient; - } - - public void saveActivity(AnalysisReportDto report) { - Optional<ComponentDto> projectOptional = loadProject(report.getProjectKey()); - Activity activity = new Activity(); - activity.setType(Activity.Type.ANALYSIS_REPORT); - activity.setAction("LOG_ANALYSIS_REPORT"); - activity - .setData("key", String.valueOf(report.getId())) - .setData("projectKey", report.getProjectKey()) - .setData("status", String.valueOf(report.getStatus())) - .setData("submittedAt", formatDateTimeNullSafe(longToDate(report.getCreatedAt()))) - .setData("startedAt", formatDateTimeNullSafe(longToDate(report.getStartedAt()))) - .setData("finishedAt", formatDateTimeNullSafe(longToDate(report.getFinishedAt()))); - if (projectOptional.isPresent()) { - ComponentDto project = projectOptional.get(); - activity - .setData("projectName", project.name()) - .setData("projectUuid", project.uuid()); - } - activityService.save(activity); - } - - private Optional<ComponentDto> loadProject(String projectKey) { - DbSession session = dbClient.openSession(false); - try { - return dbClient.componentDao().selectByKey(session, projectKey); - } finally { - MyBatis.closeQuietly(session); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolder.java index 088dbc56e71..bb76947dbda 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolder.java @@ -20,11 +20,10 @@ package org.sonar.server.computation.batch; import java.io.File; -import org.sonar.server.computation.ReportQueue; public interface BatchReportDirectoryHolder { /** - * The File of the directory where the Batch report files for the current {@link ReportQueue.Item} are stored. + * The File of the directory where the Batch report files for the current {@link org.sonar.server.computation.CeTask} are stored. * * @throws IllegalStateException if the holder is empty (ie. there is no directory yet) */ diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java index e6e3556393b..f0b4bba8d19 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java @@ -23,6 +23,7 @@ import java.io.File; import java.util.Objects; public class BatchReportDirectoryHolderImpl implements MutableBatchReportDirectoryHolder { + private File directory; @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java index 4854b389fdd..87c091348ab 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java @@ -21,17 +21,16 @@ package org.sonar.server.computation.container; import org.sonar.core.platform.ComponentContainer; import org.sonar.core.platform.ContainerPopulator; -import org.sonar.server.computation.ReportQueue.Item; /** - * The Compute Engine container. Created for a specific parent {@link ComponentContainer} and a specific {@link Item}. + * The Compute Engine container. Created for a specific parent {@link ComponentContainer} and a specific {@link org.sonar.server.computation.CeTask}. */ public interface ComputeEngineContainer extends ContainerPopulator.Container { ComponentContainer getParent(); /** - * Clean's up resources after process has been called and has returned. + * Cleans up resources after process has been called and has returned. */ void cleanup(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java index 7787a6c69e6..c1011f6969f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java @@ -20,12 +20,12 @@ package org.sonar.server.computation.container; import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.CeTask; /** * Compute */ public interface ContainerFactory { - ComputeEngineContainer create(ComponentContainer parent, ReportQueue.Item item); + ComputeEngineContainer create(ComponentContainer parent, CeTask task); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java index 90c0294b8b1..ed41a1c1f11 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java @@ -20,11 +20,11 @@ package org.sonar.server.computation.container; import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.CeTask; public class ContainerFactoryImpl implements ContainerFactory { @Override - public ComputeEngineContainer create(ComponentContainer parent, ReportQueue.Item item) { - return new ComputeEngineContainerImpl(parent, new ReportComputeEngineContainerPopulator(item)); + public ComputeEngineContainer create(ComponentContainer parent, CeTask task) { + return new ComputeEngineContainerImpl(parent, new ReportComputeEngineContainerPopulator(task)); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java index 0756e92821c..453aef13d87 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java @@ -23,10 +23,9 @@ import java.util.Arrays; import java.util.List; import org.sonar.core.issue.tracking.Tracker; import org.sonar.core.platform.ContainerPopulator; +import org.sonar.server.computation.CeTask; import org.sonar.server.computation.ComputationTempFolderProvider; -import org.sonar.server.computation.ReportProcessor; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.computation.activity.ActivityManager; +import org.sonar.server.computation.ComputationStepExecutor; import org.sonar.server.computation.analysis.ReportAnalysisMetadataHolder; import org.sonar.server.computation.batch.BatchReportDirectoryHolderImpl; import org.sonar.server.computation.batch.BatchReportReaderImpl; @@ -85,16 +84,16 @@ import org.sonar.server.computation.step.ReportComputationSteps; import org.sonar.server.view.index.ViewIndex; public final class ReportComputeEngineContainerPopulator implements ContainerPopulator<ComputeEngineContainer> { - private final ReportQueue.Item item; + private final CeTask task; - public ReportComputeEngineContainerPopulator(ReportQueue.Item item) { - this.item = item; + public ReportComputeEngineContainerPopulator(CeTask task) { + this.task = task; } @Override public void populateContainer(ComputeEngineContainer container) { ComputationSteps steps = new ReportComputationSteps(container); - container.add(item); + container.add(task); container.add(steps); container.addSingletons(componentClasses()); container.addSingletons(steps.orderedStepClasses()); @@ -106,85 +105,80 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop */ private static List componentClasses() { return Arrays.asList( - new ComputationTempFolderProvider(), - - ActivityManager.class, - - MetricModule.class, - - // holders - ReportAnalysisMetadataHolder.class, - BatchReportDirectoryHolderImpl.class, - ReportTreeRootHolderImpl.class, - PeriodsHolderImpl.class, - QualityGateHolderImpl.class, - DebtModelHolderImpl.class, - SqaleRatingSettings.class, - ActiveRulesHolderImpl.class, - MeasureComputersHolderImpl.class, - - BatchReportReaderImpl.class, - - // repositories - LanguageRepositoryImpl.class, - MeasureRepositoryImpl.class, - EventRepositoryImpl.class, - SettingsRepositoryImpl.class, - DbIdsRepositoryImpl.class, - QualityGateServiceImpl.class, - EvaluationResultTextConverterImpl.class, - - // issues - RuleCacheLoader.class, - RuleRepositoryImpl.class, - ScmAccountToUserLoader.class, - ScmAccountToUser.class, - IssueCache.class, - DefaultAssignee.class, - IssueVisitors.class, - IssueLifecycle.class, - ComponentsWithUnprocessedIssues.class, - ComponentIssuesRepositoryImpl.class, - - // common rules - CommonRuleEngineImpl.class, - BranchCoverageRule.class, - LineCoverageRule.class, - CommentDensityRule.class, - DuplicatedBlockRule.class, - TestErrorRule.class, - SkippedTestRule.class, - - // order is important: DebtAggregator then NewDebtAggregator (new debt requires debt) - DebtCalculator.class, - DebtAggregator.class, - NewDebtCalculator.class, - NewDebtAggregator.class, - IssueAssigner.class, - RuleTagsCopier.class, - IssueCounter.class, - - // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues - LoadComponentUuidsHavingOpenIssuesVisitor.class, - IntegrateIssuesVisitor.class, - CloseIssuesOnRemovedComponentsVisitor.class, - SqaleMeasuresVisitor.class, - LastCommitVisitor.class, - MeasureComputersVisitor.class, - - UpdateConflictResolver.class, - TrackerBaseInputFactory.class, - TrackerRawInputFactory.class, - Tracker.class, - TrackerExecution.class, - BaseIssuesLoader.class, - - // views - ViewIndex.class, - - // ReportProcessor - ReportProcessor.class); + ComputationStepExecutor.class, + new ComputationTempFolderProvider(), + + MetricModule.class, + + // holders + ReportAnalysisMetadataHolder.class, + BatchReportDirectoryHolderImpl.class, + ReportTreeRootHolderImpl.class, + PeriodsHolderImpl.class, + QualityGateHolderImpl.class, + DebtModelHolderImpl.class, + SqaleRatingSettings.class, + ActiveRulesHolderImpl.class, + MeasureComputersHolderImpl.class, + + BatchReportReaderImpl.class, + + // repositories + LanguageRepositoryImpl.class, + MeasureRepositoryImpl.class, + EventRepositoryImpl.class, + SettingsRepositoryImpl.class, + DbIdsRepositoryImpl.class, + QualityGateServiceImpl.class, + EvaluationResultTextConverterImpl.class, + + // issues + RuleCacheLoader.class, + RuleRepositoryImpl.class, + ScmAccountToUserLoader.class, + ScmAccountToUser.class, + IssueCache.class, + DefaultAssignee.class, + IssueVisitors.class, + IssueLifecycle.class, + ComponentsWithUnprocessedIssues.class, + ComponentIssuesRepositoryImpl.class, + + // common rules + CommonRuleEngineImpl.class, + BranchCoverageRule.class, + LineCoverageRule.class, + CommentDensityRule.class, + DuplicatedBlockRule.class, + TestErrorRule.class, + SkippedTestRule.class, + + // order is important: DebtAggregator then NewDebtAggregator (new debt requires debt) + DebtCalculator.class, + DebtAggregator.class, + NewDebtCalculator.class, + NewDebtAggregator.class, + IssueAssigner.class, + RuleTagsCopier.class, + IssueCounter.class, + + // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues + LoadComponentUuidsHavingOpenIssuesVisitor.class, + IntegrateIssuesVisitor.class, + CloseIssuesOnRemovedComponentsVisitor.class, + SqaleMeasuresVisitor.class, + LastCommitVisitor.class, + MeasureComputersVisitor.class, + + UpdateConflictResolver.class, + TrackerBaseInputFactory.class, + TrackerRawInputFactory.class, + Tracker.class, + TrackerExecution.class, + BaseIssuesLoader.class, + + // views + ViewIndex.class); } - } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java index 7877c677f5b..a5b525d06ac 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java @@ -20,7 +20,7 @@ package org.sonar.server.computation.monitoring; import java.util.LinkedHashMap; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.CeQueue; import org.sonar.server.platform.monitoring.BaseMonitorMBean; public class ComputeEngineQueueMonitor extends BaseMonitorMBean implements ComputeEngineQueueMonitorMBean { @@ -30,7 +30,7 @@ public class ComputeEngineQueueMonitor extends BaseMonitorMBean implements Compu // ReportQueue initializes CEQueueStatus and is therefor a dependency of // ComputeEngineQueueMonitor. // Do not remove this parameter, it ensures start order of components - ReportQueue reportQueue) { + CeQueue ceQueue) { this.queueStatus = queueStatus; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateHolder.java index eeffd81cd74..07ada89613c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateHolder.java @@ -19,12 +19,11 @@ */ package org.sonar.server.computation.qualitygate; -import org.sonar.server.computation.ReportQueue; import org.sonar.server.computation.component.Component; public interface MutableQualityGateHolder extends QualityGateHolder { /** - * Sets the quality gate for the project of the currently processed {@link ReportQueue.Item}. + * Sets the quality gate. * Settings a quality gate more than once is not allowed and it can never be set to {@code null}. * * @param qualityGate a {@link Component}, can not be {@code null} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateHolder.java index 9c75d885d81..ac762d22bee 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateHolder.java @@ -20,11 +20,10 @@ package org.sonar.server.computation.qualitygate; import com.google.common.base.Optional; -import org.sonar.server.computation.ReportQueue; public interface QualityGateHolder { /** - * The QualityGate for the project of the current {@link ReportQueue.Item} if there is any. + * The QualityGate for the project if there is any. * * @throws IllegalStateException if the holder has not been initialized (ie. we don't know yet what is the QualityGate) */ diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java index 232f39392e9..49a06363f60 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java @@ -24,7 +24,6 @@ import org.apache.commons.lang.StringUtils; import org.sonar.api.config.Settings; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.server.computation.ReportQueue; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.CrawlerDepthLimit; import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler; @@ -38,7 +37,7 @@ import org.sonar.server.computation.qualitygate.QualityGateService; import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; /** - * This step retrieves the QualityGate for the current {@link ReportQueue.Item} and stores it in + * This step retrieves the QualityGate and stores it in * {@link MutableQualityGateHolder}. */ public class QualityGateLoadingStep implements ComputationStep { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportExtractionStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportExtractionStep.java index f3048e9d1fd..77a597116e8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportExtractionStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportExtractionStep.java @@ -26,23 +26,26 @@ import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.ZipUtils; 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.computation.ReportQueue; +import org.sonar.server.computation.CeTask; +import org.sonar.server.computation.ReportFiles; import org.sonar.server.computation.batch.MutableBatchReportDirectoryHolder; /** - * Extracts the content zip file of the {@link ReportQueue.Item} to a temp directory and adds a {@link File} + * Extracts the content zip file of the {@link org.sonar.server.computation.CeTask} to a temp directory and adds a {@link File} * representing that temp directory to the {@link MutableBatchReportDirectoryHolder}. */ public class ReportExtractionStep implements ComputationStep { private static final Logger LOG = Loggers.get(ReportExtractionStep.class); - private final ReportQueue.Item item; + private final ReportFiles reportFiles; + private final CeTask task; private final TempFolder tempFolder; private final MutableBatchReportDirectoryHolder reportDirectoryHolder; - public ReportExtractionStep(ReportQueue.Item item, TempFolder tempFolder, MutableBatchReportDirectoryHolder reportDirectoryHolder) { - this.item = item; + public ReportExtractionStep(ReportFiles reportFiles, CeTask task, TempFolder tempFolder, + MutableBatchReportDirectoryHolder reportDirectoryHolder) { + this.reportFiles = reportFiles; + this.task = task; this.tempFolder = tempFolder; this.reportDirectoryHolder = reportDirectoryHolder; } @@ -50,23 +53,20 @@ public class ReportExtractionStep implements ComputationStep { @Override public void execute() { File dir = tempFolder.newDir(); + File zip = reportFiles.fileForUuid(task.getUuid()); try { - Profiler profiler = Profiler.createIfDebug(LOG).start(); - ZipUtils.unzip(item.zipFile, dir); - if (profiler.isDebugEnabled()) { - String message = String.format("Report extracted | size=%s | project=%s", - FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(dir)), item.dto.getProjectKey()); - profiler.stopDebug(message); - } + ZipUtils.unzip(zip, dir); reportDirectoryHolder.setDirectory(dir); + LOG.info("Analysis report extracted | size={} | compressed={}", + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(dir)), FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(zip))); } catch (IOException e) { - throw new IllegalStateException(String.format("Fail to unzip %s into %s", item.zipFile, dir), e); + throw new IllegalStateException(String.format("Fail to unzip %s into %s", zip, dir), e); } } @Override public String getDescription() { - return "Extracting batch report to temp directory"; + return "Uncompress report"; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeSubmitWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeSubmitWsAction.java new file mode 100644 index 00000000000..86c25391ce9 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeSubmitWsAction.java @@ -0,0 +1,93 @@ +/* + * 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.computation.ws; + +import java.io.InputStream; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.computation.CeTask; +import org.sonar.server.computation.ReportProcessingScheduler; +import org.sonar.server.computation.ReportSubmitter; +import org.sonar.server.ws.WsUtils; +import org.sonarqube.ws.WsCe; + +/** + * POST api/ce/submit + * <p>Submits an analysis report to the queue of Compute Engine</p> + */ +public class CeSubmitWsAction implements CeWsAction { + + public static final String PARAM_PROJECT_KEY = "projectKey"; + public static final String PARAM_PROJECT_NAME = "projectName"; + public static final String PARAM_REPORT_DATA = "report"; + + private final ReportSubmitter reportSubmitter; + private final ReportProcessingScheduler reportProcessingScheduler; + + public CeSubmitWsAction(ReportSubmitter reportSubmitter, ReportProcessingScheduler reportProcessingScheduler) { + this.reportSubmitter = reportSubmitter; + this.reportProcessingScheduler = reportProcessingScheduler; + } + + @Override + public void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("submit") + .setDescription("Submit an analysis report to the queue of Compute Engine. Report is processed asynchronously.") + .setPost(true) + .setInternal(true) + .setHandler(this) + .setResponseExample(getClass().getResource("CeSubmitWsAction/example.json")); + + action + .createParam(PARAM_PROJECT_KEY) + .setRequired(true) + .setDescription("Key of project") + .setExampleValue("my_project"); + + action + .createParam(PARAM_PROJECT_NAME) + .setRequired(false) + .setDescription("Optional name of the project, used only if the project does not exist yet.") + .setExampleValue("My Project"); + + action + .createParam(PARAM_REPORT_DATA) + .setRequired(true) + .setDescription("Report file. Format is not an API, it changes among SonarQube versions."); + } + + @Override + public void handle(Request wsRequest, Response wsResponse) throws Exception { + String projectKey = wsRequest.mandatoryParam(PARAM_PROJECT_KEY); + String projectName = StringUtils.defaultIfBlank(wsRequest.param(PARAM_PROJECT_NAME), projectKey); + InputStream reportInput = wsRequest.paramAsInputStream(PARAM_REPORT_DATA); + + CeTask task = reportSubmitter.submit(projectKey, projectName, reportInput); + reportProcessingScheduler.startAnalysisTaskNow(); + + WsCe.SubmitResponse submitResponse = WsCe.SubmitResponse.newBuilder() + .setTaskId(task.getUuid()) + .setProjectId(task.getComponentUuid()) + .build(); + WsUtils.writeProtobuf(submitResponse, wsRequest, wsResponse); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeTaskWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeTaskWsAction.java new file mode 100644 index 00000000000..1cd393180ef --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeTaskWsAction.java @@ -0,0 +1,93 @@ +/* + * 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.computation.ws; + +import com.google.common.base.Optional; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.UserRole; +import org.sonar.core.util.Uuids; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.user.UserSession; +import org.sonar.server.ws.WsUtils; +import org.sonarqube.ws.WsCe; + +public class CeTaskWsAction implements CeWsAction { + + public static final String ACTION = "task"; + + public static final String PARAM_TASK_ID = "id"; + + private final DbClient dbClient; + private final CeWsTaskFormatter wsTaskFormatter; + private final UserSession userSession; + + public CeTaskWsAction(DbClient dbClient, CeWsTaskFormatter wsTaskFormatter, UserSession userSession) { + this.dbClient = dbClient; + this.wsTaskFormatter = wsTaskFormatter; + this.userSession = userSession; + } + + @Override + public void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction(ACTION) + .setDescription("Task information") + .setInternal(true) + .setHandler(this); + + action + .createParam(PARAM_TASK_ID) + .setRequired(true) + .setDescription("Id of task") + .setExampleValue(Uuids.UUID_EXAMPLE_01); + } + + @Override + public void handle(Request wsRequest, Response wsResponse) throws Exception { + userSession.checkGlobalPermission(UserRole.ADMIN); + + String taskId = wsRequest.mandatoryParam(PARAM_TASK_ID); + DbSession dbSession = dbClient.openSession(false); + try { + WsCe.TaskResponse.Builder wsTaskResponse = WsCe.TaskResponse.newBuilder(); + Optional<CeQueueDto> queueDto = dbClient.ceQueueDao().selectByUuid(dbSession, taskId); + if (queueDto.isPresent()) { + wsTaskResponse.setTask(wsTaskFormatter.format(queueDto.get())); + } else { + Optional<CeActivityDto> activityDto = dbClient.ceActivityDao().selectByUuid(dbSession, taskId); + if (activityDto.isPresent()) { + wsTaskResponse.setTask(wsTaskFormatter.format(activityDto.get())); + } else { + throw new NotFoundException(); + } + } + WsUtils.writeProtobuf(wsTaskResponse.build(), wsRequest, wsResponse); + + } finally { + dbClient.closeSession(dbSession); + } + + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComputationWs.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWs.java index 41507396418..45d79d013b0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComputationWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWs.java @@ -22,20 +22,13 @@ package org.sonar.server.computation.ws; import org.sonar.api.server.ws.WebService; -/** - * Web service to interact with the "computation" stack : - * <ul> - * <li>queue of analysis reports to be integrated</li> - * <li>consolidation and aggregation of analysis measures</li> - * <li>persistence in datastores (database/elasticsearch)</li> - * </ul> - */ -public class ComputationWs implements WebService { - public static final String ENDPOINT = "api/computation"; +public class CeWs implements WebService { + + public static final String ENDPOINT = "api/ce"; - private final ComputationWsAction[] actions; + private final CeWsAction[] actions; - public ComputationWs(ComputationWsAction... actions) { + public CeWs(CeWsAction... actions) { this.actions = actions; } @@ -43,8 +36,8 @@ public class ComputationWs implements WebService { public void define(Context context) { NewController controller = context .createController(ENDPOINT) - .setDescription("Analysis reports processed"); - for (ComputationWsAction action : actions) { + .setDescription("Compute Engine"); + for (CeWsAction action : actions) { action.define(controller); } controller.done(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComputationWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsAction.java index 65edafb7ce4..fb695b052f3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComputationWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsAction.java @@ -23,10 +23,9 @@ import org.sonar.api.server.ws.WebService; import org.sonar.server.ws.WsAction; /** - * Used by {@link ComputationWs} to - * loop over all its actions + * Used by {@link CeWs} to loop over all its actions */ -interface ComputationWsAction extends WsAction { +interface CeWsAction extends WsAction { @Override void define(WebService.NewController controller); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsTaskFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsTaskFormatter.java new file mode 100644 index 00000000000..186175a800d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsTaskFormatter.java @@ -0,0 +1,89 @@ +/* + * 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.computation.ws; + +import java.util.Date; +import org.sonar.api.utils.DateUtils; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentService; +import org.sonarqube.ws.WsCe; + +public class CeWsTaskFormatter { + + private final ComponentService componentService; + + public CeWsTaskFormatter(ComponentService componentService) { + this.componentService = componentService; + } + + public WsCe.Task format(CeQueueDto dto) { + WsCe.Task.Builder builder = WsCe.Task.newBuilder(); + builder.setId(dto.getUuid()); + builder.setStatus(WsCe.TaskStatus.valueOf(dto.getStatus().name())); + builder.setTaskType(dto.getTaskType()); + if (dto.getComponentUuid() != null) { + builder.setComponentId(dto.getComponentUuid()); + buildComponent(builder, dto.getComponentUuid()); + } + if (dto.getSubmitterLogin() != null) { + builder.setSubmitterLogin(dto.getSubmitterLogin()); + } + builder.setSubmittedAt(DateUtils.formatDateTime(new Date(dto.getCreatedAt()))); + if (dto.getStartedAt() != null) { + builder.setStartedAt(DateUtils.formatDateTime(new Date(dto.getStartedAt()))); + } + return builder.build(); + } + + public WsCe.Task format(CeActivityDto dto) { + WsCe.Task.Builder builder = WsCe.Task.newBuilder(); + builder.setId(dto.getUuid()); + builder.setStatus(WsCe.TaskStatus.valueOf(dto.getStatus().name())); + builder.setTaskType(dto.getTaskType()); + if (dto.getComponentUuid() != null) { + builder.setComponentId(dto.getComponentUuid()); + buildComponent(builder, dto.getComponentUuid()); + } + if (dto.getSubmitterLogin() != null) { + builder.setSubmitterLogin(dto.getSubmitterLogin()); + } + builder.setSubmittedAt(DateUtils.formatDateTime(new Date(dto.getCreatedAt()))); + if (dto.getStartedAt() != null) { + builder.setStartedAt(DateUtils.formatDateTime(new Date(dto.getStartedAt()))); + } + if (dto.getFinishedAt() != null) { + builder.setFinishedAt(DateUtils.formatDateTime(new Date(dto.getFinishedAt()))); + } + if (dto.getExecutionTimeMs() != null) { + builder.setExecutionTimeMs(dto.getExecutionTimeMs()); + } + return builder.build(); + } + + private void buildComponent(WsCe.Task.Builder builder, String componentUuid) { + ComponentDto project = componentService.getNonNullByUuid(componentUuid); + if (project != null) { + builder.setComponentKey(project.getKey()); + builder.setComponentName(project.name()); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryAction.java deleted file mode 100644 index 0320fcdefdf..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryAction.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.computation.ws; - -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.text.JsonWriter; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.server.activity.Activity; -import org.sonar.server.activity.index.ActivityDoc; -import org.sonar.server.activity.index.ActivityIndex; -import org.sonar.server.activity.index.ActivityQuery; -import org.sonar.server.es.SearchOptions; -import org.sonar.server.es.SearchResult; -import org.sonar.server.issue.ws.IssuesWs; -import org.sonar.server.user.UserSession; - -import java.util.Arrays; -import java.util.Map; - -// FIXME replace by api/activities/search -public class HistoryAction implements ComputationWsAction { - private final ActivityIndex activityIndex; - private final UserSession userSession; - - public HistoryAction(ActivityIndex activityIndex, UserSession userSession) { - this.activityIndex = activityIndex; - this.userSession = userSession; - } - - @Override - public void define(WebService.NewController controller) { - WebService.NewAction action = controller - .createAction("history") - .setDescription("Past integrations of analysis reports") - .setSince("5.0") - .setInternal(true) - .setHandler(this); - - action.addPagingParams(10); - } - - @Override - public void handle(Request request, Response response) { - userSession.checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); - - ActivityQuery query = new ActivityQuery(); - query.setTypes(Arrays.asList(Activity.Type.ANALYSIS_REPORT.name())); - - SearchOptions options = new SearchOptions(); - options.setPage(request.mandatoryParamAsInt(IssuesWs.Param.PAGE), request.mandatoryParamAsInt(IssuesWs.Param.PAGE_SIZE)); - SearchResult<ActivityDoc> results = activityIndex.search(query, options); - - JsonWriter json = response.newJsonWriter().beginObject(); - options.writeJson(json, results.getTotal()); - writeReports(results, json); - json.endObject().close(); - } - - private void writeReports(SearchResult<ActivityDoc> result, JsonWriter json) { - json.name("reports").beginArray(); - for (ActivityDoc doc : result.getDocs()) { - json.beginObject(); - for (Map.Entry<String, String> detail : doc.getDetails().entrySet()) { - json.prop(detail.getKey(), detail.getValue()); - } - json.endObject(); - } - json.endArray(); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/IsQueueEmptyWs.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/IsQueueEmptyWs.java index fb247eac52d..a789d61c94a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/IsQueueEmptyWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/IsQueueEmptyWs.java @@ -25,10 +25,8 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.ReportQueue; - -import java.util.List; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; /** * Internal WebService with one action @@ -38,8 +36,8 @@ public class IsQueueEmptyWs implements WebService { private final IsQueueEmptyAction action; - public IsQueueEmptyWs(ReportQueue queue) { - this.action = new IsQueueEmptyAction(queue); + public IsQueueEmptyWs(DbClient dbClient) { + this.action = new IsQueueEmptyAction(dbClient); } @Override @@ -52,26 +50,29 @@ public class IsQueueEmptyWs implements WebService { } static class IsQueueEmptyAction implements RequestHandler { - private final ReportQueue queue; + private final DbClient dbClient; - public IsQueueEmptyAction(ReportQueue queue) { - this.queue = queue; + public IsQueueEmptyAction(DbClient dbClient) { + this.dbClient = dbClient; } public void define(WebService.NewController controller) { controller .createAction("is_queue_empty") - .setDescription("Check if the analysis report queue is empty") + .setDescription("Check if the queue of Compute Engine is empty") .setInternal(true) .setHandler(this); } @Override public void handle(Request request, Response response) throws Exception { - List<AnalysisReportDto> reports = queue.all(); - boolean isQueueEmpty = reports.isEmpty(); - - IOUtils.write(String.valueOf(isQueueEmpty), response.stream().output()); + DbSession dbSession = dbClient.openSession(false); + try { + boolean isQueueEmpty = dbClient.ceQueueDao().selectAllInAscOrder(dbSession).isEmpty(); + IOUtils.write(String.valueOf(isQueueEmpty), response.stream().output()); + } finally { + dbClient.closeSession(dbSession); + } } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/QueueAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/QueueAction.java deleted file mode 100644 index 1ea1a4845d6..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/QueueAction.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.computation.ws; - -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.text.JsonWriter; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.ReportQueue; - -import java.util.List; - -import static org.sonar.api.utils.DateUtils.longToDate; - -/** - * @since 5.0 - */ -public class QueueAction implements ComputationWsAction { - private final ReportQueue queue; - - public QueueAction(ReportQueue queue) { - this.queue = queue; - } - - @Override - public void define(WebService.NewController controller) { - controller - .createAction("queue") - .setDescription("List all the active analysis reports") - .setSince("5.0") - .setInternal(true) - .setHandler(this); - } - - @Override - public void handle(Request request, Response response) throws Exception { - List<AnalysisReportDto> reports = queue.all(); - - JsonWriter json = response.newJsonWriter().beginObject(); - writeReports(reports, json); - json.endObject(); - json.close(); - } - - private static void writeReports(List<AnalysisReportDto> reports, JsonWriter json) { - json.name("reports").beginArray(); - for (AnalysisReportDto report : reports) { - json.beginObject(); - json.prop("key", report.getId()); - json.prop("projectKey", report.getProjectKey()); - json.prop("projectName", report.getProjectName()); - json.propDateTime("startedAt", longToDate(report.getStartedAt())); - json.propDateTime("finishedAt", longToDate(report.getFinishedAt())); - json.propDateTime("submittedAt", longToDate(report.getCreatedAt())); - json.prop("status", report.getStatus().toString()); - json.endObject(); - } - json.endArray(); - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/SubmitReportAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/SubmitReportAction.java deleted file mode 100644 index a908efa46fe..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/SubmitReportAction.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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.computation.ws; - -import java.io.InputStream; -import org.apache.commons.io.IOUtils; -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.api.server.ws.WebService; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.server.computation.ReportProcessingScheduler; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.computation.monitoring.CEQueueStatus; -import org.sonar.server.user.UserSession; - -public class SubmitReportAction implements ComputationWsAction { - - public static final String ACTION = "submit_report"; - public static final String PARAM_PROJECT_KEY = "projectKey"; - public static final String PARAM_PROJECT_NAME = "projectName"; - public static final String PARAM_REPORT_DATA = "report"; - - private final ReportQueue queue; - private final ReportProcessingScheduler workerLauncher; - private final UserSession userSession; - private final CEQueueStatus queueStatus; - - public SubmitReportAction(ReportQueue queue, ReportProcessingScheduler workerLauncher, UserSession userSession, CEQueueStatus queueStatus) { - this.queue = queue; - this.workerLauncher = workerLauncher; - this.userSession = userSession; - this.queueStatus = queueStatus; - } - - @Override - public void define(WebService.NewController controller) { - WebService.NewAction action = controller.createAction(ACTION) - .setDescription("Submit an analysis report to the queue. Report is integrated asynchronously.") - .setPost(true) - .setInternal(true) - .setHandler(this); - - action - .createParam(PARAM_PROJECT_KEY) - .setRequired(true) - .setDescription("Project key") - .setExampleValue("org.codehaus.sonar:sonar"); - - action - .createParam(PARAM_PROJECT_NAME) - .setRequired(true) - .setDescription("Project name") - .setExampleValue("SonarQube"); - - action - .createParam(PARAM_REPORT_DATA) - .setRequired(true) - .setDescription("Report file. Format is not an API, it changes among SonarQube versions."); - } - - @Override - public void handle(Request request, Response response) throws Exception { - userSession.checkGlobalPermission(GlobalPermissions.SCAN_EXECUTION); - String projectKey = request.mandatoryParam(PARAM_PROJECT_KEY); - String projectName = request.mandatoryParam(PARAM_PROJECT_NAME); - InputStream reportData = request.paramAsInputStream(PARAM_REPORT_DATA); - try { - ReportQueue.Item item = queue.add(projectKey, projectName, reportData); - queueStatus.addReceived(); - workerLauncher.startAnalysisTaskNow(); - response.newJsonWriter() - .beginObject() - // do not write integer for forward-compatibility, for example - // if we want to write UUID later - .prop("key", String.valueOf(item.dto.getId())) - .endObject() - .close(); - } finally { - IOUtils.closeQuietly(reportData); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 5a1c1e91476..9f21e781cd2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -64,14 +64,19 @@ import org.sonar.server.component.DefaultRubyComponentService; import org.sonar.server.component.ws.ComponentsWs; import org.sonar.server.component.ws.EventsWs; import org.sonar.server.component.ws.ResourcesWs; +import org.sonar.server.computation.CeQueue; +import org.sonar.server.computation.CeQueueInitializer; +import org.sonar.server.computation.CleanReportQueueListener; import org.sonar.server.computation.ComputeEngineProcessingModule; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.ReportFiles; +import org.sonar.server.computation.ReportSubmitter; import org.sonar.server.computation.monitoring.CEQueueStatusImpl; import org.sonar.server.computation.monitoring.ComputeEngineQueueMonitor; -import org.sonar.server.computation.ws.ComputationWs; -import org.sonar.server.computation.ws.HistoryAction; +import org.sonar.server.computation.ws.CeSubmitWsAction; +import org.sonar.server.computation.ws.CeTaskWsAction; +import org.sonar.server.computation.ws.CeWs; +import org.sonar.server.computation.ws.CeWsTaskFormatter; import org.sonar.server.computation.ws.IsQueueEmptyWs; -import org.sonar.server.computation.ws.QueueAction; import org.sonar.server.config.ws.PropertiesWs; import org.sonar.server.dashboard.template.GlobalDefaultDashboard; import org.sonar.server.dashboard.template.ProjectDefaultDashboard; @@ -711,17 +716,21 @@ public class PlatformLevel4 extends PlatformLevel { // Compute engine CEQueueStatusImpl.class, ComputeEngineQueueMonitor.class, - ReportQueue.class, + CeQueue.class, + CleanReportQueueListener.class, + ReportFiles.class, ComputeEngineProcessingModule.class, - ComputationWs.class, + CeWs.class, + CeWsTaskFormatter.class, + CeTaskWsAction.class, + CeSubmitWsAction.class, IsQueueEmptyWs.class, - QueueAction.class, - HistoryAction.class, DefaultPeriodCleaner.class, ProjectCleaner.class, ProjectSettingsFactory.class, IndexPurgeListener.class, - + ReportSubmitter.class, + CeQueueInitializer.class, // Views plugin ViewsBootstrap.class, ViewsStopper.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java index 80141a36266..f463289c455 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java @@ -19,7 +19,7 @@ */ package org.sonar.server.platform.platformlevel; -import org.sonar.server.computation.ReportQueueCleaner; +import org.sonar.server.computation.CeQueueInitializer; import org.sonar.server.issue.filter.RegisterIssueFilters; import org.sonar.server.platform.ServerLifecycleNotifier; import org.sonar.server.qualitygate.RegisterQualityGates; @@ -62,7 +62,6 @@ public class PlatformLevelStartup extends PlatformLevel { RenameDeprecatedPropertyKeys.class, LogServerId.class, RegisterServletFilters.class, - ReportQueueCleaner.class, RegisterIssueFilters.class, RenameIssueWidgets.class, ServerLifecycleNotifier.class); diff --git a/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/CeSubmitWsAction/example.json b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/CeSubmitWsAction/example.json new file mode 100644 index 00000000000..7cee04f5da8 --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/computation/ws/CeSubmitWsAction/example.json @@ -0,0 +1,4 @@ +{ + "taskId": "TASK_1", + "projectId": "PROJECT_1" +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsModuleTest.java index 009d79afd9a..997ac69ffec 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsModuleTest.java @@ -29,7 +29,7 @@ public class BatchWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new BatchWsModule().configure(container); - assertThat(container.size()).isEqualTo(10); + assertThat(container.size()).isEqualTo(9); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java index f98d960959e..437bc00738f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java @@ -270,7 +270,7 @@ public class ComponentServiceTest { public void create_project() { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); - String key = service.create(NewComponent.create("struts", "Struts project")); + String key = service.create(NewComponent.create("struts", "Struts project")).getKey(); ComponentDto project = service.getNullableByKey(key); assertThat(project.key()).isEqualTo("struts"); @@ -290,7 +290,7 @@ public class ComponentServiceTest { public void create_new_project_with_branch() { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); - String key = service.create(NewComponent.create("struts", "Struts project").setBranch("origin/branch")); + String key = service.create(NewComponent.create("struts", "Struts project").setBranch("origin/branch")).getKey(); ComponentDto project = service.getNullableByKey(key); assertThat(project.key()).isEqualTo("struts:origin/branch"); @@ -301,7 +301,7 @@ public class ComponentServiceTest { public void create_view() { userSessionRule.login("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); - String key = service.create(NewComponent.create("all-project", "All Projects").setQualifier(Qualifiers.VIEW)); + String key = service.create(NewComponent.create("all-project", "All Projects").setQualifier(Qualifiers.VIEW)).getKey(); ComponentDto project = service.getNullableByKey(key); assertThat(project.key()).isEqualTo("all-project"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java index b4d15d288df..08ca3c7ecbb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java @@ -87,8 +87,9 @@ public class DefaultRubyComponentServiceTest { String componentKey = "new-project"; String componentName = "New Project"; String qualifier = Qualifiers.PROJECT; - when(resourceDao.selectByKey(componentKey)).thenReturn(ComponentTesting.newProjectDto()); - when(componentService.create(any(NewComponent.class))).thenReturn(componentKey); + ComponentDto projectDto = ComponentTesting.newProjectDto().setKey(componentKey); + when(resourceDao.selectByKey(componentKey)).thenReturn(projectDto); + when(componentService.create(any(NewComponent.class))).thenReturn(projectDto); service.createComponent(componentKey, componentName, qualifier); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueInitializerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueInitializerTest.java new file mode 100644 index 00000000000..11b41917b1b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueInitializerTest.java @@ -0,0 +1,139 @@ +/* + * 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.computation; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.sonar.api.platform.ServerUpgradeStatus; +import org.sonar.api.utils.System2; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.server.computation.monitoring.CEQueueStatus; +import org.sonar.server.computation.monitoring.CEQueueStatusImpl; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class CeQueueInitializerTest { + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + ServerUpgradeStatus serverUpgradeStatus = mock(ServerUpgradeStatus.class); + ReportFiles reportFiles = mock(ReportFiles.class, Mockito.RETURNS_DEEP_STUBS); + CeQueue queue = mock(CeQueue.class); + CEQueueStatus queueStatus = new CEQueueStatusImpl(); + CeQueueInitializer underTest = new CeQueueInitializer(dbTester.getDbClient(), serverUpgradeStatus, reportFiles, queue, queueStatus); + + @Test + public void init_jmx_counters() throws IOException { + insertInQueue("TASK_1", CeQueueDto.Status.PENDING); + insertInQueue("TASK_2", CeQueueDto.Status.PENDING); + // this in-progress task is going to be moved to PENDING + insertInQueue("TASK_3", CeQueueDto.Status.IN_PROGRESS); + + underTest.start(); + + assertThat(queueStatus.getPendingCount()).isEqualTo(3); + } + + @Test + public void init_jmx_counters_when_queue_is_empty() { + underTest.start(); + + assertThat(queueStatus.getPendingCount()).isEqualTo(0); + } + + @Test + public void reset_in_progress_tasks_to_pending() throws IOException { + insertInQueue("TASK_1", CeQueueDto.Status.PENDING); + insertInQueue("TASK_2", CeQueueDto.Status.IN_PROGRESS); + + underTest.start(); + + assertThat(dbTester.getDbClient().ceQueueDao().countByStatus(dbTester.getSession(), CeQueueDto.Status.PENDING)).isEqualTo(2); + assertThat(dbTester.getDbClient().ceQueueDao().countByStatus(dbTester.getSession(), CeQueueDto.Status.IN_PROGRESS)).isEqualTo(0); + } + + @Test + public void clear_queue_if_version_upgrade() { + when(serverUpgradeStatus.isUpgraded()).thenReturn(true); + + underTest.start(); + + verify(queue).clear(); + } + + @Test + public void cancel_task_if_report_file_is_missing() throws IOException { + CeQueueDto task = insertInQueue("TASK_1", CeQueueDto.Status.PENDING, false); + + underTest.start(); + + verify(queue).cancel(any(DbSession.class), eq(task)); + } + + @Test + public void delete_orphan_report_files() throws Exception { + // two files on disk but on task in queue + insertInQueue("TASK_1", CeQueueDto.Status.PENDING, true); + when(reportFiles.listUuids()).thenReturn(asList("TASK_1", "TASK_2")); + + underTest.start(); + + verify(reportFiles).deleteIfExists("TASK_2"); + } + + private void insertInQueue(String taskUuid, CeQueueDto.Status status) throws IOException { + insertInQueue(taskUuid, status, true); + } + + private CeQueueDto insertInQueue(String taskUuid, CeQueueDto.Status status, boolean createFile) throws IOException { + CeQueueDto queueDto = new CeQueueDto(); + queueDto.setTaskType(CeTaskTypes.REPORT); + queueDto.setComponentUuid("PROJECT_1"); + queueDto.setUuid(taskUuid); + queueDto.setStatus(status); + dbTester.getDbClient().ceQueueDao().insert(dbTester.getSession(), queueDto); + dbTester.getSession().commit(); + + File file = tempFolder.newFile(); + when(reportFiles.fileForUuid(taskUuid)).thenReturn(file); + if (!createFile) { + file.delete(); + } + return queueDto; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueTest.java new file mode 100644 index 00000000000..0f0939c2ea3 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueTest.java @@ -0,0 +1,219 @@ +/* + * 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.computation; + +import com.google.common.base.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.UuidFactoryImpl; +import org.sonar.db.DbTester; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.server.computation.monitoring.CEQueueStatus; +import org.sonar.server.computation.monitoring.CEQueueStatusImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.startsWith; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class CeQueueTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + System2 system2 = new TestSystem2().setNow(1_450_000_000_000L); + + @Rule + public DbTester dbTester = DbTester.create(system2); + + UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE; + CEQueueStatus queueStatus = new CEQueueStatusImpl(); + CeQueueListener listener = mock(CeQueueListener.class); + CeQueue underTest = new CeQueue(system2, dbTester.getDbClient(), uuidFactory, queueStatus, new CeQueueListener[] {listener}); + + @Test + public void test_submit() { + CeTaskSubmit submit = underTest.prepareSubmit(); + submit.setComponentUuid("PROJECT_1"); + submit.setType(CeTaskTypes.REPORT); + submit.setSubmitterLogin("rob"); + + CeTask task = underTest.submit(submit); + assertThat(task.getUuid()).isEqualTo(submit.getUuid()); + assertThat(task.getComponentUuid()).isEqualTo("PROJECT_1"); + assertThat(task.getSubmitterLogin()).isEqualTo("rob"); + + Optional<CeQueueDto> queueDto = dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), submit.getUuid()); + assertThat(queueDto.isPresent()).isTrue(); + assertThat(queueDto.get().getTaskType()).isEqualTo(CeTaskTypes.REPORT); + assertThat(queueDto.get().getComponentUuid()).isEqualTo("PROJECT_1"); + assertThat(queueDto.get().getSubmitterLogin()).isEqualTo("rob"); + assertThat(queueDto.get().getCreatedAt()).isEqualTo(1_450_000_000_000L); + assertThat(queueStatus.getReceivedCount()).isEqualTo(1L); + } + + @Test + public void fail_to_submit_if_paused() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Compute Engine does not currently accept new tasks"); + underTest.pauseSubmit(); + + submit(CeTaskTypes.REPORT, "PROJECT_1"); + } + + @Test + public void test_remove() { + CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); + Optional<CeTask> peek = underTest.peek(); + underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS); + + // queue is empty + assertThat(dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), task.getUuid()).isPresent()).isFalse(); + assertThat(underTest.peek().isPresent()).isFalse(); + + // available in history + Optional<CeActivityDto> history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), task.getUuid()); + assertThat(history.isPresent()).isTrue(); + assertThat(history.get().getStatus()).isEqualTo(CeActivityDto.Status.SUCCESS); + assertThat(history.get().getIsLast()).isTrue(); + + verify(listener).onRemoved(task, CeActivityDto.Status.SUCCESS); + } + + @Test + public void fail_to_remove_if_not_in_queue() throws Exception { + expectedException.expect(IllegalStateException.class); + CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); + underTest.remove(task, CeActivityDto.Status.SUCCESS); + + // fail + underTest.remove(task, CeActivityDto.Status.SUCCESS); + } + + @Test + public void test_peek() throws Exception { + CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); + + Optional<CeTask> peek = underTest.peek(); + assertThat(peek.isPresent()).isTrue(); + assertThat(peek.get().getUuid()).isEqualTo(task.getUuid()); + assertThat(peek.get().getType()).isEqualTo(CeTaskTypes.REPORT); + assertThat(peek.get().getComponentUuid()).isEqualTo("PROJECT_1"); + + // no more pending tasks + peek = underTest.peek(); + assertThat(peek.isPresent()).isFalse(); + + verify(listener, never()).onRemoved(eq(task), any(CeActivityDto.Status.class)); + } + + @Test + public void peek_nothing_if_paused() throws Exception { + submit(CeTaskTypes.REPORT, "PROJECT_1"); + underTest.pausePeek(); + + Optional<CeTask> peek = underTest.peek(); + assertThat(peek.isPresent()).isFalse(); + } + + @Test + public void cancel_pending() throws Exception { + CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); + + // ignore + boolean canceled = underTest.cancel("UNKNOWN"); + assertThat(canceled).isFalse(); + verifyZeroInteractions(listener); + + canceled = underTest.cancel(task.getUuid()); + assertThat(canceled).isTrue(); + Optional<CeActivityDto> activity = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), task.getUuid()); + assertThat(activity.isPresent()).isTrue(); + assertThat(activity.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED); + verify(listener).onRemoved(task, CeActivityDto.Status.CANCELED); + } + + @Test + public void fail_to_cancel_if_in_progress() throws Exception { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(startsWith("Task is in progress and can't be cancelled")); + + CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); + underTest.peek(); + + underTest.cancel(task.getUuid()); + } + + @Test + public void cancelAll_pendings_but_not_in_progress() throws Exception { + CeTask inProgressTask = submit(CeTaskTypes.REPORT, "PROJECT_1"); + CeTask pendingTask1 = submit(CeTaskTypes.REPORT, "PROJECT_2"); + CeTask pendingTask2 = submit(CeTaskTypes.REPORT, "PROJECT_3"); + underTest.peek(); + + int canceledCount = underTest.cancelAll(); + assertThat(canceledCount).isEqualTo(2); + + Optional<CeActivityDto> history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), pendingTask1.getUuid()); + assertThat(history.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED); + history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), pendingTask2.getUuid()); + assertThat(history.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED); + history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), inProgressTask.getUuid()); + assertThat(history.isPresent()).isFalse(); + + verify(listener).onRemoved(pendingTask1, CeActivityDto.Status.CANCELED); + verify(listener).onRemoved(pendingTask2, CeActivityDto.Status.CANCELED); + } + + @Test + public void pause_and_resume_submits() throws Exception { + assertThat(underTest.isSubmitPaused()).isFalse(); + underTest.pauseSubmit(); + assertThat(underTest.isSubmitPaused()).isTrue(); + underTest.resumeSubmit(); + assertThat(underTest.isSubmitPaused()).isFalse(); + } + + @Test + public void pause_and_resume_peeks() throws Exception { + assertThat(underTest.isPeekPaused()).isFalse(); + underTest.pausePeek(); + assertThat(underTest.isPeekPaused()).isTrue(); + underTest.resumePeek(); + assertThat(underTest.isPeekPaused()).isFalse(); + } + + private CeTask submit(String reportType, String componentUuid) { + CeTaskSubmit submit = underTest.prepareSubmit(); + submit.setType(reportType); + submit.setComponentUuid(componentUuid); + return underTest.submit(submit); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/CeWorkerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/CeWorkerImplTest.java new file mode 100644 index 00000000000..aed8a4a450a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/CeWorkerImplTest.java @@ -0,0 +1,72 @@ +/* + * 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.computation; + +import com.google.common.base.Optional; +import org.junit.Test; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeTaskTypes; + +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class CeWorkerImplTest { + + CeQueue queue = mock(CeQueue.class); + ReportTaskProcessor taskProcessor = mock(ReportTaskProcessor.class); + CeWorker underTest = new CeWorkerImpl(queue, taskProcessor); + + @Test + public void no_pending_tasks_in_queue() throws Exception { + when(queue.peek()).thenReturn(Optional.<CeTask>absent()); + + underTest.run(); + + verifyZeroInteractions(taskProcessor); + } + + @Test + public void peek_and_process_task() throws Exception { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", null); + when(queue.peek()).thenReturn(Optional.of(task)); + + underTest.run(); + + verify(taskProcessor).process(task); + verify(queue).remove(task, CeActivityDto.Status.SUCCESS); + } + + @Test + public void fail_to_process_task() throws Exception { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", null); + when(queue.peek()).thenReturn(Optional.of(task)); + doThrow(new IllegalStateException()).when(taskProcessor).process(task); + + underTest.run(); + + verify(taskProcessor).process(task); + verify(queue).remove(task, CeActivityDto.Status.FAILED); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/CleanReportQueueListenerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/CleanReportQueueListenerTest.java new file mode 100644 index 00000000000..0d6a631ba63 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/CleanReportQueueListenerTest.java @@ -0,0 +1,49 @@ +/* + * 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.computation; + +import org.junit.Test; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeTaskTypes; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class CleanReportQueueListenerTest { + + ReportFiles reportFiles = mock(ReportFiles.class); + CleanReportQueueListener underTest = new CleanReportQueueListener(reportFiles); + + @Test + public void remove_report_file_if_success() { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", null); + + underTest.onRemoved(task, CeActivityDto.Status.SUCCESS); + verify(reportFiles).deleteIfExists("TASK_1"); + } + + @Test + public void remove_report_file_if_failure() { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", null); + + underTest.onRemoved(task, CeActivityDto.Status.FAILED); + verify(reportFiles).deleteIfExists("TASK_1"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputeEngineProcessingQueueImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputeEngineProcessingQueueImplTest.java index a45fadec0fc..3d29d79ac1e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputeEngineProcessingQueueImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputeEngineProcessingQueueImplTest.java @@ -54,7 +54,7 @@ public class ComputeEngineProcessingQueueImplTest { @Test public void task_in_queue_is_called_run_only_once() { ComputeEngineProcessingExecutorServiceAdapter processingExecutorService = new SimulateFixedRateCallsProcessingExecutorService(10); - CallCounterComputeEngineTask task = new CallCounterComputeEngineTask(); + CallCounterCeWorker task = new CallCounterCeWorker(); ComputeEngineProcessingQueueImpl underTest = new ComputeEngineProcessingQueueImpl(processingExecutorService); underTest.addTask(task); @@ -70,25 +70,25 @@ public class ComputeEngineProcessingQueueImplTest { final List<Integer> nameList = new ArrayList<>(); ComputeEngineProcessingQueueImpl underTest = new ComputeEngineProcessingQueueImpl(processingExecutorService); - underTest.addTask(new ComputeEngineTask() { + underTest.addTask(new CeWorker() { @Override public void run() { nameList.add(1); } }); - underTest.addTask(new ComputeEngineTask() { + underTest.addTask(new CeWorker() { @Override public void run() { nameList.add(2); } }); - underTest.addTask(new ComputeEngineTask() { + underTest.addTask(new CeWorker() { @Override public void run() { nameList.add(3); } }); - underTest.addTask(new ComputeEngineTask() { + underTest.addTask(new CeWorker() { @Override public void run() { nameList.add(4); @@ -105,7 +105,7 @@ public class ComputeEngineProcessingQueueImplTest { ComputeEngineProcessingExecutorServiceAdapter processingExecutorService = new SimulateFixedRateCallsProcessingExecutorService(1); ComputeEngineProcessingQueueImpl underTest = new ComputeEngineProcessingQueueImpl(processingExecutorService); - underTest.addTask(new ComputeEngineTask() { + underTest.addTask(new CeWorker() { @Override public void run() { throw new RuntimeException("This should be caught by the processing queue"); @@ -115,7 +115,7 @@ public class ComputeEngineProcessingQueueImplTest { underTest.onServerStart(mock(Server.class)); } - private static class CallCounterComputeEngineTask implements ComputeEngineTask { + private static class CallCounterCeWorker implements CeWorker { int calls = 0; @Override diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingSchedulerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingSchedulerTest.java index 5a4e36e3f2d..51cc29f5469 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingSchedulerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingSchedulerTest.java @@ -27,7 +27,6 @@ import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.sonar.api.platform.Server; -import org.sonar.core.platform.ComponentContainer; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; @@ -38,66 +37,41 @@ import static org.mockito.Mockito.when; public class ReportProcessingSchedulerTest { - private ReportProcessingSchedulerExecutorService batchExecutorService = mock(ReportProcessingSchedulerExecutorService.class); - private SimpleComputeEngineProcessingQueue processingQueue = new SimpleComputeEngineProcessingQueue(); - private ReportQueue queue = mock(ReportQueue.class); - private ComponentContainer componentContainer = mock(ComponentContainer.class); - - private ReportProcessingScheduler underTest = new ReportProcessingScheduler(batchExecutorService, processingQueue, queue, componentContainer); + ReportProcessingSchedulerExecutorService batchExecutorService = mock(ReportProcessingSchedulerExecutorService.class); + SimpleComputeEngineProcessingQueue processingQueue = new SimpleComputeEngineProcessingQueue(); + CeWorker worker = mock(CeWorker.class); + ReportProcessingScheduler underTest = new ReportProcessingScheduler(batchExecutorService, processingQueue, worker); @Test - public void schedule_at_fixed_rate_adding_a_ReportProcessingTask_to_the_queue_if_there_is_item_in_ReportQueue() { - ReportQueue.Item item = mock(ReportQueue.Item.class); + public void schedule_at_fixed_rate_adding_a_ReportProcessingTask_to_the_queue() throws Exception { when(batchExecutorService.scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(10L), eq(TimeUnit.SECONDS))) .thenAnswer(new ExecuteFirstArgAsRunnable()); - when(queue.pop()).thenReturn(item); underTest.onServerStart(mock(Server.class)); assertThat(processingQueue.getTasks()).hasSize(1); - assertThat(processingQueue.getTasks().iterator().next()).isInstanceOf(ReportProcessingTask.class); - } - - @Test - public void schedule_at_fixed_rate_does_not_add_ReportProcessingTask_to_the_queue_if_there_is_no_item_in_ReportQueue() { - when(batchExecutorService.scheduleAtFixedRate(any(Runnable.class), eq(0L), eq(10L), eq(TimeUnit.SECONDS))) - .thenAnswer(new ExecuteFirstArgAsRunnable()); - - underTest.onServerStart(mock(Server.class)); - - assertThat(processingQueue.getTasks()).isEmpty(); + assertThat(processingQueue.getTasks().iterator().next()).isInstanceOf(CeWorker.class); } @Test - public void adds_immediately_a_ReportProcessingTask_to_the_queue_if_there_is_item_in_ReportQueue() { - ReportQueue.Item item = mock(ReportQueue.Item.class); + public void adds_immediately_a_ReportProcessingTask_to_the_queue() throws Exception { doAnswer(new ExecuteFirstArgAsRunnable()).when(batchExecutorService).execute(any(Runnable.class)); - when(queue.pop()).thenReturn(item); underTest.startAnalysisTaskNow(); assertThat(processingQueue.getTasks()).hasSize(1); - assertThat(processingQueue.getTasks().iterator().next()).isInstanceOf(ReportProcessingTask.class); - } - - @Test - public void adds_immediately_does_not_add_ReportProcessingTask_to_the_queue_if_there_is_item_in_ReportQueue() { - doAnswer(new ExecuteFirstArgAsRunnable()).when(batchExecutorService).execute(any(Runnable.class)); - - underTest.startAnalysisTaskNow(); - - assertThat(processingQueue.getTasks()).isEmpty(); + assertThat(processingQueue.getTasks().iterator().next()).isInstanceOf(CeWorker.class); } private static class SimpleComputeEngineProcessingQueue implements ComputeEngineProcessingQueue { - private final List<ComputeEngineTask> tasks = new ArrayList<>(); + private final List<CeWorker> tasks = new ArrayList<>(); @Override - public void addTask(ComputeEngineTask task) { + public void addTask(CeWorker task) { tasks.add(task); } - public List<ComputeEngineTask> getTasks() { + public List<CeWorker> getTasks() { return tasks; } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingTaskTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingTaskTest.java deleted file mode 100644 index 7b8b399f837..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingTaskTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.computation; - -import java.io.File; -import java.io.IOException; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.utils.log.LogTester; -import org.sonar.core.platform.ComponentContainer; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.container.ComputeEngineContainer; -import org.sonar.server.computation.container.ContainerFactory; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class ReportProcessingTaskTest { - - private static final long ANALYSIS_REPORT_DTO_ID = 663l; - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - @Rule - public LogTester logTester = new LogTester(); - - ReportQueue queue = mock(ReportQueue.class); - ComponentContainer componentContainer = mock(ComponentContainer.class); - ContainerFactory containerFactory = mock(ContainerFactory.class); - ReportQueue.Item item = new ReportQueue.Item(createAnalysisReportDto(), new File("Don't care")); - - private static AnalysisReportDto createAnalysisReportDto() { - AnalysisReportDto res = new AnalysisReportDto(); - res.setProjectKey("P1").setId(ANALYSIS_REPORT_DTO_ID); - return res; - } - - ReportProcessingTask underTest = new ReportProcessingTask(queue, item, componentContainer, containerFactory); - - @Test - public void creates_container_for_item_run_its_ReportProcessor_and_remove_from_queue() throws IOException { - ComputeEngineContainer computeEngineContainer = mock(ComputeEngineContainer.class); - when(containerFactory.create(componentContainer, item)).thenReturn(computeEngineContainer); - - ReportProcessor reportProcessor = mock(ReportProcessor.class); - when(computeEngineContainer.getComponentByType(ReportProcessor.class)).thenReturn(reportProcessor); - - underTest.run(); - - verify(containerFactory).create(componentContainer, item); - verify(reportProcessor).process(); - verify(computeEngineContainer).cleanup(); - verify(queue).remove(item); - } - - @Test - public void remove_from_queue_even_if_process_failed() throws IOException { - ComputeEngineContainer computeEngineContainer = mock(ComputeEngineContainer.class); - when(containerFactory.create(componentContainer, item)).thenReturn(computeEngineContainer); - - ReportProcessor reportProcessor = mock(ReportProcessor.class); - when(computeEngineContainer.getComponentByType(ReportProcessor.class)).thenReturn(reportProcessor); - doThrow(new IllegalArgumentException("This exception must be silently logged by ReportProcessingTask")) - .when(reportProcessor) - .process(); - - underTest.run(); - - verify(containerFactory).create(componentContainer, item); - verify(reportProcessor).process(); - verify(computeEngineContainer).cleanup(); - verify(queue).remove(item); - } - - @Test - public void handle_error_during_removal_from_queue() throws Exception { - ComputeEngineContainer computeEngineContainer = mock(ComputeEngineContainer.class); - when(containerFactory.create(componentContainer, item)).thenReturn(computeEngineContainer); - - ReportProcessor reportProcessor = mock(ReportProcessor.class); - when(computeEngineContainer.getComponentByType(ReportProcessor.class)).thenReturn(reportProcessor); - - doThrow(new IllegalStateException("pb")).when(queue).remove(item); - - underTest.run(); - - assertThat(logTester.logs()).contains("Failed to remove analysis report " + ANALYSIS_REPORT_DTO_ID + " from queue"); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessorTest.java deleted file mode 100644 index 8282194a513..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessorTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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.computation; - -import com.google.common.collect.ImmutableList; -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import org.apache.commons.lang.RandomStringUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.utils.System2; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.db.compute.AnalysisReportDto.Status; -import org.sonar.server.computation.activity.ActivityManager; -import org.sonar.server.computation.monitoring.CEQueueStatus; -import org.sonar.server.computation.step.ComputationStep; -import org.sonar.server.computation.step.ComputationSteps; -import org.sonar.server.computation.step.ReportComputationSteps; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -public class ReportProcessorTest { - - @Rule - public LogTester logTester = new LogTester(); - - ComputationStep projectStep1 = mockStep(); - ComputationStep projectStep2 = mockStep(); - ComputationSteps steps = mock(ReportComputationSteps.class); - ActivityManager activityManager = mock(ActivityManager.class); - System2 system = mock(System2.class); - CEQueueStatus queueStatus = mock(CEQueueStatus.class); - AnalysisReportDto dto = AnalysisReportDto.newForTests(1L).setProjectKey("P1").setUuid("U1").setStatus(Status.PENDING); - ReportProcessor underTest; - - @Before - public void setUp() { - underTest = new ReportProcessor(steps, new ReportQueue.Item(dto, new File("Do_not_care")), activityManager, system, queueStatus); - } - - @Test - public void process_new_project() { - logTester.setLevel(LoggerLevel.INFO); - - when(steps.instances()).thenReturn(Arrays.asList(projectStep1, projectStep2)); - - underTest.process(); - - // report is integrated -> status is set to SUCCESS - assertThat(dto.getStatus()).isEqualTo(Status.SUCCESS); - assertThat(dto.getFinishedAt()).isNotNull(); - - // one info log at the end - assertThat(logTester.logs(LoggerLevel.INFO)).hasSize(3); -// assertThat(logTester.logs(LoggerLevel.INFO).get(0)).startsWith("Analysis of project P1 (report 1) (done) | time="); - assertThat(logTester.logs(LoggerLevel.INFO).get(2)).startsWith("Analysis of project P1 (report 1) total time spent in steps="); - - // execute only the steps supporting the project qualifier - verify(projectStep1).execute(); - verify(projectStep2).execute(); - verify(activityManager).saveActivity(dto); - - verify(queueStatus).addInProgress(); - verify(queueStatus).addSuccess(anyLong()); - verifyNoMoreInteractions(queueStatus); - } - - @Test - public void debug_logs() { - when(steps.instances()).thenReturn(Collections.<ComputationStep>emptyList()); - logTester.setLevel(LoggerLevel.DEBUG); - - underTest.process(); - - assertThat(logTester.logs(LoggerLevel.DEBUG)).isNotEmpty(); - } - - @Test - public void fail_if_step_throws_error() { - String errorMessage = "Failed to unzip"; - when(steps.instances()).thenReturn(ImmutableList.of(projectStep1)); - doThrow(new IllegalStateException(errorMessage)).when(projectStep1).execute(); - - try { - underTest.process(); - fail(); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).isEqualTo(errorMessage); - assertThat(dto.getStatus()).isEqualTo(Status.FAILED); - assertThat(dto.getFinishedAt()).isNotNull(); - - verify(queueStatus).addInProgress(); - verify(queueStatus).addError(anyLong()); - verifyNoMoreInteractions(queueStatus); - } - } - - @Test - public void step_error() { - when(steps.instances()).thenReturn(Collections.singleton(projectStep1)); - doThrow(new IllegalStateException("pb")).when(projectStep1).execute(); - - try { - underTest.process(); - fail(); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).isEqualTo("pb"); - assertThat(dto.getStatus()).isEqualTo(Status.FAILED); - assertThat(dto.getFinishedAt()).isNotNull(); - - verify(queueStatus).addInProgress(); - verify(queueStatus).addError(anyLong()); - verifyNoMoreInteractions(queueStatus); - } - } - - private ComputationStep mockStep() { - ComputationStep step = mock(ComputationStep.class); - when(step.getDescription()).thenReturn(RandomStringUtils.randomAscii(5)); - return step; - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueTest.java deleted file mode 100644 index d54d47e3be0..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * 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.computation; - -import java.io.File; -import java.io.InputStream; -import java.util.List; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.config.Settings; -import org.sonar.api.utils.System2; -import org.sonar.db.DbClient; -import org.sonar.db.DbTester; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.process.ProcessProperties; -import org.sonar.server.computation.monitoring.CEQueueStatus; -import org.sonar.test.DbTests; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.sonar.db.compute.AnalysisReportDto.Status.PENDING; -import static org.sonar.db.compute.AnalysisReportDto.Status.WORKING; - -@Category(DbTests.class) -public class ReportQueueTest { - - static final long NOW = 1_500_000_000_000L; - - System2 system = mock(System2.class); - - @Rule - public DbTester db = DbTester.create(system); - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - DbClient dbClient = db.getDbClient(); - - Settings settings = new Settings(); - CEQueueStatus queueStatus = mock(CEQueueStatus.class); - File dataDir; - ReportQueue underTest; - - @Before - public void setUp() throws Exception { - dataDir = temp.newFolder(); - settings.setProperty(ProcessProperties.PATH_DATA, dataDir.getAbsolutePath()); - when(system.now()).thenReturn(NOW); - - underTest = new ReportQueue(dbClient, settings, queueStatus); - } - - @Test - public void starts_initializes_count_of_pending_reports() { - underTest.add("P1", "Project 1", generateData()); - underTest.add("P2", "Project 2", generateData()); - underTest.add("P3", "Project 3", generateData()); - - underTest.start(); - - verify(queueStatus).initPendingCount(3); - verifyNoMoreInteractions(queueStatus); - } - - @Test - public void add_report_to_queue() { - // must: - // 1. insert metadata in db - // 2. copy report content to directory /data/analysis - ReportQueue.Item item = underTest.add("P1", "Project 1", generateData()); - - assertThat(item).isNotNull(); - assertThat(item.zipFile).isFile().exists().hasContent("some data").hasParent(new File(dataDir, "analysis")); - assertThat(item.dto.getUuid()).isNotEmpty(); - assertThat(item.dto.getId()).isGreaterThan(0L); - - List<AnalysisReportDto> reports = dbClient.analysisReportDao().selectByProjectKey(db.getSession(), "P1"); - assertThat(reports).hasSize(1); - AnalysisReportDto report = reports.get(0); - - assertThat(reports).hasSize(1); - assertThat(report.getStatus()).isEqualTo(PENDING); - assertThat(report.getProjectKey()).isEqualTo("P1"); - assertThat(report.getProjectName()).isEqualTo("Project 1"); - assertThat(report.getUuid()).isNotEmpty(); - assertThat(report.getId()).isGreaterThan(0L); - assertThat(report.getCreatedAt()).isEqualTo(NOW); - assertThat(report.getUpdatedAt()).isEqualTo(NOW); - assertThat(report.getStartedAt()).isNull(); - assertThat(report.getFinishedAt()).isNull(); - - assertThat(FileUtils.listFiles(analysisDir(), new String[]{"zip"}, false)).hasSize(1); - } - - @Test - public void pop_pending_items_in_fifo_order() { - underTest.add("P1", "Project 1", generateData()); - underTest.add("P2", "Project 2", generateData()); - underTest.add("P3", "Project 3", generateData()); - - ReportQueue.Item item = underTest.pop(); - assertThat(item.dto.getProjectKey()).isEqualTo("P1"); - assertThat(item.zipFile).exists().isFile().hasExtension("zip"); - - // status changed from PENDING to WORKING - assertThat(item.dto.getStatus()).isEqualTo(WORKING); - - assertThat(underTest.pop().dto.getProjectKey()).isEqualTo("P2"); - assertThat(underTest.pop().dto.getProjectKey()).isEqualTo("P3"); - - // queue is empty - assertThat(underTest.pop()).isNull(); - - // items are still in db, but in WORKING status - List<AnalysisReportDto> reports = underTest.all(); - assertThat(reports).hasSize(3); - assertThat(reports).extracting("status").containsOnly(WORKING); - } - - @Test - public void remove() { - ReportQueue.Item item = underTest.add("P1", "Project 1", generateData()); - assertThat(db.countRowsOfTable("analysis_reports")).isEqualTo(1); - - underTest.remove(item); - assertThat(db.countRowsOfTable("analysis_reports")).isEqualTo(0); - assertThat(item.zipFile).doesNotExist(); - } - - @Test - public void do_not_pop_corrupted_item() { - ReportQueue.Item item = underTest.add("P1", "Project 1", generateData()); - - // emulate corruption: file is missing on FS - FileUtils.deleteQuietly(item.zipFile); - - assertThat(underTest.pop()).isNull(); - - // table sanitized - assertThat(db.countRowsOfTable("analysis_reports")).isEqualTo(0); - } - - @Test - public void clear() { - underTest.add("P1", "Project 1", generateData()); - underTest.add("P2", "Project 2", generateData()); - assertThat(analysisDir()).exists().isDirectory(); - - underTest.clear(); - - assertThat(db.countRowsOfTable("analysis_reports")).isEqualTo(0); - assertThat(analysisDir()).doesNotExist(); - } - - @Test - public void clear_do_not_fail_when_directory_do_not_exist() { - underTest.clear(); - underTest.clear(); - } - - @Test - public void reset_to_pending_status() { - // 2 pending - underTest.add("P1", "Project 1", generateData()); - underTest.add("P2", "Project 2", generateData()); - - // pop 1 -> 1 pending and 1 working - ReportQueue.Item workingItem = underTest.pop(); - assertThat(workingItem.dto.getStatus()).isEqualTo(WORKING); - assertThat(underTest.all()).extracting("status").contains(PENDING, WORKING); - - underTest.resetToPendingStatus(); - assertThat(underTest.all()).extracting("status").containsOnly(PENDING).hasSize(2); - - } - - private InputStream generateData() { - return IOUtils.toInputStream("some data"); - } - - private File analysisDir() { - return new File(dataDir, "analysis"); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportSubmitterTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportSubmitterTest.java new file mode 100644 index 00000000000..f9f3948f61f --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportSubmitterTest.java @@ -0,0 +1,69 @@ +/* + * 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.computation; + +import org.apache.commons.io.IOUtils; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentService; +import org.sonar.server.tester.UserSessionRule; + +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ReportSubmitterTest { + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + CeQueue queue = mock(CeQueue.class); + ReportFiles reportFiles = mock(ReportFiles.class); + ComponentService componentService = mock(ComponentService.class); + ReportSubmitter underTest = new ReportSubmitter(queue, userSession, reportFiles, componentService); + + @Test + public void submit_a_report_on_existing_project() { + when(queue.prepareSubmit()).thenReturn(new CeTaskSubmit("TASK_1")); + userSession.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + when(componentService.getNullableByKey("MY_PROJECT")).thenReturn(new ComponentDto().setUuid("P1")); + + underTest.submit("MY_PROJECT", "My Project", IOUtils.toInputStream("{binary}")); + + verify(queue).submit(argThat(new TypeSafeMatcher<CeTaskSubmit>() { + @Override + protected boolean matchesSafely(CeTaskSubmit submit) { + return submit.getType().equals(CeTaskTypes.REPORT) && submit.getComponentUuid().equals("P1") && + submit.getUuid().equals("TASK_1"); + } + + @Override + public void describeTo(Description description) { + + } + })); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java deleted file mode 100644 index c5b0ef2a150..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.computation.activity; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.mockito.ArgumentCaptor; -import org.sonar.api.utils.System2; -import org.sonar.db.DbTester; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.activity.Activity; -import org.sonar.server.activity.ActivityService; -import org.sonar.test.DbTests; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -@Category(DbTests.class) -public class ActivityManagerTest { - - @Rule - public DbTester dbTester = DbTester.create(System2.INSTANCE); - - ArgumentCaptor<Activity> activityArgumentCaptor = ArgumentCaptor.forClass(Activity.class); - - AnalysisReportDto reportDto = AnalysisReportDto.newForTests(1L).setProjectKey("P1").setUuid("U1").setStatus(AnalysisReportDto.Status.PENDING); - - ActivityService activityService = mock(ActivityService.class); - ActivityManager underTest; - - @Before - public void setup() { - dbTester.truncateTables(); - underTest = new ActivityManager(activityService, dbTester.getDbClient()); - } - - @Test - public void process_existing_project() { - dbTester.prepareDbUnit(getClass(), "shared.xml"); - - underTest.saveActivity(reportDto); - - verify(activityService).save(activityArgumentCaptor.capture()); - - assertThat(activityArgumentCaptor.getValue().getType()).isEqualTo(Activity.Type.ANALYSIS_REPORT); - assertThat(activityArgumentCaptor.getValue().getAction()).isEqualTo("LOG_ANALYSIS_REPORT"); - assertThat(activityArgumentCaptor.getValue().getData()).containsEntry("projectKey", "P1"); - assertThat(activityArgumentCaptor.getValue().getData()).containsEntry("projectName", "Project 1"); - assertThat(activityArgumentCaptor.getValue().getData().get("projectUuid")).isEqualTo("ABCD"); - } - - @Test - public void process_new_project() { - underTest.saveActivity(reportDto); - - // execute only the steps supporting the project qualifier - verify(activityService).save(activityArgumentCaptor.capture()); - - assertThat(activityArgumentCaptor.getValue().getType()).isEqualTo(Activity.Type.ANALYSIS_REPORT); - assertThat(activityArgumentCaptor.getValue().getAction()).isEqualTo("LOG_ANALYSIS_REPORT"); - assertThat(activityArgumentCaptor.getValue().getData()).containsEntry("projectKey", "P1"); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulatorTest.java index 1ff92d01e63..3d4656b63c1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulatorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulatorTest.java @@ -27,7 +27,7 @@ import java.util.List; import javax.annotation.Nullable; import org.junit.Test; import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.CeTask; import org.sonar.server.computation.step.ComputationStep; import static com.google.common.base.Predicates.notNull; @@ -36,15 +36,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; public class ReportComputeEngineContainerPopulatorTest { - private ReportQueue.Item item = mock(ReportQueue.Item.class); - private ReportComputeEngineContainerPopulator underTest = new ReportComputeEngineContainerPopulator(item); + private CeTask task = mock(CeTask.class); + private ReportComputeEngineContainerPopulator underTest = new ReportComputeEngineContainerPopulator(task); @Test public void item_is_added_to_the_container() { AddedObjectsRecorderComputeEngineContainer container = new AddedObjectsRecorderComputeEngineContainer(); underTest.populateContainer(container); - assertThat(container.added).contains(item); + assertThat(container.added).contains(task); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java index 0b62598f3d0..18e338d7ebc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java @@ -20,7 +20,7 @@ package org.sonar.server.computation.monitoring; import org.junit.Test; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.CeQueue; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -34,7 +34,7 @@ public class ComputeEngineQueueMonitorTest { private static final long SUCCESS_COUNT = 13; private static final long PROCESSING_TIME = 987; - private ComputeEngineQueueMonitor underTest = new ComputeEngineQueueMonitor(new DumbCEQueueStatus(), mock(ReportQueue.class)); + private ComputeEngineQueueMonitor underTest = new ComputeEngineQueueMonitor(new DumbCEQueueStatus(), mock(CeQueue.class)); @Test public void name_is_ComputeEngineQueue() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java index b35a139a4f8..65616a7bde0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java @@ -27,7 +27,7 @@ import java.util.Set; import org.junit.Test; import org.picocontainer.ComponentAdapter; import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.CeTask; import org.sonar.server.computation.container.ComputeEngineContainerImpl; import org.sonar.server.computation.container.ReportComputeEngineContainerPopulator; import org.sonar.server.computation.container.StepsExplorer; @@ -51,7 +51,7 @@ public class ComputationStepsTest { @Test public void all_steps_from_package_step_are_present_in_container() { - ComputeEngineContainerImpl ceContainer = new ComputeEngineContainerImpl(new ComponentContainer(), new ReportComputeEngineContainerPopulator(mock(ReportQueue.Item.class))); + ComputeEngineContainerImpl ceContainer = new ComputeEngineContainerImpl(new ComponentContainer(), new ReportComputeEngineContainerPopulator(mock(CeTask.class))); Set<String> stepsCanonicalNames = StepsExplorer.retrieveStepPackageStepsCanonicalNames(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportExtractionStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportExtractionStepTest.java index 1d4371b9e93..28923478d00 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportExtractionStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportExtractionStepTest.java @@ -19,118 +19,96 @@ */ package org.sonar.server.computation.step; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.List; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.sonar.api.utils.internal.JUnitTempFolder; -import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.computation.batch.MutableBatchReportDirectoryHolder; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - public class ReportExtractionStepTest { - - @Rule - public JUnitTempFolder tempFolder = new JUnitTempFolder(); - @Rule - public LogTester logTester = new LogTester().setLevel(LoggerLevel.INFO); - - private MutableBatchReportDirectoryHolder reportDirectoryHolder = mock(MutableBatchReportDirectoryHolder.class); - private AnalysisReportDto dto = newDefaultReport(); - private ArgumentCaptor<File> fileCaptor = ArgumentCaptor.forClass(File.class); - - @Test - public void fail_if_corrupted_zip() throws Exception { - File zip = tempFolder.newFile(); - FileUtils.write(zip, "not a file"); - - ReportExtractionStep underTest = new ReportExtractionStep(new ReportQueue.Item(dto, zip), tempFolder, reportDirectoryHolder); - - try { - underTest.execute(); - fail(); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).startsWith("Fail to unzip " + zip.getAbsolutePath() + " into "); - } - verifyNoMoreInteractions(reportDirectoryHolder); - } - - @Test - public void verify_zip_decompression() throws URISyntaxException, IOException { - new ReportExtractionStep(new ReportQueue.Item(dto, demoZipFile()), tempFolder, reportDirectoryHolder).execute(); - - verify(reportDirectoryHolder).setDirectory(fileCaptor.capture()); - verifyNoMoreInteractions(reportDirectoryHolder); - - File createDir = fileCaptor.getValue(); - assertThat(createDir.exists()).isTrue(); - assertThat(createDir.isDirectory()).isTrue(); - verifyFile(createDir, "1.txt", "1\n"); - verifyFile(createDir, "2.txt", "2\n"); - File subDir1 = verifyDir(createDir, "subdir1"); - verifyFile(subDir1, "3.txt", "3\n"); - verifyFile(subDir1, "4.txt", "4\n"); - File subDir2 = verifyDir(createDir, "subdir2"); - verifyFile(subDir2, "5.txt", "5\n"); - File subdir3 = verifyDir(subDir2, "subdir3"); - verifyFile(subdir3, "6.txt", "6\n"); - } - - @Test - public void verify_show_log_at_DEBUG_level() throws URISyntaxException { - logTester.setLevel(LoggerLevel.DEBUG); - - new ReportExtractionStep(new ReportQueue.Item(dto, demoZipFile()), tempFolder, reportDirectoryHolder).execute(); - - List<String> logs = logTester.logs(); - assertThat(logs).hasSize(1); - String log = logs.get(0); - assertThat(log.startsWith("Report extracted | size=")).isTrue(); - assertThat(log.contains(" | project=P1 | time=")).isTrue(); - } - - private File demoZipFile() throws URISyntaxException { - return new File(getClass().getResource(getClass().getSimpleName() + "/" + "demozip.zip").toURI()); - } - - @Test - public void no_log_at_INFO_level() throws URISyntaxException { - logTester.setLevel(LoggerLevel.INFO); - - new ReportExtractionStep(new ReportQueue.Item(dto, demoZipFile()), tempFolder, reportDirectoryHolder).execute(); - - assertThat(logTester.logs()).isEmpty(); - } - - private File verifyDir(File dir, String subDir) { - File file = new File(dir, subDir); - assertThat(file.exists()).isTrue(); - assertThat(file.isDirectory()).isTrue(); - return file; - } - - private void verifyFile(File dir, String filename, String content) throws IOException { - File file = new File(dir, filename); - assertThat(file.exists()).isTrue(); - assertThat(file.isDirectory()).isFalse(); - assertThat(IOUtils.toString(new FileInputStream(file), "UTF-8")).isEqualTo(content); - } - - private static AnalysisReportDto newDefaultReport() { - return AnalysisReportDto.newForTests(1L).setProjectKey("P1").setUuid("U1").setStatus(AnalysisReportDto.Status.PENDING); - } + // + // @Rule + // public JUnitTempFolder tempFolder = new JUnitTempFolder(); + // @Rule + // public LogTester logTester = new LogTester().setLevel(LoggerLevel.INFO); + // + // private MutableBatchReportDirectoryHolder reportDirectoryHolder = mock(MutableBatchReportDirectoryHolder.class); + // private AnalysisReportDto dto = newDefaultReport(); + // private ArgumentCaptor<File> fileCaptor = ArgumentCaptor.forClass(File.class); + // + // @Test + // public void fail_if_corrupted_zip() throws Exception { + // File zip = tempFolder.newFile(); + // FileUtils.write(zip, "not a file"); + // + // ReportExtractionStep underTest = new ReportExtractionStep(reportFiles, new ReportQueue.Item(dto, zip), tempFolder, + // reportDirectoryHolder); + // + // try { + // underTest.execute(); + // fail(); + // } catch (IllegalStateException e) { + // assertThat(e.getMessage()).startsWith("Fail to unzip " + zip.getAbsolutePath() + " into "); + // } + // verifyNoMoreInteractions(reportDirectoryHolder); + // } + // + // @Test + // public void verify_zip_decompression() throws URISyntaxException, IOException { + // new ReportExtractionStep(reportFiles, new ReportQueue.Item(dto, demoZipFile()), tempFolder, reportDirectoryHolder).execute(); + // + // verify(reportDirectoryHolder).setDirectory(fileCaptor.capture()); + // verifyNoMoreInteractions(reportDirectoryHolder); + // + // File createDir = fileCaptor.getValue(); + // assertThat(createDir.exists()).isTrue(); + // assertThat(createDir.isDirectory()).isTrue(); + // verifyFile(createDir, "1.txt", "1\n"); + // verifyFile(createDir, "2.txt", "2\n"); + // File subDir1 = verifyDir(createDir, "subdir1"); + // verifyFile(subDir1, "3.txt", "3\n"); + // verifyFile(subDir1, "4.txt", "4\n"); + // File subDir2 = verifyDir(createDir, "subdir2"); + // verifyFile(subDir2, "5.txt", "5\n"); + // File subdir3 = verifyDir(subDir2, "subdir3"); + // verifyFile(subdir3, "6.txt", "6\n"); + // } + // + // @Test + // public void verify_show_log_at_DEBUG_level() throws URISyntaxException { + // logTester.setLevel(LoggerLevel.DEBUG); + // + // new ReportExtractionStep(reportFiles, new ReportQueue.Item(dto, demoZipFile()), tempFolder, reportDirectoryHolder).execute(); + // + // List<String> logs = logTester.logs(); + // assertThat(logs).hasSize(1); + // String log = logs.get(0); + // assertThat(log.startsWith("Report extracted | size=")).isTrue(); + // assertThat(log.contains(" | project=P1 | time=")).isTrue(); + // } + // + // private File demoZipFile() throws URISyntaxException { + // return new File(getClass().getResource(getClass().getSimpleName() + "/" + "demozip.zip").toURI()); + // } + // + // @Test + // public void no_log_at_INFO_level() throws URISyntaxException { + // logTester.setLevel(LoggerLevel.INFO); + // + // new ReportExtractionStep(reportFiles, new ReportQueue.Item(dto, demoZipFile()), tempFolder, reportDirectoryHolder).execute(); + // + // assertThat(logTester.logs()).isEmpty(); + // } + // + // private File verifyDir(File dir, String subDir) { + // File file = new File(dir, subDir); + // assertThat(file.exists()).isTrue(); + // assertThat(file.isDirectory()).isTrue(); + // return file; + // } + // + // private void verifyFile(File dir, String filename, String content) throws IOException { + // File file = new File(dir, filename); + // assertThat(file.exists()).isTrue(); + // assertThat(file.isDirectory()).isFalse(); + // assertThat(IOUtils.toString(new FileInputStream(file), "UTF-8")).isEqualTo(content); + // } + // + // private static AnalysisReportDto newDefaultReport() { + // return AnalysisReportDto.newForTests(1L).setProjectKey("P1").setUuid("U1").setStatus(AnalysisReportDto.Status.PENDING); + // } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeSubmitWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeSubmitWsActionTest.java new file mode 100644 index 00000000000..deb1921077a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeSubmitWsActionTest.java @@ -0,0 +1,105 @@ +/* + * 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.computation.ws; + +import java.io.InputStream; +import org.junit.Test; +import org.sonar.core.util.Protobuf; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.server.computation.CeTask; +import org.sonar.server.computation.ReportProcessingScheduler; +import org.sonar.server.computation.ReportSubmitter; +import org.sonar.server.plugins.MimeTypes; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; +import org.sonar.test.JsonAssert; +import org.sonarqube.ws.WsCe; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class CeSubmitWsActionTest { + + ReportSubmitter reportSubmitter = mock(ReportSubmitter.class); + ReportProcessingScheduler reportProcessingScheduler = mock(ReportProcessingScheduler.class); + CeSubmitWsAction underTest = new CeSubmitWsAction(reportSubmitter, reportProcessingScheduler); + WsActionTester tester = new WsActionTester(underTest); + + @Test + public void submit_task_to_the_queue_and_ask_for_immediate_processing() { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", "robert"); + when(reportSubmitter.submit(eq("my_project"), eq("My Project"), any(InputStream.class))).thenReturn(task); + + TestResponse wsResponse = tester.newRequest() + .setParam("projectKey", "my_project") + .setParam("projectName", "My Project") + .setParam("report", "{binary}") + .setMediaType(MimeTypes.PROTOBUF) + .setMethod("POST") + .execute(); + + verify(reportSubmitter).submit(eq("my_project"), eq("My Project"), any(InputStream.class)); + verify(reportProcessingScheduler).startAnalysisTaskNow(); + + // verify the protobuf response + WsCe.SubmitResponse submitResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.SubmitResponse.PARSER); + assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1"); + assertThat(submitResponse.getProjectId()).isEqualTo("PROJECT_1"); + } + + @Test + public void test_response_example() { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", "robert"); + when(reportSubmitter.submit(eq("my_project"), eq("My Project"), any(InputStream.class))).thenReturn(task); + + TestResponse wsResponse = tester.newRequest() + .setParam("projectKey", "my_project") + .setParam("projectName", "My Project") + .setParam("report", "{binary}") + .setMediaType(MimeTypes.JSON) + .setMethod("POST") + .execute(); + + JsonAssert.assertJson(tester.getDef().responseExampleAsString()).isSimilarTo(wsResponse.getInput()); + } + + /** + * If project name is not specified, then name is the project key + */ + @Test + public void project_name_is_optional() { + CeTask task = new CeTask("TASK_1", CeTaskTypes.REPORT, "PROJECT_1", "robert"); + when(reportSubmitter.submit(eq("my_project"), eq("my_project"), any(InputStream.class))).thenReturn(task); + + tester.newRequest() + .setParam("projectKey", "my_project") + .setParam("report", "{binary}") + .setMediaType(MimeTypes.PROTOBUF) + .setMethod("POST") + .execute(); + + verify(reportSubmitter).submit(eq("my_project"), eq("my_project"), any(InputStream.class)); + + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeTaskWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeTaskWsActionTest.java new file mode 100644 index 00000000000..a07fb41b8e8 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeTaskWsActionTest.java @@ -0,0 +1,129 @@ +/* + * 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.computation.ws; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.api.web.UserRole; +import org.sonar.core.util.Protobuf; +import org.sonar.db.DbTester; +import org.sonar.db.ce.CeActivityDto; +import org.sonar.db.ce.CeQueueDto; +import org.sonar.db.ce.CeTaskTypes; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentService; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.plugins.MimeTypes; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.WsCe; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CeTaskWsActionTest { + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); + + ComponentService componentService = mock(ComponentService.class); + CeWsTaskFormatter formatter = new CeWsTaskFormatter(componentService); + CeTaskWsAction underTest = new CeTaskWsAction(dbTester.getDbClient(), formatter, userSession); + WsActionTester tester = new WsActionTester(underTest); + + @Test + public void task_is_in_queue() throws Exception { + userSession.setGlobalPermissions(UserRole.ADMIN); + + ComponentDto project = new ComponentDto().setUuid("PROJECT_1").setName("Project One").setKey("P1"); + when(componentService.getNonNullByUuid("PROJECT_1")).thenReturn(project); + + CeQueueDto queueDto = new CeQueueDto(); + queueDto.setTaskType(CeTaskTypes.REPORT); + queueDto.setUuid("TASK_1"); + queueDto.setComponentUuid(project.uuid()); + queueDto.setStatus(CeQueueDto.Status.PENDING); + queueDto.setSubmitterLogin("john"); + dbTester.getDbClient().ceQueueDao().insert(dbTester.getSession(), queueDto); + dbTester.getSession().commit(); + + TestResponse wsResponse = tester.newRequest() + .setMediaType(MimeTypes.PROTOBUF) + .setParam("id", "TASK_1") + .execute(); + + // verify the protobuf response + WsCe.TaskResponse taskResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.TaskResponse.PARSER); + assertThat(taskResponse.getTask().getId()).isEqualTo("TASK_1"); + assertThat(taskResponse.getTask().getStatus()).isEqualTo(WsCe.TaskStatus.PENDING); + assertThat(taskResponse.getTask().getSubmitterLogin()).isEqualTo("john"); + assertThat(taskResponse.getTask().getComponentId()).isEqualTo(project.uuid()); + assertThat(taskResponse.getTask().getComponentKey()).isEqualTo(project.key()); + assertThat(taskResponse.getTask().getComponentName()).isEqualTo(project.name()); + assertThat(taskResponse.getTask().hasExecutionTimeMs()).isFalse(); + } + + @Test + public void task_is_archived() throws Exception { + userSession.setGlobalPermissions(UserRole.ADMIN); + + ComponentDto project = new ComponentDto().setUuid("PROJECT_1").setName("Project One").setKey("P1"); + when(componentService.getNonNullByUuid("PROJECT_1")).thenReturn(project); + + CeQueueDto queueDto = new CeQueueDto(); + queueDto.setTaskType(CeTaskTypes.REPORT); + queueDto.setUuid("TASK_1"); + queueDto.setComponentUuid(project.uuid()); + CeActivityDto activityDto = new CeActivityDto(queueDto); + activityDto.setStatus(CeActivityDto.Status.FAILED); + activityDto.setExecutionTimeMs(500L); + dbTester.getDbClient().ceActivityDao().insert(dbTester.getSession(), activityDto); + dbTester.getSession().commit(); + + TestResponse wsResponse = tester.newRequest() + .setMediaType(MimeTypes.PROTOBUF) + .setParam("id", "TASK_1") + .execute(); + + // verify the protobuf response + WsCe.TaskResponse taskResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.TaskResponse.PARSER); + assertThat(taskResponse.getTask().getId()).isEqualTo("TASK_1"); + assertThat(taskResponse.getTask().getStatus()).isEqualTo(WsCe.TaskStatus.FAILED); + assertThat(taskResponse.getTask().getComponentId()).isEqualTo(project.uuid()); + assertThat(taskResponse.getTask().getComponentKey()).isEqualTo(project.key()); + assertThat(taskResponse.getTask().getComponentName()).isEqualTo(project.name()); + assertThat(taskResponse.getTask().getExecutionTimeMs()).isEqualTo(500L); + } + + @Test(expected = NotFoundException.class) + public void task_not_found() throws Exception { + userSession.setGlobalPermissions(UserRole.ADMIN); + + tester.newRequest() + .setParam("id", "DOES_NOT_EXIST") + .execute(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComputationWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeWsTest.java index b40aec69ade..7dcf0f5074b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComputationWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeWsTest.java @@ -17,34 +17,27 @@ * 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.computation.ws; import org.junit.Test; +import org.mockito.Mockito; import org.sonar.api.server.ws.WebService; -import org.sonar.server.activity.index.ActivityIndex; import org.sonar.server.computation.ReportProcessingScheduler; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.computation.monitoring.CEQueueStatus; -import org.sonar.server.user.UserSession; -import org.sonar.server.ws.WsTester; +import org.sonar.server.computation.ReportSubmitter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -public class ComputationWsTest { - - WsTester ws = new WsTester(new ComputationWs( - new QueueAction(mock(ReportQueue.class)), - new SubmitReportAction(mock(ReportQueue.class), mock(ReportProcessingScheduler.class), mock(UserSession.class), mock(CEQueueStatus.class)), - new HistoryAction(mock(ActivityIndex.class), mock(UserSession.class)))); +public class CeWsTest { @Test - public void define() { - WebService.Controller controller = ws.controller("api/computation"); + public void define() throws Exception { + CeWsAction wsAction = new CeSubmitWsAction(mock(ReportSubmitter.class), mock(ReportProcessingScheduler.class)); + + CeWs ws = new CeWs(wsAction); + WebService.Context context = mock(WebService.Context.class, Mockito.RETURNS_DEEP_STUBS); + ws.define(context); - assertThat(controller).isNotNull(); - assertThat(controller.description()).isNotEmpty(); - assertThat(controller.actions()).hasSize(3); + assertThat(context.controller("api/ce")).isNotNull(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryActionMediumTest.java deleted file mode 100644 index f971c31a338..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryActionMediumTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.computation.ws; - -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.server.activity.Activity; -import org.sonar.server.activity.ActivityService; -import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.tester.ServerTester; -import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.ws.WsTester; - -import java.util.Date; - -/** - * TODO replace this medium test by a small test - */ -public class HistoryActionMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester().withStartupTasks().withEsIndexes(); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester); - - HistoryAction sut; - ActivityService activityService; - - @Before - public void setUp() { - tester.clearDbAndIndexes(); - sut = tester.get(HistoryAction.class); - activityService = tester.get(ActivityService.class); - } - - @Test - public void search() throws Exception { - Activity activity1 = new Activity(); - activity1.setType(Activity.Type.ANALYSIS_REPORT); - activity1.setAction("LOG_ANALYSIS_REPORT"); - activity1.setData("projectKey", "P1"); - activity1.setData("projectName", "POne"); - activity1.setData("projectUuid", "U1"); - activity1.setData("status", AnalysisReportDto.Status.SUCCESS); - activity1.setData("submittedAt", new Date()); - activityService.save(activity1); - - Activity activity2 = new Activity(); - activity2.setType(Activity.Type.ANALYSIS_REPORT); - activity2.setAction("LOG_ANALYSIS_REPORT"); - activity2.setData("projectKey", "P2"); - activity2.setData("projectName", "PTwo"); - activity2.setData("projectUuid", "U2"); - activity2.setData("status", AnalysisReportDto.Status.FAILED); - activity2.setData("submittedAt", new Date()); - activityService.save(activity2); - - userSessionRule.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); - - WsTester.TestRequest request = tester.wsTester().newGetRequest("api/computation", "history"); - request.execute().assertJson(getClass(), "list_history_reports.json"); - } - - @Test(expected = ForbiddenException.class) - public void requires_admin_right() throws Exception { - WsTester.TestRequest request = tester.wsTester().newGetRequest("api/computation", "history"); - request.execute(); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/IsQueueEmptyWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/IsQueueEmptyWsTest.java deleted file mode 100644 index 034687fd34c..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/IsQueueEmptyWsTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.computation.ws; - -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.ReportQueue; - -import java.io.ByteArrayOutputStream; -import java.io.OutputStream; -import java.util.ArrayList; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class IsQueueEmptyWsTest { - - IsQueueEmptyWs.IsQueueEmptyAction underTest; - ReportQueue queue; - Response response; - - @Before - public void before() { - queue = mock(ReportQueue.class); - underTest = new IsQueueEmptyWs.IsQueueEmptyAction(queue); - - response = mock(Response.class); - when(response.stream()).thenReturn(new FakeStream()); - } - - @Test - public void send_true_when_queue_is_empty() throws Exception { - when(queue.all()).thenReturn(new ArrayList<AnalysisReportDto>()); - - underTest.handle(mock(Request.class), response); - - assertThat(response.stream().toString()).isEqualTo("true"); - } - - @Test - public void send_false_when_queue_is_not_empty() throws Exception { - when(queue.all()).thenReturn(Lists.newArrayList(AnalysisReportDto.newForTests(1L))); - - underTest.handle(mock(Request.class), response); - - assertThat(response.stream().toString()).isEqualTo("false"); - } - - private class FakeStream implements Response.Stream { - private ByteArrayOutputStream stream; - - private FakeStream() { - this.stream = new ByteArrayOutputStream(); - } - - public String toString() { - return stream.toString(); - } - - @Override - public Response.Stream setMediaType(String s) { - return null; - } - - @Override - public Response.Stream setStatus(int httpStatus) { - return null; - } - - @Override - public OutputStream output() { - return stream; - } - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/QueueActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/QueueActionTest.java deleted file mode 100644 index e74d9d15416..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/QueueActionTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.computation.ws; - -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.utils.DateUtils; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.ws.WsTester; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.sonar.db.compute.AnalysisReportDto.Status.PENDING; - -public class QueueActionTest { - - WsTester tester; - private ReportQueue queue; - - @Before - public void setup() { - queue = mock(ReportQueue.class); - tester = new WsTester(new ComputationWs(new QueueAction(queue))); - } - - @Test - public void list_active_reports() throws Exception { - AnalysisReportDto report = AnalysisReportDto - .newForTests(1L) - .setProjectKey("project-key") - .setProjectName("Project name") - .setStatus(PENDING) - .setUuid("PROJECT_UUID") - .setCreatedAt(DateUtils.parseDateTime("2014-10-13T00:00:00+0200").getTime()) - .setStartedAt(DateUtils.parseDateTime("2014-10-13T00:00:00+0200").getTime()) - .setFinishedAt(DateUtils.parseDateTime("2014-10-13T00:00:00+0200").getTime()); - List<AnalysisReportDto> reports = Lists.newArrayList(report); - when(queue.all()).thenReturn(reports); - - WsTester.TestRequest request = tester.newGetRequest(ComputationWs.ENDPOINT, "queue"); - request.execute().assertJson(getClass(), "list_queue_reports.json"); - } - - @Test - public void define() { - assertThat(tester.controller(ComputationWs.ENDPOINT).action("queue")).isNotNull(); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/SubmitReportActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/SubmitReportActionTest.java deleted file mode 100644 index 18f8fdfc5d0..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/SubmitReportActionTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.computation.ws; - -import java.io.InputStream; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.server.ws.WebService; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.db.compute.AnalysisReportDto; -import org.sonar.server.computation.ReportProcessingScheduler; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.computation.monitoring.CEQueueStatus; -import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.ws.WsTester; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class SubmitReportActionTest { - - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - - ReportProcessingScheduler workerLauncher = mock(ReportProcessingScheduler.class); - CEQueueStatus queueStatus = mock(CEQueueStatus.class); - ReportQueue queue = mock(ReportQueue.class); - WsTester wsTester; - SubmitReportAction underTest; - - @Before - public void before() { - underTest = new SubmitReportAction(queue, workerLauncher, userSessionRule, queueStatus); - wsTester = new WsTester(new ComputationWs(underTest)); - } - - @Test - public void define_metadata() { - WebService.Context context = new WebService.Context(); - WebService.NewController controller = context.createController("api/computation"); - underTest.define(controller); - controller.done(); - - WebService.Action action = context.controller("api/computation").action("submit_report"); - assertThat(action).isNotNull(); - assertThat(action.params()).hasSize(3); - } - - @Test - public void add_element_to_queue_and_launch_analysis_task() throws Exception { - userSessionRule.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); - AnalysisReportDto dto = mock(AnalysisReportDto.class); - when(dto.getId()).thenReturn(42L); - when(queue.add(any(String.class), any(String.class), any(InputStream.class))).thenReturn(new ReportQueue.Item(dto, null)); - - WsTester.TestRequest request = wsTester - .newPostRequest(ComputationWs.ENDPOINT, "submit_report") - .setParam(SubmitReportAction.PARAM_PROJECT_KEY, "P1") - .setParam(SubmitReportAction.PARAM_PROJECT_NAME, "Project 1") - .setParam(SubmitReportAction.PARAM_REPORT_DATA, null); - WsTester.Result response = request.execute(); - - verify(queue).add(eq("P1"), eq("Project 1"), any(InputStream.class)); - verify(workerLauncher).startAnalysisTaskNow(); - verify(queueStatus).addReceived(); - assertThat(response.outputAsString()).isEqualTo("{\"key\":\"42\"}"); - } - - @Test(expected = ForbiddenException.class) - public void requires_scan_permission() throws Exception { - userSessionRule.setGlobalPermissions(GlobalPermissions.DASHBOARD_SHARING); - - WsTester.TestRequest request = wsTester - .newPostRequest(ComputationWs.ENDPOINT, "submit_report") - .setParam(SubmitReportAction.PARAM_PROJECT_KEY, "P1") - .setParam(SubmitReportAction.PARAM_PROJECT_NAME, "Project 1") - .setParam(SubmitReportAction.PARAM_REPORT_DATA, null); - request.execute(); - - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java b/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java index 3405e132459..5d844b9757e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java @@ -23,6 +23,7 @@ import com.google.common.base.Throwables; import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import org.apache.commons.io.IOUtils; import org.sonar.api.server.ws.internal.ValidatingRequest; import static com.google.common.base.Preconditions.checkNotNull; @@ -40,7 +41,11 @@ public class TestRequest extends ValidatingRequest { @Override protected InputStream readInputStreamParam(String key) { - throw new UnsupportedOperationException("Not supported in test yet"); + String value = readParam(key); + if (value == null) { + return null; + } + return IOUtils.toInputStream(value); } @Override diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryActionMediumTest/list_history_reports.json b/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryActionMediumTest/list_history_reports.json deleted file mode 100644 index caa180819a4..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryActionMediumTest/list_history_reports.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "reports": [ - { - "status": "SUCCESS", - "projectName": "POne", - "projectKey": "P1", - "projectUuid": "U1" - }, - { - "status": "FAILED", - "projectName": "PTwo", - "projectKey": "P2", - "projectUuid": "U2" - } - ] -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/QueueActionTest/list_queue_reports.json b/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/QueueActionTest/list_queue_reports.json deleted file mode 100644 index 10247f2e942..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/QueueActionTest/list_queue_reports.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "reports": [ - { - "key": 1, - "status": "PENDING", - "projectName": "Project name", - "projectKey": "project-key", - "submittedAt": "2014-10-13T00:00:00+0200", - "startedAt": "2014-10-13T00:00:00+0200", - "finishedAt": "2014-10-13T00:00:00+0200" - } - ] -} diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/414_add_scan_and_dry_run_permissions.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/414_add_scan_and_dry_run_permissions.rb index c8ba5db51d7..d46065fdecc 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/414_add_scan_and_dry_run_permissions.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/414_add_scan_and_dry_run_permissions.rb @@ -39,6 +39,10 @@ class AddScanAndDryRunPermissions < ActiveRecord::Migration # -- Role dryRunScan -- # Anyone GroupRole.create(:group_id => nil, :role => 'dryRunScan', :resource_id => nil) + + # -- Role provisioning -- + # Anyone + GroupRole.create(:group_id => nil, :role => 'provisioning', :resource_id => nil) end end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_create_ce_activity.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_create_ce_activity.rb new file mode 100644 index 00000000000..e2bb4cb8fa5 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_create_ce_activity.rb @@ -0,0 +1,45 @@ +# +# 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. +# +# +# SonarQube 5.2 +# +class CreateCeActivity < ActiveRecord::Migration + + def self.up + create_table 'ce_activity' do |t| + t.column 'uuid', :string, :limit => 40, :null => false + t.column 'task_type', :string, :limit => 15, :null => false + t.column 'component_uuid', :string, :limit => 40, :null => true + t.column 'status', :string, :limit => 15, :null => false + t.column 'is_last', :boolean, :null => false + t.column 'is_last_key', :string, :limit => 55, :null => false + t.column 'submitter_login', :string, :limit => 255, :null => true + t.column 'submitted_at', :big_integer, :null => false + t.column 'started_at', :big_integer, :null => true + t.column 'finished_at', :big_integer, :null => true + t.column 'created_at', :big_integer, :null => false + t.column 'updated_at', :big_integer, :null => false + t.column 'execution_time_ms', :big_integer, :null => true + end + add_index 'ce_activity', 'uuid', :name => 'ce_activity_uuid', :unique => true + end + +end + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/932_create_ce_queue.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/932_create_ce_queue.rb new file mode 100644 index 00000000000..e8d0be4da42 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/932_create_ce_queue.rb @@ -0,0 +1,40 @@ +# +# 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. +# +# +# SonarQube 5.2 +# +class CreateCeQueue < ActiveRecord::Migration + + def self.up + create_table 'ce_queue' do |t| + t.column 'uuid', :string, :limit => 40, :null => false + t.column 'task_type', :string, :limit => 15, :null => false + t.column 'component_uuid', :string, :limit => 40, :null => true + t.column 'status', :string, :limit => 15, :null => false + t.column 'submitter_login', :string, :limit => 255, :null => true + t.column 'started_at', :big_integer, :null => true + t.column 'created_at', :big_integer, :null => false + t.column 'updated_at', :big_integer, :null => false + end + add_index 'ce_queue', 'uuid', :name => 'ce_queue_uuid', :unique => true + end + +end + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/933_drop_table_analysis_reports.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/933_drop_table_analysis_reports.rb new file mode 100644 index 00000000000..36e925b47e9 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/933_drop_table_analysis_reports.rb @@ -0,0 +1,30 @@ +# +# 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. +# +# +# SonarQube 5.2 +# +class DropTableAnalysisReports < ActiveRecord::Migration + + def self.up + drop_table 'analysis_reports' + end + +end + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/934_remove_analysis_reports_from_activities.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/934_remove_analysis_reports_from_activities.rb new file mode 100644 index 00000000000..3c877912ab3 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/934_remove_analysis_reports_from_activities.rb @@ -0,0 +1,30 @@ +# +# 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. +# +# +# SonarQube 5.2 +# +class RemoveAnalysisReportsFromActivities < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.db.version.v52.RemoveAnalysisReportsFromActivities') + end + +end + |