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;
@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
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();
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;
@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"));
@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));
@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());
}
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);
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());
--- /dev/null
+/*
+ * 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;
+ }
+
+}
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;
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;
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.*;
private ResourcePersisters resourcePersisters;
private Bucket<Project> rootProjectBucket;
private Bucket<Project> selectedProjectBucket;
+ private DefaultResourceCreationLock lock;
private ViolationFilters violationFilters;
private ResourceFilters resourceFilters;
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() {
projectDependency.setId(null);
registerDependency(projectDependency);
}
+
+ lock.unlock();
}
/* ------------ RESOURCES */
}
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);
Bucket parentBucket = null;
Resource parent = resource.getParent();
if (parent != null) {
- parentBucket = getOrCreateBucket(parent);
+ parentBucket = getOrCreateBucket(parent, mustExist);
} else if (!ResourceUtils.isLibrary(resource)) {
parentBucket = selectedProjectBucket;
}
/* ------------ 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()) {
if (resource == null) {
bucket = selectedProjectBucket;
} else {
- bucket = getOrCreateBucket(resource);
+ bucket = getOrCreateBucket(resource, true);
}
if (!bucket.isExcluded()) {
persistViolation(violation, bucket.getSnapshot());
}
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);
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());
/* ----------- EVENTS */
public List<Event> getEvents(Resource resource) {
- Bucket bucket = getOrCreateBucket(resource);
+ Bucket bucket = getOrCreateBucket(resource, true);
return session.getResults(Event.class, "resourceId", bucket.getResourceId());
}
}
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());
--- /dev/null
+/*
+ * 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));
+ }
+}
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;
@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");
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));
+ }
}
*/
public void analyse(Project project, SensorContext context) {
analyse(project.getFileSystem(), context);
+ onFinished();
+ }
+
+ protected void onFinished() {
+
}
protected void analyse(ProjectFileSystem fileSystem, SensorContext context) {
--- /dev/null
+/*
+ * 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();
+
+}