Carleslc / Simple-YAML

This Java API provides an easy-to-use way to store data and provide configurations using the YAML format.
https://carleslc.me/Simple-YAML
GNU General Public License v3.0
132 stars 38 forks source link

(Velocity dependency clash) java.lang.NoSuchMethodError #67

Closed exosmium closed 2 years ago

exosmium commented 2 years ago

java.lang.NoSuchMethodError: 'void org.yaml.snakeyaml.DumperOptions.setIndentWithIndicator(boolean)' at org.simpleyaml.configuration.implementation.snakeyaml.SnakeYamlImplementation.configure(SnakeYamlImplementation.java:192) ~[?:?] at org.simpleyaml.configuration.implementation.SimpleYamlImplementation.configure(SimpleYamlImplementation.java:149) ~[?:?] at org.simpleyaml.configuration.file.YamlConfiguration.setImplementation(YamlConfiguration.java:62) ~[?:?] at org.simpleyaml.configuration.file.YamlConfiguration.(YamlConfiguration.java:52) ~[?:?] at org.simpleyaml.configuration.file.YamlConfiguration.(YamlConfiguration.java:47) ~[?:?] at org.simpleyaml.configuration.file.YamlFile.(YamlFile.java:49) ~[?:?] at org.simpleyaml.configuration.file.YamlFile.(YamlFile.java:76) ~[?:?] at lv.exosmium.exodomainmanager.Main.setupConfig(Main.java:57) ~[?:?] at lv.exosmium.exodomainmanager.Main.onProxyInitialization(Main.java:41) ~[?:?] at lv.exosmium.exodomainmanager.Lmbda$1.execute(Unknown Source) ~[?:?] at com.velocitypowered.proxy.event.UntargetedEventHandler$VoidHandler.lambda$buildHandler$0(UntargetedEventHandler.java:47) ~[velocity.jar:3.1.2-SNAPSHOT (git-7d77bfb5-b184)] at com.velocitypowered.proxy.event.VelocityEventManager.fire(VelocityEventManager.java:598) ~[velocity.jar:3.1.2-SNAPSHOT (git-7d77bfb5-b184)] at com.velocitypowered.proxy.event.VelocityEventManager.lambda$fire$5(VelocityEventManager.java:479) ~[velocity.jar:3.1.2-SNAPSHOT (git-7d77bfb5-b184)] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[?:?] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[?:?] at java.lang.Thread.run(Thread.java:833) ~[?:?]

Carleslc commented 2 years ago

Update: This comment is not accurate enough. Please, refer to the last comments on this issue to read a detailed explanation of the issue and the working solution.

This is not a Simple-YAML bug, and it is a known issue related to a dependency clash between Velocity and Simple-YAML. Velocity uses an older version of snakeyaml (1.26) than the required by this library (1.30 or above, latest version uses 1.32), and maven unfortunately sees the older version first so is loaded to your plugin instead of the one needed by Simple-YAML. Latest snakeyaml version is 1.33.

Add an exclusion to your Velocity dependency to manually ignore the old version, forcing maven to select the one needed by Simple-YAML:

<exclusions>
    <exclusion>
        <groupId>org.yaml</groupId>
        <artifactId>snakeyaml</artifactId>
    </exclusion>
</exclusions>

Please have a look at the following issues for more background and workarounds:

exosmium commented 2 years ago

Thank you! Fantastic support.

exosmium commented 2 years ago

I added exclusion but nothing resolved (NoSuchMethodError). Here is my mvn dependency:tree -Dverbose**** and pom part.

`

com.velocitypowered velocity-api 3.1.2-SNAPSHOT provided org.yaml snakeyaml
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>me.carleslc.Simple-YAML</groupId>
        <artifactId>Simple-Yaml</artifactId>
        <version>1.8.2</version>
    </dependency>
</dependencies>`

