From: simonbrandhof Date: Fri, 24 Sep 2010 16:02:35 +0000 (+0000) Subject: SONAR-1711 add a lock on SensorContext/DecoratorContext in order to avoid creation... X-Git-Tag: 2.6~950 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=51c728457cb0ea1763625d3600bddc2c3d7f049b;p=sonarqube.git SONAR-1711 add a lock on SensorContext/DecoratorContext in order to avoid creation of resources in methods saveViolation() and saveMeasure(). --- diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java index b54a223fced..1b8f32a8892 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java @@ -21,6 +21,7 @@ package org.sonar.plugins.core.sensors; import org.sonar.api.batch.AbstractSourceImporter; import org.sonar.api.batch.Phase; +import org.sonar.api.batch.ResourceCreationLock; import org.sonar.api.resources.Java; import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.Resource; @@ -30,9 +31,11 @@ import java.util.List; @Phase(name = Phase.Name.PRE) public class JavaSourceImporter extends AbstractSourceImporter { + private ResourceCreationLock lock; - public JavaSourceImporter() { + public JavaSourceImporter(ResourceCreationLock lock) { super(Java.INSTANCE); + this.lock = lock; } @Override @@ -40,6 +43,11 @@ public class JavaSourceImporter extends AbstractSourceImporter { return (file != null && !file.getName().contains("$")) ? JavaFile.fromIOFile(file, sourceDirs, unitTest) : null; } + @Override + protected void onFinished() { + lock.lock(); + } + @Override public String toString() { return getClass().getSimpleName(); diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java index 34927cfb1c7..0b47543b33a 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java @@ -23,7 +23,10 @@ import org.apache.commons.io.FileUtils; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + import org.junit.Test; +import org.sonar.api.batch.ResourceCreationLock; import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.JavaPackage; import org.sonar.api.resources.Resource; @@ -37,7 +40,7 @@ public class JavaSourceImporterTest { @Test public void shouldCreateResource() throws IOException { - JavaSourceImporter importer = new JavaSourceImporter(); + JavaSourceImporter importer = new JavaSourceImporter(mock(ResourceCreationLock.class)); Resource clazz = importer.createResource(new File(newDir("source1"), "/MyClass.java"), Arrays.asList(newDir("source1")), false); assertThat(clazz, is(JavaFile.class)); assertThat(clazz.getKey(), is(JavaPackage.DEFAULT_PACKAGE_NAME + ".MyClass")); @@ -46,7 +49,7 @@ public class JavaSourceImporterTest { @Test public void shouldCreateTestResource() throws IOException { - JavaSourceImporter importer = new JavaSourceImporter(); + JavaSourceImporter importer = new JavaSourceImporter(mock(ResourceCreationLock.class)); Resource resource = importer.createResource(new File(newDir("tests"), "/MyClassTest.java"), Arrays.asList(newDir("tests")), true); assertThat(resource, is(JavaFile.class)); assertThat(ResourceUtils.isUnitTestClass(resource), is(true)); @@ -55,7 +58,7 @@ public class JavaSourceImporterTest { @Test public void doNotSaveInnerClasses() throws IOException { // example : https://svn.apache.org/repos/asf/geronimo/server/trunk/plugins/corba/geronimo-corba/src/test/java/org/apache/geronimo/corba/compiler/other/Generic$Interface.java - JavaSourceImporter importer = new JavaSourceImporter(); + JavaSourceImporter importer = new JavaSourceImporter(mock(ResourceCreationLock.class)); Resource resource = importer.createResource(new File(newDir("tests"), "/Generic$Interface.java"), Arrays.asList(newDir("tests")), true); assertThat(resource, nullValue()); } 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 rootProjectBucket; private Bucket 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 getOrCreateBucket(Resource resource) { + private Bucket 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); 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 getMeasures(Resource resource, MeasuresFilter 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 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)); + } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java index 662bfbad2e5..f32e3e951ff 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java @@ -70,6 +70,11 @@ public abstract class AbstractSourceImporter implements Sensor { */ public void analyse(Project project, SensorContext context) { analyse(project.getFileSystem(), context); + onFinished(); + } + + protected void onFinished() { + } protected void analyse(ProjectFileSystem fileSystem, SensorContext context) { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceCreationLock.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceCreationLock.java new file mode 100644 index 00000000000..12280f59237 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceCreationLock.java @@ -0,0 +1,38 @@ +/* + * 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.api.batch; + +import org.sonar.api.BatchComponent; + +/** + * 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 interface ResourceCreationLock extends BatchComponent { + + /** + * Forbids the creation of resources when saving violations and measures. By default it's unlocked, so only warnings + * are logged. When locked, then an exception is thrown. + */ + void lock(); + +}