@@ -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); |
@@ -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); | |||
} | |||
} | |||
@@ -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; |
@@ -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; | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 { | |||
} |
@@ -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())); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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 { | |||
} |
@@ -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, |
@@ -23,5 +23,5 @@ public interface ComputeEngineProcessingQueue { | |||
/** | |||
* Adds a task to the Compute Engine processing queue. | |||
*/ | |||
void addTask(ComputeEngineTask task); | |||
void addTask(CeWorker task); | |||
} |
@@ -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(); |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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 { | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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) | |||
*/ |
@@ -23,6 +23,7 @@ import java.io.File; | |||
import java.util.Objects; | |||
public class BatchReportDirectoryHolderImpl implements MutableBatchReportDirectoryHolder { | |||
private File directory; | |||
@Override |
@@ -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(); | |||
@@ -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); | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
@@ -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} |
@@ -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) | |||
*/ |
@@ -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 { |
@@ -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"; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); |
@@ -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); | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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, |
@@ -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); |
@@ -0,0 +1,4 @@ | |||
{ | |||
"taskId": "TASK_1", | |||
"projectId": "PROJECT_1" | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); |
@@ -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); | |||
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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) { | |||
} | |||
})); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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 |
@@ -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() { |
@@ -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(); | |||
@@ -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); | |||
// } | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 |
@@ -1,16 +0,0 @@ | |||
{ | |||
"reports": [ | |||
{ | |||
"status": "SUCCESS", | |||
"projectName": "POne", | |||
"projectKey": "P1", | |||
"projectUuid": "U1" | |||
}, | |||
{ | |||
"status": "FAILED", | |||
"projectName": "PTwo", | |||
"projectKey": "P2", | |||
"projectUuid": "U2" | |||
} | |||
] | |||
} |
@@ -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" | |||
} | |||
] | |||
} |
@@ -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 |
@@ -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 | |||
@@ -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 | |||
@@ -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 | |||
@@ -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 | |||
@@ -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) { |
@@ -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); | |||
@@ -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. |
@@ -19,5 +19,8 @@ | |||
*/ | |||
package org.sonar.db; | |||
/** | |||
* All implementations must be declared in {@link DaoModule} | |||
*/ | |||
public interface Dao { | |||
} |
@@ -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, |
@@ -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() { |
@@ -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); |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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"; | |||
} |
@@ -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; | |||
@@ -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); | |||
} | |||
} |