Skip to content

Commit

Permalink
SONAR-5077 Fix regression when a Sensor try to read "lines" measure. …
Browse files Browse the repository at this point in the history
…LinesSensor forced to be executed first.
  • Loading branch information
henryju committed Jan 20, 2015
1 parent 0d24e18 commit fb21d4c
Show file tree
Hide file tree
Showing 14 changed files with 538 additions and 659 deletions.
Expand Up @@ -21,10 +21,10 @@


import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap; import com.google.common.collect.SetMultimap;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.Decorator; import org.sonar.api.batch.Decorator;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.api.resources.Project; import org.sonar.api.resources.Project;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;


import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
Expand All @@ -39,9 +39,9 @@ public DecoratorsSelector(BatchExtensionDictionnary dictionnary) {
} }


public Collection<Decorator> select(Project project) { public Collection<Decorator> select(Project project) {
List<Decorator> decorators = new ArrayList<Decorator>(batchExtDictionnary.select(Decorator.class, project, false)); List<Decorator> decorators = new ArrayList<Decorator>(batchExtDictionnary.select(Decorator.class, project, false, null));
SetMultimap<Metric, Decorator> decoratorsByGeneratedMetric = getDecoratorsByMetric(decorators); SetMultimap<Metric, Decorator> decoratorsByGeneratedMetric = getDecoratorsByMetric(decorators);
for (Metric metric : batchExtDictionnary.select(Metric.class)) { for (Metric metric : batchExtDictionnary.select(Metric.class, null, false, null)) {
if (metric.getFormula() != null) { if (metric.getFormula() != null) {
decorators.add(new FormulaDecorator(metric, decoratorsByGeneratedMetric.get(metric))); decorators.add(new FormulaDecorator(metric, decoratorsByGeneratedMetric.get(metric)));
} }
Expand Down
Expand Up @@ -19,34 +19,49 @@
*/ */
package org.sonar.batch.bootstrap; package org.sonar.batch.bootstrap;


import org.sonar.batch.sensor.AnalyzerOptimizer; import com.google.common.base.Predicates;

import com.google.common.collect.Collections2;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.ClassUtils;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.CheckProject; import org.sonar.api.batch.CheckProject;
import org.sonar.api.batch.DependedUpon;
import org.sonar.api.batch.DependsUpon;
import org.sonar.api.batch.Phase; import org.sonar.api.batch.Phase;
import org.sonar.api.batch.maven.DependsUponMavenPlugin;
import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.platform.ComponentContainer; import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project; import org.sonar.api.resources.Project;
import org.sonar.api.utils.AnnotationUtils;
import org.sonar.api.utils.dag.DirectAcyclicGraph;
import org.sonar.batch.scan.SensorWrapper; import org.sonar.batch.scan.SensorWrapper;
import org.sonar.batch.sensor.AnalyzerOptimizer;
import org.sonar.batch.sensor.DefaultSensorContext; import org.sonar.batch.sensor.DefaultSensorContext;


import javax.annotation.Nullable; import javax.annotation.Nullable;


import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;


/** /**
* @since 2.6 * @since 2.6
*/ */
public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensionDictionnary { public class BatchExtensionDictionnary {


private SensorContext context; private final ComponentContainer componentContainer;
private AnalyzerOptimizer analyzerOptimizer; private final SensorContext context;
private final AnalyzerOptimizer analyzerOptimizer;


public BatchExtensionDictionnary(ComponentContainer componentContainer, DefaultSensorContext context, AnalyzerOptimizer analyzerOptimizer) { public BatchExtensionDictionnary(ComponentContainer componentContainer, DefaultSensorContext context, AnalyzerOptimizer analyzerOptimizer) {
super(componentContainer); this.componentContainer = componentContainer;
this.context = context; this.context = context;
this.analyzerOptimizer = analyzerOptimizer; this.analyzerOptimizer = analyzerOptimizer;
} }
Expand All @@ -59,13 +74,41 @@ public <T> Collection<T> select(Class<T> type, @Nullable Project project, boolea
return result; return result;
} }


@Override public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
protected Phase.Name evaluatePhase(Object extension) { List<DependsUponMavenPlugin> selectedExtensions = Lists.newArrayList();
for (Object extension : getExtensions(null)) {
if (ClassUtils.isAssignable(extension.getClass(), DependsUponMavenPlugin.class)) {
selectedExtensions.add((DependsUponMavenPlugin) extension);
}
}
List<MavenPluginHandler> handlers = Lists.newArrayList();
for (DependsUponMavenPlugin extension : selectedExtensions) {
MavenPluginHandler handler = extension.getMavenPluginHandler(project);
if (handler != null) {
boolean ok = true;
if (handler instanceof CheckProject) {
ok = ((CheckProject) handler).shouldExecuteOnProject(project);
}
if (ok) {
handlers.add(handler);
}
}
}
return handlers;
}

private Phase.Name evaluatePhase(Object extension) {
Object extensionToEvaluate;
if (extension instanceof SensorWrapper) { if (extension instanceof SensorWrapper) {
return super.evaluatePhase(((SensorWrapper) extension).wrappedSensor()); extensionToEvaluate = ((SensorWrapper) extension).wrappedSensor();
} else { } else {
return super.evaluatePhase(extension); extensionToEvaluate = extension;
}
Phase phaseAnnotation = AnnotationUtils.getAnnotation(extensionToEvaluate, Phase.class);
if (phaseAnnotation != null) {
return phaseAnnotation.name();
} }
return Phase.Name.DEFAULT;
} }


private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project project, @Nullable ExtensionMatcher matcher) { private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project project, @Nullable ExtensionMatcher matcher) {
Expand All @@ -90,6 +133,135 @@ private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project proje
return result; return result;
} }


protected List<Object> getExtensions(@Nullable Class type) {
List<Object> extensions = Lists.newArrayList();
completeBatchExtensions(componentContainer, extensions, type);
return extensions;
}

private static void completeBatchExtensions(ComponentContainer container, List<Object> extensions, @Nullable Class type) {
if (container != null) {
extensions.addAll(container.getComponentsByType(type != null ? type : BatchExtension.class));
completeBatchExtensions(container.getParent(), extensions, type);
}
}

public <T> Collection<T> sort(Collection<T> extensions) {
DirectAcyclicGraph dag = new DirectAcyclicGraph();

for (T extension : extensions) {
dag.add(extension);
for (Object dependency : getDependencies(extension)) {
dag.add(extension, dependency);
}
for (Object generates : getDependents(extension)) {
dag.add(generates, extension);
}
completePhaseDependencies(dag, extension);
}
List sortedList = dag.sort();

return Collections2.filter(sortedList, Predicates.in(extensions));
}

/**
* Extension dependencies
*/
private <T> List<Object> getDependencies(T extension) {
List<Object> result = new ArrayList<Object>();
result.addAll(evaluateAnnotatedClasses(extension, DependsUpon.class));
return result;
}

/**
* Objects that depend upon this extension.
*/
public <T> List<Object> getDependents(T extension) {
List<Object> result = new ArrayList<Object>();
result.addAll(evaluateAnnotatedClasses(extension, DependedUpon.class));
return result;
}

private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
Phase.Name phase = evaluatePhase(extension);
dag.add(extension, phase);
for (Phase.Name name : Phase.Name.values()) {
if (phase.compareTo(name) < 0) {
dag.add(name, extension);
} else if (phase.compareTo(name) > 0) {
dag.add(extension, name);
}
}
}

protected List<Object> evaluateAnnotatedClasses(Object extension, Class<? extends Annotation> annotation) {
List<Object> results = Lists.newArrayList();
Class aClass = extension.getClass();
while (aClass != null) {
evaluateClass(aClass, annotation, results);

for (Method method : aClass.getDeclaredMethods()) {
if (method.getAnnotation(annotation) != null) {
checkAnnotatedMethod(method);
evaluateMethod(extension, method, results);
}
}
aClass = aClass.getSuperclass();
}

return results;
}

private void evaluateClass(Class extensionClass, Class annotationClass, List<Object> results) {
Annotation annotation = extensionClass.getAnnotation(annotationClass);
if (annotation != null) {
if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
results.addAll(Arrays.asList(((DependsUpon) annotation).value()));

} else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
}
}

