diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-24 16:02:35 +0000 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-24 16:02:35 +0000 |
commit | 51c728457cb0ea1763625d3600bddc2c3d7f049b (patch) | |
tree | b5b29779f065bb57d4755a541db08879cb61bfc2 /sonar-batch | |
parent | a5ba257ffd83b0418f12c0dc1ba9542873f0d6e8 (diff) | |
download | sonarqube-51c728457cb0ea1763625d3600bddc2c3d7f049b.tar.gz sonarqube-51c728457cb0ea1763625d3600bddc2c3d7f049b.zip |
SONAR-1711 add a lock on SensorContext/DecoratorContext in order to avoid creation of resources in methods saveViolation() and saveMeasure().
Diffstat (limited to 'sonar-batch')
6 files changed, 133 insertions, 19 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/Batch.java index cf199af94be..4cd649b3d65 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/Batch.java @@ -68,6 +68,7 @@ public class Batch { MutablePicoContainer batchContainer = container.makeChildContainer(); batchContainer.as(Characteristics.CACHE).addComponent(ServerMetadata.class); batchContainer.as(Characteristics.CACHE).addComponent(ProjectTree.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultResourceCreationLock.class); batchContainer.as(Characteristics.CACHE).addComponent(DefaultSonarIndex.class); batchContainer.as(Characteristics.CACHE).addComponent(JpaPluginDao.class); batchContainer.as(Characteristics.CACHE).addComponent(BatchPluginRepository.class); @@ -90,7 +91,7 @@ public class Batch { URLClassLoader fullClassloader = RemoteClassLoader.createForJdbcDriver(configuration).getClassLoader(); // set as the current context classloader for hibernate, else it does not find the JDBC driver. Thread.currentThread().setContextClassLoader(fullClassloader); - + register(container, new DriverDatabaseConnector(configuration, fullClassloader)); register(container, ThreadLocalDatabaseSessionFactory.class); container.as(Characteristics.CACHE).addAdapter(new DatabaseSessionProvider()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultResourceCreationLock.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultResourceCreationLock.java new file mode 100644 index 00000000000..93b33f6b53a --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultResourceCreationLock.java @@ -0,0 +1,49 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.ResourceCreationLock; + +/** + * This lock is used to ensure that Sonar resources (files, packages, directories) are not created by buggy plugins + * when saving measures/violations on unknown resources. + * + * @since 2.3 + */ +public final class DefaultResourceCreationLock implements ResourceCreationLock { + + private boolean locked = false; + + public boolean isLocked() { + return locked; + } + + public void lock() { + this.locked = true; + } + + /** + * Unlocking is for internal use only. + */ + public void unlock() { + locked = false; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java index c3298085af1..fbe69040a35 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java @@ -23,6 +23,7 @@ import org.picocontainer.Characteristics; import org.picocontainer.MutablePicoContainer; import org.sonar.api.batch.BatchExtensionDictionnary; import org.sonar.api.batch.ProjectClasspath; +import org.sonar.api.batch.ResourceCreationLock; import org.sonar.api.batch.SensorContext; import org.sonar.api.database.DatabaseSession; import org.sonar.api.measures.CoreMetrics; diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java index 48a6bc72997..e6c2c21cd7b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java @@ -27,6 +27,8 @@ import org.slf4j.LoggerFactory; import org.sonar.api.batch.Event; import org.sonar.api.batch.SonarIndex; import org.sonar.api.database.DatabaseSession; +import org.sonar.api.utils.SonarException; +import org.sonar.batch.*; import org.sonar.jpa.dao.MeasuresDao; import org.sonar.api.database.model.MeasureModel; import org.sonar.api.database.model.ResourceModel; @@ -43,10 +45,6 @@ import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.api.rules.Violation; -import org.sonar.batch.ProjectTree; -import org.sonar.batch.ResourceFilters; -import org.sonar.batch.ViolationFilters; -import org.sonar.batch.ViolationsDao; import java.util.*; @@ -58,6 +56,7 @@ public class DefaultSonarIndex extends SonarIndex { private ResourcePersisters resourcePersisters; private Bucket<Project> rootProjectBucket; private Bucket<Project> selectedProjectBucket; + private DefaultResourceCreationLock lock; private ViolationFilters violationFilters; private ResourceFilters resourceFilters; @@ -73,10 +72,11 @@ public class DefaultSonarIndex extends SonarIndex { private MeasuresDao measuresDao; private ProjectTree projectTree; - public DefaultSonarIndex(DatabaseSession session, ProjectTree projectTree) { + public DefaultSonarIndex(DatabaseSession session, ProjectTree projectTree, DefaultResourceCreationLock lock) { this.session = session; this.projectTree = projectTree; this.resourcePersisters = new ResourcePersisters(session); + this.lock = lock; } public void start() { @@ -131,6 +131,8 @@ public class DefaultSonarIndex extends SonarIndex { projectDependency.setId(null); registerDependency(projectDependency); } + + lock.unlock(); } /* ------------ RESOURCES */ @@ -166,15 +168,19 @@ public class DefaultSonarIndex extends SonarIndex { } public Resource addResource(Resource resource) { - return getOrCreateBucket(resource).getResource(); + return getOrCreateBucket(resource, false).getResource(); } - private Bucket<Resource> getOrCreateBucket(Resource resource) { + private Bucket<Resource> getOrCreateBucket(Resource resource, boolean mustExist) { Bucket bucket = buckets.get(resource); if (bucket != null) { return bucket; } + if (mustExist && lock.isLocked() && !ResourceUtils.isLibrary(resource)) { + throw new SonarException("The following resource has not been registered before saving violation/measure/event: " + resource); + } + prepareResource(resource); bucket = new Bucket<Resource>(resource); buckets.put(resource, bucket); @@ -182,7 +188,7 @@ public class DefaultSonarIndex extends SonarIndex { Bucket parentBucket = null; Resource parent = resource.getParent(); if (parent != null) { - parentBucket = getOrCreateBucket(parent); + parentBucket = getOrCreateBucket(parent, mustExist); } else if (!ResourceUtils.isLibrary(resource)) { parentBucket = selectedProjectBucket; } @@ -203,18 +209,18 @@ public class DefaultSonarIndex extends SonarIndex { /* ------------ MEASURES */ public Measure getMeasure(Resource resource, Metric metric) { - return getOrCreateBucket(resource).getMeasures(MeasuresFilters.metric(metric)); + return getOrCreateBucket(resource, false).getMeasures(MeasuresFilters.metric(metric)); } public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) { - return getOrCreateBucket(resource).getMeasures(filter); + return getOrCreateBucket(resource, false).getMeasures(filter); } /* ------------ SOURCE CODE */ public void setSource(Resource resource, String source) { - Bucket bucket = getOrCreateBucket(resource); + Bucket bucket = getOrCreateBucket(resource, false); if (!bucket.isExcluded()) { if (bucket.isSourceSaved()) { @@ -236,7 +242,7 @@ public class DefaultSonarIndex extends SonarIndex { if (resource == null) { bucket = selectedProjectBucket; } else { - bucket = getOrCreateBucket(resource); + bucket = getOrCreateBucket(resource, true); } if (!bucket.isExcluded()) { persistViolation(violation, bucket.getSnapshot()); @@ -260,7 +266,7 @@ public class DefaultSonarIndex extends SonarIndex { } public Measure addMeasure(Resource resource, Measure measure) { - Bucket bucket = getOrCreateBucket(resource); + Bucket bucket = getOrCreateBucket(resource, true); if (!bucket.isExcluded()) { if (bucket.getMeasures(MeasuresFilters.measure(measure))!=null) { throw new IllegalArgumentException("This measure has already been saved: " + measure + ",resource: " + resource); @@ -316,8 +322,8 @@ public class DefaultSonarIndex extends SonarIndex { if (persistedDep != null && persistedDep.getId()!=null) { return persistedDep; } - Bucket from = getOrCreateBucket(dependency.getFrom()); - Bucket to = getOrCreateBucket(dependency.getTo()); + Bucket from = getOrCreateBucket(dependency.getFrom(), true); + Bucket to = getOrCreateBucket(dependency.getTo(), true); DependencyDto dto = new DependencyDto(); dto.setFromResourceId(from.getResourceId()); @@ -399,7 +405,7 @@ public class DefaultSonarIndex extends SonarIndex { /* ----------- EVENTS */ public List<Event> getEvents(Resource resource) { - Bucket bucket = getOrCreateBucket(resource); + Bucket bucket = getOrCreateBucket(resource, true); return session.getResults(Event.class, "resourceId", bucket.getResourceId()); } @@ -408,7 +414,7 @@ public class DefaultSonarIndex extends SonarIndex { } public Event createEvent(Resource resource, String name, String description, String category, Date date) { - Bucket bucket = getOrCreateBucket(resource); + Bucket bucket = getOrCreateBucket(resource, true); Event event; if (date == null) { event = new Event(name, description, category, bucket.getSnapshot()); diff --git a/sonar-batch/src/test/java/org/sonar/batch/DefaultResourceCreationLockTest.java b/sonar-batch/src/test/java/org/sonar/batch/DefaultResourceCreationLockTest.java new file mode 100644 index 00000000000..24918b4326e --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/DefaultResourceCreationLockTest.java @@ -0,0 +1,43 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.junit.Test; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class DefaultResourceCreationLockTest { + + @Test + public void shouldNotBeLockedAtStartup() { + assertThat(new DefaultResourceCreationLock().isLocked(), is(false)); + } + + @Test + public void shouldLock() { + DefaultResourceCreationLock lock = new DefaultResourceCreationLock(); + lock.lock(); + assertThat(lock.isLocked(), is(true)); + + lock.unlock(); + assertThat(lock.isLocked(), is(false)); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/indexer/DefaultSonarIndexTest.java b/sonar-batch/src/test/java/org/sonar/batch/indexer/DefaultSonarIndexTest.java index fdbc09cc47c..c818de86ad5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/indexer/DefaultSonarIndexTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/indexer/DefaultSonarIndexTest.java @@ -20,6 +20,11 @@ package org.sonar.batch.indexer; import org.junit.Test; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.PersistenceMode; +import org.sonar.api.utils.SonarException; +import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.jpa.test.AbstractDbUnitTestCase; import org.sonar.api.design.Dependency; import org.sonar.api.resources.JavaFile; @@ -33,7 +38,7 @@ public class DefaultSonarIndexTest extends AbstractDbUnitTestCase { @Test public void indexDependencies() { - DefaultSonarIndex index = new DefaultSonarIndex(getSession(), null); + DefaultSonarIndex index = new DefaultSonarIndex(getSession(), null, new DefaultResourceCreationLock()); Resource from = new JavaFile("org.foo.Foo"); Resource to = new JavaFile("org.bar.Bar"); @@ -53,4 +58,13 @@ public class DefaultSonarIndexTest extends AbstractDbUnitTestCase { assertTrue(index.getOutgoingEdges(from).contains(dependency)); assertThat(index.getOutgoingEdges(to).isEmpty(), is(true)); } + + @Test(expected = SonarException.class) + public void failIfLockedAndAddingMeasureOnUnknownResource() { + DefaultResourceCreationLock lock = new DefaultResourceCreationLock(); + lock.lock(); + + DefaultSonarIndex index = new DefaultSonarIndex(getSession(), null, lock); + index.saveMeasure(new JavaFile("org.foo.Bar"), new Measure(CoreMetrics.LINES, 200.0)); + } } |