Using Jersey for the REST layer in a project, I've got a dependency conflict when using Spring Cloud as follow:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
<scope>runtime</scope>
</dependency>
The Spring Boot Jersey dependency org.springframework.boot:spring-boot-starter-jersey:jar:1.3.2.RELEASE is used.
The error is
java.lang.NoSuchMethodError: javax.ws.rs.core.Application.getProperties()Ljava/util/Map;
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:331) ~[jersey-server-2.22.1.jar:na]
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:390) ~[jersey-container-servlet-core-2.22.1.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:172) ~[jersey-container-servlet-core-2.22.1.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:364) ~[jersey-container-servlet-core-2.22.1.jar:na]
at javax.servlet.GenericServlet.init(GenericServlet.java:158) ~[tomcat-embed-core-8.0.30.jar:8.0.30]
and it comes from the fact that both
- javax.ws.rs:javax.ws.rs-api:jar:2.0.1:compile
- javax.ws.rs:jsr311-api:jar:1.1.1:runtime
are present in the classpath.
spring-cloud-starter-zuul includes jsr311-api.jar from its ribbon-httpclient dependency
+- com.netflix.ribbon:ribbon-httpclient:jar:2.1.0:compile
[INFO] | | | +- com.sun.jersey:jersey-client:jar:1.19:runtime
[INFO] | | | | \- com.sun.jersey:jersey-core:jar:1.19:runtime
[INFO] | | | | \- javax.ws.rs:jsr311-api:jar:1.1.1:runtime
[INFO] | | | \- com.sun.jersey.contribs:jersey-apache-client4:jar:1.19:runtime
I solved it by excluding jersey-client like this
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
<scope>runtime</scope>
</dependency>
It works when testing Zuul manually but I'm not sure it would under all conditions.
It's heavy handed but I figure I already have a much newer jersey client org.glassfish.jersey.core:jersey-client:jar:2.22.1:compile included with Spring Boot Jersey.
Why would ribbon use older Jersey client dependency? Is there any reason it wouldn't work with the newest org.glassfish.jersey.core:jersey-client:jar:2.22.1 ??
Comment From: dsyer
Why would ribbon use older Jersey client dependency?
You'd have to take that up with the Ribbon devs. Ribbon is a Netflix project.
Is there any reason it wouldn't work with the newest org.glassfish.jersey.core:jersey-client:jar:2.22.1 ?
I'd be very surprised if it worked (it's a major upgrade of a core component), but if you have something that works, that's awesome.
Comment From: spencergibb
Do we document that ribbon and eureka use jersey and therefor could conflict?
Comment From: dsyer
I guess so. Note that it's only a problem with eureka server as far as I know (which has never been very embeddable).
Comment From: maihacke
There are additional problems when using ribbon clients.
The exception is
org.springframework.beans.factory.BeanInitializationException: Failed to process @EventListener annotation on bean with name 'ribbonRestClient'; nested exception is java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfig
The cause is that org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration imports
import com.sun.jersey.api.client.Client;
import com.sun.jersey.client.apache4.ApacheHttpClient4;
which belongs to JAX-RS 1.x and is not available in JAX-RS 2.
Comment From: dsyer
Yeah, that's right, sorry. You can't use ribbon with JAX-RS 2.0.
Comment From: nodje
I'm stuck with this problem agan. I have to migrate a monolithic app using Jersey 2.0 to a microservice. Has anyone been touch with Netflix about it?
I guess having them migrate their code to use a Jersey 2.0 client is the the best bet to solve this problem.
Comment From: spencergibb
@nodje see https://github.com/Netflix/eureka/issues/600
Short answer: the code allows an implementation now as there is an abstraction. Netflix isn't keen on building it themselves, but would be open to PR's. We may do some work on this, but it would likely be based on RestTemplate, not jersey.
Comment From: spencergibb
For those watching, Netflix has started a jersey 2 client. https://github.com/Netflix/eureka/tree/master/eureka-client-jersey2
Not sure how much more work they are going to do.
Comment From: voor
Update in this thread, looks like Eureka Client 1.6 will be compatible: https://github.com/Netflix/eureka/pull/821
Comment From: aksain
@spencergibb Can this be included in 1.3.0.M1 milestone as this issue resolution is quite crucial to use Jersey 2 with Spring cloud?
Comment From: spencergibb
@aksain, probably not. I don't know what is involved to get this to work and haven't had time to look at it.
Comment From: aksain
@spencergibb Alright. For now, I have created one more starter on top of Spring cloud netflix one. Sharing the changes that i had to do, as this could be handy -
- In CloudEurekaClient.java - Replaced DiscoveryClientOptionalArgs with AbstractDiscoveryClientOptionalArgs
- In MutableDiscoveryClientOptionalArgs.java - Made it extend from Jersey2DiscoveryClientOptionalArgs, Replaced ClientFilter with ClientRequestFilter and added a constructor to set TransportClientFactories in super class to Jersey2TransportClientFactories.getInstance() using setTransportClientFactories method
- In EurekaClientAutoConfiguration - Replaced DiscoveryClientOptionalArgs with AbstractDiscoveryClientOptionalArgs
Comment From: spencergibb
@aksain Pull Requests are welcome.
Comment From: michael-simons
Having Jersey 1.x on the classpath of a Spring Boot app running JAX-RS on Jetty prevents the application from starting with the Spring Boot Maven plugin. See ticket over here https://github.com/spring-projects/spring-boot/issues/13290. We use Ribbon and not Eureka and at the moment, we're just excluding Jersey-Client 1.x.
Comment From: jlelong-onepoint
Same problem from me. Is there any roadmap, news about a migration path ?
Comment From: dsyer
The migration path is to not use Jersey 1.x, I guess (https://cloud.spring.io/spring-cloud-netflix/multi/multi__service_discovery_eureka_clients.html#_eurekaclient_without_jersey). And I guess to not use Ribbon.
Comment From: jlelong-onepoint
The migration path is to not use Jersey 1.x, I guess (https://cloud.spring.io/spring-cloud-netflix/multi/multi__service_discovery_eureka_clients.html#_eurekaclient_without_jersey). And I guess to not use Ribbon.
I had wrongly guess that was not supported as this ticket is open. On my side i have also exclude the jsr311 (jax-rs 1) dependency as my point was to avoid conflict with jax-rs 2. Eureka client and core explicitly depend on it in addition to jersey.
For reference my final gradle configuration is :
implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client') {
exclude group: 'com.sun.jersey'
exclude module: 'jsr311-api'
}
We have now to test in order to assure compatibility between Eureka and jaxrs 2.
Thanks a lots for your help