Class[] interfaces = extensionClass.getInterfaces();
for (Class anInterface : interfaces) {
evaluateClass(anInterface, annotationClass, results);
}
}

private void evaluateMethod(Object extension, Method method, List<Object> results) {
try {
Object result = method.invoke(extension);
if (result != null) {
if (result instanceof Class<?>) {
results.addAll(componentContainer.getComponentsByType((Class<?>) result));

} else if (result instanceof Collection<?>) {
results.addAll((Collection<?>) result);

} else if (result.getClass().isArray()) {
for (int i = 0; i < Array.getLength(result); i++) {
results.add(Array.get(result, i));
}

} else {
results.add(result);
}
}
} catch (Exception e) {
throw new IllegalStateException("Can not invoke method " + method, e);
}
}

private void checkAnnotatedMethod(Method method) {
if (!Modifier.isPublic(method.getModifiers())) {
throw new IllegalStateException("Annotated method must be public:" + method);
}
if (method.getParameterTypes().length > 0) {
throw new IllegalStateException("Annotated method must not have parameters:" + method);
}
}

private boolean shouldKeep(Class type, Object extension, @Nullable Project project, @Nullable ExtensionMatcher matcher) { private boolean shouldKeep(Class type, Object extension, @Nullable Project project, @Nullable ExtensionMatcher matcher) {
boolean keep = (ClassUtils.isAssignable(extension.getClass(), type) boolean keep = (ClassUtils.isAssignable(extension.getClass(), type)
|| (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class))) || (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class)))
Expand Down
Expand Up @@ -21,7 +21,6 @@


