浏览代码

Replace table ANALYSIS_REPORTS by CE_QUEUE and CE_ACTIVITY

tags/5.2-RC1
Simon Brandhof 8 年前
父节点
当前提交
024672b883
共有 100 个文件被更改,包括 3031 次插入2325 次删除
  1. 0
    2
      server/sonar-server/src/main/java/org/sonar/server/batch/BatchWsModule.java
  2. 3
    3
      server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
  3. 7
    4
      server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java
  4. 230
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CeQueue.java
  5. 110
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueInitializer.java
  6. 28
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueListener.java
  7. 101
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CeTask.java
  8. 31
    19
      server/sonar-server/src/main/java/org/sonar/server/computation/CeTaskSubmit.java
  9. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/CeWorker.java
  10. 66
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CeWorkerImpl.java
  11. 36
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CleanReportQueueListener.java
  12. 13
    66
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputationStepExecutor.java
  13. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingExecutorService.java
  14. 5
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingModule.java
  15. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueue.java
  16. 3
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineProcessingQueueImpl.java
  17. 95
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportFiles.java
  18. 4
    22
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingScheduler.java
  19. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingSchedulerExecutorService.java
  20. 0
    74
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingTask.java
  21. 0
    90
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessor.java
  22. 0
    195
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueue.java
  23. 0
    59
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueueCleaner.java
  24. 71
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportSubmitter.java
  25. 44
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ReportTaskProcessor.java
  26. 0
    72
      server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java
  27. 1
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolder.java
  28. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportDirectoryHolderImpl.java
  29. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java
  30. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java
  31. 3
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java
  32. 80
    86
      server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java
  33. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java
  34. 1
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateHolder.java
  35. 1
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateHolder.java
  36. 1
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateLoadingStep.java
  37. 15
    15
      server/sonar-server/src/main/java/org/sonar/server/computation/step/ReportExtractionStep.java
  38. 93
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeSubmitWsAction.java
  39. 93
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeTaskWsAction.java
  40. 7
    14
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWs.java
  41. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsAction.java
  42. 89
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsTaskFormatter.java
  43. 0
    90
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryAction.java
  44. 15
    14
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/IsQueueEmptyWs.java
  45. 0
    80
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/QueueAction.java
  46. 0
    100
      server/sonar-server/src/main/java/org/sonar/server/computation/ws/SubmitReportAction.java
  47. 18
    9
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
  48. 1
    2
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
  49. 4
    0
      server/sonar-server/src/main/resources/org/sonar/server/computation/ws/CeSubmitWsAction/example.json
  50. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsModuleTest.java
  51. 3
    3
      server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java
  52. 3
    2
      server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java
  53. 139
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueInitializerTest.java
  54. 219
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/CeQueueTest.java
  55. 72
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/CeWorkerImplTest.java
  56. 49
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/CleanReportQueueListenerTest.java
  57. 7
    7
      server/sonar-server/src/test/java/org/sonar/server/computation/ComputeEngineProcessingQueueImplTest.java
  58. 11
    37
      server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingSchedulerTest.java
  59. 0
    111
      server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingTaskTest.java
  60. 0
    151
      server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessorTest.java
  61. 0
    210
      server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueTest.java
  62. 69
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/ReportSubmitterTest.java
  63. 0
    84
      server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java
  64. 4
    4
      server/sonar-server/src/test/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulatorTest.java
  65. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java
  66. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java
  67. 91
    113
      server/sonar-server/src/test/java/org/sonar/server/computation/step/ReportExtractionStepTest.java
  68. 105
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeSubmitWsActionTest.java
  69. 129
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeTaskWsActionTest.java
  70. 10
    17
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/CeWsTest.java
  71. 0
    91
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryActionMediumTest.java
  72. 0
    98
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/IsQueueEmptyWsTest.java
  73. 0
    71
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/QueueActionTest.java
  74. 0
    105
      server/sonar-server/src/test/java/org/sonar/server/computation/ws/SubmitReportActionTest.java
  75. 6
    1
      server/sonar-server/src/test/java/org/sonar/server/ws/TestRequest.java
  76. 0
    16
      server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryActionMediumTest/list_history_reports.json
  77. 0
    13
      server/sonar-server/src/test/resources/org/sonar/server/computation/ws/QueueActionTest/list_queue_reports.json
  78. 4
    0
      server/sonar-web/src/main/webapp/WEB-INF/db/migrate/414_add_scan_and_dry_run_permissions.rb
  79. 45
    0
      server/sonar-web/src/main/webapp/WEB-INF/db/migrate/931_create_ce_activity.rb
  80. 40
    0
      server/sonar-web/src/main/webapp/WEB-INF/db/migrate/932_create_ce_queue.rb
  81. 30
    0
      server/sonar-web/src/main/webapp/WEB-INF/db/migrate/933_drop_table_analysis_reports.rb
  82. 30
    0
      server/sonar-web/src/main/webapp/WEB-INF/db/migrate/934_remove_analysis_reports_from_activities.rb
  83. 1
    1
      sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
  84. 1
    1
      sonar-core/src/main/java/org/sonar/core/platform/PluginRepository.java
  85. 10
    0
      sonar-core/src/main/java/org/sonar/core/util/Protobuf.java
  86. 3
    0
      sonar-db/src/main/java/org/sonar/db/Dao.java
  87. 4
    2
      sonar-db/src/main/java/org/sonar/db/DaoModule.java
  88. 12
    5
      sonar-db/src/main/java/org/sonar/db/DbClient.java
  89. 5
    4
      sonar-db/src/main/java/org/sonar/db/MyBatis.java
  90. 65
    0
      sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java
  91. 186
    0
      sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java
  92. 43
    0
      sonar-db/src/main/java/org/sonar/db/ce/CeActivityMapper.java
  93. 64
    0
      sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java
  94. 108
    0
      sonar-db/src/main/java/org/sonar/db/ce/CeQueueDao.java
  95. 145
    0
      sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java
  96. 20
    16
      sonar-db/src/main/java/org/sonar/db/ce/CeQueueMapper.java
  97. 9
    3
      sonar-db/src/main/java/org/sonar/db/ce/CeTaskTypes.java
  98. 1
    2
      sonar-db/src/main/java/org/sonar/db/ce/package-info.java
  99. 0
    107
      sonar-db/src/main/java/org/sonar/db/compute/AnalysisReportDao.java
  100. 0
    0
      sonar-db/src/main/java/org/sonar/db/compute/AnalysisReportDto.java

