From: Sébastien Lesaint Date: Tue, 26 May 2015 15:01:40 +0000 (+0200) Subject: SONAR-6589 remove ComputationContainer (and use only CEContainer) X-Git-Tag: 5.2-RC1~1728 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=71aa31687b92b01207931c075613b6608abdb120;p=sonarqube.git SONAR-6589 remove ComputationContainer (and use only CEContainer) --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java deleted file mode 100644 index e8058874ca0..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContainer.java +++ /dev/null @@ -1,97 +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.util.Arrays; -import java.util.List; -import org.sonar.core.issue.db.UpdateConflictResolver; -import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.activity.CEActivityManager; -import org.sonar.server.computation.batch.CEBatchReportReader; -import org.sonar.server.computation.batch.ReportExtractor; -import org.sonar.server.computation.component.DbComponentsRefCache; -import org.sonar.server.computation.component.ProjectSettingsRepository; -import org.sonar.server.computation.container.CEContainer; -import org.sonar.server.computation.issue.IssueCache; -import org.sonar.server.computation.issue.IssueComputation; -import org.sonar.server.computation.issue.RuleCache; -import org.sonar.server.computation.issue.RuleCacheLoader; -import org.sonar.server.computation.issue.ScmAccountCache; -import org.sonar.server.computation.issue.ScmAccountCacheLoader; -import org.sonar.server.computation.issue.SourceLinesCache; -import org.sonar.server.computation.language.PlatformLanguageRepository; -import org.sonar.server.computation.measure.MetricCache; -import org.sonar.server.computation.step.ComputationSteps; -import org.sonar.server.platform.Platform; -import org.sonar.server.view.index.ViewIndex; - -public class ComputationContainer { - - /** - * List of all objects to be injected in the picocontainer dedicated to computation stack. - * Does not contain the steps declared in {@link org.sonar.server.computation.step.ComputationSteps#orderedStepClasses()}. - */ - static List componentClasses() { - return Arrays.asList( - CEActivityManager.class, - ReportExtractor.class, - CEBatchReportReader.class, - - // repositories - PlatformLanguageRepository.class, - ProjectSettingsRepository.class, - - // component caches - DbComponentsRefCache.class, - - // issues - ScmAccountCacheLoader.class, - ScmAccountCache.class, - SourceLinesCache.class, - IssueComputation.class, - RuleCache.class, - RuleCacheLoader.class, - IssueCache.class, - MetricCache.class, - UpdateConflictResolver.class, - - // views - ViewIndex.class); - } - - public void execute(ReportQueue.Item item) { - ComponentContainer container = Platform.getInstance().getContainer(); - - ComponentContainer ceContainer = new CEContainer(container); - ceContainer.add(ceContainer); - ceContainer.add(item); - ceContainer.addSingletons(componentClasses()); - ceContainer.addSingletons(ComputationSteps.orderedStepClasses()); - try { - ceContainer.getComponentByType(ComputationService.class).process(); - } finally { - ceContainer.stopComponents(); - // TODO not possible to have multiple children -> will be - // a problem when we will have multiple concurrent computation workers - container.removeChild(); - } - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java index 5646d2f6efb..a984fe2c1bd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java @@ -25,7 +25,7 @@ import org.sonar.api.utils.System2; 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.activity.CEActivityManager; +import org.sonar.server.computation.activity.ActivityManager; import org.sonar.server.computation.batch.BatchReportReader; import org.sonar.server.computation.component.ComponentTreeBuilders; import org.sonar.server.computation.language.LanguageRepository; @@ -44,12 +44,12 @@ public class ComputationService { private final ReportQueue.Item item; private final ComputationSteps steps; private final BatchReportReader reportReader; - private final CEActivityManager activityManager; + private final ActivityManager activityManager; private final System2 system; private final DbClient dbClient; private final LanguageRepository languageRepository; - public ComputationService(ReportQueue.Item item, ComputationSteps steps, CEActivityManager activityManager, System2 system, + public ComputationService(ReportQueue.Item item, ComputationSteps steps, ActivityManager activityManager, System2 system, BatchReportReader reportReader, DbClient dbClient, LanguageRepository languageRepository) { this.item = item; this.steps = steps; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThread.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThread.java index d60cc0b7f62..aa621b317e8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThread.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThread.java @@ -20,9 +20,11 @@ package org.sonar.server.computation; -import com.google.common.annotations.VisibleForTesting; 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.ComputeEngineContainer; +import org.sonar.server.computation.container.ContainerFactory; /** * This thread pops a report from the queue and integrate it. @@ -32,17 +34,13 @@ public class ComputationThread implements Runnable { private static final Logger LOG = Loggers.get(ComputationThread.class); private final ReportQueue queue; - private final ComputationContainer container; + private final ComponentContainer sqContainer; + private final ContainerFactory containerFactory; - public ComputationThread(ReportQueue queue) { + public ComputationThread(ReportQueue queue, ComponentContainer sqContainer, ContainerFactory containerFactory) { this.queue = queue; - this.container = new ComputationContainer(); - } - - @VisibleForTesting - ComputationThread(ReportQueue queue, ComputationContainer container) { - this.queue = queue; - this.container = container; + this.sqContainer = sqContainer; + this.containerFactory = containerFactory; } @Override @@ -53,15 +51,20 @@ public class ComputationThread implements Runnable { } catch (Exception e) { LOG.error("Failed to pop the queue of analysis reports", e); } - if (item != null) { - try { - container.execute(item); - } catch (Throwable e) { - LOG.error(String.format( - "Failed to process analysis report %d of project %s", item.dto.getId(), item.dto.getProjectKey()), e); - } finally { - removeSilentlyFromQueue(item); - } + if (item == null) { + return; + } + + ComputeEngineContainer computeEngineContainer = containerFactory.create(sqContainer, item); + try { + computeEngineContainer.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); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThreadLauncher.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThreadLauncher.java index c91d3c88fef..01f43c73ef3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThreadLauncher.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationThreadLauncher.java @@ -22,29 +22,35 @@ package org.sonar.server.computation; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.picocontainer.Startable; -import org.sonar.api.platform.Server; -import org.sonar.api.platform.ServerStartHandler; - import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import org.picocontainer.Startable; +import org.sonar.api.platform.Server; +import org.sonar.api.platform.ServerStartHandler; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.container.ContainerFactory; +import org.sonar.server.computation.container.ContainerFactoryImpl; public class ComputationThreadLauncher implements Startable, ServerStartHandler { public static final String THREAD_NAME_PREFIX = "computation-"; private final ReportQueue queue; + private final ComponentContainer sqContainer; private final ScheduledExecutorService executorService; + private final ContainerFactory containerFactory; private final long delayBetweenTasks; private final long delayForFirstStart; private final TimeUnit timeUnit; - public ComputationThreadLauncher(ReportQueue queue) { + public ComputationThreadLauncher(ReportQueue queue, ComponentContainer sqContainer) { this.queue = queue; + this.sqContainer = sqContainer; this.executorService = Executors.newSingleThreadScheduledExecutor(newThreadFactory()); + this.containerFactory = new ContainerFactoryImpl(); this.delayBetweenTasks = 10; this.delayForFirstStart = 0; @@ -52,8 +58,11 @@ public class ComputationThreadLauncher implements Startable, ServerStartHandler } @VisibleForTesting - ComputationThreadLauncher(ReportQueue queue, long delayForFirstStart, long delayBetweenTasks, TimeUnit timeUnit) { + ComputationThreadLauncher(ReportQueue queue, ComponentContainer sqContainer, ContainerFactory containerFactory, + long delayForFirstStart, long delayBetweenTasks, TimeUnit timeUnit) { this.queue = queue; + this.sqContainer = sqContainer; + this.containerFactory = containerFactory; this.executorService = Executors.newSingleThreadScheduledExecutor(newThreadFactory()); this.delayBetweenTasks = delayBetweenTasks; @@ -72,12 +81,12 @@ public class ComputationThreadLauncher implements Startable, ServerStartHandler } public void startAnalysisTaskNow() { - executorService.execute(new ComputationThread(queue)); + executorService.execute(new ComputationThread(queue, sqContainer, containerFactory)); } @Override public void onServerStart(Server server) { - executorService.scheduleAtFixedRate(new ComputationThread(queue), delayForFirstStart, delayBetweenTasks, timeUnit); + executorService.scheduleAtFixedRate(new ComputationThread(queue, sqContainer, containerFactory), delayForFirstStart, delayBetweenTasks, timeUnit); } private ThreadFactory newThreadFactory() { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java b/server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java new file mode 100644 index 00000000000..d3aa9de201e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/activity/ActivityManager.java @@ -0,0 +1,72 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.activity; + +import javax.annotation.CheckForNull; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.computation.db.AnalysisReportDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.activity.Activity; +import org.sonar.server.activity.ActivityService; +import org.sonar.server.db.DbClient; + +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) { + ComponentDto project = 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 (project != null) { + activity + .setData("projectName", project.name()) + .setData("projectUuid", project.uuid()); + } + activityService.save(activity); + } + + @CheckForNull + private ComponentDto loadProject(String projectKey) { + DbSession session = dbClient.openSession(false); + try { + return dbClient.componentDao().selectNullableByKey(session, projectKey); + } finally { + MyBatis.closeQuietly(session); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/CEActivityManager.java b/server/sonar-server/src/main/java/org/sonar/server/computation/activity/CEActivityManager.java deleted file mode 100644 index c77700a7ab5..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/activity/CEActivityManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.computation.activity; - -import javax.annotation.CheckForNull; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.computation.db.AnalysisReportDto; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; -import org.sonar.server.activity.Activity; -import org.sonar.server.activity.ActivityService; -import org.sonar.server.db.DbClient; - -import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe; -import static org.sonar.api.utils.DateUtils.longToDate; - -public class CEActivityManager { - private final ActivityService activityService; - private final DbClient dbClient; - - public CEActivityManager(ActivityService activityService, DbClient dbClient) { - this.activityService = activityService; - this.dbClient = dbClient; - } - - public void saveActivity(AnalysisReportDto report) { - ComponentDto project = 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 (project != null) { - activity - .setData("projectName", project.name()) - .setData("projectUuid", project.uuid()); - } - activityService.save(activity); - } - - @CheckForNull - private ComponentDto loadProject(String projectKey) { - DbSession session = dbClient.openSession(false); - try { - return dbClient.componentDao().selectNullableByKey(session, projectKey); - } finally { - MyBatis.closeQuietly(session); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java new file mode 100644 index 00000000000..7799436b9b2 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java @@ -0,0 +1,213 @@ +/* + * 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.batch; + +import com.google.common.base.Throwables; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Parser; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.NoSuchElementException; +import javax.annotation.CheckForNull; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.computation.ReportQueue; +import org.sonar.server.util.CloseableIterator; + +public class BatchReportReaderImpl implements BatchReportReader { + private final org.sonar.batch.protocol.output.BatchReportReader delegate; + + public BatchReportReaderImpl(ReportExtractor reportExtractor, ReportQueue.Item item) { + this.delegate = new org.sonar.batch.protocol.output.BatchReportReader(reportExtractor.extractReportInDir(item)); + } + + @Override + public BatchReport.Metadata readMetadata() { + return delegate.readMetadata(); + } + + @Override + public List readComponentMeasures(int componentRef) { + return delegate.readComponentMeasures(componentRef); + } + + @Override + @CheckForNull + public BatchReport.Changesets readChangesets(int componentRef) { + return delegate.readChangesets(componentRef); + } + + @Override + public BatchReport.Component readComponent(int componentRef) { + return delegate.readComponent(componentRef); + } + + @Override + public List readComponentIssues(int componentRef) { + return delegate.readComponentIssues(componentRef); + } + + @Override + public BatchReport.Issues readDeletedComponentIssues(int deletedComponentRef) { + return delegate.readDeletedComponentIssues(deletedComponentRef); + } + + @Override + public List readComponentDuplications(int componentRef) { + return delegate.readComponentDuplications(componentRef); + } + + @Override + public List readComponentSymbols(int componentRef) { + return delegate.readComponentSymbols(componentRef); + } + + @Override + @CheckForNull + public CloseableIterator readComponentSyntaxHighlighting(int fileRef) { + File file = delegate.readComponentSyntaxHighlighting(fileRef); + if (file == null) { + return CloseableIterator.emptyCloseableIterator(); + } + + try { + return new ParserCloseableIterator<>(BatchReport.SyntaxHighlighting.PARSER, FileUtils.openInputStream(file)); + } catch (IOException e) { + Throwables.propagate(e); + // actually never reached + return CloseableIterator.emptyCloseableIterator(); + } + } + + @Override + public CloseableIterator readComponentCoverage(int fileRef) { + File file = delegate.readComponentCoverage(fileRef); + if (file == null) { + return CloseableIterator.emptyCloseableIterator(); + } + + try { + return new ParserCloseableIterator<>(BatchReport.Coverage.PARSER, FileUtils.openInputStream(file)); + } catch (IOException e) { + Throwables.propagate(e); + // actually never reached + return CloseableIterator.emptyCloseableIterator(); + } + } + + @Override + public CloseableIterator readFileSource(int fileRef) { + File file = delegate.readFileSource(fileRef); + if (file == null) { + throw new IllegalStateException("Unable to find source for file #" + fileRef + ". File does not exist: " + file); + } + + try { + final LineIterator lineIterator = IOUtils.lineIterator(FileUtils.openInputStream(file), StandardCharsets.UTF_8); + return new CloseableIterator() { + @Override + public boolean hasNext() { + return lineIterator.hasNext(); + } + + @Override + public String next() { + return lineIterator.next(); + } + + @Override + protected String doNext() { + // never called anyway + throw new NoSuchElementException("Empty closeable Iterator has no element"); + } + + @Override + protected void doClose() throws Exception { + lineIterator.close(); + } + }; + } catch (IOException e) { + throw new IllegalStateException("Fail to traverse file: " + file, e); + } + } + + @Override + public CloseableIterator readTests(int testFileRef) { + File file = delegate.readTests(testFileRef); + if (file == null) { + return CloseableIterator.emptyCloseableIterator(); + } + + try { + return new ParserCloseableIterator<>(BatchReport.Test.PARSER, FileUtils.openInputStream(file)); + } catch (IOException e) { + Throwables.propagate(e); + // actually never reached + return CloseableIterator.emptyCloseableIterator(); + } + } + + @Override + public CloseableIterator readCoverageDetails(int testFileRef) { + File file = delegate.readCoverageDetails(testFileRef); + if (file == null) { + return CloseableIterator.emptyCloseableIterator(); + } + + try { + return new ParserCloseableIterator<>(BatchReport.CoverageDetail.PARSER, FileUtils.openInputStream(file)); + } catch (IOException e) { + Throwables.propagate(e); + // actually never reached + return CloseableIterator.emptyCloseableIterator(); + } + } + + private static class ParserCloseableIterator extends CloseableIterator { + private final Parser parser; + private final FileInputStream fileInputStream; + + public ParserCloseableIterator(Parser parser, FileInputStream fileInputStream) { + this.parser = parser; + this.fileInputStream = fileInputStream; + } + + @Override + protected T doNext() { + try { + return parser.parseDelimitedFrom(fileInputStream); + } catch (InvalidProtocolBufferException e) { + Throwables.propagate(e); + // actually never reached + return null; + } + } + + @Override + protected void doClose() throws Exception { + fileInputStream.close(); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/CEBatchReportReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/CEBatchReportReader.java deleted file mode 100644 index 810443eb5da..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/CEBatchReportReader.java +++ /dev/null @@ -1,206 +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.batch; - -import com.google.common.base.Throwables; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.Parser; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import javax.annotation.CheckForNull; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.LineIterator; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.util.CloseableIterator; - -public class CEBatchReportReader implements BatchReportReader { - private final org.sonar.batch.protocol.output.BatchReportReader delegate; - - public CEBatchReportReader(ReportExtractor reportExtractor, ReportQueue.Item item) { - this.delegate = new org.sonar.batch.protocol.output.BatchReportReader(reportExtractor.extractReportInDir(item)); - } - - @Override - public BatchReport.Metadata readMetadata() { - return delegate.readMetadata(); - } - - @Override - public List readComponentMeasures(int componentRef) { - return delegate.readComponentMeasures(componentRef); - } - - @Override - @CheckForNull - public BatchReport.Changesets readChangesets(int componentRef) { - return delegate.readChangesets(componentRef); - } - - @Override - public BatchReport.Component readComponent(int componentRef) { - return delegate.readComponent(componentRef); - } - - @Override - public List readComponentIssues(int componentRef) { - return delegate.readComponentIssues(componentRef); - } - - @Override - public BatchReport.Issues readDeletedComponentIssues(int deletedComponentRef) { - return delegate.readDeletedComponentIssues(deletedComponentRef); - } - - @Override - public List readComponentDuplications(int componentRef) { - return delegate.readComponentDuplications(componentRef); - } - - @Override - public List readComponentSymbols(int componentRef) { - return delegate.readComponentSymbols(componentRef); - } - - @Override - @CheckForNull - public CloseableIterator readComponentSyntaxHighlighting(int fileRef) { - File file = delegate.readComponentSyntaxHighlighting(fileRef); - if (file == null) { - return CloseableIterator.emptyCloseableIterator(); - } - - try { - return new ParserCloseableIterator<>(BatchReport.SyntaxHighlighting.PARSER, FileUtils.openInputStream(file)); - } catch (IOException e) { - Throwables.propagate(e); - // actually never reached - return CloseableIterator.emptyCloseableIterator(); - } - } - - @Override - public CloseableIterator readComponentCoverage(int fileRef) { - File file = delegate.readComponentCoverage(fileRef); - if (file == null) { - return CloseableIterator.emptyCloseableIterator(); - } - - try { - return new ParserCloseableIterator<>(BatchReport.Coverage.PARSER, FileUtils.openInputStream(file)); - } catch (IOException e) { - Throwables.propagate(e); - // actually never reached - return CloseableIterator.emptyCloseableIterator(); - } - } - - @Override - public CloseableIterator readFileSource(int fileRef) { - File file = delegate.readFileSource(fileRef); - if (file == null) { - throw new IllegalStateException("Unable to find source for file #" + fileRef + ". File does not exist: " + file); - } - - try { - final LineIterator lineIterator = IOUtils.lineIterator(FileUtils.openInputStream(file), StandardCharsets.UTF_8); - return new CloseableIterator() { - @Override - public boolean hasNext() { - return lineIterator.hasNext(); - } - - @Override - protected String doNext() { - return lineIterator.next(); - } - - @Override - protected void doClose() throws Exception { - lineIterator.close(); - } - }; - } catch (IOException e) { - throw new IllegalStateException("Fail to traverse file: " + file, e); - } - } - - @Override - public CloseableIterator readTests(int testFileRef) { - File file = delegate.readTests(testFileRef); - if (file == null) { - return CloseableIterator.emptyCloseableIterator(); - } - - try { - return new ParserCloseableIterator<>(BatchReport.Test.PARSER, FileUtils.openInputStream(file)); - } catch (IOException e) { - Throwables.propagate(e); - // actually never reached - return CloseableIterator.emptyCloseableIterator(); - } - } - - @Override - public CloseableIterator readCoverageDetails(int testFileRef) { - File file = delegate.readCoverageDetails(testFileRef); - if (file == null) { - return CloseableIterator.emptyCloseableIterator(); - } - - try { - return new ParserCloseableIterator<>(BatchReport.CoverageDetail.PARSER, FileUtils.openInputStream(file)); - } catch (IOException e) { - Throwables.propagate(e); - // actually never reached - return CloseableIterator.emptyCloseableIterator(); - } - } - - private static class ParserCloseableIterator extends CloseableIterator { - private final Parser parser; - private final FileInputStream fileInputStream; - - public ParserCloseableIterator(Parser parser, FileInputStream fileInputStream) { - this.parser = parser; - this.fileInputStream = fileInputStream; - } - - @Override - protected T doNext() { - try { - return parser.parseDelimitedFrom(fileInputStream); - } catch (InvalidProtocolBufferException e) { - Throwables.propagate(e); - // actually never reached - return null; - } - } - - @Override - protected void doClose() throws Exception { - fileInputStream.close(); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/CEContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/CEContainer.java deleted file mode 100644 index 770376b13f8..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/CEContainer.java +++ /dev/null @@ -1,55 +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.container; - -import org.picocontainer.ComponentAdapter; -import org.picocontainer.DefaultPicoContainer; -import org.picocontainer.MutablePicoContainer; -import org.picocontainer.behaviors.OptInCaching; -import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; -import org.picocontainer.monitors.NullComponentMonitor; -import org.sonar.api.utils.log.Loggers; -import org.sonar.api.utils.log.Profiler; -import org.sonar.core.platform.ComponentContainer; - -public class CEContainer extends ComponentContainer { - public CEContainer(ComponentContainer parent) { - super(createContainer(parent), parent); - } - - private static MutablePicoContainer createContainer(ComponentContainer parent) { - ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "close") { - @Override - public boolean isLazy(ComponentAdapter adapter) { - return true; - } - - @Override - public void start(Object component) { - Profiler profiler = Profiler.createIfTrace(Loggers.get(ComponentContainer.class)); - profiler.start(); - super.start(component); - profiler.stopTrace(component.getClass().getCanonicalName() + " started"); - } - }; - - return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, parent.getPicoContainer()); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java new file mode 100644 index 00000000000..6a0aa739af6 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainer.java @@ -0,0 +1,48 @@ +/* + * 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.container; + +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.ReportQueue.Item; +import org.sonar.server.computation.step.ComputationStep; + +/** + * The Compute Engine container. Created for a specific parent {@link ComponentContainer} and a specific {@link Item}. + */ +public interface ComputeEngineContainer { + Item getItem(); + + ComponentContainer getParent(); + + /** + * Process the current {@link Item} + */ + void process(); + + /** + * Clean's up resources after process has been called and has returned. + */ + void cleanup(); + + /** + */ + T getStep(Class type); + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java new file mode 100644 index 00000000000..3d35d9e7b19 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java @@ -0,0 +1,158 @@ +/* + * 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.container; + +import java.util.Arrays; +import java.util.List; +import org.picocontainer.ComponentAdapter; +import org.picocontainer.DefaultPicoContainer; +import org.picocontainer.MutablePicoContainer; +import org.picocontainer.behaviors.OptInCaching; +import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; +import org.picocontainer.monitors.NullComponentMonitor; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.sonar.core.issue.db.UpdateConflictResolver; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.ComputationService; +import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.activity.ActivityManager; +import org.sonar.server.computation.batch.BatchReportReaderImpl; +import org.sonar.server.computation.batch.ReportExtractor; +import org.sonar.server.computation.component.DbComponentsRefCache; +import org.sonar.server.computation.component.ProjectSettingsRepository; +import org.sonar.server.computation.issue.IssueCache; +import org.sonar.server.computation.issue.IssueComputation; +import org.sonar.server.computation.issue.RuleCache; +import org.sonar.server.computation.issue.RuleCacheLoader; +import org.sonar.server.computation.issue.ScmAccountCache; +import org.sonar.server.computation.issue.ScmAccountCacheLoader; +import org.sonar.server.computation.issue.SourceLinesCache; +import org.sonar.server.computation.language.PlatformLanguageRepository; +import org.sonar.server.computation.measure.MetricCache; +import org.sonar.server.computation.step.ComputationStep; +import org.sonar.server.computation.step.ComputationSteps; +import org.sonar.server.view.index.ViewIndex; + +import static java.util.Objects.requireNonNull; + +public class ComputeEngineContainerImpl extends ComponentContainer implements ComputeEngineContainer { + private final ReportQueue.Item item; + private final ComputationSteps steps; + + public ComputeEngineContainerImpl(ComponentContainer parent, ReportQueue.Item item) { + super(createContainer(requireNonNull(parent))); + + this.item = item; + this.steps = new ComputationSteps(this); + + populateContainer(requireNonNull(item)); + } + + @Override + public ReportQueue.Item getItem() { + return item; + } + + private void populateContainer(ReportQueue.Item item) { + add(item); + add(steps); + addSingletons(componentClasses()); + addSingletons(steps.orderedStepClasses()); + } + + /** + * Creates a PicContainer which extends the specified ComponentContainer but is not referenced in return + * and lazily starts its components. + */ + private static MutablePicoContainer createContainer(ComponentContainer parent) { + ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "close") { + @Override + public boolean isLazy(ComponentAdapter adapter) { + return true; + } + + @Override + public void start(Object component) { + Profiler profiler = Profiler.createIfTrace(Loggers.get(ComponentContainer.class)); + profiler.start(); + super.start(component); + profiler.stopTrace(component.getClass().getCanonicalName() + " started"); + } + }; + + return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, parent.getPicoContainer()); + } + + /** + * List of all objects to be injected in the picocontainer dedicated to computation stack. + * Does not contain the steps declared in {@link org.sonar.server.computation.step.ComputationSteps#orderedStepClasses()}. + */ + private static List componentClasses() { + return Arrays.asList( + ActivityManager.class, + ReportExtractor.class, + BatchReportReaderImpl.class, + + // repositories + PlatformLanguageRepository.class, + ProjectSettingsRepository.class, + + // component caches + DbComponentsRefCache.class, + + // issues + ScmAccountCacheLoader.class, + ScmAccountCache.class, + SourceLinesCache.class, + IssueComputation.class, + RuleCache.class, + RuleCacheLoader.class, + IssueCache.class, + MetricCache.class, + UpdateConflictResolver.class, + + // views + ViewIndex.class, + + // ComputationService + ComputationService.class + ); + } + + public void process() { + // calls the first + getComponentByType(ComputationService.class).process(); + } + + public void cleanup() { + stopComponents(); + } + + @Override + public T getStep(Class type) { + return getComponentByType(type); + } + + @Override + public String toString() { + return "ComputeEngineContainerImpl"; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java new file mode 100644 index 00000000000..7787a6c69e6 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactory.java @@ -0,0 +1,31 @@ +/* + * 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.container; + +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.ReportQueue; + +/** + * Compute + */ +public interface ContainerFactory { + + ComputeEngineContainer create(ComponentContainer parent, ReportQueue.Item item); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java new file mode 100644 index 00000000000..ce615c39841 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ContainerFactoryImpl.java @@ -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. + */ +package org.sonar.server.computation.container; + +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.ReportQueue; + +public class ContainerFactoryImpl implements ContainerFactory { + @Override + public ComputeEngineContainer create(ComponentContainer parent, ReportQueue.Item item) { + return new ComputeEngineContainerImpl(parent, item); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java index 7360cd2b20c..831299548c6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java @@ -23,7 +23,7 @@ import com.google.common.base.Function; import com.google.common.collect.Iterables; import java.util.Arrays; import java.util.List; -import org.sonar.server.computation.container.CEContainer; +import org.sonar.server.computation.container.ComputeEngineContainer; /** * Ordered list of steps to be executed @@ -34,7 +34,7 @@ public class ComputationSteps { * List of all {@link org.sonar.server.computation.step.ComputationStep}, * ordered by execution sequence. */ - public static List> orderedStepClasses() { + public List> orderedStepClasses() { return Arrays.asList( PopulateComponentsUuidAndKeyStep.class, ValidateProjectStep.class, @@ -71,17 +71,17 @@ public class ComputationSteps { SendIssueNotificationsStep.class); } - private final CEContainer ceContainer; + private final ComputeEngineContainer computeEngineContainer; - public ComputationSteps(CEContainer ceContainer) { - this.ceContainer = ceContainer; + public ComputationSteps(ComputeEngineContainer computeEngineContainer) { + this.computeEngineContainer = computeEngineContainer; } public Iterable instances() { return Iterables.transform(orderedStepClasses(), new Function, ComputationStep>() { @Override public ComputationStep apply(Class input) { - ComputationStep computationStepType = ceContainer.getComponentByType(input); + ComputationStep computationStepType = computeEngineContainer.getStep(input); if (computationStepType == null) { throw new IllegalStateException(String.format("Component not found: %s", input)); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContainerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContainerTest.java deleted file mode 100644 index 43b640ddbce..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationContainerTest.java +++ /dev/null @@ -1,32 +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.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ComputationContainerTest { - - @Test - public void componentClasses() { - assertThat(ComputationContainer.componentClasses()).isNotEmpty(); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java index eaeaa3dc09f..41155fee14d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java @@ -35,7 +35,7 @@ import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.computation.db.AnalysisReportDto.Status; -import org.sonar.server.computation.activity.CEActivityManager; +import org.sonar.server.computation.activity.ActivityManager; import org.sonar.server.computation.batch.BatchReportReaderRule; import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.computation.step.ComputationStep; @@ -60,7 +60,7 @@ public class ComputationServiceTest { ComputationStep projectStep1 = mockStep(); ComputationStep projectStep2 = mockStep(); ComputationSteps steps = mock(ComputationSteps.class); - CEActivityManager activityManager = mock(CEActivityManager.class); + ActivityManager activityManager = mock(ActivityManager.class); System2 system = mock(System2.class); AnalysisReportDto dto = AnalysisReportDto.newForTests(1L).setProjectKey("P1").setUuid("U1").setStatus(Status.PENDING); ComputationService sut; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadLauncherTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadLauncherTest.java index 09f80a61872..b12880951c6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadLauncherTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadLauncherTest.java @@ -20,6 +20,7 @@ package org.sonar.server.computation; +import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -28,10 +29,12 @@ import org.junit.rules.DisableOnDebug; import org.junit.rules.TestRule; import org.junit.rules.Timeout; import org.sonar.api.platform.Server; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.container.ContainerFactory; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class ComputationThreadLauncherTest { @@ -40,10 +43,14 @@ public class ComputationThreadLauncherTest { ComputationThreadLauncher sut; ReportQueue queue; + ComponentContainer componentContainer; + ContainerFactory containerFactory; @Before public void before() { this.queue = mock(ReportQueue.class); + this.componentContainer = mock(ComponentContainer.class); + this.containerFactory = mock(ContainerFactory.class); } @After @@ -53,7 +60,7 @@ public class ComputationThreadLauncherTest { @Test public void call_findAndBook_when_launching_a_recurrent_task() throws Exception { - sut = new ComputationThreadLauncher(queue, 0, 1, TimeUnit.MILLISECONDS); + sut = new ComputationThreadLauncher(queue, componentContainer, containerFactory, 0, 1, TimeUnit.MILLISECONDS); sut.onServerStart(mock(Server.class)); @@ -64,7 +71,7 @@ public class ComputationThreadLauncherTest { @Test public void call_findAndBook_when_executing_task_immediately() throws Exception { - sut = new ComputationThreadLauncher(queue, 1, 1, TimeUnit.HOURS); + sut = new ComputationThreadLauncher(queue, componentContainer, containerFactory, 1, 1, TimeUnit.HOURS); sut.start(); sut.startAnalysisTaskNow(); @@ -76,7 +83,7 @@ public class ComputationThreadLauncherTest { @Test public void test_real_constructor() throws Exception { - sut = new ComputationThreadLauncher(queue); + sut = new ComputationThreadLauncher(queue, componentContainer); sut.start(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadTest.java index f3d0993db63..00d3652e08a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadTest.java @@ -20,28 +20,35 @@ package org.sonar.server.computation; +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.computation.db.AnalysisReportDto; - -import java.io.IOException; +import org.sonar.core.platform.ComponentContainer; +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.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; public class ComputationThreadTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - @Rule public LogTester logTester = new LogTester(); - ComputationContainer container = mock(ComputationContainer.class); ReportQueue queue = mock(ReportQueue.class); - ComputationThread sut = new ComputationThread(queue, container); + ComponentContainer componentContainer = mock(ComponentContainer.class); + ContainerFactory containerFactory = mock(ContainerFactory.class); + ComputationThread sut = new ComputationThread(queue, componentContainer, containerFactory); @Test public void do_nothing_if_queue_empty() { @@ -50,19 +57,21 @@ public class ComputationThreadTest { sut.run(); verify(queue).pop(); - verifyZeroInteractions(container); + verifyZeroInteractions(containerFactory); } @Test public void pop_queue_and_integrate_report() throws IOException { AnalysisReportDto report = AnalysisReportDto.newForTests(1L); ReportQueue.Item item = new ReportQueue.Item(report, temp.newFile()); + when(queue.pop()).thenReturn(item); + when(containerFactory.create(componentContainer, item)).thenReturn(mock(ComputeEngineContainer.class)); sut.run(); verify(queue).pop(); - verify(container).execute(item); + verify(containerFactory).create(componentContainer, item); } @Test @@ -74,20 +83,10 @@ public class ComputationThreadTest { assertThat(logTester.logs()).contains("Failed to pop the queue of analysis reports"); } - @Test - public void handle_error_during_integration() throws Exception { - AnalysisReportDto report = AnalysisReportDto.newForTests(1L).setProjectKey("P1"); - ReportQueue.Item item = new ReportQueue.Item(report, temp.newFile()); - when(queue.pop()).thenReturn(item); - doThrow(new IllegalStateException("pb")).when(container).execute(item); - - sut.run(); - - assertThat(logTester.logs()).contains("Failed to process analysis report 1 of project P1"); - } - @Test public void handle_error_during_removal_from_queue() throws Exception { + when(containerFactory.create(any(ComponentContainer.class), any(ReportQueue.Item.class))).thenReturn(mock(ComputeEngineContainer.class)); + AnalysisReportDto report = AnalysisReportDto.newForTests(1L).setProjectKey("P1"); ReportQueue.Item item = new ReportQueue.Item(report, temp.newFile()); when(queue.pop()).thenReturn(item); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java new file mode 100644 index 00000000000..4a00b613b68 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/activity/ActivityManagerTest.java @@ -0,0 +1,86 @@ +/* + * 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.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.mockito.ArgumentCaptor; +import org.sonar.core.computation.db.AnalysisReportDto; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.activity.Activity; +import org.sonar.server.activity.ActivityService; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.db.DbClient; +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 { + @ClassRule + public static DbTester dbTester = new DbTester(); + + ArgumentCaptor 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() throws Exception { + dbTester.truncateTables(); + DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao()); + + underTest = new ActivityManager(activityService, dbClient); + } + + @Test + public void process_existing_project() throws Exception { + 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() throws Exception { + underTest.saveActivity(reportDto); + + // execute only the steps supporting the project qualifier + verify(activityService).save(activityArgumentCaptor.capture()); + + assertThat(activityArgumentCaptor.getValue().getType()).isEqualTo(Activity.Type.ANALYSIS_REPORT); + assertThat(activityArgumentCaptor.getValue().getAction()).isEqualTo("LOG_ANALYSIS_REPORT"); + assertThat(activityArgumentCaptor.getValue().getData()).containsEntry("projectKey", "P1"); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/activity/CEActivityManagerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/activity/CEActivityManagerTest.java deleted file mode 100644 index 82b267fcf14..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/activity/CEActivityManagerTest.java +++ /dev/null @@ -1,86 +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.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.mockito.ArgumentCaptor; -import org.sonar.core.computation.db.AnalysisReportDto; -import org.sonar.core.persistence.DbTester; -import org.sonar.server.activity.Activity; -import org.sonar.server.activity.ActivityService; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.db.DbClient; -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 CEActivityManagerTest { - @ClassRule - public static DbTester dbTester = new DbTester(); - - ArgumentCaptor activityArgumentCaptor = ArgumentCaptor.forClass(Activity.class); - - AnalysisReportDto reportDto = AnalysisReportDto.newForTests(1L).setProjectKey("P1").setUuid("U1").setStatus(AnalysisReportDto.Status.PENDING); - - ActivityService activityService = mock(ActivityService.class); - CEActivityManager underTest; - - @Before - public void setup() throws Exception { - dbTester.truncateTables(); - DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao()); - - underTest = new CEActivityManager(activityService, dbClient); - } - - @Test - public void process_existing_project() throws Exception { - 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() throws Exception { - underTest.saveActivity(reportDto); - - // execute only the steps supporting the project qualifier - verify(activityService).save(activityArgumentCaptor.capture()); - - assertThat(activityArgumentCaptor.getValue().getType()).isEqualTo(Activity.Type.ANALYSIS_REPORT); - assertThat(activityArgumentCaptor.getValue().getAction()).isEqualTo("LOG_ANALYSIS_REPORT"); - assertThat(activityArgumentCaptor.getValue().getData()).containsEntry("projectKey", "P1"); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java new file mode 100644 index 00000000000..389e3d3d538 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/container/ComputeEngineContainerImplTest.java @@ -0,0 +1,109 @@ +/* + * 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.container; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import java.util.Set; +import org.junit.Test; +import org.picocontainer.ComponentAdapter; +import org.reflections.Reflections; +import org.sonar.core.platform.ComponentContainer; +import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.step.ComputationStep; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class ComputeEngineContainerImplTest { + @Test(expected = NullPointerException.class) + public void constructor_fails_fast_on_null_container() { + new ComputeEngineContainerImpl(null, mock(ReportQueue.Item.class)); + } + @Test(expected = NullPointerException.class) + public void constructor_fails_fast_on_null_item() { + new ComputeEngineContainerImpl(new ComponentContainer(), null); + } + + @Test + public void ce_container_is_not_child_of_specified_container() { + ComponentContainer parent = new ComponentContainer(); + ComputeEngineContainerImpl ceContainer = new ComputeEngineContainerImpl(parent, mock(ReportQueue.Item.class)); + + assertThat(parent.getChild()).isNull(); + assertThat(parent.getPicoContainer().removeChildContainer(ceContainer.getPicoContainer())).isFalse(); + } + + @Test + public void all_steps_from_package_step_are_present_in_container() { + ComputeEngineContainerImpl ceContainer = new ComputeEngineContainerImpl(new ComponentContainer(), mock(ReportQueue.Item.class)); + + Set stepsCanonicalNames = retrieveStepPackageStepsCanonicalNames(); + + Set typesInContainer = Sets.newHashSet( + Iterables.transform( + Iterables.filter( + Iterables.transform( + ceContainer.getPicoContainer().getComponentAdapters(), + ComponentAdapterToImplementationClass.INSTANCE), + IsComputationStep.INSTANCE), + ClassToCanonicalName.INSTANCE)); + + assertThat(typesInContainer).isEqualTo(stepsCanonicalNames); + } + + /** + * Compute set of canonical names of classes implementing ComputationStep in package step using reflection. + */ + private Set retrieveStepPackageStepsCanonicalNames() { + Reflections reflections = new Reflections("org.sonar.server.computation.step"); + + return Sets.newHashSet(Iterables.transform(reflections.getSubTypesOf(ComputationStep.class), ClassToCanonicalName.INSTANCE)); + } + + private enum ClassToCanonicalName implements Function, String> { + INSTANCE; + + @Override + public String apply(Class input) { + return input.getCanonicalName(); + } + } + + private enum ComponentAdapterToImplementationClass implements Function, Class> { + INSTANCE; + + @Override + public Class apply(ComponentAdapter input) { + return input.getComponentImplementation(); + } + } + + private enum IsComputationStep implements Predicate> { + INSTANCE; + + @Override + public boolean apply(Class input) { + return ComputationStep.class.isAssignableFrom(input); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java index 72cf7bb897b..bc254a28747 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java @@ -20,11 +20,18 @@ package org.sonar.server.computation.step; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import java.util.List; +import com.google.common.collect.Sets; +import java.util.Set; import org.junit.Test; +import org.picocontainer.ComponentAdapter; +import org.reflections.Reflections; import org.sonar.core.platform.ComponentContainer; -import org.sonar.server.computation.container.CEContainer; +import org.sonar.server.computation.ReportQueue; +import org.sonar.server.computation.container.ComputeEngineContainerImpl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -32,48 +39,67 @@ import static org.mockito.Mockito.mock; public class ComputationStepsTest { - @Test - public void ordered_steps() { - CEContainer ceContainer = new CEContainer(new ComponentContainer()); - ceContainer.add( - // unordered - mock(ApplyPermissionsStep.class), - mock(ParseReportStep.class), - mock(IndexSourceLinesStep.class), - mock(PersistIssuesStep.class), - mock(IndexIssuesStep.class), - mock(SwitchSnapshotStep.class), - mock(PurgeDatastoresStep.class), - mock(SendIssueNotificationsStep.class), - mock(IndexComponentsStep.class), - mock(PersistProjectLinksStep.class), - mock(PersistMeasuresStep.class), - mock(PersistEventsStep.class), - mock(PersistDuplicationsStep.class), - mock(PersistNumberOfDaysSinceLastCommitStep.class), - mock(PersistFileSourcesStep.class), - mock(PersistTestsStep.class), - mock(IndexTestsStep.class), - mock(PopulateComponentsUuidAndKeyStep.class), - mock(PersistComponentsStep.class), - mock(QualityProfileEventsStep.class), - mock(ValidateProjectStep.class) - ); - ComputationSteps computationSteps = new ComputationSteps(ceContainer); - - List steps = Lists.newArrayList(computationSteps.instances()); - assertThat(steps).hasSize(21); - assertThat(steps.get(0)).isInstanceOf(PopulateComponentsUuidAndKeyStep.class); - assertThat(steps.get(20)).isInstanceOf(SendIssueNotificationsStep.class); - } - @Test public void fail_if_a_step_is_not_registered_in_picocontainer() { try { - Lists.newArrayList(new ComputationSteps(new CEContainer(new ComponentContainer())).instances()); + Lists.newArrayList(new ComputationSteps(mock(ComputeEngineContainerImpl.class)).instances()); fail(); } catch (IllegalStateException e) { assertThat(e).hasMessageContaining("Component not found"); } } + + @Test + public void all_steps_from_package_step_are_present_in_container() { + ComputeEngineContainerImpl ceContainer = new ComputeEngineContainerImpl(new ComponentContainer(), mock(ReportQueue.Item.class)); + + Set stepsCanonicalNames = retrieveStepPackageStepsCanonicalNames(); + + Set typesInContainer = Sets.newHashSet( + Iterables.transform( + Iterables.filter( + Iterables.transform( + ceContainer.getPicoContainer().getComponentAdapters(), + ComponentAdapterToImplementationClass.INSTANCE), + IsComputationStep.INSTANCE), + ClassToCanonicalName.INSTANCE)); + + assertThat(typesInContainer).isEqualTo(stepsCanonicalNames); + } + + /** + * Compute set of canonical names of classes implementing ComputationStep in package step using reflection. + */ + private Set retrieveStepPackageStepsCanonicalNames() { + Reflections reflections = new Reflections("org.sonar.server.computation.step"); + + return Sets.newHashSet(Iterables.transform(reflections.getSubTypesOf(ComputationStep.class), ClassToCanonicalName.INSTANCE)); + } + + private enum ClassToCanonicalName implements Function, String> { + INSTANCE; + + @Override + public String apply(Class input) { + return input.getCanonicalName(); + } + } + + private enum ComponentAdapterToImplementationClass implements Function, Class> { + INSTANCE; + + @Override + public Class apply(ComponentAdapter input) { + return input.getComponentImplementation(); + } + } + + private enum IsComputationStep implements Predicate> { + INSTANCE; + + @Override + public boolean apply(Class input) { + return ComputationStep.class.isAssignableFrom(input); + } + } } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/activity/ActivityManagerTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/activity/ActivityManagerTest/shared.xml new file mode 100644 index 00000000000..2ac38c2fb0b --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/activity/ActivityManagerTest/shared.xml @@ -0,0 +1,11 @@ + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/activity/CEActivityManagerTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/activity/CEActivityManagerTest/shared.xml deleted file mode 100644 index 2ac38c2fb0b..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/activity/CEActivityManagerTest/shared.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java b/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java index e0bafcf8e9c..d6a2a258a75 100644 --- a/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java +++ b/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java @@ -50,9 +50,13 @@ public class ComponentContainer { * Create root container */ public ComponentContainer() { + this(createPicoContainer()); + } + + protected ComponentContainer(MutablePicoContainer picoContainer) { this.parent = null; this.child = null; - this.pico = createPicoContainer(); + this.pico = picoContainer; this.componentKeys = new ComponentKeys(); propertyDefinitions = new PropertyDefinitions(); addSingleton(propertyDefinitions); @@ -63,15 +67,8 @@ public class ComponentContainer { * Create child container */ protected ComponentContainer(ComponentContainer parent) { - this(parent.pico.makeChildContainer(), parent); - } - - /** - * Create child container - */ - protected ComponentContainer(MutablePicoContainer picoContainer, ComponentContainer parent) { this.parent = parent; - this.pico = picoContainer; + this.pico = parent.pico.makeChildContainer(); this.parent.child = this; this.propertyDefinitions = parent.propertyDefinitions; this.componentKeys = new ComponentKeys(); @@ -216,8 +213,8 @@ public class ComponentContainer { return this; } - public T getComponentByType(Class tClass) { - return pico.getComponent(tClass); + public T getComponentByType(Class type) { + return pico.getComponent(type); } public Object getComponentByKey(Object key) {