NPE with @Rules

I’m trying to work with the @Rules plugin and am getting an NPE. Here is the relevant code that is causing it and the stack trace that I’m getting.

  @Rules
  void applyToAllComponents(PegasusSchemaComponentRules rules, @Each PegasusSchemaComponentSpecInternal spec) {
    rules.setTarget(spec);
  }

  static abstract class PegasusSchemaComponentRules extends RuleSource {
    @RuleTarget
    abstract PegasusSchemaComponentSpecInternal getTarget();

    abstract void setTarget(PegasusSchemaComponentSpecInternal target);

    @Defaults
    void componentDefaults(final PegasusConfigurationExtension extension, @Path("buildDir") final File buildDir) {
      PegasusSchemaComponentSpecInternal spec = getTarget();
      logger.lifecycle("Setting defaults for {}", spec.getName());
      spec.setPegasusConfigurationExtension(extension);
      spec.setBuildDir(buildDir);
      if (StringUtils.containsIgnoreCase(spec.getName(), "test")) {
        spec.dependenciesFrom("testDataModel");
      } else {
        spec.dependenciesFrom("dataModel");
      }
    }

    @Rules
    void binaryDefaults(PegasusSchemaBinaryRules rules, @Each PegasusJavaDataSchemaBinaryInternal binarySpec) {
      rules.setComponent(getTarget());
      rules.setTarget(binarySpec);
    }
  }

  static abstract class PegasusSchemaBinaryRules extends RuleSource {
    @RuleInput
    abstract PegasusSchemaComponentSpecInternal getComponent();

    abstract void setComponent(PegasusSchemaComponentSpecInternal component);

    @RuleTarget
    abstract PegasusJavaDataSchemaBinaryInternal getTarget();

    abstract void setTarget(PegasusJavaDataSchemaBinaryInternal target);

    @Defaults
    void setDefaultsForBinaries(@Path("buildDir") final File buildDir) {
      File generateSourceDir = new File(buildDir, "generated-sources/" + getTarget().getName());
      getTarget().setGeneratedDirectory(generateSourceDir);
      getComponent().getSources().create("generatedPegasus", JavaSourceSet.class, languageSourceSet -> languageSourceSet.getSource().srcDir(generateSourceDir));
    }
  }
FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':test-pegasus'.
> Exception thrown while executing model rule: ComponentModelBasePlugin.PluginRules#addComponentSourcesSetsToProjectSourceSet
   > java.lang.NullPointerException (no error message)

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':test-pegasus'.
        at org.gradle.execution.TaskNameResolver.discoverTasks(TaskNameResolver.java:100)
        at org.gradle.execution.TaskNameResolver.access$100(TaskNameResolver.java:32)
        at org.gradle.execution.TaskNameResolver$MultiProjectTaskSelectionResult.collect(TaskNameResolver.java:171)
        at org.gradle.execution.TaskNameResolver$MultiProjectTaskSelectionResult.collectTasks(TaskNameResolver.java:166)
        at org.gradle.execution.TaskNameResolver.selectWithName(TaskNameResolver.java:55)
        at org.gradle.execution.TaskSelector.getSelection(TaskSelector.java:87)
        at org.gradle.execution.TaskSelector.getSelection(TaskSelector.java:75)
        at org.gradle.execution.commandline.CommandLineTaskParser.parseTasks(CommandLineTaskParser.java:42)
        at org.gradle.execution.TaskNameResolvingBuildConfigurationAction.configure(TaskNameResolvingBuildConfigurationAction.java:44)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.configure(DefaultBuildConfigurationActionExecuter.java:48)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.access$000(DefaultBuildConfigurationActionExecuter.java:25)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter$1.proceed(DefaultBuildConfigurationActionExecuter.java:54)
        at org.gradle.execution.DefaultTasksBuildExecutionAction.configure(DefaultTasksBuildExecutionAction.java:44)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.configure(DefaultBuildConfigurationActionExecuter.java:48)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.access$000(DefaultBuildConfigurationActionExecuter.java:25)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter$1.proceed(DefaultBuildConfigurationActionExecuter.java:54)
        at org.gradle.execution.ExcludedTaskFilteringBuildConfigurationAction.configure(ExcludedTaskFilteringBuildConfigurationAction.java:47)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.configure(DefaultBuildConfigurationActionExecuter.java:48)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.select(DefaultBuildConfigurationActionExecuter.java:36)
        at org.gradle.initialization.DefaultGradleLauncher$3.run(DefaultGradleLauncher.java:143)
        at org.gradle.internal.Factories$1.create(Factories.java:22)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:52)
        at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:140)
        at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:32)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:99)
        at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:93)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:62)
        at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:93)
        at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:82)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:94)
        at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
        at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:43)
        at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
        at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:45)
        at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:52)
        at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
        at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
        at org.gradle.util.Swapper.swap(Swapper.java:38)
        at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.health.DaemonHealthTracker.execute(DaemonHealthTracker.java:40)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:66)
        at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
        at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.health.HintGCAfterBuild.execute(HintGCAfterBuild.java:41)
        at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
        at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
        at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:246)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
        at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: org.gradle.model.internal.core.ModelRuleExecutionException: Exception thrown while executing model rule: ComponentModelBasePlugin.PluginRules#addComponentSourcesSetsToProjectSourceSet
        at org.gradle.model.internal.registry.DefaultModelRegistry.fireAction(DefaultModelRegistry.java:451)
        at org.gradle.model.internal.registry.DefaultModelRegistry.access$600(DefaultModelRegistry.java:41)
        at org.gradle.model.internal.registry.DefaultModelRegistry$RunModelAction.apply(DefaultModelRegistry.java:1145)
        at org.gradle.model.internal.registry.DefaultModelRegistry.transitionTo(DefaultModelRegistry.java:351)
        at org.gradle.model.internal.registry.DefaultModelRegistry.transition(DefaultModelRegistry.java:430)
        at org.gradle.model.internal.registry.DefaultModelRegistry.atStateOrMaybeLater(DefaultModelRegistry.java:194)
        at org.gradle.model.internal.registry.DefaultModelRegistry.atStateOrLater(DefaultModelRegistry.java:181)
        at org.gradle.api.internal.tasks.DefaultTaskContainer.discoverTasks(DefaultTaskContainer.java:200)
        at org.gradle.execution.TaskNameResolver.discoverTasks(TaskNameResolver.java:98)
        ... 65 more
Caused by: java.lang.NullPointerException
        at org.gradle.language.base.plugins.ComponentModelBasePlugin$PluginRules.addComponentSourcesSetsToProjectSourceSet(ComponentModelBasePlugin.java:211)
        at org.gradle.model.internal.method.WeaklyTypeReferencingMethod.invoke(WeaklyTypeReferencingMethod.java:98)
        at org.gradle.model.internal.inspect.DefaultModelRuleInvoker.invoke(DefaultModelRuleInvoker.java:35)
        at org.gradle.model.internal.inspect.MethodBackedModelAction.execute(MethodBackedModelAction.java:45)
        at org.gradle.model.internal.inspect.AbstractMethodRuleAction.execute(AbstractMethodRuleAction.java:48)
        at org.gradle.model.internal.inspect.ModelRuleExtractor$DefaultExtractedRuleSource$2$1.execute(ModelRuleExtractor.java:424)
        at org.gradle.model.internal.registry.DefaultModelRegistry$4.run(DefaultModelRegistry.java:446)
        at org.gradle.model.internal.registry.RuleContext.run(RuleContext.java:42)
        at org.gradle.model.internal.registry.DefaultModelRegistry.fireAction(DefaultModelRegistry.java:443)
        ... 73 more

Hi Ethan,

It looks like when the addComponentSourcesSetsToProjectSourceSet rule is called in ComponentModelBasePlugin is called, the sources are not created yet. It looks like a rule ordering issue, and definitely a bug in Gradle. Basically it misses a rule that makes sure that the sources are fully configured before they are copied.

We’ll come back to you as soon as we have a fix.

Hey Cedric,

I debugged this a little bit, and found out that in

void addComponentSourcesSetsToProjectSourceSet(@Each ComponentSpec component, final ProjectSourceSet projectSourceSet) {
            component.getSources().afterEach(new Action<LanguageSourceSet>() {
                @Override
                public void execute(LanguageSourceSet languageSourceSet) {
                    projectSourceSet.add(languageSourceSet);
                }
            });
        }

component is null, not getSources().

When this happens, I notice that this is the second time that this gets called. Maybe its the @Each that’s having the issue?

Interesting, yes, that should not be happening. I’ll talk to Lóránt about this and let you know!