diff options
Diffstat (limited to 'sonar-batch')
52 files changed, 987 insertions, 648 deletions
diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index 3d95250a29d..2696cbe7fff 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.codehaus.sonar</groupId> <artifactId>sonar</artifactId> - <version>5.0-SNAPSHOT</version> + <version>5.1-SNAPSHOT</version> </parent> <artifactId>sonar-batch</artifactId> @@ -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/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index a3bf5ed529e..8d0f2bfb00d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -32,8 +32,8 @@ import org.sonar.batch.maven.MavenProjectBuilder; import org.sonar.batch.maven.MavenProjectConverter; import org.sonar.batch.scm.ScmConfiguration; import org.sonar.batch.scm.ScmSensor; -import org.sonar.server.computation.dbcleaner.DefaultPurgeTask; -import org.sonar.server.computation.dbcleaner.period.DefaultPeriodCleaner; +import org.sonar.core.computation.dbcleaner.DefaultPurgeTask; +import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; import org.sonar.core.config.CorePropertyDefinitions; import java.util.Collection; 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..e05a5a59519 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; } @@ -481,7 +497,7 @@ public class DefaultIndex extends SonarIndex { Resource resource = getResource(reference); if (resource instanceof File) { File file = (File) resource; - Project module = (Project) file.getParent().getParent(); + Project module = currentProject; ProjectDefinition def = projectTree.getProjectDefinition(module); try { return FileUtils.readFileToString(new java.io.File(def.getBaseDir(), file.getPath())); @@ -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 84e486f605e..04c27434027 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); - addToCache(project, snapshot); + public void saveProject(Project project, @Nullable Project parent) { + BatchResource batchResource = resourceCache.get(project.getEffectiveKey()); + if (batchResource == null) { + Snapshot snapshot = persistProject(project, parent); + addToCache(project, project.getParent(), 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, @Nullable Resource parent, Snapshot snapshot) { + return resourceCache.add(resource, parent, 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, parent, 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..01add749f84 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 javax.annotation.Nullable; + +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, @Nullable Resource parentResource, Snapshot s) { String componentKey = resource.getEffectiveKey(); Preconditions.checkState(!Strings.isNullOrEmpty(componentKey), "Missing resource effective key"); - resources.put(componentKey, resource); - return this; + 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 342c3503fb6..252d988be85 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 @@ -132,7 +132,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 fd8d97ec68c..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,12 +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; @@ -37,16 +38,20 @@ import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; +import java.util.ArrayList; +import java.util.Arrays; +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; } @@ -56,8 +61,12 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent { IssueMapper issueMapper = session.getMapper(IssueMapper.class); long componentId = componentId(issue); long projectId = projectId(); - int ruleId = ruleId(issue); - IssueDto dto = IssueDto.toDtoForBatchInsert(issue, componentId, projectId, ruleId, now); + Rule rule = rule(issue); + List<String> allTags = new ArrayList<String>(); + allTags.addAll(Arrays.asList(rule.getTags())); + allTags.addAll(Arrays.asList(rule.getSystemTags())); + issue.setTags(allTags); + IssueDto dto = IssueDto.toDtoForBatchInsert(issue, componentId, projectId, rule.getId(), now); issueMapper.insert(dto); } @@ -81,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) @@ -94,7 +103,6 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent { return resourceDto.getId(); } - @VisibleForTesting long projectId() { return projectTree.getRootProject().getId(); 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/ComponentsPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java new file mode 100644 index 00000000000..8e617830527 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java @@ -0,0 +1,109 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.report; + +import org.apache.commons.io.FileUtils; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.resources.Language; +import org.sonar.api.resources.Project; +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.ReportComponent; +import org.sonar.batch.protocol.output.resource.ReportComponents; + +import javax.annotation.CheckForNull; + +import java.io.File; +import java.io.IOException; + +public class ComponentsPublisher implements ReportPublisher { + + private final ResourceCache resourceCache; + private final ProjectReactor reactor; + + public ComponentsPublisher(ProjectReactor reactor, ResourceCache resourceCache) { + this.reactor = reactor; + this.resourceCache = resourceCache; + } + + @Override + public void export(File reportDir) throws IOException { + ReportComponents components = new ReportComponents(); + BatchResource rootProject = resourceCache.get(reactor.getRoot().getKeyWithBranch()); + components.setRoot(buildResourceForReport(rootProject)); + components.setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate()); + File resourcesFile = new File(reportDir, "components.json"); + FileUtils.write(resourcesFile, components.toJson()); + } + + private ReportComponent buildResourceForReport(BatchResource batchResource) { + Resource r = batchResource.resource(); + ReportComponent result = new ReportComponent() + .setBatchId(batchResource.batchId()) + .setSnapshotId(batchResource.snapshotId()) + .setId(r.getId()) + .setName(getName(r)) + .setPath(r.getPath()) + .setType(getType(r)) + .setLanguageKey(getLanguageKey(r)) + .setTest(isTest(r)); + for (BatchResource child : batchResource.children()) { + result.addChild(buildResourceForReport(child)); + } + return result; + } + + @CheckForNull + private Boolean isTest(Resource r) { + return ResourceUtils.isFile(r) ? ResourceUtils.isUnitTestClass(r) : null; + } + + @CheckForNull + private String getLanguageKey(Resource r) { + Language language = r.getLanguage(); + return ResourceUtils.isFile(r) && language != null ? language.getKey() : null; + } + + @CheckForNull + private String getName(Resource r) { + // Don't return name for directories and files since it can be guessed from the path + return (ResourceUtils.isFile(r) || ResourceUtils.isDirectory(r)) ? null : r.getName(); + } + + private ReportComponent.Type getType(Resource r) { + if (ResourceUtils.isFile(r)) { + return ReportComponent.Type.FIL; + } else if (ResourceUtils.isDirectory(r)) { + return ReportComponent.Type.DIR; + } else if (ResourceUtils.isModuleProject(r)) { + return ReportComponent.Type.MOD; + } else if (ResourceUtils.isRootProject(r)) { + return ReportComponent.Type.PRJ; + } else if (ResourceUtils.isView(r)) { + return ReportComponent.Type.VIEW; + } else if (ResourceUtils.isSubview(r)) { + return ReportComponent.Type.SUBVIEW; + } + throw new IllegalArgumentException("Unknow resource type: " + r); + } + +} 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..290820d11bc --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java @@ -0,0 +1,100 @@ +/* + * 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.BatchResource; +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) { + BatchResource batchResource = resourceCache.get(issue.componentKey()); + return new ReportIssue() + .setKey(issue.key()) + .setComponentBatchId(batchResource != null ? batchResource.batchId() : null) + .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..fdaa2843e9a --- /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", null, "application/octet-stream", 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/test/java/org/sonar/batch/index/SnapshotCacheTest.java b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java index 96c2a1033b6..c9d42f0a849 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/SnapshotCacheTest.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java @@ -17,24 +17,13 @@ * 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.report; -import org.junit.Test; -import org.sonar.api.database.model.Snapshot; +import java.io.File; +import java.io.IOException; -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; +public interface ReportPublisher { -public class SnapshotCacheTest { + void export(File reportDir) throws IOException; - Snapshot snapshot = mock(Snapshot.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(); - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SnapshotCache.java b/sonar-batch/src/main/java/org/sonar/batch/report/package-info.java index 6e21c2d2200..bfaf6f9e767 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SnapshotCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/package-info.java @@ -17,33 +17,7 @@ * 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; +@ParametersAreNonnullByDefault +package org.sonar.batch.report; -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 java.util.Map; -import java.util.Set; - -/** - * 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 Snapshot get(String componentKey) { - return snapshots.get(componentKey); - } - - public SnapshotCache put(String componentKey, Snapshot snapshot) { - snapshots.put(componentKey, snapshot); - return this; - } - - public Set<Map.Entry<String, Snapshot>> snapshots() { - return snapshots.entrySet(); - } -} +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..c022067683e 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.ComponentsPublisher; 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(), + ComponentsPublisher.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..f1e8458f757 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 @@ -19,10 +19,12 @@ */ package org.sonar.batch.index; +import org.apache.commons.io.FileUtils; 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 +51,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; @@ -67,6 +70,8 @@ public class DefaultIndexTest { Project moduleB; Project moduleB1; + private java.io.File baseDir; + @Before public void createIndex() throws IOException { deprecatedViolations = mock(DeprecatedViolations.class); @@ -75,10 +80,12 @@ 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(); + baseDir = temp.newFolder(); project = new Project("project"); when(projectTree.getProjectDefinition(project)).thenReturn(ProjectDefinition.create().setBaseDir(baseDir)); moduleA = new Project("moduleA").setParent(project); @@ -88,6 +95,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); @@ -128,6 +138,19 @@ public class DefaultIndexTest { } @Test + public void shouldGetSource() throws Exception { + Directory directory = Directory.create("src/org/foo", "org/foo"); + File file = File.create("src/org/foo/Bar.java", "org/foo/Bar.java", Java.INSTANCE, false); + FileUtils.write(new java.io.File(baseDir, "src/org/foo/Bar.java"), "Foo bar"); + + assertThat(index.index(directory)).isTrue(); + assertThat(index.index(file, directory)).isTrue(); + + File fileRef = File.create("src/org/foo/Bar.java", "org/foo/Bar.java", null, false); + assertThat(index.getSource(fileRef)).isEqualTo("Foo bar"); + } + + @Test public void shouldIndexLibraryOutsideProjectTree() { Library lib = new Library("junit", "4.8"); assertThat(index.index(lib)).isTrue(); 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 9228e408737..a8f64618eaf 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..c66809ec396 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), null, 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..bc4330f8734 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, null, 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, null, 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 4b405cca96c..40d6d14ce34 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 @@ -384,7 +384,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/IssueCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java index 8cdf7fcc0ed..d1cb4dd4844 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssueCacheTest.java @@ -35,6 +35,7 @@ import org.sonar.batch.index.CachesTest; import javax.annotation.Nullable; +import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -63,11 +64,12 @@ public class IssueCacheTest { IssueCache cache = new IssueCache(caches); DefaultIssue issue1 = new DefaultIssue().setKey("111").setComponentKey("org.struts.Action"); DefaultIssue issue2 = new DefaultIssue().setKey("222").setComponentKey("org.struts.Action"); - DefaultIssue issue3 = new DefaultIssue().setKey("333").setComponentKey("org.struts.Filter"); + DefaultIssue issue3 = new DefaultIssue().setKey("333").setComponentKey("org.struts.Filter").setTags(Arrays.asList("foo", "bar")); cache.put(issue1).put(issue2).put(issue3); assertThat(issueKeys(cache.byComponent("org.struts.Action"))).containsOnly("111", "222"); assertThat(issueKeys(cache.byComponent("org.struts.Filter"))).containsOnly("333"); + assertThat(cache.byComponent("org.struts.Filter").iterator().next().tags()).containsOnly("foo", "bar"); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuePersisterTest.java index eb0950ac88d..ba333576c03 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuePersisterTest.java @@ -28,11 +28,7 @@ import org.sonar.core.persistence.AbstractDaoTestCase; import java.util.Arrays; import java.util.List; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class IssuePersisterTest extends AbstractDaoTestCase { @@ -53,7 +49,7 @@ public class IssuePersisterTest extends AbstractDaoTestCase { } @Test - public void should_persist_all_issues() throws Exception { + public void should_not_persist_issues_anymore() throws Exception { persister.persist(); verify(storage, times(1)).save(issues); 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..f323737ca37 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, null, snapshot); + + verifier = new QualityGateVerifier(qualityGate, resourceCache, periods, i18n, durations); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java new file mode 100644 index 00000000000..28932c22148 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java @@ -0,0 +1,153 @@ +/* + * 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.apache.commons.io.IOUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.skyscreamer.jsonassert.JSONAssert; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.*; +import org.sonar.batch.index.ResourceCache; + +import java.io.File; +import java.text.SimpleDateFormat; + +public class ComponentsPublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void testComponentPublisher() throws Exception { + ProjectReactor reactor = new ProjectReactor(ProjectDefinition.create().setKey("foo")); + ResourceCache resourceCache = new ResourceCache(); + ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache); + + Project root = new Project("foo").setName("Root project").setAnalysisDate(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012")); + root.setId(1); + resourceCache.add(root, null, new Snapshot().setId(11)); + Project module1 = new Project("module1").setName("Module1"); + module1.setParent(root); + module1.setId(2); + resourceCache.add(module1, root, new Snapshot().setId(12)); + Directory dir1 = Directory.create("src"); + dir1.setEffectiveKey("foo:src"); + dir1.setId(3); + resourceCache.add(dir1, module1, new Snapshot().setId(13)); + org.sonar.api.resources.File mainFile = org.sonar.api.resources.File.create("src/Foo.java", "Foo.java", Java.INSTANCE, false); + mainFile.setEffectiveKey("foo:src/Foo.java"); + mainFile.setId(4); + resourceCache.add(mainFile, dir1, new Snapshot().setId(14)); + Directory dir2 = Directory.create("test"); + dir2.setEffectiveKey("foo:test"); + dir2.setId(5); + resourceCache.add(dir2, module1, new Snapshot().setId(15)); + org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", "FooTest.java", Java.INSTANCE, true); + testFile.setEffectiveKey("foo:test/FooTest.java"); + testFile.setId(6); + resourceCache.add(testFile, dir2, new Snapshot().setId(16)); + + File exportDir = temp.newFolder(); + publisher.export(exportDir); + + JSONAssert + .assertEquals( + IOUtils.toString(this.getClass().getResourceAsStream("ComponentsPublisherTest/expected.json"), "UTF-8"), + FileUtils.readFileToString(new File(exportDir, "components.json")), true); + } + + @Test + public void testComponentPublisher_containing_file_without_language() throws Exception { + ProjectReactor reactor = new ProjectReactor(ProjectDefinition.create().setKey("ALL_PROJECT")); + ResourceCache resourceCache = new ResourceCache(); + ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache); + + View view = new View("ALL_PROJECT"); + view.setId(1); + view.setAnalysisDate(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012")); + resourceCache.add(view, null, new Snapshot().setId(11)); + + org.sonar.api.resources.File mainFile = org.sonar.api.resources.File.create("ALL_PROJECTsample", "ALL_PROJECTsample", null, false); + mainFile.setEffectiveKey("ALL_PROJECTsample"); + mainFile.setId(2); + resourceCache.add(mainFile, view, new Snapshot().setId(12)); + + File exportDir = temp.newFolder(); + publisher.export(exportDir); + + JSONAssert + .assertEquals( + IOUtils.toString(this.getClass().getResourceAsStream("ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json"), "UTF-8"), + FileUtils.readFileToString(new File(exportDir, "components.json")), true); + } + + private static class View extends Project { + + private View(String key) { + super(key); + } + + @Override + public String getName() { + return "All Projects"; + } + + @Override + public String getLongName() { + return null; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public Language getLanguage() { + return null; + } + + @Override + public String getScope() { + return Scopes.PROJECT; + } + + @Override + public String getQualifier() { + return Qualifiers.VIEW; + } + + @Override + public Project getParent() { + return null; + } + + @Override + public boolean matchFilePattern(String antPattern) { + return false; + } + } + +} 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..86fa1d726b5 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, null, snapshot); + loader = new PreviousFileHashLoader(project, resourceCache, snapshotDataDao, pastSnapshotFinder); + } @Test public void should_return_null_if_no_previous_snapshot() throws Exception { diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml index 59d288be419..458ce2553c2 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml @@ -14,6 +14,7 @@ updated_at="[null]" reporter="emmerik" issue_attributes="foo=bar" + tags="[null]" action_plan_key="[null]" issue_creation_date="2013-05-18" issue_update_date="2013-05-18" diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates-result.xml index c18f8076427..e421b224b5e 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates-result.xml @@ -23,6 +23,7 @@ rule_id="200" reporter="[null]" issue_attributes="JIRA=http://jira.com" + tags="[null]" action_plan_key="[null]" created_at="1400000000000" updated_at="1400000000000" diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml index 7cae675e982..ba65e9df145 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml @@ -19,6 +19,7 @@ updated_at="1400000000000" reporter="emmerik" issue_attributes="foo=bar" + tags="[null]" action_plan_key="[null]" issue_creation_date="2013-05-18 00:00:00.0" issue_update_date="2013-05-18 00:00:00.0" diff --git a/sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/expected.json b/sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/expected.json new file mode 100644 index 00000000000..09d60b6c0dd --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/expected.json @@ -0,0 +1,59 @@ +{ + "analysisDate": "2012-12-12T00:00:00+0100", + "root": { + "batchId": 1, + "id": 1, + "snapshotId": 11, + "name": "Root project", + "type": "PRJ", + "children": [ + { + "batchId": 2, + "id": 2, + "snapshotId": 12, + "name": "Module1", + "type": "MOD", + "children": [ + { + "batchId": 3, + "id": 3, + "snapshotId": 13, + "path": "src", + "type": "DIR", + "children": [ + { + "batchId": 4, + "id": 4, + "snapshotId": 14, + "path": "src/Foo.java", + "type": "FIL", + "isTest": false, + "languageKey": "java", + "children": [] + } + ] + }, + { + "batchId": 5, + "id": 5, + "snapshotId": 15, + "path": "test", + "type": "DIR", + "children": [ + { + "batchId": 6, + "id": 6, + "snapshotId": 16, + "path": "test/FooTest.java", + "type": "FIL", + "isTest": true, + "languageKey": "java", + "children": [] + } + ] + } + ] + } + ] + } +}
\ No newline at end of file diff --git a/sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json b/sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json new file mode 100644 index 00000000000..4c82576ee87 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json @@ -0,0 +1,21 @@ +{ + "analysisDate": "2012-12-12T00:00:00+0100", + "root": { + "batchId": 1, + "id": 1, + "snapshotId": 11, + "name": "All Projects", + "type": "VIEW", + "children": [ + { + "batchId": 2, + "id": 2, + "snapshotId": 12, + "path": "ALL_PROJECTsample", + "type": "FIL", + "isTest": false, + "children": [] + } + ] + } +} |