+ 0
- 2
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);

+ 3
- 3
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);
}
}


+ 7
- 4
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;

+ 230
- 0
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;
}
}

+ 110
- 0
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));
}
}

+ 28
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/CeQueueListener.java 查看文件

@@ -0,0 +1,28 @@
/*
* 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 interface CeQueueListener {

void onRemoved(CeTask task, CeActivityDto.Status status);

}

+ 101
- 0
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();
}
}

server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueCleanerTest.java → 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;
}
}

server/sonar-server/src/main/java/org/sonar/server/computation/ComputeEngineTask.java → 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 {
}

+ 66
- 0
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()));
}
}
}

+ 36
- 0
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());
}
}

+ 13
- 66
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());
}
}
}

+ 1
- 1
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 {
}

+ 5
- 0
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,

+ 1
- 1
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);
}

+ 3
- 3
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();

+ 95
- 0
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;
}
}

+ 4
- 22
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);
}
}
}

+ 1
- 1
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 {
}

+ 0
- 74
server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessingTask.java 查看文件

@@ -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);
}
}
}

+ 0
- 90
server/sonar-server/src/main/java/org/sonar/server/computation/ReportProcessor.java 查看文件

@@ -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);
}
}

}

+ 0
- 195
server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueue.java 查看文件

@@ -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;
}
}
}

+ 0
- 59
server/sonar-server/src/main/java/org/sonar/server/computation/ReportQueueCleaner.java 查看文件

@@ -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
}
}

+ 71
- 0
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);
}
}

+ 44
- 0
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();
}
}
}

+ 0
- 72
server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java 查看文件

@@ -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);
}
}
}

+ 1
- 2
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)
*/

+ 1
- 0
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

+ 2
- 3
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();


+ 2
- 2
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);
}

+ 3
- 3
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));
}
}

+ 80
- 86
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);
}


}

+ 2
- 2
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;
}


+ 1
- 2
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}

+ 1
- 2
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)
*/

+ 1
- 2
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 {

+ 15
- 15
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";
}

}

+ 93
- 0
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);
}
}

+ 93
- 0
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);
}

}
}

server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComputationWs.java → 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();

server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComputationWsAction.java → 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);
}

+ 89
- 0
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());
}
}
}

+ 0
- 90
server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryAction.java 查看文件

@@ -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();
}
}

+ 15
- 14
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);
}
}
}
}

+ 0
- 80
server/sonar-server/src/main/java/org/sonar/server/computation/ws/QueueAction.java 查看文件

@@ -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();
}

}

+ 0
- 100
server/sonar-server/src/main/java/org/sonar/server/computation/ws/SubmitReportAction.java 查看文件

@@ -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);
}
}
}

+ 18
- 9
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,

+ 1
- 2
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);

+ 4
- 0
server/sonar-server/src/main/resources/org/sonar/server/computation/ws/CeSubmitWsAction/example.json 查看文件

@@ -0,0 +1,4 @@
{
"taskId": "TASK_1",
"projectId": "PROJECT_1"
}

