AlessioDP / libby

A runtime dependency management library for plugins running in Java-based Minecraft server platforms.
MIT License
76 stars 20 forks source link

Fetch libraries, repositories etc. from a JSON file. #23

Closed kyngs closed 10 months ago

kyngs commented 10 months ago

Currently, the only way to configure Libby is by using the relevant methods on LibraryManager. While this is great, Libby currently lacks some data-driven approach. This PR aims to do the following things only by using a JSON file:

How could this be used

While this may sound like bloat initially, it has very lucrative uses.

Build system plugins

This PR (assuming a relevant build system plugin exists) allows specifying which dependencies should be downloaded at runtime directly in the build system configuration (pom.xml, build.gradle, etc.) Due to the nature of these systems, they can easily resolve transitive dependencies and calculate the checksum. This puts an end to writing boilerplate code and trying to guess dependencies that are required for a dependency to function (and thus partially addresses #18).

Gradle

To demonstrate the usefulness of this feature, I've created a plugin that aims to accomplish this.

I'll take my plugin as an example: An 100+LOC hard to maintain file which includes the dependencies for my plugin, can (using this gradle plugin) be rewritten only by modifying a few lines in the build.gradle file:

dependencies {
    //MySQL
    libby 'org.mariadb.jdbc:mariadb-java-client:3.2.0'
    libby 'com.zaxxer:HikariCP:5.0.1'

    //SQLite
    libby 'org.xerial:sqlite-jdbc:3.43.0.0'

    //PostgreSQL
    libby 'org.postgresql:postgresql:42.6.0'

     //Utils
    libby 'com.github.ben-manes.caffeine:caffeine:3.1.8'
    libby 'org.spongepowered:configurate-hocon:4.1.2'
    libby 'at.favre.lib:bcrypt:0.10.2'
    libby 'dev.samstevens.totp:totp:1.7.1'
    libby 'org.bouncycastle:bcprov-jdk18on:1.76'
    libby 'org.apache.commons:commons-email:1.5'
    libby 'net.kyori:adventure-text-minimessage:4.14.0'
}

And then calling a simple bootstrap method:

libraryManager.configureFromJSON();

Maven (and other systems)

The same thing can be accomplished with any build system which supports plugins.

Other usages

Considering this PR allows to load the configuration from any JSON file (not only the libby.json file in the plugin JAR), it probably can be used for other things.

Technical info

This patch does not break the API in any way or matter

Considering there's really no way to document a JSON, I've documented the format in the LibraryManager#configureFromJSON method.

Currently, Libby does not bundle any JSON library. To keep the size as small as possible, I've decided to choose the fantastic NanoJSON library which only adds about 25kB to the final JAR.

I've tested both the plugin and this PR by converting my plugin to this approach.

Finally

While I think the implementation is good as-is, I would love to hear some feedback (positive, or negative).

AlessioDP commented 10 months ago

I really like the idea. Really useful especially if transitive dependencies are handled, personally I would not use transitive dependencies because I need to know on compile what libraries will be downloaded but the gradle plugin may handle that too when the JSON file is generated.

Just some ideas that came to my mind:

Data file

I use a similar approach in my projects to handle library versions synced with build.gradle, I personally use java.util.Properties to load a libraries.properties file and get the versions. To prevent NanoJSON bundling we may use different approaches like:

Java Properties

Just key-value based but still possible to make a list like:

[repositories]
[repo1]
url = "..."

[dependencies]
[dep1]
group = "..."
name = "..."
version = "..."
...

This is just a basic approach with no external libraries. I think it may possible to store it as XML too with Properties.storeToXML(...).

GSON

Google GSON is usually already bundled in all server platforms, so no need to download it or shade it if already class loaded. Between Properties and generic JSON, I would prefer JSON because easier to handle and probably already bundled (or loaded).

No need to shade

The library already downloads on runtime jar-relocator (and other libraries) to relocate dependencies, NanoJSON (or GSON) may be handled like that in case configureFromJSON is called.

Plugins

I think both gradle and maven plugin should be in the same project, gradle-plugin/maven-plugin or plugins/gradle/plugins/maven folders to better maintain them.

kyngs commented 10 months ago

Honestly, while properties or XML would probably work, I think JSON is the best choice here. I don't want to make Libby directly dependent on the assumption that GSON is already present in the classpath. While that assumption might work for the currently supported platforms, it doesn't necessarily need to work for new ones. Thus, if someone wanted to add support for a platform that does not use GSON, they would have to shade it. That's a really bad situation considering the size of GSON.

While Nanojson could be downloaded at runtime, I believe that the negatives would outweigh the positives. (Positives are saving 25kB (which is not a lot), and the negatives are that I would have to relocate the JSON parsing part into a separate class to prevent classloading issues. But if you insist, I will download it at runtime.

As for bundling the plugins into this mono repo, I don't think that's a great idea. This PR only aims to add a data-driven way of handling libraries, it does not aim to provide specific implementations. The libby-gradle-plugin was added as a example usage for this data-driven functionality, not as something to be directly part of Libby itself.

AlessioDP commented 10 months ago

What I wrote are just some ideas to talk about :)

NanoJSON, its really lightweight 25kb instead of 280kb and may be shaded or easily downloaded runtime, I have no complain about that.

Shade vs download, its a way easier shade it instead of download in on runtime especially because its just 25kb but this approach is done for jar-relocator too that is just 18kb (+ required libraries). Furthermore, it would be downloaded only if actually used in the code, like the relocator.

About plugins, its not a problem to keep them separate but in this case I think that Libby should have an example on how to handle the JSON in the repo, like some examples of JSON that you can use + the given alternative to generate them via plugins. (Just a documentation page)

kyngs commented 10 months ago

About plugins, its not a problem to keep them separate but in this case I think that Libby should have an example on how to handle the JSON in the repo, like some examples of JSON that you can use + the given alternative to generate them via plugins. (Just a documentation page)

Do you want to update the README accordingly, or should I edit it myself?

AlessioDP commented 10 months ago

I will probably approve the PR the next week and change some things: I got the com.alessiodp package from Maven central and I have plan to refactor the package name and switch to Gradle KTS the project.

I will just change the README after those changes ;)