import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.sonar.api.BatchComponent; import org.sonar.api.BatchComponent;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.Decorator; import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.SonarIndex;
Expand All @@ -32,6 +31,7 @@
import org.sonar.api.utils.SonarException; import org.sonar.api.utils.SonarException;
import org.sonar.batch.DecoratorsSelector; import org.sonar.batch.DecoratorsSelector;
import org.sonar.batch.DefaultDecoratorContext; import org.sonar.batch.DefaultDecoratorContext;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.events.EventBus; import org.sonar.batch.events.EventBus;
import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.scan.measure.MeasureCache;
Expand Down
Expand Up @@ -23,12 +23,12 @@
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.Initializer; import org.sonar.api.batch.Initializer;
import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.DependsUponMavenPlugin;
import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.resources.Project; import org.sonar.api.resources.Project;
import org.sonar.api.utils.TimeProfiler; import org.sonar.api.utils.TimeProfiler;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
import org.sonar.batch.events.EventBus; import org.sonar.batch.events.EventBus;
import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
import org.sonar.batch.scan.maven.MavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor;
Expand All @@ -55,7 +55,7 @@ public InitializersExecutor(BatchExtensionDictionnary selector, Project project,
} }


public void execute() { public void execute() {
Collection<Initializer> initializers = selector.select(Initializer.class, project, true); Collection<Initializer> initializers = selector.select(Initializer.class, project, true, null);
eventBus.fireEvent(new InitializersPhaseEvent(Lists.newArrayList(initializers), true)); eventBus.fireEvent(new InitializersPhaseEvent(Lists.newArrayList(initializers), true));
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> ")); LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> "));
Expand Down
Expand Up @@ -24,12 +24,12 @@
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent; import org.sonar.api.BatchComponent;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.PostJob; import org.sonar.api.batch.PostJob;
import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.DependsUponMavenPlugin;
import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.resources.Project; import org.sonar.api.resources.Project;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
import org.sonar.batch.events.EventBus; import org.sonar.batch.events.EventBus;
import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
import org.sonar.batch.scan.maven.MavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor;
Expand All @@ -46,7 +46,7 @@ public class PostJobsExecutor implements BatchComponent {
private final EventBus eventBus; private final EventBus eventBus;


public PostJobsExecutor(BatchExtensionDictionnary selector, Project project, DefaultModuleFileSystem fs, MavenPluginExecutor mavenExecutor, public PostJobsExecutor(BatchExtensionDictionnary selector, Project project, DefaultModuleFileSystem fs, MavenPluginExecutor mavenExecutor,
EventBus eventBus) { EventBus eventBus) {
this.selector = selector; this.selector = selector;
this.project = project; this.project = project;
this.fs = fs; this.fs = fs;
Expand All @@ -55,7 +55,7 @@ public PostJobsExecutor(BatchExtensionDictionnary selector, Project project, Def
} }


public void execute(SensorContext context) { public void execute(SensorContext context) {
Collection<PostJob> postJobs = selector.select(PostJob.class, project, true); Collection<PostJob> postJobs = selector.select(PostJob.class, project, true, null);


eventBus.fireEvent(new PostJobPhaseEvent(Lists.newArrayList(postJobs), true)); eventBus.fireEvent(new PostJobPhaseEvent(Lists.newArrayList(postJobs), true));
execute(context, postJobs); execute(context, postJobs);
Expand Down
Expand Up @@ -24,10 +24,10 @@
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.sonar.api.BatchComponent; import org.sonar.api.BatchComponent;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.maven.MavenPlugin; import org.sonar.api.batch.maven.MavenPlugin;
import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.resources.Project; import org.sonar.api.resources.Project;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;


import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
Expand Down
Expand Up @@ -19,6 +19,7 @@
*/ */
package org.sonar.batch.source; package org.sonar.batch.source;


import org.sonar.api.batch.Phase;
import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.InputFile.Type;
Expand All @@ -28,6 +29,7 @@
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.CoreMetrics;


@Phase(name = Phase.Name.PRE)
public final class LinesSensor implements Sensor { public final class LinesSensor implements Sensor {


private final FileSystem fs; private final FileSystem fs;
Expand Down
Expand Up @@ -21,7 +21,6 @@


import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.Decorator; import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.DependedUpon; import org.sonar.api.batch.DependedUpon;
Expand All @@ -33,6 +32,7 @@
import org.sonar.api.platform.ComponentContainer; import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project; import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource; import org.sonar.api.resources.Resource;
import org.sonar.batch.bootstrap.BatchExtensionDictionnary;


import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
Expand Down Expand Up @@ -81,7 +81,7 @@ private BatchExtensionDictionnary newBatchDictionnary(Object... extensions) {
for (Object extension : extensions) { for (Object extension : extensions) {
ioc.addSingleton(extension); ioc.addSingleton(extension);
} }
return new BatchExtensionDictionnary(ioc); return new BatchExtensionDictionnary(ioc, null, null);
} }


class FakeFormula implements Formula { class FakeFormula implements Formula {
Expand Down

0 comments on commit fb21d4c

Please sign in to comment.