Service Loader Can't Find Provider - kotlin

I am having trouble getting the service loader to find my providers. My set up is as follows:
I have 4 modules:
EntryPoint
Library
Version1
Version2
In Library, I have an interface defined:
interface Parent{
fun doSomething()
fun getName()
}
In Version1 and Version2, I override that interface with a class called Impl. I also place
META-INF/services/parent.package.path.Parent
in the resources folder, with
child.package.path.Impl
as its contents.
In EntryPoint I have a main function with the following code:
fun main(args<String>){
val impls = ServiceLoader.load(Parent::class.java)
val implToUse = impls.find { i -> i.getName() == args[0]}
implToUse!!.doSomething()
}
The idea is to be able to load the implementation classes from the entry point dynamically to cut down on build times. everything compiles, but when I run the main function, I get a null pointer exception because none of the providers were loaded.
Not sure what else is required to get the ServiceLoader to "see" the providers. A few notes:
in the build folder generated during compilation, the META-INF resource does (in fact) appear in resouces.
in the build folder generated during compilation, there is no jar being created. Not sure if that is what is causing the issue, or if it is, how to fix

Related

How do you set up a property in a custom gradle task?

I want to write a task that takes a directory from , does something with the files in it and writes the result into some other directory to.
I've been led to believe this was the way to define such a task (kotlin dsl):
package my.app
abstract class FooBarTask : DefaultTask() {
#get:InputDirectory
abstract val from: Property<Directory>
#get:OutputDirectory
abstract val to: Property<Directory>
#TaskAction
fun doSomething() {
println("Hakuna Matata")
}
}
now how do I set the from and to value in a groovy-based build.gradle?
def myTask = tasks.register('myTask', FooBarTask) {
from = layout.projectDirectory.dir("foo")
to = layout.buildDirectory.dir("bar")
}
this results in
Could not create task ':my-subproject:myTask'.
> Please use the ObjectFactory.directoryProperty() method to create a property of type Directory.
and it shouldn't.
How do you correctly define a directory property in a custom task?
Gradle has the specialized DirectoryProperty, that offers some additional functionality, compared to the plain Property<Directory> which is one of the implemented interfaces. So this specialized type should be used when declaring directory inputs/outputs.
I'm actually not a 100% sure what caused the error you saw.

Kotlin anonymous extension with apply shared between jar causing java.lang.LinkageError: loader constraint violation: loader A wants to load interface

I'm having a strange issue where I have created a Gradle multi-project build, where I have library A, B and consumer X. Here library B depends on library A, and consumer depends on both A and B.
The idea is that consumer can extend library B with their own needs, one interesting constraint here is that each project is built into its own jar, and that way libraries A and B are available at runtime for the use and extension of consumer X.
Fact here is that in my consumer, I have created an extension method for a class inside Library B, internally this uses kotlin DSL methods, defined like this:
class MyAwesomeLevel : Level() {
fun setup() {
// DSL created with extension method in consumer X
customer {
position(-174.0, 66.0, -105.0)
rotation(180.0, 0.0)
data("team", "team_1")
}
// DSL function existing in the Level base class.
spawnpoint {
position(-174.0, 66.0, -105.0)
rotation(180.0, 0.0)
data("team", "team_1")
}
}
}
So this class is instanciated in my same consumer X, and passed to the B library with a reference to the runtime instance of a service, lets call it levelService.loadLevel(level). And this method will call setup() function of my level. Problem here is that when it tries to execute the DSL from the B library it fails with the following exception:
java.lang.LinkageError: loader constraint violation: loader 'library-b.jar' #67faeea4 wants to load interface Shadow.kotlin.jvm.functions.Function1. A different interface with the same name was previously loaded by 'consumer-x.jar' #195c6c25. (Shadow.kotlin.jvm.functions.Function1 is in unnamed module of loader 'consumer-x.jar' #195c6c25, parent loader java.net.URLClassLoader #18769467)
The DSL method looks like this:
abstract class Level(...) {
// ...
fun spawnpoint(body: SpawnpointBuilder.() -> Unit) {
val builder = SpawnpointBuilder(this)
builder.apply(body)
addElement(builder.build())
}
}
So the main issue here is trying to do builder.apply from the consumer-x even when the DSL is part of the library-b.
If anyone has any clue on how can I fix this, I'd thank you forever.

What's the use for Kotlin's #JvmSynthetic on a file target?

The #JvmSynthetic annotation is allowed to be used on a file, but I can't figure out what the purpose of this would be.
I was hoping I could hide a file containing a bunch of Kotlin-only extension methods from Java users, but that doesn't seem to be the case:
// Extensions.kt
#file:JvmSynthetic
#JvmSynthetic
fun Foo.mySyntheticExtension() = ...
fun Foo.myExtension() = ...
// Java usage
// This doesn't compile (as expected)
Extensions.mySyntheticExtension(foo);
// This compiles fine, so #JvmSynthetic on a file does not trickle down to all its functions
Extensions.myExtension(foo);
Even without the non-synthetic method Java users still see the cluttering ExtensionsKt class, although it appears empty to them.
If #file:JvmSynthetic doesn't hide the file('s generated class) from Java, nor trickles down the synthetic status to all functions in it, what is its intended purpose?
The original proposal that caused this annotation target to be added was KT-41884:
The rationale given was:
This would apply to the synthesized class which encapsulates top-level members. This allows hiding those members from Java when they are internal visibility.
For example:
// ManyInternals.kt, in module A
#file:JvmSynthetic
internal fun foo() {
}
internal fun bar() {
}
// Main.java, in module B
public class Main {
public static void main(String[] args) {
ManyInternalsKt.foo(); // error
}
}

