How can I work with custom Shared Build Services and custom tasks when developing plugin?

Hello. I been having problems integrating a custom Shared Build Service that I want to use to provide custom dependency injection to my plugin project using java.

I’m following this wiki https://docs.gradle.org/current/userguide/build_services.html

Here is my code:

Shared Build Service - DependencyInjection.java

public abstract class DependencyInjection implements BuildService<DependencyInjection.Params>{

    interface Params extends BuildServiceParameters {
        Property<Injector> getDependencyInjector();
    }

    private final Injector injector;
    
    public DependencyInjection(){
        injector = getParameters().getDependencyInjector().get();
    }

    public Injector getInjector(){
        return injector;
    }
    
}

Plugin - FooPlugin.java

public class FooPlugin implements Plugin<Project> {

    public void apply(Project project) {

        //create provider with the injector and register module
        Provider<DependencyInjection> serviceProvider = project.getGradle().getSharedServices().registerIfAbsent("di", DependencyInjection.class, 
            spec -> spec.getParameters().getDependencyInjector().set(createDependencyInjector())
        );

        // Register a task
        project.getTasks().register("barTask", BarTask.class, task ->{
            task.getDI().set(serviceProvider);
        });
    }

    private Injector createDependencyInjector(){
        return Guice.createInjector(new PluginModule());
    }
}

Custom Task - BarTask.java

public abstract class BarTask extends DefaultTask {

    private final Logger logger = getLogger();

    @Internal
    public abstract Property<DependencyInjection> getDI();

    @TaskAction
    public void processTask() {
        logger.quiet("Processing Task");

        Injector injector = getDI().get().getInjector();

        try{
            Awesome awesome = injector.getInstance(Awesome.class);
            awesome.dance();

        }catch(Exception e){
            logger.error(e.getMessage());
        }
    
    }

}

Build Gradle - build.gradle

tasks.register('customBarTask', org.me.BarTask) {
            doFirst {
                println("Hello World!")
            }
}

One of my problems is in the initialization and registry of the custom Shared Build Service with my project. when running my task in gradle ./gradlew barTask I get this error.

    Execution failed for task ':barTask'.
    > Failed to query the value of task ':barTask' property 'DI'.
       > Could not isolate value org.me.DependencyInjection$Params_Decorated@39952730 of type DependencyInjection.Params
          > Could not serialize value of type InjectorImpl

This happens when I’m trying to use the Provider created from the registry of the service. <Provider>.get().

As a work around I removed the BuildServiceParameters in my Shared Build Service.

Modified Shared Build Service - DependencyInjection.java

public abstract class DependencyInjection implements BuildService<BuildServiceParameters.None>{

    private final Injector injector;
    
    public DependencyInjection(){
        injector = Guice.createInjector(new PluginModule());
    }

    public Injector getInjector(){
        return injector;
    }
}

And removed the configuration of the Shared Build Service in the plugin file FooPlugin.java

...
        //create provider with the injector and register module
        Provider<DependencyInjection> serviceProvider = project.getGradle().getSharedServices().registerIfAbsent("di", DependencyInjection.class, 
            spec -> {}
        );
...

With this, now running my task ./gradlew barTask works fine.

Now the problem is that, If I want to run my custom task from the gradle.build file ./gradlew customBarTask I get this error:

    Execution failed for task ':customBarTask'.
    > Cannot query the value of task ':customBarTask' property 'DI' because it has no value available.

So, finally, as a work around for that, I created a private function in my task file BarTask.java to initialize the shared build service in the task class like this.

public abstract class BarTask extends DefaultTask {

    private final Logger logger = getLogger();

    // I changed this as well
    @Internal
    private final Provider<DependencyInjection> di = getInjectorService();

    @TaskAction
    public void processTask() {
        logger.quiet("Processing Task");

        //This also change
        Injector injector = di.get().getInjector();

        try{
            Awesome awesome = injector.getInstance(Awesome.class);
            awesome.dance();

        }catch(Exception e){
            logger.error(e.getMessage());
        }
    }

    @SuppressWarnings("unchecked")
    private Provider<DependencyInjection> getInjectorService(){
        return (Provider<DependencyInjection>)getProject()
            .getGradle()
            .getSharedServices()
            .getRegistrations()
            .findByName("di")
            .getService();
    }
}

I also removed the configuration of the task from the plugin class FooPlugin.java

...
        // Register a task
        project.getTasks().register("barTask", BarTask.class, task ->{});
    }

...

With that everything works now.

I’m not sure if I’m missing something. I only want to be able to provide this resource “Custom Dependency Injection” to my task files.

Any help will be appreciated.

Thanks

The reason this happened is that the DI property needs to be set, Gradle does not inject a reference to the service. Example 3 in the docs you referenced show this when registering the download task.

task.getServer().set(serviceProvider)

If you don’t want to require that every registered task is individually configured, the plugin could configure it for any task of type BarTask.

Provider<DependencyInjection> serviceProvider = ...
project.getTasks().withType( BarTask.class ).configureEach( t -> t.getDI().set( serviceProvider ) )

Thank you for the response. I was able to configure the tasks of type BarTask in this way. I’m still unable to configure the Shared Build Service from the plugin but this is good.

Thanks