Allow disabling the under the hood use of JerseyClient with Eureka Client. Fixes gh-4185.

This commit is contained in:
Olga Maciaszek-Sharma
2024-07-08 16:34:19 +02:00
parent aa0670b1ee
commit b23c587319
13 changed files with 127 additions and 37 deletions

View File

@@ -294,6 +294,8 @@ The following example shows the dependencies you need to add:
</dependencies>
----
If you have `JerseyClient` on the classpath but do not wish to use it in your `EuerekaClient`, make sure to set `eureka.client.jersey.enabled` to `false`.
=== Alternatives to the Native Netflix EurekaClient
You need not use the raw Netflix `EurekaClient`.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,9 +33,9 @@ import org.springframework.beans.factory.BeanCreationException;
import static org.assertj.core.api.Assertions.assertThat;
abstract class BaseCertTest {
abstract class BaseCertTests {
private static final Log log = LogFactory.getLog(BaseCertTest.class);
private static final Log log = LogFactory.getLog(BaseCertTests.class);
protected static final String KEY_STORE_PASSWORD = "test-key-store-password";
@@ -53,7 +53,7 @@ abstract class BaseCertTest {
protected static File wrongClientCert;
protected BaseCertTest() {
protected BaseCertTests() {
}
@SuppressWarnings("rawtypes")

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.junit.runners.Suite;
* already shutdown.
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({ EurekaClientTest.class, RestTemplateEurekaClientTest.class })
@Suite.SuiteClasses({ EurekaClientTests.class, RestTemplateEurekaClientTests.class })
public class EurekaClientSuite {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,9 +27,9 @@ import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import static org.assertj.core.api.Assertions.assertThat;
public class EurekaClientTest extends BaseCertTest {
public class EurekaClientTests extends BaseCertTests {
private static final Log log = LogFactory.getLog(EurekaClientTest.class);
private static final Log log = LogFactory.getLog(EurekaClientTests.class);
static EurekaServerRunner server;
@@ -37,11 +37,11 @@ public class EurekaClientTest extends BaseCertTest {
@BeforeAll
public static void setupAll() {
server = startEurekaServer(EurekaClientTest.TestEurekaServer.class);
service = startService(server, EurekaClientTest.TestApp.class);
server = startEurekaServer(EurekaClientTests.TestEurekaServer.class);
service = startService(server, EurekaClientTests.TestApp.class);
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
log.info("Successfully asserted that Jersey will be used");
waitForRegistration(() -> new EurekaClientTest().createEurekaClient());
waitForRegistration(() -> new EurekaClientTests().createEurekaClient());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 the original author or authors.
* Copyright 2018-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,9 +38,9 @@ import org.springframework.context.annotation.Bean;
import static org.assertj.core.api.Assertions.assertThat;
public class RestTemplateEurekaClientTest extends BaseCertTest {
public class RestTemplateEurekaClientTests extends BaseCertTests {
private static final Log log = LogFactory.getLog(RestTemplateEurekaClientTest.class);
private static final Log log = LogFactory.getLog(RestTemplateEurekaClientTests.class);
private static EurekaServerRunner server;
@@ -48,12 +48,12 @@ public class RestTemplateEurekaClientTest extends BaseCertTest {
@BeforeAll
public static void setupAll() {
server = startEurekaServer(RestTemplateEurekaClientTest.RestTemplateTestEurekaServer.class);
service = startService(server, RestTemplateEurekaClientTest.RestTemplateTestApp.class);
server = startEurekaServer(RestTemplateEurekaClientTests.RestTemplateTestEurekaServer.class);
service = startService(server, RestTemplateEurekaClientTests.RestTemplateTestApp.class);
// Will use RestTemplate
assertThat(service.discoveryClientOptionalArgs()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
log.info("Successfully asserted that RestTemplate will be used");
waitForRegistration(() -> new RestTemplateEurekaClientTest().createEurekaClient());
waitForRegistration(() -> new RestTemplateEurekaClientTests().createEurekaClient());
}
@AfterAll

View File

@@ -26,6 +26,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -45,12 +47,14 @@ import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClient
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
/**
* @author Daniel Lavoie
* @author Armin Krezovic
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
@@ -66,7 +70,7 @@ public class DiscoveryClientOptionalArgsConfiguration {
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class }, search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
@@ -82,7 +86,7 @@ public class DiscoveryClientOptionalArgsConfiguration {
@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnMissingBean(value = { TransportClientFactories.class }, search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
@@ -108,7 +112,7 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.glassfish.jersey.client.JerseyClient")
@Conditional(JerseyClientPresentAndEnabledCondition.class)
@ConditionalOnBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
static class DiscoveryClientOptionalArgsTlsConfiguration {
@@ -120,7 +124,7 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientConfiguration {
@@ -151,8 +155,8 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
@Configuration
@ConditionalOnMissingClass({ "org.glassfish.jersey.client.JerseyClient",
"org.springframework.web.reactive.function.client.WebClient" })
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@ConditionalOnMissingClass("org.springframework.web.reactive.function.client.WebClient")
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientNotFoundConfiguration {
@@ -164,4 +168,40 @@ public class DiscoveryClientOptionalArgsConfiguration {
}
static class JerseyClientPresentAndEnabledCondition extends AllNestedConditions {
JerseyClientPresentAndEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass(name = "org.glassfish.jersey.client.JerseyClient")
static class OnJerseyClientPresent {
}
@ConditionalOnProperty(value = "eureka.client.jersey.enabled", matchIfMissing = true)
static class OnJerseyClientEnabled {
}
}
static class JerseyClientNotPresentOrNotEnabledCondition extends AnyNestedCondition {
JerseyClientNotPresentOrNotEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnMissingClass("org.glassfish.jersey.client.JerseyClient")
static class OnJerseyClientMissing {
}
@ConditionalOnProperty(value = "eureka.client.jersey.enabled", havingValue = "false")
static class OnJerseyClientDisabled {
}
}
}

View File

@@ -264,10 +264,10 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
@Override
protected boolean hasError(HttpStatusCode statusCode) {
/**
* When the Eureka server restarts and a client tries to sent a heartbeat the
* server will respond with a 404. By default RestTemplate will throw an
* When the Eureka server restarts and a client tries to send a heartbeat the
* server will respond with a 404. By default, RestTemplate will throw an
* exception in this case. What we want is to return the 404 to the upstream
* code so it will send another registration request to the server.
* code, so it will send another registration request to the server.
*/
if (statusCode.is4xxClientError()) {
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ import static org.mockserver.model.HttpResponse.response;
* @author Ryan Baxter
*/
@Testcontainers
public class EurekaConfigServerBootstrapperIT {
public class EurekaConfigServerBootstrapperIntegrationTests {
public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("mockserver/mockserver")
.withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@RunWith(ModifiedClassPathRunner.class)
@ClassPathExclusions({ "jersey-client-*", "jersey-core-*", "jersey-apache-client4-*", "spring-webflux-*" })
@SpringBootTest(classes = EurekaSampleApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class EurekaHttpClientsOptionalArgsConfigurationNoWebfluxTest {
public class EurekaHttpClientsOptionalArgsConfigurationNoWebFluxTests {
@Test
public void contextFailsWithoutWebClient() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@RunWith(ModifiedClassPathRunner.class)
@ClassPathExclusions({ "jersey-client-*", "jersey-core-*", "jersey-apache-client4-*" })
@SpringBootTest(classes = EurekaSampleApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class EurekaHttpClientsOptionalArgsConfigurationTest {
public class EurekaHttpClientsOptionalArgsConfigurationTests {
@Test
public void contextLoadsWithRestTemplate() {

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2017-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.eureka.config;
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for Jersey client setup in DiscoveryClientOptionalArgsConfiguration.
*
* @author Olga Maciaszek-Sharma
*/
public class JerseyClientOptionalArgsConfigurationTests {
@SuppressWarnings("OptionalGetWithoutIsPresent")
@Test
void shouldCreateRestTemplateDiscoveryClientOptionalArgsWhenJerseyClientDisabled() {
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(DiscoveryClientOptionalArgsConfiguration.class))
.withPropertyValues("eureka.client.jersey.enabled=false").run(context -> {
assertThat(context).hasSingleBean(AbstractDiscoveryClientOptionalArgs.class);
assertThat(context.getBeansOfType(AbstractDiscoveryClientOptionalArgs.class).values().stream()
.findFirst().get()).isInstanceOf(RestTemplateDiscoveryClientOptionalArgs.class);
assertThat(context).hasSingleBean(RestTemplateDiscoveryClientOptionalArgs.class);
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@DirtiesContext
@SpringBootTest(classes = EurekaSampleApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
class JerseyOptionalArgsConfigurationTest {
class JerseyOptionalArgsConfigurationApplicationTests {
@Autowired
private AbstractDiscoveryClientOptionalArgs optionalArgs;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 the original author or authors.
* Copyright 2017-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test;
/**
* @author Daniel Lavoie
*/
class RestTemplateTransportClientFactoryTest {
class RestTemplateTransportClientFactoryTests {
private RestTemplateTransportClientFactory transportClientFatory;