jenkins-rest icon indicating copy to clipboard operation
jenkins-rest copied to clipboard

Incompatibility with Spring BOM when activating jcloud's request/response logging - NoSuchMethodError: 'void ConstructorConstructor.<init>(Map)' at GsonModule.provideGson

Open alb-i986 opened this issue 3 years ago • 6 comments

Similar to #246 (but not that much actually).

I was able to squeeze the issue in the mvn project attached: project.tgz

Run the build (mvn clean test) (or the one and only test from your IDE), and you get:

com.google.inject.CreationException: Unable to create injector, see the following errors:

1) [Guice/ErrorInCustomProvider]: NoSuchMethodError: 'void ConstructorConstructor.<init>(Map)'
  at GsonModule.provideGson(GsonModule.java:99)
      \_ installed by: JenkinsHttpApiModule -> GsonModule
  at GsonWrapper.<init>(GsonWrapper.java:38)
      \_ for 1st parameter
  at GsonWrapper.class(GsonWrapper.java:32)
  while locating GsonWrapper
  while locating Json

Learn more:
  https://github.com/google/guice/wiki/ERROR_IN_CUSTOM_PROVIDER
Caused by: NoSuchMethodError: 'void ConstructorConstructor.<init>(Map)'
	at GsonModule.provideGson(GsonModule.java:130)
	at GsonModule$$FastClassByGuice$$2722177.GUICE$TRAMPOLINE(<generated>)
	at GsonModule$$FastClassByGuice$$2722177.apply(<generated>)
	at ProviderMethod$FastClassProviderMethod.doProvision(ProviderMethod.java:260)
[..]

Turn on <classifier>all</classifier> in the pom, and you get:

method modules in class com.cdancy.jenkins.rest.JenkinsClient.Builder cannot be applied to given types;
[ERROR]   required: io.github.cdancy.jenkins.rest.shaded.com.google.inject.Module[]
[ERROR]   found: org.jclouds.logging.slf4j.config.SLF4JLoggingModule
[ERROR]   reason: varargs mismatch; org.jclouds.logging.slf4j.config.SLF4JLoggingModule cannot be converted to io.github.cdancy.jenkins.rest.shaded.com.google.inject.Module

Now comment out <classifier>all</classifier> and comment out Spring's BOM, like this:

<!--    <dependencyManagement>-->
<!--        <dependencies>-->
<!--            <dependency>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-dependencies</artifactId>-->
<!--                <version>2.7.6</version>-->
<!--                <type>pom</type>-->
<!--                <scope>import</scope>-->
<!--            </dependency>-->
<!--        </dependencies>-->
<!--    </dependencyManagement>-->

    <dependencies>
        <dependency>
            <groupId>io.github.cdancy</groupId>
            <artifactId>jenkins-rest</artifactId>
            <version>1.0.2</version>
<!--            <classifier>all</classifier>-->
        </dependency>

and run the build again.

The test will now pass.

The issue has to do with the setup of the request/response logging:

JenkinsClient.builder()
                .endPoint("asd")
                .credentials("asd:asd")
                .modules(new SLF4JLoggingModule())
                .build();

Your Environment

  • Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
  • Java version: 11.0.17
  • jenkins-rest: 1.0.2

alb-i986 avatar Nov 30 '22 16:11 alb-i986

Yep, the all-version is not compatible with org.apache.jclouds.driver:jclouds-slf4j because it shades com.google.inject:guice.

I guess because it rewrites the client API, in this case com.cdancy.jenkins.rest.JenkinsClient.Builder#modules it would be considered a bug.

anderso avatar Aug 22 '23 17:08 anderso

Interesting ... I currently don't have the time to look into this but if someone wants to send in a fix or has an idea feel free to do so and I can merge.

cdancy avatar Aug 22 '23 23:08 cdancy

The issue is that the intersection of what is shaded and what is part of the public api of jenkins-rest is not empty.

Specifically in this case, this line

https://github.com/cdancy/jenkins-rest/blob/3c75844300f721f913edea37778bf5cf713d0caf/gradle/additional-artifacts.gradle#L11C19-L11C19

means that each class in the com.google package is relocated to io.github.cdancy.jenkins.rest.shaded.com.google and all imports are rewritten in the artifact with the all-classifier.

And this line declares a method with a parameter of type com.google.inject.Module

https://github.com/cdancy/jenkins-rest/blob/3c75844300f721f913edea37778bf5cf713d0caf/src/main/java/com/cdancy/jenkins/rest/JenkinsClient.java#L204C35-L204C35

which means that this method has different signatures in the different artifacts.

Fixes:

  1. Deprecate the all-classifier-artifact, but I guess it's there for a reason and people are using it.

  2. Refactor the api so that the intersection becomes empty.

  3. Change what is shaded

I'm guessing 3 is the most practical but maybe not trivial in case there are interdependencies.

The gradle library plugin supports separation of api and implementation dependencies

https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation

a suggestion would be to use this plugin in jenkins-rest, and declare com.google.inject:guice as api dependence.

In the best of worlds this would be recognized by the shadow jar plugin and it would do the right thing, but I do not know if this is the case.

anderso avatar Aug 23 '23 08:08 anderso

@anderso Your solution worked for me using JDK 17 and Spring Boot 3.+, I was able to build the shadowjar after setting guice to api instead of implementation and my spring boot project is able to access jenkins.

@cdancy for FYI

BLishman avatar Feb 21 '24 20:02 BLishman

@BLishman send in a PR and I'll take a look!

cdancy avatar Feb 22 '24 02:02 cdancy

That sounds like a good solution to me. This will cause guice to become a regular dependency of the shadow jar whereas it previously was shadowed, but on the other hand the api does not need to change.

anderso avatar Feb 22 '24 10:02 anderso