[INFO] lv.exosmium.exoDomainManager:exoDomainManager:jar:1.0 [INFO] +- com.velocitypowered:velocity-api:jar:3.1.2-SNAPSHOT:provided [INFO] | +- com.google.code.gson:gson:jar:2.9.0:provided [INFO] | +- com.google.guava:guava:jar:25.1-jre:provided [INFO] | | +- com.google.code.findbugs:jsr305:jar:3.0.2:provided [INFO] | | +- (org.checkerframework:checker-qual:jar:2.0.0:provided - omitted for conflict with 3.6.1) [INFO] | | +- com.google.errorprone:error_prone_annotations:jar:2.1.3:provided [INFO] | | +- com.google.j2objc:j2objc-annotations:jar:1.1:provided [INFO] | | - org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:provided [INFO] | +- com.moandjiezana.toml:toml4j:jar:0.7.2:provided [INFO] | | - (com.google.code.gson:gson:jar:2.8.1:provided - omitted for conflict with 2.9.0) [INFO] | +- net.kyori:adventure-api:jar:4.11.0:provided [INFO] | | +- net.kyori:adventure-key:jar:4.11.0:provided [INFO] | | | +- (net.kyori:examination-api:jar:1.3.0:provided - omitted for duplicate) [INFO] | | | +- (net.kyori:examination-string:jar:1.3.0:provided - omitted for duplicate) [INFO] | | | - (org.jetbrains:annotations:jar:23.0.0:provided - omitted for duplicate) [INFO] | | +- net.kyori:examination-api:jar:1.3.0:provided [INFO] | | | - (org.jetbrains:annotations:jar:22.0.0:provided - omitted for conflict with 23.0.0) [INFO] | | +- net.kyori:examination-string:jar:1.3.0:provided [INFO] | | | - (net.kyori:examination-api:jar:1.3.0:provided - omitted for duplicate) [INFO] | | - org.jetbrains:annotations:jar:23.0.0:provided [INFO] | +- net.kyori:adventure-text-serializer-gson:jar:4.11.0:provided [INFO] | | +- (net.kyori:adventure-api:jar:4.11.0:provided - omitted for duplicate) [INFO] | | - (com.google.code.gson:gson:jar:2.8.0:provided - omitted for conflict with 2.9.0) [INFO] | +- net.kyori:adventure-text-serializer-legacy:jar:4.11.0:provided [INFO] | | - (net.kyori:adventure-api:jar:4.11.0:provided - omitted for duplicate) [INFO] | +- net.kyori:adventure-text-serializer-plain:jar:4.11.0:provided [INFO] | | - (net.kyori:adventure-api:jar:4.11.0:provided - omitted for duplicate) [INFO] | +- net.kyori:adventure-text-minimessage:jar:4.11.0:provided [INFO] | | - (net.kyori:adventure-api:jar:4.11.0:provided - omitted for duplicate) [INFO] | +- org.slf4j:slf4j-api:jar:1.7.30:provided [INFO] | +- com.google.inject:guice:jar:5.0.1:provided [INFO] | | +- javax.inject:javax.inject:jar:1:provided [INFO] | | +- aopalliance:aopalliance:jar:1.0:provided [INFO] | | - (com.google.guava:guava:jar:30.1-jre:provided - omitted for conflict with 25.1-jre) [INFO] | +- org.checkerframework:checker-qual:jar:3.6.1:provided [INFO] | +- com.velocitypowered:velocity-brigadier:jar:1.0.0-SNAPSHOT:provided [INFO] | +- org.spongepowered:configurate-hocon:jar:3.7.3:provided [INFO] | | +- org.spongepowered:configurate-core:jar:3.7.3:provided [INFO] | | | +- (com.google.guava:guava:jar:21.0:provided - omitted for conflict with 25.1-jre) [INFO] | | | +- (org.checkerframework:checker-qual:jar:2.4.0:provided - omitted for conflict with 3.6.1) [INFO] | | | - (com.google.inject:guice:jar:4.2.3:provided - omitted for conflict with 5.0.1) [INFO] | | - com.typesafe:config:jar:1.4.0:provided [INFO] | +- org.spongepowered:configurate-yaml:jar:3.7.3:provided [INFO] | | - (org.spongepowered:configurate-core:jar:3.7.3:provided - omitted for duplicate) [INFO] | - org.spongepowered:configurate-gson:jar:3.7.3:provided [INFO] | +- (org.spongepowered:configurate-core:jar:3.7.3:provided - omitted for duplicate) [INFO] | - (com.google.code.gson:gson:jar:2.8.0:provided - omitted for conflict with 2.9.0) [INFO] +- mysql:mysql-connector-java:jar:8.0.30:compile [INFO] | - com.google.protobuf:protobuf-java:jar:3.19.4:compile [INFO] - me.carleslc.Simple-YAML:Simple-Yaml:jar:1.8.2:compile [INFO] +- me.carleslc.Simple-YAML:Simple-Configuration:jar:1.8.2:compile [INFO] - org.yaml:snakeyaml:jar:1.32:compile

