diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-12-15 14:22:10 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-12-17 13:14:49 +0100 |
commit | 789df9c9ba0ccdf72e5e5ab24de04b47798e4f72 (patch) | |
tree | 224d9625710131d51bc0f23cfbfbb2109b604112 | |
parent | 597cf003728da27dac4a51a624358263ff8e88f7 (diff) | |
download | sonarqube-789df9c9ba0ccdf72e5e5ab24de04b47798e4f72.tar.gz sonarqube-789df9c9ba0ccdf72e5e5ab24de04b47798e4f72.zip |
SONAR-5945 Upload issues in analysis report
67 files changed, 1521 insertions, 711 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersister.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersister.java index 4b8a0225162..17cee708dd0 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersister.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersister.java @@ -30,6 +30,7 @@ import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.ResourceCache; import java.util.List; @@ -37,24 +38,25 @@ import java.util.List; public final class TimeMachineConfigurationPersister implements Decorator { private final TimeMachineConfiguration timeMachineConfiguration; - private Snapshot projectSnapshot; + private ResourceCache resourceCache; private DatabaseSession session; - public TimeMachineConfigurationPersister(TimeMachineConfiguration timeMachineConfiguration, Snapshot projectSnapshot, DatabaseSession session) { + public TimeMachineConfigurationPersister(TimeMachineConfiguration timeMachineConfiguration, ResourceCache resourceCache, DatabaseSession session) { this.timeMachineConfiguration = timeMachineConfiguration; - this.projectSnapshot = projectSnapshot; + this.resourceCache = resourceCache; this.session = session; } @Override public void decorate(Resource resource, DecoratorContext context) { if (ResourceUtils.isProject(resource)) { - persistConfiguration(); + persistConfiguration(resource); } } - void persistConfiguration() { + void persistConfiguration(Resource module) { List<PastSnapshot> pastSnapshots = timeMachineConfiguration.getProjectPastSnapshots(); + Snapshot projectSnapshot = resourceCache.get(module.getEffectiveKey()).snapshot(); for (PastSnapshot pastSnapshot : pastSnapshots) { Snapshot snapshot = session.reattach(Snapshot.class, projectSnapshot.getId()); updatePeriodParams(snapshot, pastSnapshot); diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersisterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersisterTest.java index a34e57b340a..4f9ac63c11f 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersisterTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TimeMachineConfigurationPersisterTest.java @@ -21,9 +21,11 @@ package org.sonar.plugins.core.timemachine; import org.junit.Test; import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; import org.sonar.api.utils.DateUtils; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.TimeMachineConfiguration; +import org.sonar.batch.index.ResourceCache; import org.sonar.jpa.test.AbstractDbUnitTestCase; import java.util.Arrays; @@ -45,8 +47,13 @@ public class TimeMachineConfigurationPersisterTest extends AbstractDbUnitTestCas when(timeMachineConfiguration.getProjectPastSnapshots()).thenReturn(Arrays.asList(vs1, vs3)); Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", 1000); - TimeMachineConfigurationPersister persister = new TimeMachineConfigurationPersister(timeMachineConfiguration, projectSnapshot, getSession()); - persister.persistConfiguration(); + ResourceCache resourceCache = new ResourceCache(); + Project project = new Project("foo"); + resourceCache.add(project, projectSnapshot); + + TimeMachineConfigurationPersister persister = new TimeMachineConfigurationPersister(timeMachineConfiguration, resourceCache, getSession()); + + persister.persistConfiguration(project); checkTables("shouldSaveConfigurationInSnapshotsTable", "snapshots"); } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java index 2b587690753..d3258bfa7f6 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java @@ -22,14 +22,18 @@ package org.sonar.plugins.cpd.index; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Project; -import org.sonar.batch.index.ResourcePersister; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.duplication.DuplicationDao; import org.sonar.core.duplication.DuplicationUnitDto; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; +import javax.persistence.Query; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -37,28 +41,39 @@ import java.util.Map; public class DbDuplicationsIndex { + private static final String RESOURCE_ID = "resourceId"; + private static final String LAST = "last"; + private final Map<ByteArray, Collection<Block>> cache = Maps.newHashMap(); - private final ResourcePersister resourcePersister; private final int currentProjectSnapshotId; private final Integer lastSnapshotId; private final String languageKey; + private final DuplicationDao dao; + private final DatabaseSession session; + private final ResourceCache resourceCache; - private DuplicationDao dao; - - public DbDuplicationsIndex(ResourcePersister resourcePersister, Project currentProject, DuplicationDao dao, - String language) { + public DbDuplicationsIndex(Project currentProject, DuplicationDao dao, + String language, DatabaseSession session, ResourceCache resourceCache) { this.dao = dao; - this.resourcePersister = resourcePersister; - Snapshot currentSnapshot = resourcePersister.getSnapshotOrFail(currentProject); - Snapshot lastSnapshot = resourcePersister.getLastSnapshot(currentSnapshot, false); - this.currentProjectSnapshotId = currentSnapshot.getId(); + this.session = session; + this.resourceCache = resourceCache; + Snapshot lastSnapshot = getLastSnapshot(currentProject.getId()); + this.currentProjectSnapshotId = resourceCache.get(currentProject.getEffectiveKey()).snapshotId(); this.lastSnapshotId = lastSnapshot == null ? null : lastSnapshot.getId(); this.languageKey = language; } + private Snapshot getLastSnapshot(int resourceId) { + String hql = "SELECT s FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=:last AND s.resourceId=:resourceId"; + Query query = session.createQuery(hql); + query.setParameter(LAST, true); + query.setParameter(RESOURCE_ID, resourceId); + return session.getSingleResult(query, null); + } + int getSnapshotIdFor(InputFile inputFile) { - return resourcePersister.getSnapshotOrFail(inputFile).getId(); + return resourceCache.get(((DefaultInputFile) inputFile).key()).snapshotId(); } public void prepareCache(InputFile inputFile) { diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java index 9830f4b1dd0..34a6110c32f 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java @@ -26,9 +26,10 @@ import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; +import org.sonar.api.database.DatabaseSession; import org.sonar.api.resources.Project; import org.sonar.batch.bootstrap.AnalysisMode; -import org.sonar.batch.index.ResourcePersister; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.duplication.DuplicationDao; import javax.annotation.Nullable; @@ -38,27 +39,29 @@ public class IndexFactory implements BatchComponent { private static final Logger LOG = LoggerFactory.getLogger(IndexFactory.class); private final Settings settings; - private final ResourcePersister resourcePersister; private final DuplicationDao dao; private final AnalysisMode mode; + private final DatabaseSession session; + private final ResourceCache resourceCache; - public IndexFactory(AnalysisMode mode, Settings settings, @Nullable ResourcePersister resourcePersister, @Nullable DuplicationDao dao) { + public IndexFactory(AnalysisMode mode, Settings settings, @Nullable DuplicationDao dao, @Nullable DatabaseSession session, ResourceCache resourceCache) { this.mode = mode; this.settings = settings; - this.resourcePersister = resourcePersister; this.dao = dao; + this.session = session; + this.resourceCache = resourceCache; } /** * Used by new sensor mode */ - public IndexFactory(AnalysisMode mode, Settings settings) { - this(mode, settings, null, null); + public IndexFactory(AnalysisMode mode, Settings settings, ResourceCache resourceCache) { + this(mode, settings, null, null, resourceCache); } public SonarDuplicationsIndex create(@Nullable Project project, String languageKey) { - if (verifyCrossProject(project, LOG) && dao != null && resourcePersister != null) { - return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao, languageKey)); + if (verifyCrossProject(project, LOG) && dao != null && session != null) { + return new SonarDuplicationsIndex(new DbDuplicationsIndex(project, dao, languageKey, session, resourceCache)); } return new SonarDuplicationsIndex(); } diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java index 7b9fa524fec..e97ab77daf7 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java @@ -24,9 +24,10 @@ import org.junit.Test; import org.slf4j.Logger; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; +import org.sonar.api.database.DatabaseSession; import org.sonar.api.resources.Project; import org.sonar.batch.bootstrap.AnalysisMode; -import org.sonar.batch.index.ResourcePersister; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.duplication.DuplicationDao; import static org.fest.assertions.Assertions.assertThat; @@ -47,7 +48,7 @@ public class IndexFactoryTest { project = new Project("foo"); settings = new Settings(); analysisMode = mock(AnalysisMode.class); - factory = new IndexFactory(analysisMode, settings, mock(ResourcePersister.class), mock(DuplicationDao.class)); + factory = new IndexFactory(analysisMode, settings, mock(DuplicationDao.class), mock(DatabaseSession.class), new ResourceCache()); logger = mock(Logger.class); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/SnapshotCacheTest.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/GsonHelper.java index 96c2a1033b6..b5ea008a538 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/SnapshotCacheTest.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/GsonHelper.java @@ -17,24 +17,19 @@ * 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.batch.index; +package org.sonar.batch.protocol; -import org.junit.Test; -import org.sonar.api.database.model.Snapshot; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; +public class GsonHelper { -public class SnapshotCacheTest { - - Snapshot snapshot = mock(Snapshot.class); + private GsonHelper() { + // Utility class + } - @Test - public void should_cache_snapshots() throws Exception { - SnapshotCache cache = new SnapshotCache(); - String componentKey = "org.apache.struts:struts-core"; - cache.put(componentKey, snapshot); - assertThat(cache.get(componentKey)).isSameAs(snapshot); - assertThat(cache.get("other")).isNull(); + public static Gson create() { + return new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").setPrettyPrinting().create(); } + } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/GlobalReferentials.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/GlobalReferentials.java index 4fa32d5d68b..e7501e4d1de 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/GlobalReferentials.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/GlobalReferentials.java @@ -19,7 +19,7 @@ */ package org.sonar.batch.protocol.input; -import com.google.gson.Gson; +import org.sonar.batch.protocol.GsonHelper; import java.util.ArrayList; import java.util.Collection; @@ -63,11 +63,11 @@ public class GlobalReferentials { } public String toJson() { - return new Gson().toJson(this); + return GsonHelper.create().toJson(this); } public static GlobalReferentials fromJson(String json) { - return new Gson().fromJson(json, GlobalReferentials.class); + return GsonHelper.create().fromJson(json, GlobalReferentials.class); } } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java index 68d06e73b48..b945da6f7b8 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java @@ -19,8 +19,7 @@ */ package org.sonar.batch.protocol.input; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import org.sonar.batch.protocol.GsonHelper; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -114,13 +113,11 @@ public class ProjectReferentials { } public String toJson() { - Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create(); - return gson.toJson(this); + return GsonHelper.create().toJson(this); } public static ProjectReferentials fromJson(String json) { - Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create(); - return gson.fromJson(json, ProjectReferentials.class); + return GsonHelper.create().fromJson(json, ProjectReferentials.class); } } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/ReportIssue.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/ReportIssue.java new file mode 100644 index 00000000000..3917d54d2a2 --- /dev/null +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/ReportIssue.java @@ -0,0 +1,275 @@ +/* + * 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.batch.protocol.output.issue; + +import javax.annotation.Nullable; + +import java.util.Date; + +public class ReportIssue { + + private long resourceBatchId; + private boolean isNew; + private String ruleKey; + private String ruleRepo; + private String key; + private Integer line; + private String message; + private Double effortToFix; + private Long debtInMinutes; + private String resolution; + private String status; + private String severity; + private String checksum; + private boolean manualSeverity; + private String reporter; + private String assignee; + private String actionPlanKey; + private String attributes; + private String authorLogin; + private Date creationDate; + private Date closeDate; + private Date updateDate; + private Long selectedAt; + private String diffFields; + private boolean isChanged; + + public ReportIssue setKey(String key) { + this.key = key; + return this; + } + + public String key() { + return key; + } + + public ReportIssue setResourceBatchId(long resourceBatchId) { + this.resourceBatchId = resourceBatchId; + return this; + } + + public long resourceBatchId() { + return resourceBatchId; + } + + public ReportIssue setNew(boolean isNew) { + this.isNew = isNew; + return this; + } + + public boolean isNew() { + return isNew; + } + + public ReportIssue setLine(Integer line) { + this.line = line; + return this; + } + + public Integer line() { + return line; + } + + public ReportIssue setMessage(String message) { + this.message = message; + return this; + } + + public String message() { + return message; + } + + public ReportIssue setEffortToFix(Double effortToFix) { + this.effortToFix = effortToFix; + return this; + } + + public Double effortToFix() { + return effortToFix; + } + + public ReportIssue setDebt(Long debtInMinutes) { + this.debtInMinutes = debtInMinutes; + return this; + } + + public Long debt() { + return debtInMinutes; + } + + public ReportIssue setResolution(String resolution) { + this.resolution = resolution; + return this; + } + + public String resolution() { + return resolution; + } + + public ReportIssue setStatus(String status) { + this.status = status; + return this; + } + + public String status() { + return status; + } + + public ReportIssue setSeverity(String severity) { + this.severity = severity; + return this; + } + + public String severity() { + return severity; + } + + public ReportIssue setChecksum(String checksum) { + this.checksum = checksum; + return this; + } + + public String checksum() { + return checksum; + } + + public ReportIssue setManualSeverity(boolean manualSeverity) { + this.manualSeverity = manualSeverity; + return this; + } + + public boolean isManualSeverity() { + return manualSeverity; + } + + public ReportIssue setReporter(String reporter) { + this.reporter = reporter; + return this; + } + + public String reporter() { + return reporter; + } + + public ReportIssue setAssignee(String assignee) { + this.assignee = assignee; + return this; + } + + public String assignee() { + return assignee; + } + + public ReportIssue setRuleKey(String ruleRepo, String ruleKey) { + this.ruleRepo = ruleRepo; + this.ruleKey = ruleKey; + return this; + } + + public String ruleRepo() { + return ruleRepo; + } + + public String ruleKey() { + return ruleKey; + } + + public ReportIssue setActionPlanKey(String actionPlanKey) { + this.actionPlanKey = actionPlanKey; + return this; + } + + public String actionPlanKey() { + return actionPlanKey; + } + + public ReportIssue setAttributes(String attributes) { + this.attributes = attributes; + return this; + } + + public String issueAttributes() { + return attributes; + } + + public ReportIssue setAuthorLogin(String authorLogin) { + this.authorLogin = authorLogin; + return this; + } + + public String authorLogin() { + return authorLogin; + } + + public ReportIssue setCreationDate(Date creationDate) { + this.creationDate = creationDate; + return this; + } + + public Date creationDate() { + return creationDate; + } + + public ReportIssue setCloseDate(Date closeDate) { + this.closeDate = closeDate; + return this; + } + + public Date closeDate() { + return closeDate; + } + + public ReportIssue setUpdateDate(Date updateDate) { + this.updateDate = updateDate; + return this; + } + + public Date updateDate() { + return updateDate; + } + + public ReportIssue setSelectedAt(Long selectedAt) { + this.selectedAt = selectedAt; + return this; + } + + public Long selectedAt() { + return selectedAt; + } + + public ReportIssue setDiffFields(@Nullable String diffFields) { + this.diffFields = diffFields; + return this; + } + + public String diffFields() { + return diffFields; + } + + public ReportIssue setChanged(boolean isChanged) { + this.isChanged = isChanged; + return this; + } + + public boolean isChanged() { + return isChanged; + } + +} diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/package-info.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/package-info.java new file mode 100644 index 00000000000..444216d69f1 --- /dev/null +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.batch.protocol.output.issue; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/ReportResource.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/ReportResource.java new file mode 100644 index 00000000000..44ceb7386ce --- /dev/null +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/ReportResource.java @@ -0,0 +1,106 @@ +/* + * 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.batch.protocol.output.resource; + +import java.util.ArrayList; +import java.util.Collection; + +public class ReportResource { + + public enum Type { + PRJ, + MOD, + DIR, + FIL + } + + private long batchId; + private int id; + private int snapshotId; + private String path; + private String name; + private Type type; + + private Collection<ReportResource> children = new ArrayList<ReportResource>(); + + public ReportResource setBatchId(long batchId) { + this.batchId = batchId; + return this; + } + + public long batchId() { + return batchId; + } + + public ReportResource setId(int id) { + this.id = id; + return this; + } + + public int id() { + return id; + } + + public ReportResource setSnapshotId(int snapshotId) { + this.snapshotId = snapshotId; + return this; + } + + public int snapshotId() { + return snapshotId; + } + + public ReportResource setPath(String path) { + this.path = path; + return this; + } + + public String path() { + return path; + } + + public ReportResource setName(String name) { + this.name = name; + return this; + } + + public String name() { + return name; + } + + public ReportResource setType(Type type) { + this.type = type; + return this; + } + + public Type type() { + return type; + } + + public ReportResource addChild(ReportResource child) { + this.children.add(child); + return this; + } + + public Collection<ReportResource> children() { + return children; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SnapshotCache.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/ReportResources.java index 6e21c2d2200..e78e41c14d8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SnapshotCache.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/ReportResources.java @@ -17,33 +17,41 @@ * 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.batch.index; +package org.sonar.batch.protocol.output.resource; -import com.google.common.collect.Maps; -import org.sonar.api.BatchComponent; -import org.sonar.api.database.model.Snapshot; -import org.sonar.api.resources.Library; +import org.sonar.batch.protocol.GsonHelper; -import java.util.Map; -import java.util.Set; +import java.util.Date; -/** - * Does not contains snapshots of {@link Library} as effectiveKey can be the same than a project. - */ -public class SnapshotCache implements BatchComponent { - // snapshots by component key - private final Map<String, Snapshot> snapshots = Maps.newHashMap(); +public class ReportResources { + + private Date analysisDate; + + private ReportResource root; + + public void setAnalysisDate(Date analysisDate) { + this.analysisDate = analysisDate; + } - public Snapshot get(String componentKey) { - return snapshots.get(componentKey); + public Date analysisDate() { + return analysisDate; } - public SnapshotCache put(String componentKey, Snapshot snapshot) { - snapshots.put(componentKey, snapshot); + public ReportResources setRoot(ReportResource r) { + this.root = r; return this; } - public Set<Map.Entry<String, Snapshot>> snapshots() { - return snapshots.entrySet(); + public ReportResource root() { + return root; } + + public String toJson() { + return GsonHelper.create().toJson(this); + } + + public static ReportResources fromJson(String json) { + return GsonHelper.create().fromJson(json, ReportResources.class); + } + } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/package-info.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/package-info.java new file mode 100644 index 00000000000..5e45e2dee7a --- /dev/null +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/resource/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.batch.protocol.output.resource; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/ProjectReferentialsTest.java b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/ProjectReferentialsTest.java index 8d55f4054dc..316d59acc31 100644 --- a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/ProjectReferentialsTest.java +++ b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/ProjectReferentialsTest.java @@ -51,6 +51,7 @@ public class ProjectReferentialsTest { ref.setLastAnalysisDate(new SimpleDateFormat("dd/MM/yyyy").parse("31/10/2014")); ref.setTimestamp(10); ref.addFileData("foo", "src/main/java/Foo.java", new FileData("xyz", "1=12345,2=3456", "1=345,2=345", "1=henryju,2=gaudin")); + ref.addFileData("foo", "src/main/java/Foo2.java", new FileData("xyz", "1=12345,2=3456", "1=345,2=345", "1=henryju,2=gaudin")); System.out.println(ref.toJson()); JSONAssert @@ -59,7 +60,8 @@ public class ProjectReferentialsTest { + "qprofilesByLanguage:{java:{key:\"squid-java\",name:Java,language:java,rulesUpdatedAt:\"1984-03-14T00:00:00+0100\"}}," + "activeRules:[{repositoryKey:repo,ruleKey:rule,name:Rule,severity:MAJOR,internalKey:rule,language:java,params:{param1:value1}}]," + "settingsByModule:{foo:{prop1:value1,prop2:value2,prop:value}}," - + "fileDataByModuleAndPath:{foo:{\"src/main/java/Foo.java\":{hash:xyz,scmLastCommitDatetimesByLine:\"1\u003d12345,2\u003d3456\",scmRevisionsByLine:\"1\u003d345,2\u003d345\",scmAuthorsByLine:\"1\u003dhenryju,2\u003dgaudin\"}}}," + + "fileDataByModuleAndPath:{foo:{\"src/main/java/Foo.java\":{hash:xyz,scmLastCommitDatetimesByLine:\"1\u003d12345,2\u003d3456\",scmRevisionsByLine:\"1\u003d345,2\u003d345\",scmAuthorsByLine:\"1\u003dhenryju,2\u003dgaudin\"}," + + "\"src/main/java/Foo2.java\":{hash:xyz,scmLastCommitDatetimesByLine:\"1\u003d12345,2\u003d3456\",scmRevisionsByLine:\"1\u003d345,2\u003d345\",scmAuthorsByLine:\"1\u003dhenryju,2\u003dgaudin\"}}}," + "lastAnalysisDate:\"2014-10-31T00:00:00+0100\"}", ref.toJson(), true); } @@ -91,6 +93,8 @@ public class ProjectReferentialsTest { assertThat(qProfile.rulesUpdatedAt()).isEqualTo(new SimpleDateFormat("dd/MM/yyyy").parse("14/03/1984")); assertThat(ref.settings("foo")).includes(MapAssert.entry("prop", "value")); + assertThat(ref.fileData("foo2", "src/main/java/Foo3.java")).isNull(); + assertThat(ref.fileData("foo", "src/main/java/Foo.java").hash()).isEqualTo("xyz"); assertThat(ref.fileData("foo", "src/main/java/Foo.java").scmAuthorsByLine()).isEqualTo("1=henryju,2=gaudin"); assertThat(ref.fileData("foo", "src/main/java/Foo.java").scmLastCommitDatetimesByLine()).isEqualTo("1=12345,2=3456"); diff --git a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/issue/ReportIssueTest.java b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/issue/ReportIssueTest.java new file mode 100644 index 00000000000..06edbf1ab8d --- /dev/null +++ b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/issue/ReportIssueTest.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.batch.protocol.output.issue; + +import org.junit.Test; + +import java.text.SimpleDateFormat; + +import static org.fest.assertions.Assertions.assertThat; + +public class ReportIssueTest { + + @Test + public void testGetterSetter() throws Exception { + SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); + ReportIssue issue = new ReportIssue() + .setActionPlanKey("plan") + .setAssignee("assignee") + .setAuthorLogin("author") + .setChanged(true) + .setChecksum("checksum") + .setDebt(3L) + .setDiffFields("diff") + .setEffortToFix(2.0) + .setAttributes("attributes") + .setCloseDate(sdf.parse("11/12/2012")) + .setCreationDate(sdf.parse("12/12/2012")) + .setUpdateDate(sdf.parse("13/12/2012")) + .setKey("key") + .setLine(3) + .setManualSeverity(true) + .setMessage("message") + .setNew(true) + .setReporter("reporter") + .setResolution("resolution") + .setResourceBatchId(4L) + .setRuleKey("repo", "rule") + .setSelectedAt(234L) + .setSeverity("severity") + .setStatus("status"); + + assertThat(issue.actionPlanKey()).isEqualTo("plan"); + assertThat(issue.assignee()).isEqualTo("assignee"); + assertThat(issue.authorLogin()).isEqualTo("author"); + assertThat(issue.isChanged()).isTrue(); + assertThat(issue.checksum()).isEqualTo("checksum"); + assertThat(issue.debt()).isEqualTo(3L); + assertThat(issue.diffFields()).isEqualTo("diff"); + assertThat(issue.effortToFix()).isEqualTo(2.0); + assertThat(issue.issueAttributes()).isEqualTo("attributes"); + assertThat(issue.closeDate()).isEqualTo(sdf.parse("11/12/2012")); + assertThat(issue.creationDate()).isEqualTo(sdf.parse("12/12/2012")); + assertThat(issue.updateDate()).isEqualTo(sdf.parse("13/12/2012")); + assertThat(issue.key()).isEqualTo("key"); + assertThat(issue.line()).isEqualTo(3); + assertThat(issue.isManualSeverity()).isTrue(); + assertThat(issue.message()).isEqualTo("message"); + assertThat(issue.isNew()).isTrue(); + assertThat(issue.reporter()).isEqualTo("reporter"); + assertThat(issue.resolution()).isEqualTo("resolution"); + assertThat(issue.resourceBatchId()).isEqualTo(4L); + assertThat(issue.ruleRepo()).isEqualTo("repo"); + assertThat(issue.ruleKey()).isEqualTo("rule"); + assertThat(issue.selectedAt()).isEqualTo(234L); + assertThat(issue.severity()).isEqualTo("severity"); + assertThat(issue.status()).isEqualTo("status"); + } + +} diff --git a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/resource/ReportResourcesTest.java b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/resource/ReportResourcesTest.java new file mode 100644 index 00000000000..d48d3ccc76d --- /dev/null +++ b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/resource/ReportResourcesTest.java @@ -0,0 +1,94 @@ +/* + * 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.batch.protocol.output.resource; + +import org.apache.commons.io.IOUtils; +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.sonar.batch.protocol.output.resource.ReportResource.Type; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import static org.fest.assertions.Assertions.assertThat; + +public class ReportResourcesTest { + + @Test + public void to_json() throws Exception { + ReportResources res = new ReportResources(); + Date d = new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012"); + res.setAnalysisDate(d); + ReportResource root = new ReportResource() + .setBatchId(1) + .setId(11) + .setName("Root project") + .setSnapshotId(111) + .setType(Type.PRJ); + ReportResource module = new ReportResource() + .setBatchId(2) + .setId(22) + .setName("Module") + .setSnapshotId(222) + .setPath("module1") + .setType(Type.MOD); + root.addChild(module); + ReportResource dir = new ReportResource() + .setBatchId(3) + .setId(33) + .setName("src") + .setSnapshotId(333) + .setPath("src") + .setType(Type.DIR); + module.addChild(dir); + ReportResource file = new ReportResource() + .setBatchId(4) + .setId(44) + .setName("Foo.java") + .setSnapshotId(444) + .setPath("Foo.java") + .setType(Type.FIL); + dir.addChild(file); + res.setRoot(root); + + JSONAssert + .assertEquals( + IOUtils.toString(this.getClass().getResourceAsStream("ReportResourceTest/expected.json"), "UTF-8"), + res.toJson(), true); + } + + @Test + public void from_json() throws Exception { + ReportResources res = ReportResources + .fromJson( + IOUtils.toString(this.getClass().getResourceAsStream("ReportResourceTest/expected.json"), "UTF-8")); + + assertThat(res.analysisDate()).isEqualTo(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012")); + ReportResource root = res.root(); + assertThat(root.batchId()).isEqualTo(1); + assertThat(root.id()).isEqualTo(11); + assertThat(root.name()).isEqualTo("Root project"); + assertThat(root.snapshotId()).isEqualTo(111); + assertThat(root.path()).isNull(); + assertThat(root.type()).isEqualTo(Type.PRJ); + assertThat(root.children()).hasSize(1); + + } +} diff --git a/sonar-batch-protocol/src/test/resources/org/sonar/batch/protocol/output/resource/ReportResourceTest/expected.json b/sonar-batch-protocol/src/test/resources/org/sonar/batch/protocol/output/resource/ReportResourceTest/expected.json new file mode 100644 index 00000000000..4c642a06ba0 --- /dev/null +++ b/sonar-batch-protocol/src/test/resources/org/sonar/batch/protocol/output/resource/ReportResourceTest/expected.json @@ -0,0 +1,41 @@ +{ + "analysisDate": "2012-12-12T00:00:00+0100", + "root": { + "batchId": 1, + "id": 11, + "snapshotId": 111, + "name": "Root project", + "type": "PRJ", + "children": [ + { + "batchId": 2, + "id": 22, + "snapshotId": 222, + "path": "module1", + "name": "Module", + "type": "MOD", + "children": [ + { + "batchId": 3, + "id": 33, + "snapshotId": 333, + "path": "src", + "name": "src", + "type": "DIR", + "children": [ + { + "batchId": 4, + "id": 44, + "snapshotId": 444, + "path": "Foo.java", + "name": "Foo.java", + "type": "FIL", + "children": [] + } + ] + } + ] + } + ] + } +}
\ No newline at end of file diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index eadff107bcb..2696cbe7fff 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -123,6 +123,10 @@ <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> + <dependency> + <groupId>com.github.kevinsawicki</groupId> + <artifactId>http-request</artifactId> + </dependency> <!-- unit tests --> <dependency> diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java index 0acc0a94232..470f0f79686 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java @@ -132,18 +132,18 @@ public class ServerClient implements BatchComponent { return new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]", he.getResponseCode(), he.getUri()), he); } - private String getMessageWhenNotAuthorized() { + public String getMessageWhenNotAuthorized() { if (Strings.isNullOrEmpty(getLogin()) && Strings.isNullOrEmpty(getPassword())) { return "Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties %s and %s."; } return "Not authorized. Please check the properties %s and %s."; } - private String getLogin() { + public String getLogin() { return props.property(CoreProperties.LOGIN); } - private String getPassword() { + public String getPassword() { return props.property(CoreProperties.PASSWORD); } @@ -155,4 +155,5 @@ public class ServerClient implements BatchComponent { throw new IllegalStateException("Encoding not supported", e); } } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java b/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java index 2e5de75acb1..dd3406b824b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java @@ -19,33 +19,55 @@ */ package org.sonar.batch.index; -import org.sonar.api.batch.Event; import org.sonar.api.database.model.Snapshot; -import org.sonar.api.design.Dependency; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.List; +import java.util.ArrayList; +import java.util.Collection; -public interface PersistenceManager { - void clear(); +public class BatchResource { - void saveProject(Project project, @Nullable Project parent); + private final long batchId; + private final Resource r; + private final Snapshot s; + private final BatchResource parent; + private final Collection<BatchResource> children = new ArrayList<BatchResource>(); - Snapshot saveResource(Project project, Resource resource, @Nullable Resource parent); + public BatchResource(long batchId, Resource r, Snapshot s, @Nullable BatchResource parent) { + this.batchId = batchId; + this.r = r; + this.s = s; + this.parent = parent; + if (parent != null) { + parent.children.add(this); + } + } - void saveDependency(Project project, Dependency dependency, Dependency parentDependency); + public long batchId() { + return batchId; + } - void saveLink(Project project, ProjectLink link); + public Resource resource() { + return r; + } - void deleteLink(Project project, String key); + public int snapshotId() { + return s.getId(); + } - List<Event> getEvents(Resource resource); + public Snapshot snapshot() { + return s; + } - void deleteEvent(Event event); + @CheckForNull + public BatchResource parent() { + return parent; + } - void saveEvent(Resource resource, Event event); + public Collection<BatchResource> children() { + return children; + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index a3133af5c25..c111c485e49 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.index; +import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -30,7 +31,6 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.Event; import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.bootstrap.ProjectDefinition; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; @@ -76,7 +76,7 @@ public class DefaultIndex extends SonarIndex { private static final Logger LOG = LoggerFactory.getLogger(DefaultIndex.class); - private PersistenceManager persistence; + private ResourcePersister resourcePersister; private MetricFinder metricFinder; private final ScanGraph graph; @@ -91,12 +91,18 @@ public class DefaultIndex extends SonarIndex { private final DeprecatedViolations deprecatedViolations; private ModuleIssues moduleIssues; private final MeasureCache measureCache; + private final ResourceKeyMigration migration; + private final DependencyPersister dependencyPersister; + private final LinkPersister linkPersister; + private final EventPersister eventPersister; - private ResourceKeyMigration migration; - - public DefaultIndex(PersistenceManager persistence, ProjectTree projectTree, MetricFinder metricFinder, + public DefaultIndex(ResourcePersister resourcePersister, DependencyPersister dependencyPersister, + LinkPersister linkPersister, EventPersister eventPersister, ProjectTree projectTree, MetricFinder metricFinder, ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration, MeasureCache measureCache) { - this.persistence = persistence; + this.resourcePersister = resourcePersister; + this.dependencyPersister = dependencyPersister; + this.linkPersister = linkPersister; + this.eventPersister = eventPersister; this.projectTree = projectTree; this.metricFinder = metricFinder; this.graph = graph; @@ -116,7 +122,7 @@ public class DefaultIndex extends SonarIndex { Bucket bucket = new Bucket(rootProject); addBucket(rootProject, bucket); migration.checkIfMigrationNeeded(rootProject); - persistence.saveProject(rootProject, null); + resourcePersister.saveProject(rootProject, null); currentProject = rootProject; for (Project module : rootProject.getModules()) { @@ -247,7 +253,13 @@ public class DefaultIndex extends SonarIndex { @Override public Dependency addDependency(Dependency dependency) { - Dependency existingDep = getEdge(dependency.getFrom(), dependency.getTo()); + // Reload resources + Resource from = getResource(dependency.getFrom()); + Preconditions.checkArgument(from != null, dependency.getFrom() + " is not indexed"); + Resource to = getResource(dependency.getTo()); + Preconditions.checkArgument(to != null, dependency.getTo() + " is not indexed"); + + Dependency existingDep = getEdge(from, to); if (existingDep != null) { return existingDep; } @@ -258,7 +270,7 @@ public class DefaultIndex extends SonarIndex { } if (registerDependency(dependency)) { - persistence.saveDependency(currentProject, dependency, parentDependency); + dependencyPersister.saveDependency(currentProject, from, to, dependency, parentDependency); } return dependency; } @@ -439,12 +451,12 @@ public class DefaultIndex extends SonarIndex { @Override public void addLink(ProjectLink link) { - persistence.saveLink(currentProject, link); + linkPersister.saveLink(currentProject, link); } @Override public void deleteLink(String key) { - persistence.deleteLink(currentProject, key); + linkPersister.deleteLink(currentProject, key); } // @@ -458,12 +470,16 @@ public class DefaultIndex extends SonarIndex { @Override public List<Event> getEvents(Resource resource) { // currently events are not cached in memory - return persistence.getEvents(resource); + Resource reload = getResource(resource); + if (reload == null) { + return Collections.emptyList(); + } + return eventPersister.getEvents(reload); } @Override public void deleteEvent(Event event) { - persistence.deleteEvent(event); + eventPersister.deleteEvent(event); } @Override @@ -472,7 +488,7 @@ public class DefaultIndex extends SonarIndex { event.setDate(date); event.setCreatedAt(new Date()); - persistence.saveEvent(resource, event); + eventPersister.saveEvent(resource, event); return null; } @@ -579,9 +595,9 @@ public class DefaultIndex extends SonarIndex { addBucket(resource, bucket); Resource parentResource = parentBucket != null ? parentBucket.getResource() : null; - Snapshot snapshot = persistence.saveResource(currentProject, resource, parentResource); + BatchResource batchResource = resourcePersister.saveResource(currentProject, resource, parentResource); if (ResourceUtils.isPersistable(resource) && !Qualifiers.LIBRARY.equals(resource.getQualifier())) { - graph.addComponent(resource, snapshot); + graph.addComponent(resource, batchResource.snapshotId()); } return bucket; diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java deleted file mode 100644 index 8e36e703b28..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.batch.index; - -import org.sonar.api.batch.Event; -import org.sonar.api.database.model.Snapshot; -import org.sonar.api.design.Dependency; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectLink; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; - -import javax.annotation.Nullable; - -import java.util.List; - -public final class DefaultPersistenceManager implements PersistenceManager { - - private ResourcePersister resourcePersister; - private DependencyPersister dependencyPersister; - private LinkPersister linkPersister; - private EventPersister eventPersister; - - public DefaultPersistenceManager(ResourcePersister resourcePersister, - DependencyPersister dependencyPersister, LinkPersister linkPersister, EventPersister eventPersister) { - this.resourcePersister = resourcePersister; - this.dependencyPersister = dependencyPersister; - this.linkPersister = linkPersister; - this.eventPersister = eventPersister; - } - - @Override - public void clear() { - resourcePersister.clear(); - } - - @Override - public void saveProject(Project project, @Nullable Project parent) { - resourcePersister.saveProject(project, parent); - } - - @Override - public Snapshot saveResource(Project project, Resource resource, @Nullable Resource parent) { - if (ResourceUtils.isPersistable(resource)) { - return resourcePersister.saveResource(project, resource, parent); - } - return null; - } - - @Override - public void saveDependency(Project project, Dependency dependency, Dependency parentDependency) { - if (ResourceUtils.isPersistable(dependency.getFrom()) && ResourceUtils.isPersistable(dependency.getTo())) { - dependencyPersister.saveDependency(project, dependency, parentDependency); - } - } - - @Override - public void saveLink(Project project, ProjectLink link) { - linkPersister.saveLink(project, link); - } - - @Override - public void deleteLink(Project project, String key) { - linkPersister.deleteLink(project, key); - } - - @Override - public List<Event> getEvents(Resource resource) { - return eventPersister.getEvents(resource); - } - - @Override - public void deleteEvent(Event event) { - eventPersister.deleteEvent(event); - } - - @Override - public void saveEvent(Resource resource, Event event) { - if (ResourceUtils.isPersistable(resource)) { - eventPersister.saveEvent(resource, event); - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java index 0f7faddb25f..b7d5418555c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java @@ -19,14 +19,11 @@ */ package org.sonar.batch.index; -import com.google.common.collect.Maps; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.fs.InputFile; import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.ResourceModel; import org.sonar.api.database.model.Snapshot; -import org.sonar.api.resources.File; import org.sonar.api.resources.Language; import org.sonar.api.resources.Library; import org.sonar.api.resources.Project; @@ -38,15 +35,12 @@ import org.sonar.api.security.ResourcePermissions; import org.sonar.api.utils.SonarException; import org.sonar.api.utils.internal.Uuids; -import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.persistence.NonUniqueResultException; import javax.persistence.Query; import java.util.Date; -import java.util.Iterator; import java.util.List; -import java.util.Map; public final class DefaultResourcePersister implements ResourcePersister { @@ -57,37 +51,26 @@ public final class DefaultResourcePersister implements ResourcePersister { private static final String QUALIFIER = "qualifier"; private final DatabaseSession session; - private final Map<Resource, Snapshot> snapshotsByResource = Maps.newHashMap(); private final ResourcePermissions permissions; - private final SnapshotCache snapshotCache; private final ResourceCache resourceCache; - public DefaultResourcePersister(DatabaseSession session, ResourcePermissions permissions, SnapshotCache snapshotCache, ResourceCache resourceCache) { + public DefaultResourcePersister(DatabaseSession session, ResourcePermissions permissions, ResourceCache resourceCache) { this.session = session; this.permissions = permissions; - this.snapshotCache = snapshotCache; this.resourceCache = resourceCache; } @Override - public Snapshot saveProject(Project project, @Nullable Project parent) { - Snapshot snapshot = snapshotsByResource.get(project); - if (snapshot == null) { - snapshot = persistProject(project, parent); + public void saveProject(Project project, @Nullable Project parent) { + BatchResource batchResource = resourceCache.get(project.getEffectiveKey()); + if (batchResource == null) { + Snapshot snapshot = persistProject(project, parent); addToCache(project, snapshot); } - return snapshot; } - private void addToCache(Resource resource, Snapshot snapshot) { - if (snapshot != null) { - snapshotsByResource.put(resource, snapshot); - resourceCache.add(resource); - if (!(resource instanceof Library)) { - // Maven libraries can have the same effective key than a project so we can't cache by effectiveKey - snapshotCache.put(resource.getEffectiveKey(), snapshot); - } - } + private BatchResource addToCache(Resource resource, Snapshot snapshot) { + return resourceCache.add(resource, snapshot); } private Snapshot persistProject(Project project, @Nullable Project parent) { @@ -108,7 +91,7 @@ public final class DefaultResourcePersister implements ResourcePersister { Snapshot parentSnapshot = null; if (parent != null) { // assume that the parent project has already been saved - parentSnapshot = snapshotsByResource.get(project.getParent()); + parentSnapshot = resourceCache.get(project.getParent().getEffectiveKey()).snapshot(); model.setRootId((Integer) ObjectUtils.defaultIfNull(parentSnapshot.getRootProjectId(), parentSnapshot.getResourceId())); } else { model.setRootId(null); @@ -132,49 +115,18 @@ public final class DefaultResourcePersister implements ResourcePersister { } @Override - @CheckForNull - public Snapshot getSnapshot(@Nullable Resource reference) { - return snapshotsByResource.get(reference); - } - - @Override - public Snapshot getSnapshotOrFail(Resource resource) { - Snapshot snapshot = getSnapshot(resource); - if (snapshot == null) { - throw new ResourceNotPersistedException(resource); - } - return snapshot; - } - - @Override - public Snapshot getSnapshotOrFail(InputFile inputFile) { - return getSnapshotOrFail(fromInputFile(inputFile)); - } - - private Resource fromInputFile(InputFile inputFile) { - return File.create(inputFile.relativePath()); - } - - /** - * just for unit tests - */ - Map<Resource, Snapshot> getSnapshotsByResource() { - return snapshotsByResource; - } - - @Override - public Snapshot saveResource(Project project, Resource resource) { + public BatchResource saveResource(Project project, Resource resource) { return saveResource(project, resource, null); } @Override - public Snapshot saveResource(Project project, Resource resource, @Nullable Resource parent) { - Snapshot snapshot = snapshotsByResource.get(resource); - if (snapshot == null) { - snapshot = persist(project, resource, parent); - addToCache(resource, snapshot); + public BatchResource saveResource(Project project, Resource resource, @Nullable Resource parent) { + BatchResource batchResource = resourceCache.get(resource.getEffectiveKey()); + if (batchResource == null || ResourceUtils.isLibrary(resource)) { + Snapshot s = persist(project, resource, parent); + batchResource = addToCache(resource, s); } - return snapshot; + return batchResource; } private Snapshot persist(Project project, Resource resource, @Nullable Resource parent) { @@ -237,15 +189,21 @@ public final class DefaultResourcePersister implements ResourcePersister { * Everything except project and library */ private Snapshot persistFileOrDirectory(Project project, Resource resource, @Nullable Resource parentReference) { - Snapshot moduleSnapshot = snapshotsByResource.get(project); - Integer moduleId = moduleSnapshot.getResourceId(); + BatchResource moduleResource = resourceCache.get(project.getEffectiveKey()); + Integer moduleId = moduleResource.resource().getId(); ResourceModel model = findOrCreateModel(resource, parentReference != null ? parentReference : project); model.setRootId(moduleId); model = session.save(model); resource.setId(model.getId()); resource.setUuid(model.getUuid()); - Snapshot parentSnapshot = (Snapshot) ObjectUtils.defaultIfNull(getSnapshot(parentReference), moduleSnapshot); + Snapshot parentSnapshot; + if (parentReference != null) { + parentSnapshot = resourceCache.get(parentReference.getEffectiveKey()).snapshot(); + } else { + parentSnapshot = moduleResource.snapshot(); + } + Snapshot snapshot = new Snapshot(model, parentSnapshot); snapshot.setBuildDate(new Date()); snapshot = session.save(snapshot); @@ -253,33 +211,6 @@ public final class DefaultResourcePersister implements ResourcePersister { return snapshot; } - @Override - @CheckForNull - public Snapshot getLastSnapshot(Snapshot snapshot, boolean onlyOlder) { - String hql = "SELECT s FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=:last AND s.resourceId=:resourceId"; - if (onlyOlder) { - hql += " AND s.createdAt<:date"; - } - Query query = session.createQuery(hql); - query.setParameter(LAST, true); - query.setParameter(RESOURCE_ID, snapshot.getResourceId()); - if (onlyOlder) { - query.setParameter("date", snapshot.getCreatedAt()); - } - return session.getSingleResult(query, null); - } - - @Override - public void clear() { - // we keep cache of projects - for (Iterator<Map.Entry<Resource, Snapshot>> it = snapshotsByResource.entrySet().iterator(); it.hasNext();) { - Map.Entry<Resource, Snapshot> entry = it.next(); - if (!ResourceUtils.isSet(entry.getKey())) { - it.remove(); - } - } - } - private ResourceModel findOrCreateModel(Resource resource, @Nullable Resource parentResource) { ResourceModel model; try { diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java index e20680b10fc..15886b6b4ac 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DependencyPersister.java @@ -20,38 +20,38 @@ package org.sonar.batch.index; import org.sonar.api.database.DatabaseSession; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.design.Dependency; import org.sonar.api.design.DependencyDto; import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; public final class DependencyPersister { - private ResourcePersister resourcePersister; + private ResourceCache resourceCache; private DatabaseSession session; - public DependencyPersister(ResourcePersister resourcePersister, DatabaseSession session) { - this.resourcePersister = resourcePersister; + public DependencyPersister(ResourceCache resourceCache, DatabaseSession session) { + this.resourceCache = resourceCache; this.session = session; } - public void saveDependency(Project project, Dependency dependency, Dependency parentDependency) { - Snapshot fromSnapshot = resourcePersister.saveResource(project, dependency.getFrom()); - Snapshot toSnapshot = resourcePersister.saveResource(project, dependency.getTo()); - Snapshot projectSnapshot = resourcePersister.getSnapshotOrFail(project); + public void saveDependency(Project project, Resource from, Resource to, Dependency dependency, Dependency parentDependency) { + BatchResource fromResource = resourceCache.get(from); + BatchResource toResource = resourceCache.get(to); + BatchResource projectResource = resourceCache.get(project); DependencyDto model = new DependencyDto(); - model.setProjectSnapshotId(projectSnapshot.getId()); + model.setProjectSnapshotId(projectResource.snapshotId()); model.setUsage(dependency.getUsage()); model.setWeight(dependency.getWeight()); - model.setFromResourceId(fromSnapshot.getResourceId()); - model.setFromScope(fromSnapshot.getScope()); - model.setFromSnapshotId(fromSnapshot.getId()); + model.setFromResourceId(fromResource.resource().getId()); + model.setFromScope(fromResource.resource().getScope()); + model.setFromSnapshotId(fromResource.snapshotId()); - model.setToResourceId(toSnapshot.getResourceId()); - model.setToSnapshotId(toSnapshot.getId()); - model.setToScope(toSnapshot.getScope()); + model.setToResourceId(toResource.resource().getId()); + model.setToScope(toResource.resource().getScope()); + model.setToSnapshotId(toResource.snapshotId()); if (parentDependency != null) { // assume that it has been previously saved diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DuplicationPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DuplicationPersister.java index ae234e553cf..ce3067839f4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DuplicationPersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DuplicationPersister.java @@ -22,11 +22,9 @@ package org.sonar.batch.index; import org.sonar.api.batch.sensor.duplication.DuplicationGroup; import org.sonar.api.database.model.MeasureMapper; import org.sonar.api.database.model.MeasureModel; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.PersistenceMode; -import org.sonar.api.resources.Resource; import org.sonar.api.rules.RuleFinder; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.duplication.DuplicationUtils; @@ -39,17 +37,14 @@ import java.util.List; public final class DuplicationPersister implements ScanPersister { private final MyBatis mybatis; private final RuleFinder ruleFinder; - private final SnapshotCache snapshotCache; private final ResourceCache resourceCache; private final DuplicationCache duplicationCache; private final org.sonar.api.measures.MetricFinder metricFinder; - public DuplicationPersister(MyBatis mybatis, RuleFinder ruleFinder, - SnapshotCache snapshotCache, ResourceCache resourceCache, + public DuplicationPersister(MyBatis mybatis, RuleFinder ruleFinder, ResourceCache resourceCache, DuplicationCache duplicationCache, org.sonar.api.measures.MetricFinder metricFinder) { this.mybatis = mybatis; this.ruleFinder = ruleFinder; - this.snapshotCache = snapshotCache; this.resourceCache = resourceCache; this.duplicationCache = duplicationCache; this.metricFinder = metricFinder; @@ -65,11 +60,10 @@ public final class DuplicationPersister implements ScanPersister { for (Entry<List<DuplicationGroup>> entry : duplicationCache.entries()) { String effectiveKey = entry.key()[0].toString(); Measure measure = new Measure(duplicationMetricWithId, DuplicationUtils.toXml(entry.value())).setPersistenceMode(PersistenceMode.DATABASE); - Resource resource = resourceCache.get(effectiveKey); + BatchResource batchResource = resourceCache.get(effectiveKey); - if (MeasurePersister.shouldPersistMeasure(resource, measure)) { - Snapshot snapshot = snapshotCache.get(effectiveKey); - MeasureModel measureModel = MeasurePersister.model(measure, ruleFinder).setSnapshotId(snapshot.getId()); + if (MeasurePersister.shouldPersistMeasure(batchResource.resource(), measure)) { + MeasureModel measureModel = MeasurePersister.model(measure, ruleFinder).setSnapshotId(batchResource.snapshotId()); mapper.insert(measureModel); session.commit(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java index 3968464c9b1..36ac996ae58 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java @@ -21,27 +21,21 @@ package org.sonar.batch.index; import org.sonar.api.batch.Event; import org.sonar.api.database.DatabaseSession; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Resource; -import java.util.Collections; import java.util.List; -public final class EventPersister { +public class EventPersister { private DatabaseSession session; - private ResourcePersister resourcePersister; + private ResourceCache resourceCache; - public EventPersister(DatabaseSession session, ResourcePersister resourcePersister) { + public EventPersister(DatabaseSession session, ResourceCache resourceCache) { this.session = session; - this.resourcePersister = resourcePersister; + this.resourceCache = resourceCache; } public List<Event> getEvents(Resource resource) { - Snapshot snapshot = resourcePersister.getSnapshot(resource); - if (snapshot == null) { - return Collections.emptyList(); - } - return session.getResults(Event.class, "resourceId", snapshot.getResourceId()); + return session.getResults(Event.class, "resourceId", resource.getId()); } public void deleteEvent(Event event) { @@ -50,11 +44,11 @@ public final class EventPersister { } public void saveEvent(Resource resource, Event event) { - Snapshot snapshot = resourcePersister.getSnapshotOrFail(resource); + BatchResource batchResource = resourceCache.get(resource.getEffectiveKey()); if (event.getDate() == null) { - event.setSnapshot(snapshot); + event.setSnapshot(batchResource.snapshot()); } else { - event.setResourceId(snapshot.getResourceId()); + event.setResourceId(batchResource.resource().getId()); } session.save(event); session.commit(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/FileHashesPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/FileHashesPersister.java index 9ecc91bf0cf..85e182281fa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/FileHashesPersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/FileHashesPersister.java @@ -19,28 +19,25 @@ */ package org.sonar.batch.index; -import org.sonar.api.database.model.Snapshot; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.SnapshotDataDao; import org.sonar.core.source.db.SnapshotDataDto; -import java.util.Map; - /** * Store file hashes in snapshot_data for reuse in next analysis to know if a file is modified */ public class FileHashesPersister implements ScanPersister { private final ComponentDataCache data; - private final SnapshotCache snapshots; + private final ResourceCache resourceCache; private final SnapshotDataDao dao; private final MyBatis mybatis; - public FileHashesPersister(ComponentDataCache data, SnapshotCache snapshots, + public FileHashesPersister(ComponentDataCache data, ResourceCache resourceCache, SnapshotDataDao dao, MyBatis mybatis) { this.data = data; - this.snapshots = snapshots; + this.resourceCache = resourceCache; this.dao = dao; this.mybatis = mybatis; } @@ -49,14 +46,13 @@ public class FileHashesPersister implements ScanPersister { public void persist() { DbSession session = mybatis.openSession(true); try { - for (Map.Entry<String, Snapshot> componentEntry : snapshots.snapshots()) { - String componentKey = componentEntry.getKey(); - Snapshot snapshot = componentEntry.getValue(); + for (BatchResource batchResource : resourceCache.all()) { + String componentKey = batchResource.resource().getEffectiveKey(); String fileHashesdata = data.getStringData(componentKey, SnapshotDataTypes.FILE_HASHES); if (fileHashesdata != null) { SnapshotDataDto dto = new SnapshotDataDto(); - dto.setSnapshotId(snapshot.getId()); - dto.setResourceId(snapshot.getResourceId()); + dto.setSnapshotId(batchResource.snapshotId()); + dto.setResourceId(batchResource.resource().getId()); dto.setDataType(SnapshotDataTypes.FILE_HASHES); dto.setData(fileHashesdata); dao.insert(session, dto); diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/LinkPersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/LinkPersister.java index b29a66d8c9a..3ab649150ae 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/LinkPersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/LinkPersister.java @@ -21,22 +21,21 @@ package org.sonar.batch.index; import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.ResourceModel; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Project; import org.sonar.api.resources.ProjectLink; public final class LinkPersister { private DatabaseSession session; - private ResourcePersister resourcePersister; + private ResourceCache resourceCache; - public LinkPersister(DatabaseSession session, ResourcePersister resourcePersister) { + public LinkPersister(DatabaseSession session, ResourceCache resourcePersister) { this.session = session; - this.resourcePersister = resourcePersister; + this.resourceCache = resourcePersister; } public void saveLink(Project project, ProjectLink link) { - Snapshot snapshot = resourcePersister.getSnapshotOrFail(project); - ResourceModel projectDao = session.reattach(ResourceModel.class, snapshot.getResourceId()); + BatchResource batchResource = resourceCache.get(project.getEffectiveKey()); + ResourceModel projectDao = session.reattach(ResourceModel.class, batchResource.resource().getId()); ProjectLink dbLink = projectDao.getProjectLink(link.getKey()); if (dbLink == null) { link.setResource(projectDao); @@ -52,9 +51,9 @@ public final class LinkPersister { } public void deleteLink(Project project, String linkKey) { - Snapshot snapshot = resourcePersister.getSnapshot(project); - if (snapshot != null) { - ResourceModel model = session.reattach(ResourceModel.class, snapshot.getResourceId()); + BatchResource batchResource = resourceCache.get(project.getEffectiveKey()); + if (batchResource != null) { + ResourceModel model = session.reattach(ResourceModel.class, batchResource.resource().getId()); ProjectLink dbLink = model.getProjectLink(linkKey); if (dbLink != null) { session.remove(dbLink); diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java index 6c382abfa1a..4d4b2b26433 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java @@ -22,7 +22,6 @@ package org.sonar.batch.index; import com.google.common.annotations.VisibleForTesting; import org.sonar.api.database.model.MeasureMapper; import org.sonar.api.database.model.MeasureModel; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.measures.Measure; import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Resource; @@ -42,15 +41,13 @@ public final class MeasurePersister implements ScanPersister { private final MyBatis mybatis; private final RuleFinder ruleFinder; private final MeasureCache measureCache; - private final SnapshotCache snapshotCache; private final ResourceCache resourceCache; public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder, - MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) { + MeasureCache measureCache, ResourceCache resourceCache) { this.mybatis = mybatis; this.ruleFinder = ruleFinder; this.measureCache = measureCache; - this.snapshotCache = snapshotCache; this.resourceCache = resourceCache; } @@ -63,11 +60,10 @@ public final class MeasurePersister implements ScanPersister { for (Entry<Measure> entry : measureCache.entries()) { String effectiveKey = entry.key()[0].toString(); Measure measure = entry.value(); - Resource resource = resourceCache.get(effectiveKey); + BatchResource batchResource = resourceCache.get(effectiveKey); - if (shouldPersistMeasure(resource, measure)) { - Snapshot snapshot = snapshotCache.get(effectiveKey); - MeasureModel measureModel = model(measure, ruleFinder).setSnapshotId(snapshot.getId()); + if (shouldPersistMeasure(batchResource.resource(), measure)) { + MeasureModel measureModel = model(measure, ruleFinder).setSnapshotId(batchResource.snapshotId()); mapper.insert(measureModel); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ResourceCache.java b/sonar-batch/src/main/java/org/sonar/batch/index/ResourceCache.java index 329a0184b46..088a50de84c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/ResourceCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/ResourceCache.java @@ -23,25 +23,51 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Maps; import org.sonar.api.BatchComponent; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Library; import org.sonar.api.resources.Resource; +import javax.annotation.CheckForNull; + +import java.util.Collection; import java.util.Map; -/** - * @since 3.6 - */ public class ResourceCache implements BatchComponent { // resource by component key - private final Map<String, Resource> resources = Maps.newHashMap(); + private final Map<String, BatchResource> resources = Maps.newHashMap(); + // dedicated cache for libraries + private final Map<Library, BatchResource> libraries = Maps.newHashMap(); - public Resource get(String componentKey) { + @CheckForNull + public BatchResource get(String componentKey) { return resources.get(componentKey); } - public ResourceCache add(Resource resource) { + @CheckForNull + public BatchResource get(Resource resource) { + if (!(resource instanceof Library)) { + return resources.get(resource.getEffectiveKey()); + } else { + return libraries.get(resource); + } + } + + public BatchResource add(Resource resource, Snapshot s) { String componentKey = resource.getEffectiveKey(); Preconditions.checkState(!Strings.isNullOrEmpty(componentKey), "Missing resource effective key"); - resources.put(componentKey, resource); - return this; + Resource parentResource = resource.getParent(); + BatchResource parent = parentResource != null ? get(parentResource.getEffectiveKey()) : null; + BatchResource batchResource = new BatchResource((long) resources.size() + 1, resource, s, parent); + if (!(resource instanceof Library)) { + // Libraries can have the same effective key than a project so we can't cache by effectiveKey + resources.put(componentKey, batchResource); + } else { + libraries.put((Library) resource, batchResource); + } + return batchResource; + } + + public Collection<BatchResource> all() { + return resources.values(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java index d5a535dc27a..edb47fb6f54 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java @@ -19,41 +19,23 @@ */ package org.sonar.batch.index; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; -import javax.annotation.CheckForNull; import javax.annotation.Nullable; public interface ResourcePersister { - Snapshot saveProject(Project project, @Nullable Project parent); + void saveProject(Project project, @Nullable Project parent); /** - * Persist a resource in database. Returns null if the resource must not be persisted (scope lower than file) + * Persist a resource in database. */ - Snapshot saveResource(Project project, Resource resource, @Nullable Resource parent); + BatchResource saveResource(Project project, Resource resource, @Nullable Resource parent); /** - * Persist a resource in database. Returns null if the resource must not be persisted (scope lower than file) + * Persist a resource in database. */ - Snapshot saveResource(Project project, Resource resource); + BatchResource saveResource(Project project, Resource resource); - @CheckForNull - Snapshot getSnapshot(Resource resource); - - Snapshot getSnapshotOrFail(Resource resource); - - Snapshot getSnapshotOrFail(InputFile resource); - - /** - * The current snapshot which is flagged as "last", different than the current analysis. - * @param onlyOlder true if the result must be anterior to the snapshot parameter - */ - @CheckForNull - Snapshot getLastSnapshot(Snapshot snapshot, boolean onlyOlder); - - void clear(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java index 186e37edaa6..5f10cbabf58 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java @@ -130,7 +130,7 @@ public class SourcePersister implements ScanPersister { private void persist(DbSession session, FileSourceMapper mapper, InputPath inputPath, Map<String, FileSourceDto> fileSourceDtoByFileUuid) { DefaultInputFile inputFile = (DefaultInputFile) inputPath; LOG.debug("Processing {}", inputFile.absolutePath()); - org.sonar.api.resources.File file = (org.sonar.api.resources.File) resourceCache.get(inputFile.key()); + org.sonar.api.resources.File file = (org.sonar.api.resources.File) resourceCache.get(inputFile.key()).resource(); String fileUuid = file.getUuid(); FileSourceDto previous = fileSourceDtoByFileUuid.get(fileUuid); String newData = getSourceData(inputFile); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DeprecatedViolations.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DeprecatedViolations.java index 883c854af76..13e03a3f9c6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/DeprecatedViolations.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DeprecatedViolations.java @@ -59,7 +59,7 @@ public class DeprecatedViolations implements BatchComponent { public Violation toViolation(DefaultIssue issue) { Rule rule = ruleFinder.findByKey(issue.ruleKey()); - Resource resource = resourceCache.get(issue.componentKey()); + Resource resource = resourceCache.get(issue.componentKey()).resource(); Violation violation = new Violation(rule, resource); violation.setNew(issue.isNew()); violation.setChecksum(issue.checksum()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java index 040bb9cce0e..87e93205009 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java @@ -21,13 +21,13 @@ package org.sonar.batch.issue; import com.google.common.annotations.VisibleForTesting; import org.sonar.api.BatchComponent; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.batch.ProjectTree; -import org.sonar.batch.index.SnapshotCache; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.issue.db.IssueDto; import org.sonar.core.issue.db.IssueMapper; import org.sonar.core.issue.db.IssueStorage; @@ -44,14 +44,14 @@ import java.util.List; public class ScanIssueStorage extends IssueStorage implements BatchComponent { - private final SnapshotCache snapshotCache; + private final ResourceCache resourceCache; private final ResourceDao resourceDao; private final ProjectTree projectTree; private final UpdateConflictResolver conflictResolver = new UpdateConflictResolver(); - public ScanIssueStorage(MyBatis mybatis, RuleFinder ruleFinder, SnapshotCache snapshotCache, ResourceDao resourceDao, ProjectTree projectTree) { + public ScanIssueStorage(MyBatis mybatis, RuleFinder ruleFinder, ResourceCache resourceCache, ResourceDao resourceDao, ProjectTree projectTree) { super(mybatis, ruleFinder); - this.snapshotCache = snapshotCache; + this.resourceCache = resourceCache; this.resourceDao = resourceDao; this.projectTree = projectTree; } @@ -90,9 +90,9 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent { @VisibleForTesting long componentId(DefaultIssue issue) { - Snapshot snapshot = snapshotCache.get(issue.componentKey()); - if (snapshot != null) { - return snapshot.getResourceId(); + BatchResource resource = resourceCache.get(issue.componentKey()); + if (resource != null) { + return resource.resource().getId(); } // Load from db when component does not exist in cache (deleted file for example) diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java index aba3f6fa9e2..286a20476b4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java @@ -28,9 +28,9 @@ import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.events.BatchStepEvent; import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.PersistenceManager; import org.sonar.batch.index.ScanPersister; import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader; +import org.sonar.batch.report.PublishReportJob; import org.sonar.batch.rule.QProfileVerifier; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.FileSystemLogger; @@ -50,8 +50,7 @@ public final class PhaseExecutor { private final PostJobsExecutor postJobsExecutor; private final InitializersExecutor initializersExecutor; private final SensorsExecutor sensorsExecutor; - private final UpdateStatusJob updateStatusJob; - private final PersistenceManager persistenceManager; + private final PublishReportJob publishReportJob; private final SensorContext sensorContext; private final DefaultIndex index; private final ProjectInitializer pi; @@ -66,8 +65,8 @@ public final class PhaseExecutor { public PhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, - PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index, - EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi, + SensorContext sensorContext, DefaultIndex index, + EventBus eventBus, PublishReportJob publishReportJob, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, AnalysisMode analysisMode) { this.phases = phases; @@ -76,11 +75,10 @@ public final class PhaseExecutor { this.postJobsExecutor = postJobsExecutor; this.initializersExecutor = initializersExecutor; this.sensorsExecutor = sensorsExecutor; - this.persistenceManager = persistenceManager; this.sensorContext = sensorContext; this.index = index; this.eventBus = eventBus; - this.updateStatusJob = updateStatusJob; + this.publishReportJob = publishReportJob; this.pi = pi; this.persisters = persisters; this.fsLogger = fsLogger; @@ -94,7 +92,7 @@ public final class PhaseExecutor { public static Collection<Class> getPhaseClasses() { return Lists.<Class>newArrayList(DecoratorsExecutor.class, MavenPluginsConfigurator.class, PostJobsExecutor.class, SensorsExecutor.class, - InitializersExecutor.class, ProjectInitializer.class, UpdateStatusJob.class); + InitializersExecutor.class, ProjectInitializer.class, PublishReportJob.class); } /** @@ -130,7 +128,7 @@ public final class PhaseExecutor { jsonReport.execute(); executePersisters(); - updateStatusJob(); + publishReportJob(); if (phases.isEnabled(Phases.Phase.POSTJOB)) { postJobsExecutor.execute(sensorContext); } @@ -168,13 +166,11 @@ public final class PhaseExecutor { } } - private void updateStatusJob() { - if (updateStatusJob != null) { - String stepName = "Update status job"; - eventBus.fireEvent(new BatchStepEvent(stepName, true)); - this.updateStatusJob.execute(); - eventBus.fireEvent(new BatchStepEvent(stepName, false)); - } + private void publishReportJob() { + String stepName = "Publish report"; + eventBus.fireEvent(new BatchStepEvent(stepName, true)); + this.publishReportJob.execute(); + eventBus.fireEvent(new BatchStepEvent(stepName, false)); } private void executeInitializersPhase() { @@ -195,7 +191,6 @@ public final class PhaseExecutor { private void cleanMemory() { String cleanMemory = "Clean memory"; eventBus.fireEvent(new BatchStepEvent(cleanMemory, true)); - persistenceManager.clear(); index.clear(); eventBus.fireEvent(new BatchStepEvent(cleanMemory, false)); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/UpdateStatusJob.java b/sonar-batch/src/main/java/org/sonar/batch/phases/UpdateStatusJob.java deleted file mode 100644 index de7c51bb02b..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/UpdateStatusJob.java +++ /dev/null @@ -1,92 +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.batch.phases; - -import com.google.common.annotations.VisibleForTesting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.BatchComponent; -import org.sonar.api.CoreProperties; -import org.sonar.api.config.Settings; -import org.sonar.api.database.model.Snapshot; -import org.sonar.api.resources.Project; -import org.sonar.batch.bootstrap.AnalysisMode; -import org.sonar.batch.bootstrap.ServerClient; - -public class UpdateStatusJob implements BatchComponent { - - private static final Logger LOG = LoggerFactory.getLogger(UpdateStatusJob.class); - - private ServerClient server; - // TODO remove this component - private Snapshot snapshot; - private Settings settings; - private Project project; - private AnalysisMode analysisMode; - - public UpdateStatusJob(Settings settings, ServerClient server, - Project project, Snapshot snapshot, AnalysisMode analysisMode) { - this.server = server; - this.project = project; - this.snapshot = snapshot; - this.settings = settings; - this.analysisMode = analysisMode; - } - - public void execute() { - uploadReport(); - logSuccess(LoggerFactory.getLogger(getClass())); - } - - @VisibleForTesting - void uploadReport() { - if (analysisMode.isPreview()) { - // If this is a preview analysis then we should not upload reports - return; - } - String url = "/batch/upload_report?project=" + project.getEffectiveKey() + "&snapshot=" + snapshot.getId(); - try { - LOG.debug("Publish results"); - server.request(url, "POST"); - } catch (Exception e) { - throw new IllegalStateException("Unable to publish results: " + url, e); - } - } - - @VisibleForTesting - void logSuccess(Logger logger) { - if (analysisMode.isPreview()) { - logger.info("ANALYSIS SUCCESSFUL"); - - } else { - String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL); - if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) { - // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side - baseUrl = server.getURL(); - } - if (!baseUrl.endsWith("/")) { - baseUrl += "/"; - } - String url = baseUrl + "dashboard/index/" + project.getKey(); - logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url); - logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java index 36eabaec79f..bf709694f6f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java +++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java @@ -38,6 +38,7 @@ import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.qualitygate.db.QualityGateConditionDto; import org.sonar.core.timemachine.Periods; @@ -59,14 +60,14 @@ public class QualityGateVerifier implements Decorator { private QualityGate qualityGate; - private Snapshot snapshot; private Periods periods; private I18n i18n; private Durations durations; + private ResourceCache resourceCache; - public QualityGateVerifier(QualityGate qualityGate, Snapshot snapshot, Periods periods, I18n i18n, Durations durations) { + public QualityGateVerifier(QualityGate qualityGate, ResourceCache resourceCache, Periods periods, I18n i18n, Durations durations) { this.qualityGate = qualityGate; - this.snapshot = snapshot; + this.resourceCache = resourceCache; this.periods = periods; this.i18n = i18n; this.durations = durations; @@ -99,11 +100,11 @@ public class QualityGateVerifier implements Decorator { @Override public void decorate(Resource resource, DecoratorContext context) { if (ResourceUtils.isRootProject(resource)) { - checkProjectConditions(context); + checkProjectConditions(resource, context); } } - private void checkProjectConditions(DecoratorContext context) { + private void checkProjectConditions(Resource project, DecoratorContext context) { Metric.Level globalLevel = Metric.Level.OK; QualityGateDetails details = new QualityGateDetails(); List<String> labels = Lists.newArrayList(); @@ -114,7 +115,7 @@ public class QualityGateVerifier implements Decorator { Metric.Level level = ConditionUtils.getLevel(condition, measure); measure.setAlertStatus(level); - String text = getText(condition, level); + String text = getText(project, condition, level); if (!StringUtils.isBlank(text)) { measure.setAlertText(text); labels.add(text); @@ -144,14 +145,14 @@ public class QualityGateVerifier implements Decorator { } - private String getText(ResolvedCondition condition, Metric.Level level) { + private String getText(Resource project, ResolvedCondition condition, Metric.Level level) { if (level == Metric.Level.OK) { return null; } - return getAlertLabel(condition, level); + return getAlertLabel(project, condition, level); } - private String getAlertLabel(ResolvedCondition condition, Metric.Level level) { + private String getAlertLabel(Resource project, ResolvedCondition condition, Metric.Level level) { Integer alertPeriod = condition.period(); String metric = i18n.message(Locale.ENGLISH, "metric." + condition.metricKey() + ".name", condition.metric().getName()); @@ -168,6 +169,7 @@ public class QualityGateVerifier implements Decorator { .append(alertValue(condition, level)); if (alertPeriod != null) { + Snapshot snapshot = resourceCache.get(project.getEffectiveKey()).snapshot(); stringBuilder.append(" ").append(periods.label(snapshot, alertPeriod)); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java new file mode 100644 index 00000000000..2ac67b1f3b2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java @@ -0,0 +1,99 @@ +/* + * 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.batch.report; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonWriter; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.protocol.GsonHelper; +import org.sonar.batch.protocol.output.issue.ReportIssue; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +public class IssuesPublisher implements ReportPublisher { + + private final ResourceCache resourceCache; + private final IssueCache issueCache; + + public IssuesPublisher(ResourceCache resourceCache, IssueCache issueCache) { + this.resourceCache = resourceCache; + this.issueCache = issueCache; + } + + @Override + public void export(File reportDir) throws IOException { + Gson gson = GsonHelper.create(); + File issuesFile = new File(reportDir, "issues.json"); + OutputStream out = new BufferedOutputStream(new FileOutputStream(issuesFile)); + + JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8")); + writer.setIndent(" "); + writer.beginArray(); + for (DefaultIssue issue : issueCache.all()) { + ReportIssue reportIssue = toReportIssue(issue); + gson.toJson(reportIssue, ReportIssue.class, writer); + } + writer.endArray(); + writer.close(); + } + + private ReportIssue toReportIssue(DefaultIssue issue) { + long componentBatchId = resourceCache.get(issue.componentKey()).batchId(); + return new ReportIssue() + .setKey(issue.key()) + .setResourceBatchId(componentBatchId) + .setNew(issue.isNew()) + .setLine(issue.line()) + .setMessage(issue.message()) + .setEffortToFix(issue.effortToFix()) + .setDebt(issue.debtInMinutes()) + .setResolution(issue.resolution()) + .setStatus(issue.status()) + .setSeverity(issue.severity()) + .setChecksum(issue.checksum()) + .setManualSeverity(issue.manualSeverity()) + .setReporter(issue.reporter()) + .setAssignee(issue.assignee()) + .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) + .setActionPlanKey(issue.actionPlanKey()) + .setAttributes(KeyValueFormat.format(issue.attributes())) + .setAuthorLogin(issue.authorLogin()) + .setCreationDate(issue.creationDate()) + .setCloseDate(issue.closeDate()) + .setUpdateDate(issue.updateDate()) + .setSelectedAt(issue.selectedAt()) + .setDiffFields(toString(issue.currentChange())) + .setChanged(issue.isChanged()); + } + + private String toString(FieldDiffs currentChange) { + return currentChange != null ? currentChange.toString() : null; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java b/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java new file mode 100644 index 00000000000..16b5dbd59d7 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java @@ -0,0 +1,147 @@ +/* + * 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.batch.report; + +import com.github.kevinsawicki.http.HttpRequest; +import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchComponent; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.api.platform.Server; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.TempFolder; +import org.sonar.api.utils.ZipUtils; +import org.sonar.batch.bootstrap.AnalysisMode; +import org.sonar.batch.bootstrap.ServerClient; +import org.sonar.batch.index.ResourceCache; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +public class PublishReportJob implements BatchComponent { + + private static final Logger LOG = LoggerFactory.getLogger(PublishReportJob.class); + + private final ServerClient serverClient; + private final Server server; + private final Settings settings; + private final Project project; + private final AnalysisMode analysisMode; + private final ResourceCache resourceCache; + private final TempFolder temp; + + private ReportPublisher[] publishers; + + public PublishReportJob(Settings settings, ServerClient serverClient, Server server, + Project project, AnalysisMode analysisMode, TempFolder temp, ResourceCache resourceCache, ReportPublisher[] publishers) { + this.serverClient = serverClient; + this.server = server; + this.project = project; + this.settings = settings; + this.analysisMode = analysisMode; + this.temp = temp; + this.resourceCache = resourceCache; + this.publishers = publishers; + } + + public PublishReportJob(Settings settings, ServerClient serverClient, Server server, + Project project, AnalysisMode analysisMode, TempFolder temp, ResourceCache resourceCache) { + this(settings, serverClient, server, project, analysisMode, temp, resourceCache, new ReportPublisher[0]); + } + + public void execute() { + // If this is a preview analysis then we should not upload reports + if (!analysisMode.isPreview()) { + File report = prepareReport(); + uploadMultiPartReport(report); + } + logSuccess(LoggerFactory.getLogger(getClass())); + } + + private File prepareReport() { + try { + File reportDir = temp.newDir("batch-report"); + for (ReportPublisher publisher : publishers) { + publisher.export(reportDir); + } + + File reportZip = temp.newFile("batch-report", ".zip"); + ZipUtils.zipDir(reportDir, reportZip); + FileUtils.deleteDirectory(reportDir); + return reportZip; + } catch (IOException e) { + throw new IllegalStateException("Unable to prepare batch report", e); + } + } + + @VisibleForTesting + void uploadMultiPartReport(File report) { + LOG.debug("Publish results"); + URL url; + try { + int snapshotId = resourceCache.get(project.getEffectiveKey()).snapshotId(); + url = new URL(serverClient.getURL() + "/batch/upload_report?project=" + project.getEffectiveKey() + "&snapshot=" + snapshotId); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL", e); + } + HttpRequest request = HttpRequest.post(url); + request.trustAllCerts(); + request.trustAllHosts(); + request.header("User-Agent", String.format("SonarQube %s", server.getVersion())); + request.basic(serverClient.getLogin(), serverClient.getPassword()); + request.part("report", report); + if (!request.ok()) { + int responseCode = request.code(); + if (responseCode == 401) { + throw new IllegalStateException(String.format(serverClient.getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD)); + } + if (responseCode == 403) { + // SONAR-4397 Details are in response content + throw new IllegalStateException(request.body()); + } + throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body())); + } + } + + @VisibleForTesting + void logSuccess(Logger logger) { + if (analysisMode.isPreview()) { + logger.info("ANALYSIS SUCCESSFUL"); + + } else { + String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL); + if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) { + // If server base URL was not configured in Sonar server then is is better to take URL configured on batch side + baseUrl = serverClient.getURL(); + } + if (!baseUrl.endsWith("/")) { + baseUrl += "/"; + } + String url = baseUrl + "dashboard/index/" + project.getKey(); + logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url); + logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java new file mode 100644 index 00000000000..c9d42f0a849 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java @@ -0,0 +1,29 @@ +/* + * 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.batch.report; + +import java.io.File; +import java.io.IOException; + +public interface ReportPublisher { + + void export(File reportDir) throws IOException; + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ResourcesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ResourcesPublisher.java new file mode 100644 index 00000000000..1bdbf2579a7 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ResourcesPublisher.java @@ -0,0 +1,82 @@ +/* + * 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.batch.report; + +import org.apache.commons.io.FileUtils; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.protocol.output.resource.ReportResource; +import org.sonar.batch.protocol.output.resource.ReportResource.Type; +import org.sonar.batch.protocol.output.resource.ReportResources; + +import java.io.File; +import java.io.IOException; + +public class ResourcesPublisher implements ReportPublisher { + + private final ResourceCache resourceCache; + private final ProjectReactor reactor; + + public ResourcesPublisher(ProjectReactor reactor, ResourceCache resourceCache) { + this.reactor = reactor; + this.resourceCache = resourceCache; + } + + @Override + public void export(File reportDir) throws IOException { + ReportResources resources = new ReportResources(); + BatchResource rootProject = resourceCache.get(reactor.getRoot().getKeyWithBranch()); + resources.setRoot(buildResourceForReport(rootProject)); + File resourcesFile = new File(reportDir, "resources.json"); + FileUtils.write(resourcesFile, resources.toJson()); + } + + private ReportResource buildResourceForReport(BatchResource batchResource) { + Resource r = batchResource.resource(); + ReportResource result = new ReportResource() + .setBatchId(batchResource.batchId()) + .setSnapshotId(batchResource.snapshotId()) + .setId(r.getId()) + .setName(r.getName()) + .setPath(r.getPath()) + .setType(getType(r)); + for (BatchResource child : batchResource.children()) { + result.addChild(buildResourceForReport(child)); + } + return result; + } + + private Type getType(Resource r) { + if (ResourceUtils.isFile(r)) { + return ReportResource.Type.FIL; + } else if (ResourceUtils.isDirectory(r)) { + return ReportResource.Type.DIR; + } else if (ResourceUtils.isModuleProject(r)) { + return ReportResource.Type.MOD; + } else if (ResourceUtils.isRootProject(r)) { + return ReportResource.Type.PRJ; + } + throw new IllegalArgumentException("Unknow resource type: " + r); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/report/package-info.java new file mode 100644 index 00000000000..bfaf6f9e767 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.batch.report; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java index 53fb0894817..f3d7bffc035 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java @@ -36,7 +36,7 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Resource; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.batch.index.PersistenceManager; +import org.sonar.batch.index.EventPersister; import org.sonar.core.UtcDateUtils; import javax.annotation.CheckForNull; @@ -49,12 +49,12 @@ public class QProfileEventsDecorator implements Decorator { private final TimeMachine timeMachine; private final Languages languages; - private final PersistenceManager persistenceManager; + private final EventPersister eventPersister; - public QProfileEventsDecorator(TimeMachine timeMachine, Languages languages, PersistenceManager pm) { + public QProfileEventsDecorator(TimeMachine timeMachine, Languages languages, EventPersister eventPersister) { this.timeMachine = timeMachine; this.languages = languages; - this.persistenceManager = pm; + this.eventPersister = eventPersister; } @DependsUpon @@ -123,7 +123,7 @@ public class QProfileEventsDecorator implements Decorator { "from", UtcDateUtils.formatDateTime(fixDate(from)), "to", UtcDateUtils.formatDateTime(fixDate(profile.getRulesUpdatedAt())))); event.setData(data); - persistenceManager.saveEvent(context.getResource(), event); + eventPersister.saveEvent(context.getResource(), event); } /** diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 21bb31470bd..5898b9452b7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -46,7 +46,6 @@ import org.sonar.batch.debt.SqaleRatingDecorator; import org.sonar.batch.debt.SqaleRatingSettings; import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.ResourcePersister; import org.sonar.batch.issue.IssuableFactory; import org.sonar.batch.issue.IssueFilters; import org.sonar.batch.issue.ModuleIssues; @@ -62,6 +61,8 @@ import org.sonar.batch.phases.PhasesTimeProfiler; import org.sonar.batch.qualitygate.GenerateQualityGateEvents; import org.sonar.batch.qualitygate.QualityGateProvider; import org.sonar.batch.qualitygate.QualityGateVerifier; +import org.sonar.batch.report.IssuesPublisher; +import org.sonar.batch.report.ResourcesPublisher; import org.sonar.batch.rule.ActiveRulesProvider; import org.sonar.batch.rule.ModuleQProfiles; import org.sonar.batch.rule.QProfileDecorator; @@ -119,6 +120,8 @@ public class ModuleScanContainer extends ComponentContainer { PhaseExecutor.class, PhasesTimeProfiler.class, PhaseExecutor.getPhaseClasses(), + ResourcesPublisher.class, + IssuesPublisher.class, moduleDefinition.getContainerExtensions(), // file system @@ -142,9 +145,6 @@ public class ModuleScanContainer extends ComponentContainer { AnalyzerOptimizer.class, - // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor) - getComponentByType(ResourcePersister.class).getSnapshot(module), - TimeMachineConfiguration.class, DefaultSensorContext.class, SensorContextAdapter.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index d9f23dc2a39..d364c1ab797 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -46,18 +46,16 @@ import org.sonar.batch.duplication.BlockCache; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.Caches; import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.index.FileHashesPersister; import org.sonar.batch.index.DefaultIndex; -import org.sonar.batch.index.DefaultPersistenceManager; import org.sonar.batch.index.DefaultResourcePersister; import org.sonar.batch.index.DependencyPersister; import org.sonar.batch.index.DuplicationPersister; import org.sonar.batch.index.EventPersister; +import org.sonar.batch.index.FileHashesPersister; import org.sonar.batch.index.LinkPersister; import org.sonar.batch.index.MeasurePersister; import org.sonar.batch.index.ResourceCache; import org.sonar.batch.index.ResourceKeyMigration; -import org.sonar.batch.index.SnapshotCache; import org.sonar.batch.index.SourcePersister; import org.sonar.batch.issue.DefaultProjectIssues; import org.sonar.batch.issue.DeprecatedViolations; @@ -133,7 +131,6 @@ public class ProjectScanContainer extends ComponentContainer { add( new ProjectReferentialsProvider(), DefaultResourceCreationLock.class, - DefaultPersistenceManager.class, DependencyPersister.class, EventPersister.class, LinkPersister.class, @@ -151,7 +148,6 @@ public class ProjectScanContainer extends ComponentContainer { ProjectLock.class, LastLineHashes.class, Caches.class, - SnapshotCache.class, ResourceCache.class, ComponentDataCache.class, FileHashesPersister.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoader.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoader.java index b86b501225e..79b1019e362 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoader.java @@ -22,9 +22,11 @@ package org.sonar.batch.scan.filesystem; import com.google.common.collect.Maps; import org.sonar.api.BatchComponent; import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.PastSnapshotFinder; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.SnapshotDataDao; import org.sonar.core.source.db.SnapshotDataDto; @@ -37,10 +39,12 @@ public class PreviousFileHashLoader implements BatchComponent { private final SnapshotDataDao dao; private final PastSnapshotFinder pastSnapshotFinder; - private final Snapshot snapshot; + private final Project project; + private final ResourceCache resourceCache; - public PreviousFileHashLoader(Snapshot snapshot, SnapshotDataDao dao, PastSnapshotFinder pastSnapshotFinder) { - this.snapshot = snapshot; + public PreviousFileHashLoader(Project project, ResourceCache resourceCache, SnapshotDataDao dao, PastSnapshotFinder pastSnapshotFinder) { + this.project = project; + this.resourceCache = resourceCache; this.dao = dao; this.pastSnapshotFinder = pastSnapshotFinder; } @@ -49,6 +53,7 @@ public class PreviousFileHashLoader implements BatchComponent { * Extract hash of the files parsed during the previous analysis */ public Map<String, String> hashByRelativePath() { + Snapshot snapshot = resourceCache.get(project.getEffectiveKey()).snapshot(); Map<String, String> map = Maps.newHashMap(); PastSnapshot pastSnapshot = pastSnapshotFinder.findPreviousAnalysis(snapshot); if (pastSnapshot.isRelatedToSnapshot()) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java index 0da1a2f8d0d..5f796661473 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java @@ -38,6 +38,7 @@ import org.sonar.batch.duplication.BlockCache; import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.Caches; import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.ResourceCache; import org.sonar.batch.languages.DefaultLanguagesReferential; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; import org.sonar.batch.referential.DefaultProjectReferentialsLoader; @@ -95,6 +96,7 @@ public class ProjectScanContainer extends ComponentContainer { new ProjectReferentialsProvider(), ProjectSettings.class, Caches.class, + ResourceCache.class, // lang Languages.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java index 9a162fbf425..05efe6cf0ae 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.database.model.Snapshot; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilters; @@ -49,6 +50,7 @@ import java.io.IOException; import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -75,7 +77,9 @@ public class DefaultIndexTest { ruleFinder = mock(RuleFinder.class); ProjectTree projectTree = mock(ProjectTree.class); - index = new DefaultIndex(mock(PersistenceManager.class), projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.class), + ResourcePersister resourcePersister = mock(ResourcePersister.class); + index = new DefaultIndex(resourcePersister, null, null, null, projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, + mock(ResourceKeyMigration.class), mock(MeasureCache.class)); java.io.File baseDir = temp.newFolder(); @@ -88,6 +92,9 @@ public class DefaultIndexTest { moduleB1 = new Project("moduleB1").setParent(moduleB); when(projectTree.getProjectDefinition(moduleB1)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB/moduleB1"))); + when(resourcePersister.saveResource(any(Project.class), any(Resource.class), any(Resource.class))).thenReturn( + new BatchResource(1, mock(Resource.class), new Snapshot().setId(1), null)); + RulesProfile rulesProfile = RulesProfile.create(); rule = Rule.create("repoKey", "ruleKey", "Rule"); rule.setId(1); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java index 23ad3689cf6..d99ef18c058 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultResourcePersisterTest.java @@ -53,9 +53,6 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import static org.fest.assertions.Assertions.assertThat; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -69,12 +66,13 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { @Rule public TemporaryFolder temp = new TemporaryFolder(); - Project singleProject, singleCopyProject, multiModuleProject, moduleA, moduleB, moduleB1, existingProject; - SnapshotCache snapshotCache = mock(SnapshotCache.class); - ResourceCache resourceCache = mock(ResourceCache.class); + private Project singleProject, singleCopyProject, multiModuleProject, moduleA, moduleB, moduleB1, existingProject; + private ResourceCache resourceCache; @Before public void before() throws ParseException { + resourceCache = new ResourceCache(); + SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); singleProject = newProject("foo", "java"); singleProject.setName("Foo").setDescription("some description").setAnalysisDate(format.parse("25/12/2010")); @@ -108,7 +106,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewProject() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); persister.saveProject(singleProject, null); checkTables("shouldSaveNewProject", new String[] {"build_date", "created_at", "authorization_updated_at", "uuid", "project_uuid", "module_uuid", "module_uuid_path"}, @@ -134,7 +132,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveCopyProject() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); persister.saveProject(singleCopyProject, null); checkTables("shouldSaveCopyProject", new String[] {"build_date", "created_at", "authorization_updated_at", "uuid", "project_uuid", "module_uuid", "module_uuid_path"}, @@ -157,7 +155,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewMultiModulesProject() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); persister.saveProject(multiModuleProject, null); persister.saveProject(moduleA, multiModuleProject); persister.saveProject(moduleB, multiModuleProject); @@ -221,7 +219,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { java.io.File baseDir = temp.newFolder(); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); ProjectTree projectTree = mock(ProjectTree.class); when(projectTree.getRootProject()).thenReturn(multiModuleProject); @@ -230,8 +228,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { when(projectTree.getProjectDefinition(moduleB)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB"))); when(projectTree.getProjectDefinition(moduleB1)).thenReturn(ProjectDefinition.create().setBaseDir(new java.io.File(baseDir, "moduleB/moduleB1"))); - PersistenceManager persistenceManager = new DefaultPersistenceManager(persister, null, null, null); - DefaultIndex index = new DefaultIndex(persistenceManager, projectTree, mock(MetricFinder.class), mock(ScanGraph.class), mock(DeprecatedViolations.class), + DefaultIndex index = new DefaultIndex(persister, null, null, null, projectTree, mock(MetricFinder.class), mock(ScanGraph.class), mock(DeprecatedViolations.class), mock(ResourceKeyMigration.class), mock(MeasureCache.class)); @@ -300,7 +297,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewDirectory() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); persister.saveProject(singleProject, null); persister.saveResource(singleProject, Directory.create("src/main/java/org/foo", "org.foo").setEffectiveKey("foo:src/main/java/org/foo")); @@ -337,7 +334,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldSaveNewLibrary() { setupData("shared"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); persister.saveProject(singleProject, null); persister.saveResource(singleProject, new Library("junit:junit", "4.8.2").setEffectiveKey("junit:junit")); persister.saveResource(singleProject, new Library("junit:junit", "4.8.2").setEffectiveKey("junit:junit"));// do nothing, already saved @@ -362,26 +359,10 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { } @Test - public void shouldClearResourcesExceptProjects() { - setupData("shared"); - - DefaultResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); - persister.saveProject(multiModuleProject, null); - persister.saveProject(moduleA, multiModuleProject); - persister.saveResource(moduleA, new Directory("org/foo").setEffectiveKey("a:org/foo")); - persister.saveResource(moduleA, new File("org/foo/MyClass.java").setEffectiveKey("a:org/foo/MyClass.java")); - persister.clear(); - - assertThat(persister.getSnapshotsByResource().size(), is(2)); - assertThat(persister.getSnapshotsByResource().get(multiModuleProject), notNullValue()); - assertThat(persister.getSnapshotsByResource().get(moduleA), notNullValue()); - } - - @Test public void shouldUpdateExistingResource() { setupData("shouldUpdateExistingResource"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); singleProject.setName("new name"); singleProject.setDescription("new description"); persister.saveProject(singleProject, null); @@ -394,7 +375,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { public void shouldRemoveRootIndexIfResourceIsProject() { setupData("shouldRemoveRootIndexIfResourceIsProject"); - ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), mock(ResourcePermissions.class), resourceCache); persister.saveProject(singleProject, null); checkTables("shouldRemoveRootIndexIfResourceIsProject", new String[] {"build_date", "created_at", "authorization_updated_at"}, "projects", "snapshots"); @@ -407,7 +388,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { ResourcePermissions permissions = mock(ResourcePermissions.class); when(permissions.hasRoles(singleProject)).thenReturn(false); - ResourcePersister persister = new DefaultResourcePersister(getSession(), permissions, snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), permissions, resourceCache); persister.saveProject(singleProject, null); verify(permissions).grantDefaultRoles(singleProject); @@ -420,7 +401,7 @@ public class DefaultResourcePersisterTest extends AbstractDbUnitTestCase { ResourcePermissions permissions = mock(ResourcePermissions.class); when(permissions.hasRoles(singleProject)).thenReturn(true); - ResourcePersister persister = new DefaultResourcePersister(getSession(), permissions, snapshotCache, resourceCache); + ResourcePersister persister = new DefaultResourcePersister(getSession(), permissions, resourceCache); persister.saveProject(singleProject, null); verify(permissions, never()).grantDefaultRoles(singleProject); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DuplicationPersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DuplicationPersisterTest.java index 644d7400999..3517b28789f 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DuplicationPersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DuplicationPersisterTest.java @@ -48,7 +48,6 @@ public class DuplicationPersisterTest extends AbstractDaoTestCase { RuleFinder ruleFinder = mock(RuleFinder.class); File aFile = new File("org/foo/Bar.java"); Snapshot fileSnapshot = snapshot(FILE_SNAPSHOT_ID); - SnapshotCache snapshotCache; private DuplicationCache duplicationCache; @@ -56,15 +55,16 @@ public class DuplicationPersisterTest extends AbstractDaoTestCase { public void mockResourcePersister() { duplicationCache = mock(DuplicationCache.class); - snapshotCache = mock(SnapshotCache.class); ResourceCache resourceCache = mock(ResourceCache.class); - when(snapshotCache.get("foo:org/foo/Bar.java")).thenReturn(fileSnapshot); - when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(aFile); + BatchResource batchResource = mock(BatchResource.class); + when(batchResource.resource()).thenReturn(aFile); + when(batchResource.snapshotId()).thenReturn(FILE_SNAPSHOT_ID); + when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(batchResource); MetricFinder metricFinder = mock(MetricFinder.class); when(metricFinder.findByKey(CoreMetrics.DUPLICATIONS_DATA_KEY)).thenReturn(CoreMetrics.DUPLICATIONS_DATA.setId(2)); - duplicationPersister = new DuplicationPersister(getMyBatis(), ruleFinder, snapshotCache, resourceCache, duplicationCache, metricFinder); + duplicationPersister = new DuplicationPersister(getMyBatis(), ruleFinder, resourceCache, duplicationCache, metricFinder); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/FileHashesPersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/FileHashesPersisterTest.java index 29c9e0cef1e..3e6c4866245 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/FileHashesPersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/FileHashesPersisterTest.java @@ -25,6 +25,7 @@ import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.SnapshotDataDao; @@ -34,12 +35,13 @@ public class FileHashesPersisterTest extends AbstractDaoTestCase { @ClassRule public static TemporaryFolder temp = new TemporaryFolder(); - SnapshotCache snapshots = new SnapshotCache(); + ResourceCache resourceCache; ComponentDataCache data; Caches caches; @Before public void start() throws Exception { + resourceCache = new ResourceCache(); caches = CachesTest.createCacheOnTemp(temp); caches.start(); } @@ -55,13 +57,13 @@ public class FileHashesPersisterTest extends AbstractDaoTestCase { Snapshot snapshot = new Snapshot(); snapshot.setId(100); snapshot.setResourceId(200); - snapshots.put("myProject", snapshot); + resourceCache.add(new Project("myProject").setId(200), snapshot); data = new ComponentDataCache(caches); data.setStringData("myProject", SnapshotDataTypes.FILE_HASHES, "org/struts/Action.java=123ABC"); SnapshotDataDao dataDao = new SnapshotDataDao(getMyBatis()); - FileHashesPersister persister = new FileHashesPersister(data, snapshots, dataDao, getMyBatis()); + FileHashesPersister persister = new FileHashesPersister(data, resourceCache, dataDao, getMyBatis()); persister.persist(); checkTables("should_persist_component_data", new String[] {"id", "created_at", "updated_at"}, "snapshot_data"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java index fe8a1a45a65..6c4bcc731eb 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java @@ -32,6 +32,7 @@ import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Directory; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; @@ -61,23 +62,20 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { Project project = new Project("foo"); Directory aDirectory = new Directory("org/foo"); File aFile = new File("org/foo/Bar.java"); - Snapshot projectSnapshot = snapshot(PROJECT_SNAPSHOT_ID); - Snapshot packageSnapshot = snapshot(PACKAGE_SNAPSHOT_ID); - SnapshotCache snapshotCache; + BatchResource projectResource = batchResource(project, PROJECT_SNAPSHOT_ID); + BatchResource dirResource = batchResource(aDirectory, PACKAGE_SNAPSHOT_ID); + BatchResource fileResource = batchResource(aFile, FILE_SNAPSHOT_ID); MeasureCache measureCache; @Before public void mockResourcePersister() { - snapshotCache = mock(SnapshotCache.class); measureCache = mock(MeasureCache.class); ResourceCache resourceCache = mock(ResourceCache.class); - when(snapshotCache.get("foo")).thenReturn(projectSnapshot); - when(snapshotCache.get("foo:org/foo")).thenReturn(packageSnapshot); - when(resourceCache.get("foo")).thenReturn(project); - when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(aFile); - when(resourceCache.get("foo:org/foo")).thenReturn(aDirectory); + when(resourceCache.get("foo")).thenReturn(projectResource); + when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(fileResource); + when(resourceCache.get("foo:org/foo")).thenReturn(dirResource); - measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache); + measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, resourceCache); } @Test @@ -193,10 +191,10 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { assertThat(MeasurePersister.shouldPersistMeasure(aFile, duplicatedLines)).isFalse(); } - private static Snapshot snapshot(int id) { + private static BatchResource batchResource(Resource resource, int id) { Snapshot snapshot = mock(Snapshot.class); when(snapshot.getId()).thenReturn(id); - return snapshot; + return new BatchResource(1, resource, snapshot, null); } private static Metric ncloc() { diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/ResourceCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/ResourceCacheTest.java index 20e04957309..d59709a0bd6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/ResourceCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/ResourceCacheTest.java @@ -20,6 +20,7 @@ package org.sonar.batch.index; import org.junit.Test; +import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.File; import org.sonar.api.resources.Resource; @@ -32,9 +33,9 @@ public class ResourceCacheTest { ResourceCache cache = new ResourceCache(); String componentKey = "struts:src/org/struts/Action.java"; Resource resource = new File("org/struts/Action.java").setEffectiveKey(componentKey); - cache.add(resource); + cache.add(resource, new Snapshot()); - assertThat(cache.get(componentKey)).isSameAs(resource); + assertThat(cache.get(componentKey).resource()).isSameAs(resource); assertThat(cache.get("other")).isNull(); } @@ -43,7 +44,7 @@ public class ResourceCacheTest { ResourceCache cache = new ResourceCache(); Resource resource = new File("org/struts/Action.java").setEffectiveKey(null); try { - cache.add(resource); + cache.add(resource, new Snapshot()); fail(); } catch (IllegalStateException e) { // success diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java index af2c5474d94..7c69ffb6061 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java @@ -365,7 +365,7 @@ public class SourcePersisterTest extends AbstractDaoTestCase { private void mockResourceCache(String relativePathEmpty, String projectKey, String uuid) { File sonarFile = File.create(relativePathEmpty); sonarFile.setUuid(uuid); - when(resourceCache.get(projectKey + ":" + relativePathEmpty)).thenReturn(sonarFile); + when(resourceCache.get(projectKey + ":" + relativePathEmpty)).thenReturn(new BatchResource(1, sonarFile, new Snapshot(), null)); } private byte[] md5(String string) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DeprecatedViolationsTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DeprecatedViolationsTest.java index 43b51226d25..a94578a5769 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/DeprecatedViolationsTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DeprecatedViolationsTest.java @@ -20,6 +20,7 @@ package org.sonar.batch.issue; import org.junit.Test; +import org.sonar.api.database.model.Snapshot; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.resources.Project; import org.sonar.api.rule.RuleKey; @@ -28,6 +29,7 @@ import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; import org.sonar.api.rules.Violation; +import org.sonar.batch.index.BatchResource; import org.sonar.batch.index.ResourceCache; import java.util.Arrays; @@ -48,7 +50,7 @@ public class DeprecatedViolationsTest { public void test_toViolation() throws Exception { RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles"); when(ruleFinder.findByKey(ruleKey)).thenReturn(new Rule("squid", "AvoidCycles")); - when(resourceCache.get("org.apache:struts")).thenReturn(new Project("org.apache:struts")); + when(resourceCache.get("org.apache:struts")).thenReturn(new BatchResource(1, new Project("org.apache:struts"), new Snapshot(), null)); DefaultIssue issue = newIssue(ruleKey); @@ -78,7 +80,7 @@ public class DeprecatedViolationsTest { public void test_get() throws Exception { RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles"); when(ruleFinder.findByKey(ruleKey)).thenReturn(new Rule("squid", "AvoidCycles")); - when(resourceCache.get("org.apache:struts")).thenReturn(new Project("org.apache:struts")); + when(resourceCache.get("org.apache:struts")).thenReturn(new BatchResource(1, new Project("org.apache:struts"), new Snapshot(), null)); when(issueCache.byComponent("org.apache:struts")).thenReturn(Arrays.asList(newIssue(ruleKey))); List<Violation> violations = deprecatedViolations.get("org.apache:struts"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java index 386c4949251..e76825eaca5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java @@ -28,6 +28,7 @@ import org.sonar.api.database.model.Snapshot; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; @@ -37,7 +38,8 @@ import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.System2; import org.sonar.batch.ProjectTree; -import org.sonar.batch.index.SnapshotCache; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.resource.ResourceDao; @@ -52,7 +54,7 @@ import static org.mockito.Mockito.when; public class ScanIssueStorageTest extends AbstractDaoTestCase { @Mock - SnapshotCache snapshotCache; + ResourceCache resourceCache; @Mock ProjectTree projectTree; @@ -61,12 +63,12 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { @Before public void setUp() throws Exception { - storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis(), System2.INSTANCE), projectTree); + storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), resourceCache, new ResourceDao(getMyBatis(), System2.INSTANCE), projectTree); } @Test public void should_load_component_id_from_cache() throws Exception { - when(snapshotCache.get("struts:Action.java")).thenReturn(new Snapshot().setResourceId(123)); + when(resourceCache.get("struts:Action.java")).thenReturn(new BatchResource(1, File.create("Action.java").setId(123), new Snapshot(), null)); long componentId = storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java")); @@ -76,7 +78,7 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { @Test public void should_load_component_id_from_db() throws Exception { setupData("should_load_component_id_from_db"); - when(snapshotCache.get("struts:Action.java")).thenReturn(null); + when(resourceCache.get("struts:Action.java")).thenReturn(null); long componentId = storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java")); @@ -86,7 +88,7 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { @Test public void should_fail_to_load_component_id_if_unknown_component() throws Exception { setupData("should_fail_to_load_component_id_if_unknown_component"); - when(snapshotCache.get("struts:Action.java")).thenReturn(null); + when(resourceCache.get("struts:Action.java")).thenReturn(null); try { storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java")); @@ -139,7 +141,7 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { storage.save(issue); - checkTables("should_insert_new_issues", new String[]{"id", "created_at", "updated_at", "issue_change_creation_date"}, "issues", "issue_changes"); + checkTables("should_insert_new_issues", new String[] {"id", "created_at", "updated_at", "issue_change_creation_date"}, "issues", "issue_changes"); } @Test @@ -162,7 +164,7 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { .setNew(false) .setChanged(true) - // updated fields + // updated fields .setLine(5000) .setDebt(Duration.create(10L)) .setChecksum("FFFFF") @@ -179,13 +181,13 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { .setUpdateDate(date) .setCloseDate(date) - // unmodifiable fields + // unmodifiable fields .setRuleKey(RuleKey.of("xxx", "unknown")) .setComponentKey("not:a:component"); storage.save(issue); - checkTables("should_update_issues", new String[]{"id", "created_at", "updated_at", "issue_change_creation_date"}, "issues", "issue_changes"); + checkTables("should_update_issues", new String[] {"id", "created_at", "updated_at", "issue_change_creation_date"}, "issues", "issue_changes"); } @Test @@ -206,23 +208,23 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase { .setRuleKey(RuleKey.of("squid", "AvoidCycles")) .setComponentKey("struts:Action") - // issue in database has been updated in 2015, after the loading by scan + // issue in database has been updated in 2015, after the loading by scan .setSelectedAt(1400000000000L) - // fields to be updated + // fields to be updated .setLine(444) .setSeverity("BLOCKER") .setChecksum("FFFFF") .setAttribute("JIRA", "http://jira.com") - // fields overridden by end-user -> do not save + // fields overridden by end-user -> do not save .setAssignee("looser") .setResolution(null) .setStatus("REOPEN"); storage.save(issue); - checkTables("should_resolve_conflicts_on_updates", new String[]{"id", "created_at", "updated_at", "issue_change_creation_date"}, "issues"); + checkTables("should_resolve_conflicts_on_updates", new String[] {"id", "created_at", "updated_at", "issue_change_creation_date"}, "issues"); } static class FakeRuleFinder implements RuleFinder { diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java index 9dd6a731f12..63a11312309 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java @@ -39,6 +39,7 @@ import org.sonar.api.resources.Resource; import org.sonar.api.test.IsMeasure; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.qualitygate.db.QualityGateConditionDto; import org.sonar.core.timemachine.Periods; @@ -69,6 +70,7 @@ public class QualityGateVerifierTest { Periods periods; I18n i18n; Durations durations; + private ResourceCache resourceCache; @Before public void before() { @@ -89,8 +91,13 @@ public class QualityGateVerifierTest { snapshot = mock(Snapshot.class); qualityGate = mock(QualityGate.class); when(qualityGate.isEnabled()).thenReturn(true); - verifier = new QualityGateVerifier(qualityGate, snapshot, periods, i18n, durations); + project = new Project("foo"); + + resourceCache = new ResourceCache(); + resourceCache.add(project, snapshot); + + verifier = new QualityGateVerifier(qualityGate, resourceCache, periods, i18n, durations); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java index 6c329eef3a1..e28e0643fb0 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/phases/UpdateStatusJobTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/PublishReportJobTest.java @@ -17,31 +17,31 @@ * 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.batch.phases; +package org.sonar.batch.report; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; -import org.sonar.api.database.model.Snapshot; +import org.sonar.api.platform.Server; import org.sonar.api.resources.Project; +import org.sonar.api.utils.TempFolder; import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.bootstrap.ServerClient; +import org.sonar.batch.index.ResourceCache; import org.sonar.jpa.test.AbstractDbUnitTestCase; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.contains; -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.when; -public class UpdateStatusJobTest extends AbstractDbUnitTestCase { +public class PublishReportJobTest extends AbstractDbUnitTestCase { private AnalysisMode mode; + ResourceCache resourceCache = mock(ResourceCache.class); + @Before public void setUp() { mode = mock(AnalysisMode.class); @@ -52,7 +52,7 @@ public class UpdateStatusJobTest extends AbstractDbUnitTestCase { Settings settings = new Settings(); settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/"); Project project = new Project("struts"); - UpdateStatusJob job = new UpdateStatusJob(settings, mock(ServerClient.class), project, mock(Snapshot.class), mode); + PublishReportJob job = new PublishReportJob(settings, mock(ServerClient.class), mock(Server.class), project, mode, mock(TempFolder.class), mock(ResourceCache.class)); Logger logger = mock(Logger.class); job.logSuccess(logger); @@ -66,7 +66,7 @@ public class UpdateStatusJobTest extends AbstractDbUnitTestCase { Settings settings = new Settings(); when(mode.isPreview()).thenReturn(true); Project project = new Project("struts"); - UpdateStatusJob job = new UpdateStatusJob(settings, mock(ServerClient.class), project, mock(Snapshot.class), mode); + PublishReportJob job = new PublishReportJob(settings, mock(ServerClient.class), mock(Server.class), project, mode, mock(TempFolder.class), mock(ResourceCache.class)); Logger logger = mock(Logger.class); job.logSuccess(logger); @@ -74,26 +74,4 @@ public class UpdateStatusJobTest extends AbstractDbUnitTestCase { verify(logger).info("ANALYSIS SUCCESSFUL"); } - @Test - public void should_publish_results_for_regular_analysis() throws Exception { - Settings settings = new Settings(); - Project project = new Project("struts"); - ServerClient serverClient = mock(ServerClient.class); - UpdateStatusJob job = new UpdateStatusJob(settings, serverClient, project, mock(Snapshot.class), mode); - - job.uploadReport(); - verify(serverClient).request(contains("/batch/upload_report"), eq("POST")); - } - - @Test - public void should_not_publish_results_for_preview_analysis() throws Exception { - Settings settings = new Settings(); - when(mode.isPreview()).thenReturn(true); - Project project = new Project("struts"); - ServerClient serverClient = mock(ServerClient.class); - UpdateStatusJob job = new UpdateStatusJob(settings, serverClient, project, mock(Snapshot.class), mode); - - job.uploadReport(); - verify(serverClient, never()).request(anyString()); - } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java index 6618cc7f832..cc77fe913ef 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java @@ -51,7 +51,7 @@ import org.sonar.api.resources.Java; import org.sonar.api.resources.Languages; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; -import org.sonar.batch.index.PersistenceManager; +import org.sonar.batch.index.EventPersister; import java.util.Arrays; import java.util.Date; @@ -59,10 +59,13 @@ import java.util.Date; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.same; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class QProfileEventsDecoratorTest { @@ -74,8 +77,8 @@ public class QProfileEventsDecoratorTest { DecoratorContext decoratorContext = mock(DecoratorContext.class); TimeMachine timeMachine = mock(TimeMachine.class); Languages languages = mock(Languages.class); - PersistenceManager persistenceManager = mock(PersistenceManager.class); - QProfileEventsDecorator decorator = new QProfileEventsDecorator(timeMachine, languages, persistenceManager); + EventPersister eventPersister = mock(EventPersister.class); + QProfileEventsDecorator decorator = new QProfileEventsDecorator(timeMachine, languages, eventPersister); @Test public void basic_tests() { @@ -112,7 +115,7 @@ public class QProfileEventsDecoratorTest { decorator.decorate(project, decoratorContext); - verify(persistenceManager).saveEvent(any(Resource.class), argThat(new BaseMatcher<Event>() { + verify(eventPersister).saveEvent(any(Resource.class), argThat(new BaseMatcher<Event>() { @Override public void describeTo(Description description) { } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoaderTest.java index 8a4afa2a017..98664860bae 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PreviousFileHashLoaderTest.java @@ -19,13 +19,16 @@ */ package org.sonar.batch.scan.filesystem; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.PastSnapshotFinder; +import org.sonar.batch.index.ResourceCache; import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.SnapshotDataDao; import org.sonar.core.source.db.SnapshotDataDto; @@ -46,10 +49,19 @@ public class PreviousFileHashLoaderTest { @Rule public ExpectedException thrown = ExpectedException.none(); - PastSnapshotFinder pastSnapshotFinder = mock(PastSnapshotFinder.class); - Snapshot snapshot = mock(Snapshot.class); - SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class); - PreviousFileHashLoader loader = new PreviousFileHashLoader(snapshot, snapshotDataDao, pastSnapshotFinder); + private PastSnapshotFinder pastSnapshotFinder = mock(PastSnapshotFinder.class); + private Snapshot snapshot = mock(Snapshot.class); + private SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class); + private Project project = new Project("foo"); + private ResourceCache resourceCache; + private PreviousFileHashLoader loader; + + @Before + public void prepare() { + resourceCache = new ResourceCache(); + resourceCache.add(project, snapshot); + loader = new PreviousFileHashLoader(project, resourceCache, snapshotDataDao, pastSnapshotFinder); + } @Test public void should_return_null_if_no_previous_snapshot() throws Exception { diff --git a/sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java b/sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java index 73fde1c1077..cb19d408d47 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ResourceComponent.java @@ -37,6 +37,10 @@ public class ResourceComponent implements Component { private Long resourceId; public ResourceComponent(Resource resource, @Nullable Snapshot snapshot) { + this(resource, snapshot != null ? snapshot.getId() : null); + } + + public ResourceComponent(Resource resource, @Nullable Integer snapshotId) { this.key = resource.getEffectiveKey(); this.path = resource.getPath(); if (Strings.isNullOrEmpty(key)) { @@ -46,14 +50,14 @@ public class ResourceComponent implements Component { this.longName = resource.getLongName(); this.qualifier = resource.getQualifier(); this.scope = resource.getScope(); - if (snapshot != null && snapshot.getId() != null) { - this.snapshotId = snapshot.getId().longValue(); - this.resourceId = snapshot.getResourceId().longValue(); + if (snapshotId != null) { + this.snapshotId = snapshotId.longValue(); + this.resourceId = resource.getId().longValue(); } } public ResourceComponent(Resource resource) { - this(resource, null); + this(resource, (Integer) null); } @Override diff --git a/sonar-core/src/main/java/org/sonar/core/component/ScanGraph.java b/sonar-core/src/main/java/org/sonar/core/component/ScanGraph.java index 93e7529f0eb..bdc7bc2142f 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ScanGraph.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ScanGraph.java @@ -25,7 +25,6 @@ import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.impls.tg.TinkerGraph; import org.sonar.api.BatchComponent; import org.sonar.api.component.Component; -import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Resource; import org.sonar.core.graph.BeanGraph; import org.sonar.core.graph.BeanIterable; @@ -59,8 +58,8 @@ public class ScanGraph extends BeanGraph implements BatchComponent { return vertex != null ? wrapComponent(vertex) : null; } - public ComponentVertex addComponent(Resource resource, @Nullable Snapshot snapshot) { - return addComponent(new ResourceComponent(resource, snapshot)); + public ComponentVertex addComponent(Resource resource, @Nullable Integer snapshotId) { + return addComponent(new ResourceComponent(resource, snapshotId)); } public Iterable<ComponentVertex> getComponents() { diff --git a/sonar-core/src/test/java/org/sonar/core/component/ResourceComponentTest.java b/sonar-core/src/test/java/org/sonar/core/component/ResourceComponentTest.java index aa83169c91a..5b060c9e1e0 100644 --- a/sonar-core/src/test/java/org/sonar/core/component/ResourceComponentTest.java +++ b/sonar-core/src/test/java/org/sonar/core/component/ResourceComponentTest.java @@ -50,6 +50,7 @@ public class ResourceComponentTest { public void db_ids_should_be_set() { Snapshot snapshot = new Snapshot(); snapshot.setId(123); + file.setId(456); snapshot.setResourceId(456); ResourceComponent component = new ResourceComponent(file, snapshot); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java index 01d17a9fbe8..edafda1b3c8 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java @@ -23,9 +23,14 @@ import org.apache.commons.lang.builder.ToStringBuilder; import org.sonar.api.database.BaseIdentifiable; import org.sonar.api.database.model.Snapshot; -import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; -import javax.persistence.*; +import java.util.Date; /** * @since 1.10 @@ -71,29 +76,6 @@ public class Event extends BaseIdentifiable { this.category = category; } - /** - * @deprecated in 2.5 - */ - @Deprecated - public Event(String name, String description, String category, Date date, Integer resourceId) { - this.name = name; - this.description = description; - this.category = category; - this.date = date; - this.resourceId = resourceId; - } - - /** - * @deprecated in 2.5 - */ - @Deprecated - public Event(String name, String description, String category, Snapshot snapshot) { - this.name = name; - this.description = description; - this.category = category; - setSnapshot(snapshot); - } - public String getName() { return name; } @@ -174,11 +156,11 @@ public class Event extends BaseIdentifiable { @Override public String toString() { return new ToStringBuilder(this) - .append("name", name) - .append("categ", category) - .append("date", date) - .append("snapshot", snapshot) - .append("resource", resourceId) - .toString(); + .append("name", name) + .append("categ", category) + .append("date", date) + .append("snapshot", snapshot) + .append("resource", resourceId) + .toString(); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java index d9b24868134..a1a4eb688c3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java @@ -25,7 +25,7 @@ import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass -public class BaseIdentifiable { +public class BaseIdentifiable<G> { @Id @Column(name = "id") @@ -36,7 +36,8 @@ public class BaseIdentifiable { return id; } - public void setId(Integer id) { + public G setId(Integer id) { this.id = id; + return (G) this; } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java index 021007dd94b..0912b7d6675 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java @@ -40,7 +40,7 @@ import java.util.Date; */ @Entity @Table(name = "snapshots") -public class Snapshot extends BaseIdentifiable implements Serializable { +public class Snapshot extends BaseIdentifiable<Snapshot> implements Serializable { /** * This status is set on the snapshot at the beginning of the batch @@ -161,10 +161,10 @@ public class Snapshot extends BaseIdentifiable implements Serializable { this.createdAt = parent.getCreatedAt(); this.depth = parent.getDepth() + 1; this.path = new StringBuilder() - .append(parent.getPath()) - .append(parent.getId()) - .append(".") - .toString(); + .append(parent.getPath()) + .append(parent.getId()) + .append(".") + .toString(); } this.rootProjectId = guessRootProjectId(resource, parent); } @@ -702,17 +702,17 @@ public class Snapshot extends BaseIdentifiable implements Serializable { } Snapshot other = (Snapshot) obj; return new EqualsBuilder() - .append(resourceId, other.getResourceId()) - .append(createdAt, other.getCreatedAt()) - .isEquals(); + .append(resourceId, other.getResourceId()) + .append(createdAt, other.getCreatedAt()) + .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) - .append(resourceId) - .append(createdAt) - .toHashCode(); + .append(resourceId) + .append(createdAt) + .toHashCode(); } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java index 2542aeb19d5..02005504b80 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/internal/DefaultIssue.java @@ -547,7 +547,6 @@ public class DefaultIssue implements Issue { } @Override - @SuppressWarnings("unchcked") public List<IssueComment> comments() { if (comments == null) { return Collections.emptyList(); |