Alternative way to define bootRun task in Gradle doesn't work - gradle

I usually define tasks in Gradle (using Groovy) like tasks.withType(Type); e.g.: tasks.withType(JavaCompile), tasks.withType(Test), etc.
Now, I want to do the same with some provided Spring Boot tasks, namely: bootRun and bootStartScripts, but Gradle cannot find it.
I know it's silly and I could get away just by using bootRun and bootStartScripts, but I would like to understand why those cannot be configured/defined in such way.

I guess with define you mean configure, because withType can only be used to configure existing tasks. It takes a task type (a class) and a closure that can be used to configure all available tasks of that type. This needs to be considered, because a project may contain multiple tasks of the same type that should actually do completely different things. Whether to configure all those tasks or just a specific one is important!
To pass the task type to the method withType you need to know the name of the class implementing the task type. This name is not necessarily related to the name(s) of the actual task(s). For the tasks test and compileJava of the Gradle Java Plugin those classes are org.gradle.api.tasks.testing.Test and org.gradle.api.tasks.compile.JavaCompile. Since those classes are provided by Gradle, they are automatically imported and can be referenced via their simple names Test and JavaCompile. But the Spring Boot Plugin is a third-party plugin, so the classes need to be referenced by their full names.
The task bootStartScripts from your question is of type CreateStartScript, that is provided by Gradle. Therefore it can be configured like this:
tasks.withType(CreateStartScripts) {
// configure
}
The task bootRun is of type org.springframework.boot.gradle.tasks.run.BootRun, that is provided by the Spring Boot Plugin. So you need to specify the full name:
tasks.withType(org.springframework.boot.gradle.tasks.run.BootRun) {
// configure
}

Related

adding other modules in classpath for a Gradle plugin task

I have a Gradle plugin that works well so far, however, I am facing an issue in a multi-module project.
The plugin runs its task in every module of the project, but sometimes, I need to access classes from other modules to analyze them - typically, in the context of hexagonal architecture, I want to perform some checks on classes in infrastructure that implement an interface defined in domain module.
So, when the task runs (after the tests) within infrastructure module, it finds the class. But it's not able to load the interface (from domain) that the class implements. When I do :
Object interfaceLoadedDynamically = Class.forName(i.getName());
I clearly see it can't be loaded, and that prevents me from performing the checks I need to do.
here are :
the plugin
the task
Is there a way to configure one of them to say : "when you run within a module, make sure all the other project modules that have been compiled before are available in the classpath" ?
I've seen maybe I could use Gradle's Configuration , but I am really not sure where/how to start.

Gradle configurations integration

I've found this code at work in build.gradle
configurations {
all {
resolutionStrategy {
cacheDynamicVersionsFor 0, 'seconds'
}
}
integration
}
I can't find anywhere what integration keyword stands for. Can you explain to me?
In this example, the build is declaring a new configuration called integration. And a configuration can for the most part be thought of as a bucket or collection of dependencies. If a plugin or the Gradle core new about a particular configuration, there would usually be no need to declare it as it would already exist to begin with.
Let's assume that 'integration' is short for 'integration test'. Then what's going on here is that your build is saying: "Hey, I need a bunch of dependencies for running my integration test, but I don't want to pollute the classpath for the other kinds of runtime environments. So please make me a bucket of dependencies to isolate the integration test".
Later in the build file (which you didn't show), you will then find a dependencies block where the integration configuration is populated with the modules needed for running the test. And lastly, some task that actually uses it, presumably for setting the classpath.
It could be used for a number of other things of cause. But whatever it is, it is probably something custom and you could rename it (and all references to it) to 'aCollectionOfAwesomeDependenciesUsedForRunningOurIntegrationTest' if you like.

How should i set main class for Spring Boot application in Gradle?

I read spring boot documentation and i see there are at least 2 ways to set main class:
bootRepackage {
mainClass = 'demo.Application'
}
and
springBoot {
mainClass = "demo.Application"
}
Which one should i use or they are both required for different tasks? I do not want to repeat myself.
In Gradle terms, springBoot is an extension. When you use it to configure the main class, you're configuring it for every repackaging task in the project. On the other hand bootRepackage is referencing a single repackaging task so you're just configuring the mainClass for that one task.
Which one should i use or they are both required for different tasks?
If you only have a single repackaging task (as is the default) this is a matter of personal preference.
If you have configured additional repackaging tasks, you are probably better configuring it on each individual task rather than using the springBoot extension. If you use a mixture of the two, the setting on an individual repackaging task will take precedence over whatever you have configured using springBoot.
This is from documentation for springBoot plugin:
The gradle plugin automatically extends your build script DSL with a
springBoot element for global configuration of the Boot plugin. Set
the appropriate properties as you would with any other Gradle
extension (see below for a list of configuration options):
And below that there is an example of configuration of mainClass element of bootRepackage plugin:
The main class that should be run. If not specified, and you
have applied the application plugin, the mainClassName project
property will be used. If the application plugin has not been
applied or no mainClassName has been specified, the archive will
be searched for a suitable class. "Suitable" means a unique class
with a well-formed main() method (if more than one is found the
build will fail). If you have applied the application plugin,
the main class can also be specified via its "run" task
(main property) and/or its "startScripts" task
(mainClassName property) as an alternative to using the
"springBoot" configuration.
In other words those two are identical.

Gradle Plugin that must load the compiled classes from the project

As part of writing a Gradle plugin for Flyway, we stumbled upon a problem when dealing with Java migrations.
What is the best way to provide a Gradle plugin access on its classpath to the compiled classes of the project so that it can load and execute them?
So the situation is that we have a plugin that adds a task that wants to execute code contained in the project that the plugin is applied to. In this case, the task (class) should have an input property of type Iterable<File> that gets configured (by the plugin) with the class path of the code to be executed (e.g. sourceSets.main.runtimeClasspath). The task can then choose between the following ways to execute the code:
The task uses project.javaexec {} to execute the code in a separate JVM. If the code isn't directly executable, the task may need to inject some bootstrap code onto the javaexec class path. A potential alternative to using project.javaexec is to use a JavaExec task in the first place.
The task creates a new class loader, populates it with the class path, loads and instantiates the classes that serve as the entry point(s) to the API, and makes use of them as appropriate. If the task is written in Groovy, it can leverage duck typing, and no reflective code will be necessary beyond creating the entry points.

Timely Gradle task inputs/outputs configuration

I'm having ordering issues with Gradle task configuration.
I'm writing a plugin that creates a task based on information that is only available when supplied via the build script. I can create the task in the plugin's apply() method, and configure it in its doFirst() method (by which time the necessary information is available). However, I also want to make sure that the task is only executed when necessary via the inputs/outputs properties.
This is problematic, because I have to do this in the apply() method (as far as I can tell), but at that point the information required to specify the inputs/outputs property values isn't available.
I tried using a convention object but that's not available under after apply() has completed.
Is there any way around this that won't make me feel dirty?
For those parts of the task configuration that need to be deferred, you can either use a hook like project.afterEvaluate {} or gradle.projectsEvaluated {}, or use convention mapping. The latter is used extensively by Gradle's own plugins, but isn't currently considered a public API.

Resources