How can I pass a custom configuration to a CobolCheck class?

I’m working on a simple custom Cobol plugin, based entirely on the sample custom Cobol plugin project. I want to code a rule that operates on statement nodes. I’d like that rule to be configurable, preferably at the server level, as the rule would be applied enterprise-wide.

How do I get the property definitions I’ve created in my plugin to the CobolCheck class that implements my rule logic? I don’t see anything in the JavaDoc that relates to the the Configuration interface.

Thanks!

Could you try something like that and tell us whether it helps you?

public class MyCheckRepository 
  implements CobolCheckRepository, RulesDefinition {

  private Configuration config;  

  public MyCheckRepository(Configuration configuration) {
    this.config = configuration;
  }
  
  @Override
  public Collection<Object> getCheckClassesOrObjects() {
    return Arrays.asList(new MyCustomRule(this.config));
  }
}

I get a ClassCastException:

@Override
public Collection getCheckClassesOrObjects() {

    return Arrays.asList(new OneRuleToRuleThemAll(this.config));
}

Log entry:

2019.03.22 12:05:47 ERROR web[o.s.s.p.Platform] Background initialization failed. Stopping SonarQube
java.lang.ClassCastException: Cannot cast com.sauron.OneRuleToRuleThemAll to java.lang.Class
at java.lang.Class.cast(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at com.sonar.sauron.CobolCustomCheckRepository.define(CobolCustomCheckRepository.java:45)
at org.sonar.server.rule.RuleDefinitionsLoader.load(RuleDefinitionsLoader.java:56)
at org.sonar.server.rule.RegisterRules.start(RegisterRules.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.picocontainer.lifecycle.ReflectionLifecycleStrategy.invokeMethod(ReflectionLifecycleStrategy.java:110)
at org.picocontainer.lifecycle.ReflectionLifecycleStrategy.start(ReflectionLifecycleStrategy.java:89)
at org.picocontainer.injectors.AbstractInjectionFactory$LifecycleAdapter.start(AbstractInjectionFactory.java:84)
at org.picocontainer.behaviors.AbstractBehavior.start(AbstractBehavior.java:169)
at org.picocontainer.behaviors.Stored$RealComponentLifecycle.start(Stored.java:132)
at org.picocontainer.behaviors.Stored.start(Stored.java:110)
at org.picocontainer.DefaultPicoContainer.potentiallyStartAdapter(DefaultPicoContainer.java:1016)
at org.picocontainer.DefaultPicoContainer.startAdapters(DefaultPicoContainer.java:1009)
at org.picocontainer.DefaultPicoContainer.start(DefaultPicoContainer.java:767)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
at org.sonar.server.platform.platformlevel.PlatformLevel.start(PlatformLevel.java:90)
at org.sonar.server.platform.platformlevel.PlatformLevelStartup.access$001(PlatformLevelStartup.java:46)
at org.sonar.server.platform.platformlevel.PlatformLevelStartup$1.doPrivileged(PlatformLevelStartup.java:82)
at org.sonar.server.user.DoPrivileged.execute(DoPrivileged.java:46)
at org.sonar.server.platform.platformlevel.PlatformLevelStartup.start(PlatformLevelStartup.java:79)
at org.sonar.server.platform.Platform.executeStartupTasks(Platform.java:196)
at org.sonar.server.platform.Platform.access$400(Platform.java:46)
at org.sonar.server.platform.Platform$1.lambda$doRun$1(Platform.java:121)
at org.sonar.server.platform.Platform$AutoStarterRunnable.runIfNotAborted(Platform.java:371)
at org.sonar.server.platform.Platform$1.doRun(Platform.java:121)
at org.sonar.server.platform.Platform$AutoStarterRunnable.run(Platform.java:355)
at java.lang.Thread.run(Unknown Source)
2019.03.22 12:05:47 INFO web[o.s.p.StopWatcher] Stopping process

Yes, that comes from another part of the code of the sample plugin which you copy-pasted.

You have to update it since getCheckClassesOrObjects now returns objects which are not classes:

annotationLoader.load(repository, OneRuleToRuleThemAll.class);

If I remove the cast on line 73, I get the expected compiler error on line 74, as the RulesDefinitionAnnotationLoader.load(RulesDefinition.NewExtendedRepository,Class... ) method still expects a class. I don’t see any overloaded method signatures that take an instance of any kind. Am I missing something?

I think I could subclass CobolCheck, add Configuration instance to it and use a Sensor implementation to populate it in the execute method, but I really want to leverage the existing SonarCobol sensor.

I was suggesting to replace:

getCheckClassesOrObjects().stream()
  .map(Class.class::cast)
  .forEach(ruleClass -> annotationLoader.load(repository, ruleClass));

with

annotationLoader.load(repository, OneRuleToRuleThemAll.class);

Did you try that?

I adjusted my code, adding a method that returns a list of classes based on the list of objects getCheckClassesOrObjects returns, and now my custom rule has access to the Configuration.

Thank you for your help. I do have a related question. Is there some documentation you can point me to that describes how dependency injection works in the plugin framework? I’ve seen numerous classes that use DI in an almost arbitrary way, but I’m sure that’s because I don’t understand how it’s used within the SonarQube server.

I found one documentation page which briefly mentions dependency injection:
https://docs.sonarqube.org/display/DEV/API+Basics#APIBasics-Lifecycle

If you want more details on that topic, you should start a new thread: the people who may help you would be more likely to see it than the current one which mentions COBOL…

Thank you for the link. It’s the page that first made me wonder how DI works in SonarQube, but I still don’t understand why it appears some constructors can simply declare an argument and have it automagically satisfied by the container.

I’ll probably create a new topic for it. Thanks again for your help.