]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1711 add a lock on SensorContext/DecoratorContext in order to avoid creation...
authorsimonbrandhof <simon.brandhof@gmail.com>
Fri, 24 Sep 2010 16:02:35 +0000 (16:02 +0000)
committersimonbrandhof <simon.brandhof@gmail.com>
Fri, 24 Sep 2010 16:02:35 +0000 (16:02 +0000)
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java
sonar-batch/src/main/java/org/sonar/batch/Batch.java
sonar-batch/src/main/java/org/sonar/batch/DefaultResourceCreationLock.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java
sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java
sonar-batch/src/test/java/org/sonar/batch/DefaultResourceCreationLockTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/indexer/DefaultSonarIndexTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceCreationLock.java [new file with mode: 0644]

index b54a223fced9e92861ed506605ce0e461506fdf8..1b8f32a88920324fad47c009e6148c3a75a88b3e 100644 (file)
@@ -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();
index 34927cfb1c7a4b33e47601c805a0f3463ff638f2..0b47543b33ac4b84d0ebc66f3777efc330f69cc7 100644 (file)
@@ -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());
   }
index cf199af94beafa10b8053fb8e2bd23c50b63f48f..4cd649b3d65a01836539a53e562d607632f5199a 100644 (file)
@@ -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 (file)
index 0000000..93b33f6
--- /dev/null
@@ -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;
+  }
+
+}
index c3298085af17187e281b35dcf74b9056b0bb059c..fbe69040a353ea2094641bb404980a28390793e4 100644 (file)
@@ -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;
index 48a6bc729978050177039a60a35b9dd52aa55fbe..e6c2c21cd7b72ad70dc327ec4b43a2cfd85ec350 100644 (file)
@@ -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 (file)
index 0000000..24918b4
--- /dev/null
@@ -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));
+  }
+}
index fdbc09cc47cf4eff53694e6839b448874c2eeb44..c818de86ad5984636038d49dbdf4d317f7360706 100644 (file)
 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));
+  }
 }
index 662bfbad2e5957cce7683bbf00e0f78e8d267bfc..f32e3e951ffa5119bbd98e183d33c7287014ad38 100644 (file)
@@ -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 (file)
index 0000000..12280f5
--- /dev/null
@@ -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();
+  
+}