Carleslc commented 2 years ago

Try another workaround. For instance, manually add the conflicting dependency, specifying the updated version of snakeyaml:

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.32</version>
</dependency>

I've specified 1.32 because is the one used by the last version of Simple-YAML (1.8.2), but it should also work fine with 1.33 which is the latest version of snakeyaml.

exosmium commented 2 years ago

Nothing worked. Here is my pom.xml: https://pastebin.com/Pr7JzM45

Carleslc commented 2 years ago

Is the same error identical as in your first message, or has it changed something in the stacktrace?

The different approaches that should solve the problem are specified in #57 (this comment), along with useful links about transitive dependencies and version collisions.

Try them independently. I mean, if you set the dependency as in my previous comment, remove the exclusion in the Velocity dependency from your pom.

If that still does not work, the third approach is, having your pom as in the original error message (without the exclusion and without specifying the snakeyaml dependency), add the conflicting dependency in a dependencyManagement section.

Like this:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.32</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.velocitypowered</groupId>
        <artifactId>velocity-api</artifactId>
        <version>3.1.2-SNAPSHOT</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>me.carleslc.Simple-YAML</groupId>
        <artifactId>Simple-Yaml</artifactId>
        <version>1.8.2</version>
    </dependency>
</dependencies>
exosmium commented 2 years ago

It is a disaster! I tried everything what you have adviced, moreover, checked all articles on StackOverflow about it but it is not working... I resolved all possible maven confilcts, tried to reinstall couple of plugins but no. Last chance for me is ask your discord, but if not, i understand you.

exosmium commented 2 years ago

Can you give me your discord? I will turn on screen demo and i hope we will resolve it very fast. Please...

MrFreasy commented 2 years ago

Hi, I had the same issue as you. I solved it by relocating and adding snakeyaml(1.32) to the maven dependencies, as written in the comment. Try replacing the pom.xml file with this, it should work.

Carleslc commented 2 years ago

Hi, I had the same issue as you. I solved it by relocating and adding snakeyaml(1.32) to the maven dependencies, as written in the comment. Try replacing the pom.xml file with this, it should work.

Thank you for the contribution @MrFreasy. Let's see if the relocation with maven-shade-plugin works for @exosmium.

Some considerations:

  1. When including snakeyaml directly as a dependency, the exclusion in velocity is probably redundant.
  2. The dependencyManagement solution probably works as well with relocation, without including the snakeyaml dependency explicitly.

Another approach I've been thinking is just to reorder the dependencies so Simple-YAML is before the Velocity dependency, as maven selects the first version found of snakeyaml when transitive dependencies are on the same level in the dependency tree, and the updated one is the one required by Simple-YAML.

Carleslc commented 2 years ago

I've been doing some more testing now with an actual Velocity plugin to see the root cause of the error and to find a minimum solution without configuration redundancy in the maven pom.

These are my detailed findings and conclusions on the issue:

The root cause is not maven selecting the wrong snakeyaml version as I was previously thinking. Sorry for the misguidance. It builds successfully and, inspecting the dependency tree, maven selects the snakeyaml 1.32 through Simple-YAML as expected. Velocity requires snakeyaml 1.26 through org.spongepowered:configurate-yaml 3.7.3 dependency (depth 2) required by velocity-api 3.1.2, so that version is omitted for conflict because the snakeyaml 1.32 is required directly by Simple-Yaml 1.8.2 (depth 1), being closer to the root and then preferred for the maven build.