How do I add new type-safe accessors to a Gradle Project?

We have a plugin which defines additional properties and adds them as extension properties, like:
project.extra["copyright"] = "Copyright ..."
Then in the build scripts, I can access this like:
project.extra["copyright"]
I'd like to just write:
project.copyright
Some Gradle plugins seem to do something like this. I can access project.sourceSets or project.kotlin even though those certainly aren't in the Project interface.
Using an IDE, I can drill into those convenience methods, which then lands me in some autogenerated code, so I know it's being autogenerated somewhere, but I haven't been able to find any clues to how to get this to happen for our own plugin. The Gradle docs mention type-safe accessors which is ultimately what allowed me to phrase the question, but the docs don't say how to add new ones.
How do we get this treatment for our own plugin?
Creating DSL-like APIS is documented here: https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#modeling_dsl_like_apis.
It's pretty easy to do. Here's a quick guide, and some tips.
Define an extension.
import org.gradle.api.provider.Property
interface MyExtension {
val copyright: Property<String>
}
It looks pretty boring! What's important is that it's either be an abstract class, or an interface - this is so Gradle can create a new instance (see 'Managed types'), and this is where the Gradle magic begins.
Aside: I've used Property<String> instead of String, although both will work. I recommend using types compatible with Lazy Configuration.
Register the extension.
import org.gradle.api.*
abstract class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val myExtension: MyExtension =
project.extensions.create("myPlugin", MyExtension::class.java)
}
}
This is the magic part. Simply by registering the extension against the project, Gradle will make the extension available and automatically generate Kotlin-DSL convenience methods.
Apply the plugin
// build.gradle.kts
plugins {
id("my-plugin")
}
myPlugin {
copyright.set("blah blah 2022")
}
Just like that, Gradle will generate Kotlin DSL accessors. Here's one of them:
// Accessorsajp3oxzka99ro52ctxwv0petb.kt
/**
* Configures the [myPlugin][MyExtension] extension.
*/
fun org.gradle.api.Project.`myPlugin`(configure: Action<MyExtension>): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("myPlugin", configure)
Use the extension values
Going back to the Plugin definition, lets say you want to register your own task:
// a demo task
abstract class MyTask : DefaultTask() {
#get:Input
abstract val copyright: Property<String>
#TaskAction
fun run() {
println("Copyright is ${copyright.get()}")
}
}
(Note that this task, like the extension, is a 'managed type').
Now the custom task can be registered, and a default value for copyright set.
abstract class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val myExtension: MyExtension = project.extensions.create("myPlugin", MyExtension::class.java)
project.tasks.register("myCustomTask", MyTask::class.java) {
copyright.set(myExtension.copyright)
}
}
}
It's good that both MyExtension and MyTask use Property<String> - the actual value will be evaluated lazily, and only if required.
Now if I run ./gradlew :myCustomTask, I see:
> Task :myCustomTask
Copyright is blah blah 2022
Further reading
What if you want to have multiple copyrights? Then you can create a configuration container
What if MyExtension has lots of properties and you want to provide them all to MyTask? Then you can use #Nested inputs
The extension properties can have default values.

Create a Gradle function for dependencies block in Kotlin

Currently, I'm creating a function, which is available for the dependencies block in Groovy with:
project.dependencies.ext.foo = { String value ->
project.files(extension.getFooDependency(project).jarFiles).asFileTree
}
Thanks to that, I'm able to do:
afterEvaluate {
dependencies {
compileOnly foo('junit')
}
}
I'm converting the Groovy code to Kotlin, and I'm wondering how to rewrite this foo extension.
I've ended up with:
project.dependencies.extensions.extraProperties.set("foo", Action { value: String ->
project.files(extension.getIdeaDependency(project).jarFiles).asFileTree
})
After calling foo('junit'), I get the following exception:
> Could not find method foo() for arguments [junit] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
I do not think that would work the same way in Kotlin DSL. Instead, you may declare a Kotlin extension function somewhere in the project. Then calling it would include all necessary receivers to you.
For multiple projects, I would recommend using a buildSrc project. Declarations there are visible to all project files below.
Speaking about Groovy and Kotlin support, I would do something like that:
private fun getFooImpl(scope: getFooImpl, name: String) { /*here is the implementation */ }
fun DependencyHandlerScope.getFoo(name:String) = getFooImpl(this, name)
//in Groovy
project.dependencies.extensions.extraProperties.set("foo", {getFooImpl(..)})
The same code could fit into a plugin as well. A more generic way could be to register a custom DLS extension, so to allow a custom block-like thisIsMyPlugin { .. } in the Gradle DSL and define all necessary helper functions in the extension class. Here the downside is in forcing users to wrap their code into the thisIsMyPlugin block.