+ 1
- 1
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);
}

}

+ 3
- 3
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");

+ 3
- 2
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);


+ 139
- 0
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;
}
}

+ 219
- 0
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);
}
}

+ 72
- 0
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);
}
}

+ 49
- 0
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");
}
}

+ 7
- 7
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

+ 11
- 37
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;
}
}

+ 0
- 111
server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessingTaskTest.java 查看文件

@@ -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");
}
}

+ 0
- 151
server/sonar-server/src/test/java/org/sonar/server/computation/ReportProcessorTest.java 查看文件

@@ -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;
}

}

+ 0
- 210
server/sonar-server/src/test/java/org/sonar/server/computation/ReportQueueTest.java 查看文件

@@ -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");
}
}

+ 69
- 0
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) {

}
}));
}
}

+ 0
- 84
server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java 查看文件

@@ -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");
}

}

+ 4
- 4
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

+ 2
- 2
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() {

+ 2
- 2
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();


+ 91
- 113
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);
// }
}

+ 105
- 0
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));

}
}

+ 129
- 0
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();
}
}

server/sonar-server/src/test/java/org/sonar/server/computation/ws/ComputationWsTest.java → 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();
}
}

+ 0
- 91
server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryActionMediumTest.java 查看文件

@@ -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();
}
}

+ 0
- 98
server/sonar-server/src/test/java/org/sonar/server/computation/ws/IsQueueEmptyWsTest.java 查看文件

@@ -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;
}
}
}

+ 0
- 71
server/sonar-server/src/test/java/org/sonar/server/computation/ws/QueueActionTest.java 查看文件

@@ -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();
}
}

+ 0
- 105
server/sonar-server/src/test/java/org/sonar/server/computation/ws/SubmitReportActionTest.java 查看文件

@@ -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();

}
}

+ 6
- 1
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

+ 0
- 16
server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryActionMediumTest/list_history_reports.json 查看文件

@@ -1,16 +0,0 @@
{
"reports": [
{
"status": "SUCCESS",
"projectName": "POne",
"projectKey": "P1",
"projectUuid": "U1"
},
{
"status": "FAILED",
"projectName": "PTwo",
"projectKey": "P2",
"projectUuid": "U2"
}
]
}

+ 0
- 13
server/sonar-server/src/test/resources/org/sonar/server/computation/ws/QueueActionTest/list_queue_reports.json 查看文件

@@ -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"
}
]
}

+ 4
- 0
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

+ 45
- 0
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


+ 40
- 0
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


+ 30
- 0
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


+ 30
- 0
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


+ 1
- 1
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java 查看文件

