I’m trying to replace some library class with my own version (I know this is usually a bad practice, but it’s the least bad way in my case). After some research I understood that I just need to create a class with the same name as the class I want to replace, and as long as it will be before in the classpath, it will be the one that will be used and completely replace the original class. But the question is, can I be sure that gradle will add my project classes to the classpath before the dependency classes?
Depends on what you do.
If you for example use the application
plugin, it should be the case, unless you maybe use an own template for the start scripts and so on.
If you want to be super sure, you could use an artifact transform, that removes the class from that libraries jar, then the class is removed on the fly and guaranteed not available so cannot be confused, no matter what you do.
Thanks for the reply! Actually, I’m using a plugin called GradleRio (it’s a plugin for the robotics competition I’m writing the code to). I don’t have much experience in Gradle let alone Gradle plugins, so I’m not sure how I can check how they configured it. Is there a specific place or something responsible for configuring this that I can look for in the plugin’s source code?
Also, I just tested overriding a method from dependency, and it did work, can I safely assume this will be consistent and just how it is configured?
And I would be happy to get some more info/link to some resource about the artifact transform (:
Is there a specific place or something responsible for configuring this that I can look for in the plugin’s source code?
I have no idea as I don’t know the plugin.
You would have to check each and every way how you can run your project, like from the IDE, through any Gradle task that runs the code, through any other means to run the code.
Also, I just tested overriding a method from dependency, and it did work, can I safely assume this will be consistent and just how it is configured?
For the way you run the code, most probably. If it behaves differently when doing the same repeatedly, then there is a bug in some place.
And I would be happy to get some more info/link to some resource about the artifact transform (:
Actually, more an advanced Gradle topic.
But here is the docs about it: Transforming dependency artifacts on resolution
Thank you very much!
Will look at the source code. As far as I know, it only has one command to build the code (running ./gradlew build
). So I just need to check the build.gradle? is there anything that can maybe affect the order of classpath I should look for? because inside the build.gradle I cannot see anything, should I look inside the java Plugin class? Sorry if I’m not specific enough or asking nonsensical, I’m very new to this topic.
Gradle is a build tool.
You only use it to build your software.
To finally run it after it is built, you do not use Gradle.
So it is not only a question of the Gradle build logic.
I really cannot tell you where to look, as I have no idea about that plugin / project you were referring to.
As I said, if you want to be sure, use an artifact transform to throw out the unwanted class from the dependency. At least if the dependency is not signed. If it is signed or sealed, it would need additional actions.
Oh, I thought that gradle is the one that creates the classpath that will be used in runtime, will try to investigate the code more. Thank you again for your time and patient, I really appreciate it!
I found it now! It seems like it does put my classes before the library’s classes. I’m just a little bit confused about what does gradle do then with Classpath (if it does) because I did read that it somehow manages it or something…
If you build your application with Gradle and then run java -cp jars/* foo.Bar
to run it, Gradle has no influence what you do and how you order the classpath, like in the example. If you run through the Gradle application plugin’s run
task for example, or through the application
plugin’s generated start scripts without own customization, then yes, your code comes first, as the application
plugin orders it that way.
If you use custom JavaExec
tasks, or other plugins, or any other method to later run your code, I can only guess what the situation is, as it depends on how it is done.
I figured out it does not work, and actually not always chooses my version over the dependency version, so I need to choose another way. What is the easiest way to do what I want (replace some dependency classes with my custom implementaion of those classes)?
I already told you.
Make an artifact transform that throws out the original class file.
Something like
val classExcluded = Attribute.of("classExcluded", Boolean::class.javaObjectType)
abstract class ExcludeClass : TransformAction<TransformParameters.None> {
@get:PathSensitive(NAME_ONLY)
@get:InputArtifact
abstract val inputArtifact: Provider<FileSystemLocation>
override
fun transform(outputs: TransformOutputs) {
val inputFile = inputArtifact.get().asFile
if (!inputFile.name.startsWith("problematic-artifact-")) {
outputs.file(inputArtifact)
return
}
JarOutputStream(outputs.file("${inputFile.nameWithoutExtension}-stripped.jar").outputStream().buffered()).use { outJar ->
ZipFile(inputFile).use { inZip ->
for (entry in inZip.entries()) {
if (entry.name != "path/to/Problematic.class") {
outJar.putNextEntry(ZipEntry(entry))
inZip.getInputStream(entry).use { it.copyTo(outJar) }
outJar.closeEntry()
}
}
}
}
}
}
configurations {
runtimeClasspath {
attributes.attribute(classExcluded, true)
}
}
dependencies {
attributesSchema {
attribute(classExcluded)
}
artifactTypes.getByName("jar") {
attributes.attribute(classExcluded, false)
}
registerTransform(ExcludeClass::class) {
from.attribute(classExcluded, false).attribute(ARTIFACT_TYPE_ATTRIBUTE, "jar")
to.attribute(classExcluded, true).attribute(ARTIFACT_TYPE_ATTRIBUTE, "jar")
}
}
Thank you, was hoping there is simpler solution but if this is what it takes I guess I should take learning Gradle as a project if I want to do that.
You can make sure that the classpath order is like expected if possible as you initially thought.
Or you can probably also have some task that post-processes your packaging result to throw out the class there.
But imho the cleanest solution is an artifact transform. And I also don’t think it is majorly more complex than the other solutions.