Specifying the snakeyaml 1.32 dependency manually in the root (depth 0) is therefore redundant because it is already closer through Simple-Yaml than through velocity-api. Exclusion did not work for the same reason, because even if it removes the older version from being selected and removes the conflict, it was not being selected in the first place because it was omitted. The same applies to specifying the desired snakeyaml version in the dependencyManagement section, it removes the conflict, but the updated version was already being selected. Also with the reordering I suggested in my previous message, that would fix the conflict by selecting the updated snakeyaml version required by Simple-Yaml if both snakeyaml dependency declarations were on the same depth in the dependency tree, but that's not the case. You can still include it to resolve the maven conflict, but it is not strictly required to fix this error as it is right now. If velocity-api included snakeyaml as a direct dependency instead than through configurate-yaml, then it would be required to select the desired version in your plugin either via exclusion in velocity-api, reordering the dependencies, selecting the desired version in dependencyManagement or manually adding the desired dependency version.

The root cause is the JVM selecting the wrong snakeyaml version classes, because it has loaded the older snakeyaml 1.26 through Velocity first, which is the executable server loaded first. Your usage of Velocity is through a plugin (that's why you use the velocity-api dependency after all), so your plugin classes (including your dependencies built with maven, like the correct snakeyaml 1.32) are loaded in runtime by the Java class loader in the velocity instance. Because there is a path conflict with org.snakeyaml package (1.26 was loaded by the server and you're trying to load 1.32 through your plugin), the first loaded classes prevail, so 1.26 is not overridden. Therefore, your plugin ends up using snakeyaml 1.26 classes, and when Simple-Yaml access the DumperOptions class, it does not find the setIndentWithIndicator method, because that method was added in a later version (1.27). And then it raises the NoSuchMethodError. The method was in the snakeyaml 1.32 classes you were trying to load (they are in the plugin's built jar), but those classes are skipped by the velocity's java class loader when loading your plugin to the server.

So we have two conflicts here: one in the maven build (which is already handled correctly via the default maven dependency mediation), and another conflict in the runtime loading of the velocity plugin inside a velocity server, which causes the error of this issue.

Relocation is therefore required to avoid the runtime path conflict with the class loader, adding the following inside maven-shade-plugin configuration, as @MrFreasy suggested:

<relocations>
    <relocation>
        <pattern>org.yaml.snakeyaml</pattern>
        <shadedPattern>lv.exosmium.exoDomainManager.libs.snakeyaml</shadedPattern>
    </relocation>
</relocations>

You can edit the shadedPattern to your liking.

Nothing else than this single relocation is required to fix the issue. The org.simpleyaml relocation is unnecessary as that package is not conflicting with anything inside the velocity server.

With this relocation, snakeyaml 1.32 classes are compiled in your plugin using the shaded path, so when your plugin is injected and the dependency classes are loaded via the Velocity server, they do not conflict with the already loaded snakeyaml 1.26 classes. It is a bit redundant to have multiple versions of the same dependency packed into your server, but this way Simple-Yaml access to the correct and updated DumperOptions class. To remove this redundancy, and also to improve the security of the velocity server, velocity-api and their configurate-yaml dependency should update the snakeyaml version to the latest available in a future velocity-api release, so it is updated in the velocity server in the first place.

This pom.xml is edited and working with only the minimum relocation needed, without any exclusion or manually adding the snakeyaml dependency.

I hope everyone now has a better understanding of where the error comes from, how is fixed and why it works, and that people with the same error in the future find this helpful, as older related issues messages are not precise enough and the solution, while it was there in the comments, although with redundancy, required further investigation leading to confusion, trial & error and was not immediate to implement, as it should be now.

exosmium commented 2 years ago

Finally. Thank you so much!

Carleslc commented 2 years ago

Since 1.8.3 the manual relocation is not needed anymore as it is already included. You can update now to 1.8.3 and remove the relocation from your pom.