@@ -138,7 +138,7 @@ public class ReportPublisher implements Startable {
void sendOrDumpReport(File report) {
ProjectDefinition projectDefinition = projectReactor.getRoot();
String effectiveKey = projectDefinition.getKeyWithBranch();
String relativeUrl = "/api/computation/submit_report?projectKey=" + effectiveKey + "&projectName=" + BatchUtils.encodeForUrl(projectDefinition.getName());
String relativeUrl = "/api/ce/submit?projectKey=" + effectiveKey + "&projectName=" + BatchUtils.encodeForUrl(projectDefinition.getName());

String dumpDirLocation = settings.getString(DUMP_REPORT_PROP_KEY);
if (dumpDirLocation == null) {

+ 1
- 1
sonar-core/src/main/java/org/sonar/core/platform/PluginRepository.java 查看文件

@@ -36,7 +36,7 @@ public interface PluginRepository {
PluginInfo getPluginInfo(String key);

/**
* @return the instance of {@link Plugin} for the given plugin key. Never return null.
* @return the instance of {@link SonarPlugin} for the given plugin key. Never return null.
*/
SonarPlugin getPluginInstance(String key);


+ 10
- 0
sonar-core/src/main/java/org/sonar/core/util/Protobuf.java 查看文件

@@ -56,6 +56,16 @@ public class Protobuf {
}
}

public static <MSG extends Message> MSG read(InputStream input, Parser<MSG> parser) {
try {
return parser.parseFrom(input);
} catch (Exception e) {
throw ContextException.of("Unable to read message", e);
} finally {
IOUtils.closeQuietly(input);
}
}

/**
* Writes a single message to {@code file}. Existing content is replaced, the message is not
* appended.

+ 3
- 0
sonar-db/src/main/java/org/sonar/db/Dao.java 查看文件

@@ -19,5 +19,8 @@
*/
package org.sonar.db;

/**
* All implementations must be declared in {@link DaoModule}
*/
public interface Dao {
}

+ 4
- 2
sonar-db/src/main/java/org/sonar/db/DaoModule.java 查看文件

@@ -24,13 +24,14 @@ import com.google.common.collect.ImmutableList;
import java.util.List;
import org.sonar.core.platform.Module;
import org.sonar.db.activity.ActivityDao;
import org.sonar.db.ce.CeActivityDao;
import org.sonar.db.ce.CeQueueDao;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentLinkDao;
import org.sonar.db.component.ResourceDao;
import org.sonar.db.component.ResourceIndexDao;
import org.sonar.db.component.ResourceKeyUpdaterDao;
import org.sonar.db.component.SnapshotDao;
import org.sonar.db.compute.AnalysisReportDao;
import org.sonar.db.dashboard.ActiveDashboardDao;
import org.sonar.db.dashboard.DashboardDao;
import org.sonar.db.dashboard.WidgetDao;
@@ -74,9 +75,10 @@ public class DaoModule extends Module {
ActionPlanStatsDao.class,
ActiveDashboardDao.class,
ActivityDao.class,
AnalysisReportDao.class,
AuthorDao.class,
AuthorizationDao.class,
CeActivityDao.class,
CeQueueDao.class,
ComponentDao.class,
ComponentLinkDao.class,
CustomMeasureDao.class,

+ 12
- 5
sonar-db/src/main/java/org/sonar/db/DbClient.java 查看文件

@@ -23,13 +23,14 @@ import java.util.IdentityHashMap;
import java.util.Map;
import javax.annotation.Nullable;
import org.sonar.db.activity.ActivityDao;
import org.sonar.db.ce.CeActivityDao;
import org.sonar.db.ce.CeQueueDao;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentLinkDao;
import org.sonar.db.component.ResourceDao;
import org.sonar.db.component.ResourceIndexDao;
import org.sonar.db.component.ResourceKeyUpdaterDao;
import org.sonar.db.component.SnapshotDao;
import org.sonar.db.compute.AnalysisReportDao;
import org.sonar.db.dashboard.ActiveDashboardDao;
import org.sonar.db.dashboard.DashboardDao;
import org.sonar.db.dashboard.WidgetDao;
@@ -95,7 +96,8 @@ public class DbClient {
private final IssueChangeDao issueChangeDao;
private final ActionPlanDao actionPlanDao;
private final ActionPlanStatsDao actionPlanStatsDao;
private final AnalysisReportDao analysisReportDao;
private final CeQueueDao ceQueueDao;
private final CeActivityDao ceActivityDao;
private final DashboardDao dashboardDao;
private final ActiveDashboardDao activeDashboardDao;
private final WidgetDao widgetDao;
@@ -148,7 +150,8 @@ public class DbClient {
issueChangeDao = getDao(map, IssueChangeDao.class);
actionPlanDao = getDao(map, ActionPlanDao.class);
actionPlanStatsDao = getDao(map, ActionPlanStatsDao.class);
analysisReportDao = getDao(map, AnalysisReportDao.class);
ceQueueDao = getDao(map, CeQueueDao.class);
ceActivityDao = getDao(map, CeActivityDao.class);
dashboardDao = getDao(map, DashboardDao.class);
activeDashboardDao = getDao(map, ActiveDashboardDao.class);
widgetDao = getDao(map, WidgetDao.class);
@@ -280,8 +283,12 @@ public class DbClient {
return actionPlanDao;
}

public AnalysisReportDao analysisReportDao() {
return analysisReportDao;
public CeQueueDao ceQueueDao() {
return ceQueueDao;
}

public CeActivityDao ceActivityDao() {
return ceActivityDao;
}

public DashboardDao dashboardDao() {

+ 5
- 4
sonar-db/src/main/java/org/sonar/db/MyBatis.java 查看文件

@@ -32,6 +32,8 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.activity.ActivityDto;
import org.sonar.db.activity.ActivityMapper;
import org.sonar.db.ce.CeActivityMapper;
import org.sonar.db.ce.CeQueueMapper;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentLinkDto;
import org.sonar.db.component.ComponentLinkMapper;
@@ -46,8 +48,6 @@ import org.sonar.db.component.SnapshotDto;
import org.sonar.db.component.SnapshotMapper;
import org.sonar.db.component.UuidWithProjectUuidDto;
import org.sonar.db.component.ViewsSnapshotDto;
import org.sonar.db.compute.AnalysisReportDto;
import org.sonar.db.compute.AnalysisReportMapper;
import org.sonar.db.dashboard.ActiveDashboardDto;
import org.sonar.db.dashboard.ActiveDashboardMapper;
import org.sonar.db.dashboard.DashboardDto;
@@ -95,6 +95,7 @@ import org.sonar.db.permission.PermissionTemplateUserDto;
import org.sonar.db.permission.UserWithPermissionDto;
import org.sonar.db.property.PropertiesMapper;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.purge.IdUuidPair;
import org.sonar.db.purge.PurgeMapper;
import org.sonar.db.purge.PurgeableSnapshotDto;
import org.sonar.db.qualitygate.ProjectQgateAssociationDto;
@@ -204,7 +205,7 @@ public class MyBatis {
confBuilder.loadAlias("ActiveRuleParam", ActiveRuleParamDto.class);
confBuilder.loadAlias("RequirementMigration", RequirementMigrationDto.class);
confBuilder.loadAlias("Activity", ActivityDto.class);
confBuilder.loadAlias("AnalysisReport", AnalysisReportDto.class);
confBuilder.loadAlias("IdUuidPair", IdUuidPair.class);
confBuilder.loadAlias("FilePathWithHash", FilePathWithHashDto.class);
confBuilder.loadAlias("UuidWithProjectUuid", UuidWithProjectUuidDto.class);
confBuilder.loadAlias("Event", EventDto.class);
@@ -230,7 +231,7 @@ public class MyBatis {
GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class,
MeasureMapper.class, MetricMapper.class, CustomMeasureMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class,
ProjectQgateAssociationMapper.class, EventMapper.class,
AnalysisReportMapper.class, ComponentLinkMapper.class,
CeQueueMapper.class, CeActivityMapper.class, ComponentLinkMapper.class,
Migration45Mapper.class, Migration50Mapper.class
};
confBuilder.loadMappers(mappers);

+ 65
- 0
sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java 查看文件

@@ -0,0 +1,65 @@
/*
* 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.db.ce;

import com.google.common.base.Optional;
import java.util.List;
import org.apache.ibatis.session.RowBounds;
import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;

public class CeActivityDao implements Dao {

private final System2 system2;

public CeActivityDao(System2 system2) {
this.system2 = system2;
}

public Optional<CeActivityDto> selectByUuid(DbSession dbSession, String uuid) {
return Optional.fromNullable(mapper(dbSession).selectByUuid(uuid));
}

public void insert(DbSession dbSession, CeActivityDto dto) {
dto.setCreatedAt(system2.now());
dto.setUpdatedAt(system2.now());
dto.setIsLast(false);
mapper(dbSession).insert(dto);

List<String> uuids = mapper(dbSession).selectUuidsOfRecentlyCreatedByIsLastKey(dto.getIsLastKey(), new RowBounds(0, 1));
// should never be empty, as a row was just inserted!
if (!uuids.isEmpty()) {
mapper(dbSession).updateIsLastToFalseForLastKey(dto.getIsLastKey(), dto.getUpdatedAt());
mapper(dbSession).updateIsLastToTrueForUuid(uuids.get(0), dto.getUpdatedAt());
}
}

/**
* Ordered by id asc -> oldest to newest
*/
public List<CeActivityDto> selectByQuery(DbSession dbSession, CeActivityQuery query, RowBounds rowBounds) {
return mapper(dbSession).selectByQuery(query, rowBounds);
}

private CeActivityMapper mapper(DbSession dbSession) {
return dbSession.getMapper(CeActivityMapper.class);
}
}

+ 186
- 0
sonar-db/src/main/java/org/sonar/db/ce/CeActivityDto.java 查看文件

@@ -0,0 +1,186 @@
/*
* 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.db.ce;

import com.google.common.base.Objects;
import com.google.common.base.Strings;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;

public class CeActivityDto {

public enum Status {
SUCCESS, FAILED, CANCELED
}

private String uuid;
private String componentUuid;
private Status status;
private String taskType;
private boolean isLast;
private String isLastKey;
private String submitterLogin;
private long submittedAt;
private Long startedAt;
private Long finishedAt;
private long createdAt;
private long updatedAt;
private Long executionTimeMs;

CeActivityDto() {
// required for MyBatis
}

public CeActivityDto(CeQueueDto queueDto) {
this.uuid = queueDto.getUuid();
this.taskType = queueDto.getTaskType();
this.componentUuid = queueDto.getComponentUuid();
this.isLastKey = format("%s%s", taskType, Strings.nullToEmpty(componentUuid));
this.submitterLogin = queueDto.getSubmitterLogin();
this.submittedAt = queueDto.getCreatedAt();
this.startedAt = queueDto.getStartedAt();
}

public String getUuid() {
return uuid;
}

public void setUuid(String s) {
checkArgument(s.length() <= 40, "Value is too long for column CE_ACTIVITY.UUID: %s", s);
this.uuid = s;
}

public String getTaskType() {
return taskType;
}

public void setTaskType(String s) {
this.taskType = s;
}

@CheckForNull
public String getComponentUuid() {
return componentUuid;
}

public void setComponentUuid(@Nullable String s) {
checkArgument(s == null || s.length() <= 40, "Value is too long for column CE_ACTIVITY.COMPONENT_UUID: %s", s);
this.componentUuid = s;
}

public Status getStatus() {
return status;
}

public void setStatus(Status s) {
this.status = s;
}

public boolean getIsLast() {
return isLast;
}

void setIsLast(boolean b) {
this.isLast = b;
}

public String getIsLastKey() {
return isLastKey;
}

@CheckForNull
public String getSubmitterLogin() {
return submitterLogin;
}

public long getSubmittedAt() {
return submittedAt;
}

public void setSubmittedAt(long submittedAt) {
this.submittedAt = submittedAt;
}

@CheckForNull
public Long getStartedAt() {
return startedAt;
}

public void setStartedAt(@Nullable Long l) {
this.startedAt = l;
}

@CheckForNull
public Long getFinishedAt() {
return finishedAt;
}

public void setFinishedAt(@Nullable Long l) {
this.finishedAt = l;
}

public long getCreatedAt() {
return createdAt;
}

public void setCreatedAt(long l) {
this.createdAt = l;
}

public long getUpdatedAt() {
return updatedAt;
}

public void setUpdatedAt(long l) {
this.updatedAt = l;
}

@CheckForNull
public Long getExecutionTimeMs() {
return executionTimeMs;
}

public void setExecutionTimeMs(@Nullable Long l) {
checkArgument(l == null || l >= 0, "Execution time must be positive: %s", l);
this.executionTimeMs = l;
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("uuid", uuid)
.add("taskType", taskType)
.add("componentUuid", componentUuid)
.add("status", status)
.add("isLast", isLast)
.add("isLastKey", isLastKey)
.add("submitterLogin", submitterLogin)
.add("submittedAt", submittedAt)
.add("startedAt", startedAt)
.add("finishedAt", finishedAt)
.add("createdAt", createdAt)
.add("updatedAt", updatedAt)
.add("executionTimeMs", executionTimeMs)
.toString();
}
}

+ 43
- 0
sonar-db/src/main/java/org/sonar/db/ce/CeActivityMapper.java 查看文件

@@ -0,0 +1,43 @@
/*
* 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.db.ce;

import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;

public interface CeActivityMapper {

List<String> selectUuidsOfRecentlyCreatedByIsLastKey(@Param("isLastKey") String isLastKey, RowBounds rowBounds);

@CheckForNull
CeActivityDto selectByUuid(@Param("uuid") String uuid);

List<CeActivityDto> selectByComponentUuid(@Param("componentUuid") String componentUuid);

List<CeActivityDto> selectByQuery(@Param("query") CeActivityQuery query, RowBounds rowBounds);

void insert(CeActivityDto dto);

void updateIsLastToFalseForLastKey(@Param("isLastKey") String isLastKey, @Param("updatedAt") long updatedAt);

void updateIsLastToTrueForUuid(@Param("uuid") String uuid, @Param("updatedAt") long updatedAt);
}

+ 64
- 0
sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java 查看文件

@@ -0,0 +1,64 @@
/*
* 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.db.ce;

public class CeActivityQuery {

private boolean onlyCurrents = false;
private String componentUuid;
private CeActivityDto.Status status;
private String type;

public String getComponentUuid() {
return componentUuid;
}

public CeActivityQuery setComponentUuid(String componentUuid) {
this.componentUuid = componentUuid;
return this;
}

public boolean isOnlyCurrents() {
return onlyCurrents;
}

public CeActivityQuery setOnlyCurrents(boolean onlyCurrents) {
this.onlyCurrents = onlyCurrents;
return this;
}

public CeActivityDto.Status getStatus() {
return status;
}

public CeActivityQuery setStatus(CeActivityDto.Status status) {
this.status = status;
return this;
}

public String getType() {
return type;
}

public CeActivityQuery setType(String type) {
this.type = type;
return this;
}
}

+ 108
- 0
sonar-db/src/main/java/org/sonar/db/ce/CeQueueDao.java 查看文件

@@ -0,0 +1,108 @@
/*
* 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.db.ce;

import com.google.common.base.Optional;
import java.util.List;
import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;

import static org.sonar.db.ce.CeQueueDto.Status.PENDING;
import static org.sonar.db.ce.CeQueueDto.Status.IN_PROGRESS;

public class CeQueueDao implements Dao {

private final System2 system2;

public CeQueueDao(System2 system2) {
this.system2 = system2;
}

/**
* Ordered by ascending id: oldest to newest
*/
public List<CeQueueDto> selectAllInAscOrder(DbSession session) {
return mapper(session).selectAllInAscOrder();
}

/**
* Ordered by ascending id: oldest to newest
*/
public List<CeQueueDto> selectByComponentUuid(DbSession session, String componentUuid) {
return mapper(session).selectByComponentUuid(componentUuid);
}

public Optional<CeQueueDto> selectByUuid(DbSession session, String uuid) {
return Optional.fromNullable(mapper(session).selectByUuid(uuid));
}

public CeQueueDto insert(DbSession session, CeQueueDto dto) {
dto.setCreatedAt(system2.now());
dto.setUpdatedAt(system2.now());
mapper(session).insert(dto);
return dto;
}

public void deleteByUuid(DbSession session, String uuid) {
mapper(session).deleteByUuid(uuid);
}

/**
* Update all rows with: STATUS='PENDING', STARTED_AT=NULL, UPDATED_AT={now}
*/
public void resetAllToPendingStatus(DbSession session) {
mapper(session).resetAllToPendingStatus(system2.now());
}

public int countByStatus(DbSession dbSession, CeQueueDto.Status status) {
return mapper(dbSession).countByStatus(status);
}

public int countAll(DbSession dbSession) {
return mapper(dbSession).countAll();
}

public Optional<CeQueueDto> peek(DbSession session) {
List<String> taskUuids = mapper(session).selectEligibleForPeek();
if (taskUuids.isEmpty()) {
return Optional.absent();
}

String taskUuid = taskUuids.get(0);
return tryToPeek(session, taskUuid);
}

private Optional<CeQueueDto> tryToPeek(DbSession session, String taskUuid) {
int touchedRows = mapper(session).updateIfStatus(taskUuid, IN_PROGRESS, system2.now(), system2.now(), PENDING);
if (touchedRows != 1) {
session.rollback();
return Optional.absent();
}

CeQueueDto result = mapper(session).selectByUuid(taskUuid);
session.commit();
return Optional.of(result);
}

private CeQueueMapper mapper(DbSession session) {
return session.getMapper(CeQueueMapper.class);
}
}

+ 145
- 0
sonar-db/src/main/java/org/sonar/db/ce/CeQueueDto.java 查看文件

@@ -0,0 +1,145 @@
/*
* 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.db.ce;

import com.google.common.base.Objects;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import static com.google.common.base.Preconditions.checkArgument;

public class CeQueueDto {

public enum Status {
PENDING, IN_PROGRESS
}

private String uuid;
private String taskType;
private String componentUuid;
private Status status;
private String submitterLogin;
private Long startedAt;
private long createdAt;
private long updatedAt;

public String getUuid() {
return uuid;
}

public void setUuid(String s) {
checkArgument(s.length() <= 40, "Value is too long for column CE_QUEUE.UUID: %s", s);
this.uuid = s;
}

@CheckForNull
public String getComponentUuid() {
return componentUuid;
}

public void setComponentUuid(@Nullable String s) {
checkArgument(s == null || s.length() <= 40, "Value is too long for column CE_QUEUE.COMPONENT_UUID: %s", s);
this.componentUuid = s;
}

public Status getStatus() {
return status;
}

public void setStatus(Status s) {
this.status = s;
}

public String getTaskType() {
return taskType;
}

public void setTaskType(String s) {
checkArgument(s.length() <= 15, "Value is too long for column CE_QUEUE.TASK_TYPE: %s", s);
this.taskType = s;
}

@CheckForNull
public String getSubmitterLogin() {
return submitterLogin;
}

public void setSubmitterLogin(@Nullable String s) {
checkArgument(s == null || s.length() <= 255, "Value is too long for column CE_QUEUE.SUBMITTER_LOGIN: %s", s);
this.submitterLogin = s;
}

@CheckForNull
public Long getStartedAt() {
return startedAt;
}

public void setStartedAt(@Nullable Long l) {
this.startedAt = l;
}

public long getCreatedAt() {
return createdAt;
}

public void setCreatedAt(long l) {
this.createdAt = l;
}

public long getUpdatedAt() {
return updatedAt;
}

public void setUpdatedAt(long l) {
this.updatedAt = l;
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("uuid", uuid)
.add("taskType", taskType)
.add("componentUuid", componentUuid)
.add("status", status)
.add("submitterLogin", submitterLogin)
.add("startedAt", startedAt)
.add("createdAt", createdAt)
.add("updatedAt", updatedAt)
.toString();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CeQueueDto that = (CeQueueDto) o;
return uuid.equals(that.uuid);

}

@Override
public int hashCode() {
return uuid.hashCode();
}
}

sonar-db/src/main/java/org/sonar/db/compute/AnalysisReportMapper.java → sonar-db/src/main/java/org/sonar/db/ce/CeQueueMapper.java 查看文件

@@ -17,33 +17,37 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.compute;
package org.sonar.db.ce;

import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.ibatis.annotations.Param;

public interface AnalysisReportMapper {
List<AnalysisReportDto> selectByProjectKey(String projectKey);
public interface CeQueueMapper {

List<Long> selectAvailables(
@Param("availableStatus") AnalysisReportDto.Status availableStatus,
@Param("busyStatus") AnalysisReportDto.Status busyStatus);
List<CeQueueDto> selectByComponentUuid(@Param("componentUuid") String componentUuid);

void resetAllToPendingStatus(@Param("updatedAt") long updatedAt);
List<CeQueueDto> selectAllInAscOrder();

List<String> selectEligibleForPeek();

void truncate();
@CheckForNull
CeQueueDto selectByUuid(@Param("uuid") String uuid);

void insert(AnalysisReportDto reportDto);
int countByStatus(@Param("status") CeQueueDto.Status status);

int update(AnalysisReportDto report);
int countAll();

int updateWithBookingReport(@Param("id") Long id, @Param("startedAt") long startedAt,
@Param("availableStatus") AnalysisReportDto.Status availableStatus,
@Param("busyStatus") AnalysisReportDto.Status busyStatus);
void insert(CeQueueDto dto);

AnalysisReportDto selectById(long id);
void resetAllToPendingStatus(@Param("updatedAt") long updatedAt);

void delete(long id);
int updateIfStatus(@Param("uuid") String uuid,
@Param("newStatus") CeQueueDto.Status newStatus,
@Nullable @Param("startedAt") Long startedAt,
@Param("updatedAt") long updatedAt,
@Param("oldStatus") CeQueueDto.Status oldStatus);

List<AnalysisReportDto> selectAll();
void deleteByUuid(@Param("uuid") String uuid);
}

server/sonar-server/src/main/java/org/sonar/server/computation/activity/package-info.java → sonar-db/src/main/java/org/sonar/db/ce/CeTaskTypes.java 查看文件

@@ -17,8 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.ce;

@ParametersAreNonnullByDefault
package org.sonar.server.computation.activity;
public class CeTaskTypes {

import javax.annotation.ParametersAreNonnullByDefault;
private CeTaskTypes() {
// only statics
}

public static final String REPORT = "REPORT";

}

sonar-db/src/main/java/org/sonar/db/compute/package-info.java → sonar-db/src/main/java/org/sonar/db/ce/package-info.java 查看文件

@@ -17,9 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

@ParametersAreNonnullByDefault
package org.sonar.db.compute;
package org.sonar.db.ce;

import javax.annotation.ParametersAreNonnullByDefault;


+ 0
- 107
sonar-db/src/main/java/org/sonar/db/compute/AnalysisReportDao.java 查看文件

@@ -1,107 +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.db.compute;

import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import javax.annotation.CheckForNull;
import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;

import static org.sonar.db.compute.AnalysisReportDto.Status.PENDING;
import static org.sonar.db.compute.AnalysisReportDto.Status.WORKING;

public class AnalysisReportDao implements Dao {

private System2 system2;

public AnalysisReportDao(System2 system2) {
this.system2 = system2;
}

/**
* Update all rows with: STATUS='PENDING', STARTED_AT=NULL, UPDATED_AT={now}
*/
public void resetAllToPendingStatus(DbSession session) {
mapper(session).resetAllToPendingStatus(system2.now());
}

public void truncate(DbSession session) {
mapper(session).truncate();
}

public List<AnalysisReportDto> selectByProjectKey(DbSession session, String projectKey) {
return mapper(session).selectByProjectKey(projectKey);
}

@VisibleForTesting
AnalysisReportDto selectById(DbSession session, long id) {
return mapper(session).selectById(id);
}

@CheckForNull
public AnalysisReportDto pop(DbSession session) {
List<Long> reportIds = mapper(session).selectAvailables(PENDING, WORKING);
if (reportIds.isEmpty()) {
return null;
}

long reportId = reportIds.get(0);
return tryToPop(session, reportId);
}

public long countPending(DbSession session) {
return mapper(session).selectAvailables(PENDING, WORKING).size();
}

@VisibleForTesting
AnalysisReportDto tryToPop(DbSession session, long reportId) {
AnalysisReportMapper mapper = mapper(session);
int nbOfReportBooked = mapper.updateWithBookingReport(reportId, system2.now(), PENDING, WORKING);
if (nbOfReportBooked == 0) {
return null;
}

AnalysisReportDto result = mapper.selectById(reportId);
session.commit();
return result;
}

public List<AnalysisReportDto> selectAll(DbSession session) {
return mapper(session).selectAll();
}

public AnalysisReportDto insert(DbSession session, AnalysisReportDto report) {
report.setCreatedAt(system2.now());
report.setUpdatedAt(system2.now());
mapper(session).insert(report);
return report;
}

public void delete(DbSession session, long id) {
mapper(session).delete(id);
}

private AnalysisReportMapper mapper(DbSession session) {
return session.getMapper(AnalysisReportMapper.class);
}
}

+ 0
- 0
sonar-db/src/main/java/org/sonar/db/compute/AnalysisReportDto.java 查看文件


部分文件因为文件数量过多而无法显示

正在加载...
取消
保存