Unverified Commit 92277a01 by Spencer Gibb

Merge branch 'master' into 2.0.x

parents 73f33456 7425f974
......@@ -1107,7 +1107,7 @@ favor for an opt-in approach.
[[spring-cloud-feign-hystrix-fallback]]
=== Feign Hystrix Fallbacks
Hystrix supports the notion of a fallback: a default code path that is executed when they circuit is open or there is an error. To enable fallbacks for a given `@FeignClient` set the `fallback` attribute to the class name that implements the fallback.
Hystrix supports the notion of a fallback: a default code path that is executed when they circuit is open or there is an error. To enable fallbacks for a given `@FeignClient` set the `fallback` attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.
[source,java,indent=0]
----
......
......@@ -107,7 +107,7 @@ public @interface FeignClient {
String path() default "";
/**
* Whether to mark the feign proxy as a primary bean. Defaults to false.
* Whether to mark the feign proxy as a primary bean. Defaults to true.
*/
boolean primary() default true;
......
......@@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
......@@ -48,9 +49,10 @@ public class SpringDecoder implements Decoder {
}
@Override
public Object decode(final Response response, Type type) throws IOException,
FeignException {
if (type instanceof Class || type instanceof ParameterizedType) {
public Object decode(final Response response, Type type)
throws IOException, FeignException {
if (type instanceof Class || type instanceof ParameterizedType
|| type instanceof WildcardType) {
@SuppressWarnings({ "unchecked", "rawtypes" })
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
type, this.messageConverters.getObject().getConverters());
......
......@@ -74,11 +74,6 @@ public class HystrixSecurityAutoConfiguration {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = false)
static class HystrixEnabled {
}
@ConditionalOnProperty(name = "hystrix.shareSecurityContext")
static class ShareSecurityContext {
......
......@@ -83,6 +83,11 @@ public class SimpleRouteLocator implements RouteLocator, Ordered {
@Override
public Route getMatchingRoute(final String path) {
return getSimpleMatchingRoute(path);
}
protected Route getSimpleMatchingRoute(final String path) {
if (log.isDebugEnabled()) {
log.debug("Finding route for path: " + path);
}
......@@ -102,29 +107,31 @@ public class SimpleRouteLocator implements RouteLocator, Ordered {
String adjustedPath = adjustPath(path);
ZuulRoute route = null;
ZuulRoute route = getZuulRoute(adjustedPath);
return getRoute(route, adjustedPath);
}
protected ZuulRoute getZuulRoute(String adjustedPath) {
if (!matchesIgnoredPatterns(adjustedPath)) {
for (Entry<String, ZuulRoute> entry : this.routes.get().entrySet()) {
String pattern = entry.getKey();
log.debug("Matching pattern:" + pattern);
if (this.pathMatcher.match(pattern, adjustedPath)) {
route = entry.getValue();
break;
return entry.getValue();
}
}
}
if (log.isDebugEnabled()) {
log.debug("route matched=" + route);
}
return getRoute(route, adjustedPath);
return null;
}
private Route getRoute(ZuulRoute route, String path) {
protected Route getRoute(ZuulRoute route, String path) {
if (route == null) {
return null;
}
if (log.isDebugEnabled()) {
log.debug("route matched=" + route);
}
String targetPath = path;
String prefix = this.properties.getPrefix();
if (path.startsWith(prefix) && this.properties.isStripPrefix()) {
......
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 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,6 +33,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE;
......@@ -40,6 +41,7 @@ import static com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStr
* @author Spencer Gibb
* @author Dave Syer
* @author Mathias Düsterhöft
* @author Bilal Alp
*/
@Data
@ConfigurationProperties("zuul")
......@@ -326,6 +328,14 @@ public class ZuulProperties {
* The maximum number of connections that can be used by a single route.
*/
private int maxPerRouteConnections = 20;
/**
* The lifetime for the connection pool.
*/
private long timeToLive = -1;
/**
* The time unit for timeToLive.
*/
private TimeUnit timeUnit = TimeUnit.MILLISECONDS;
}
@Data
......
......@@ -71,7 +71,7 @@ public class SendResponseFilter extends ZuulFilter {
public SendResponseFilter() {
super();
// To support Servlet API 3.0.1 we need to check if setcontentLengthLong exists
// To support Servlet API 3.1 we need to check if setContentLengthLong exists
try {
HttpServletResponse.class.getMethod("setContentLengthLong");
} catch(NoSuchMethodException e) {
......
......@@ -68,7 +68,7 @@ public class RibbonRoutingFilter extends ZuulFilter {
this.helper = helper;
this.ribbonCommandFactory = ribbonCommandFactory;
this.requestCustomizers = requestCustomizers;
// To support Servlet API 3.0.1 we need to check if getcontentLengthLong exists
// To support Servlet API 3.1 we need to check if getContentLengthLong exists
try {
HttpServletRequest.class.getMethod("getContentLengthLong");
} catch(NoSuchMethodException e) {
......
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 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.
......@@ -92,6 +92,7 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
*
* @author Spencer Gibb
* @author Dave Syer
* @author Bilal Alp
*/
public class SimpleHostRoutingFilter extends ZuulFilter {
......@@ -235,7 +236,8 @@ public class SimpleHostRoutingFilter extends ZuulFilter {
}
final Registry<ConnectionSocketFactory> registry = registryBuilder.build();
this.connectionManager = new PoolingHttpClientConnectionManager(registry);
this.connectionManager = new PoolingHttpClientConnectionManager(registry, null, null, null,
hostProperties.getTimeToLive(), hostProperties.getTimeUnit());
this.connectionManager
.setMaxTotal(this.hostProperties.getMaxTotalConnections());
this.connectionManager.setDefaultMaxPerRoute(
......
......@@ -19,9 +19,11 @@ package org.springframework.cloud.netflix.feign;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -36,6 +38,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
......@@ -109,6 +112,19 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
}
@Test
@SuppressWarnings("unchecked")
public void testWildcardTypeDecode() {
ResponseEntity<?> wildcard = testClient().getWildcard();
assertNotNull("wildcard was null", wildcard);
assertEquals("wrong status code", HttpStatus.OK, wildcard.getStatusCode());
Object wildcardBody = wildcard.getBody();
assertNotNull("wildcardBody was null", wildcardBody);
assertTrue("wildcard not an instance of Map", wildcardBody instanceof Map);
Map<String, String> hello = (Map<String, String>) wildcardBody;
assertEquals("first hello didn't match", "wildcard", hello.get("message"));
}
@Test
public void testResponseEntityVoid() {
ResponseEntity<Void> response = testClient().getHelloVoid();
assertNotNull("response was null", response);
......@@ -156,6 +172,9 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
ResponseEntity<String> getNotFound();
@GetMapping("/helloWildcard")
ResponseEntity<?> getWildcard();
}
@Configuration
......@@ -199,6 +218,11 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body((String) null);
}
@Override
public ResponseEntity<?> getWildcard() {
return ResponseEntity.ok(new Hello("wildcard"));
}
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.properties("spring.application.name=springdecodertest",
......
/*
* Copyright 2013-2017 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
*
* http://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.hystrix.security;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest(classes = HystrixSecurityApplication.class)
public class HystrixSecurityNoFeignTests {
@Test
public void testSecurityConcurrencyStrategyInstalled() {
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
assertThat(concurrencyStrategy).isInstanceOf(SecurityContextConcurrencyStrategy.class);
}
}
......@@ -18,6 +18,8 @@ package org.springframework.cloud.netflix.hystrix.security;
import java.util.Base64;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -33,6 +35,8 @@ import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests that a secured web service returning values using a feign client properly access
* the security context from a hystrix command.
......@@ -40,7 +44,9 @@ import org.springframework.web.client.RestTemplate;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
@SpringBootTest(classes = HystrixSecurityApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"username.ribbon.listOfServers=localhost:${local.server.port}","feign.hystrix.enabled=true"})
@SpringBootTest(classes = HystrixSecurityApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "username.ribbon.listOfServers=localhost:${local.server.port}",
"feign.hystrix.enabled=true"})
public class HystrixSecurityTests {
@Autowired
private CustomConcurrenyStrategy customConcurrenyStrategy;
......@@ -55,6 +61,12 @@ public class HystrixSecurityTests {
private String password;
@Test
public void testSecurityConcurrencyStrategyInstalled() {
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
assertThat(concurrencyStrategy).isInstanceOf(SecurityContextConcurrencyStrategy.class);
}
@Test
public void testFeignHystrixSecurity() {
HttpHeaders headers = HystrixSecurityTests.createBasicAuthHeader(username,
password);
......
/*
* Copyright 2013-2017 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
*
* http://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.ribbon;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.IClientConfig;
/**
* @author Tyler Van Gorder
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RibbonClientPreprocessorOverridesRetryTests.TestConfiguration.class, value = {
"customRetry.ribbon.MaxAutoRetries=0",
"customRetry.ribbon.MaxAutoRetriesNextServer=1",
"customRetry.ribbon.OkToRetryOnAllOperations=true" })
@DirtiesContext
public class RibbonClientPreprocessorOverridesRetryTests {
@Autowired
private SpringClientFactory factory;
@Test
public void customRetryIsConfigured() throws Exception {
RibbonLoadBalancerContext context = (RibbonLoadBalancerContext) this.factory
.getLoadBalancerContext("customRetry");
Assert.isInstanceOf(RetryRibbonConfiguration.CustomRetryHandler.class,
context.getRetryHandler());
Assert.isTrue(context.getRetryHandler().getMaxRetriesOnSameServer() == 0);
Assert.isTrue(context.getRetryHandler().getMaxRetriesOnNextServer() == 1);
Assert.isTrue(context.getRetryHandler()
.isCircuitTrippingException(new UnknownHostException("Unknown Host")));
}
@Configuration
@RibbonClient(name = "customRetry", configuration = RetryRibbonConfiguration.class)
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
RibbonAutoConfiguration.class })
protected static class TestConfiguration {
}
}
@Configuration
class RetryRibbonConfiguration {
@Bean
public RetryHandler retryHandler(IClientConfig config) {
return new CustomRetryHandler(config);
}
class CustomRetryHandler extends DefaultLoadBalancerRetryHandler {
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> retriable = new ArrayList() {
{
add(UnknownHostException.class);
add(ConnectException.class);
add(SocketTimeoutException.class);
}
};
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> circuitRelated = new ArrayList() {
{
add(UnknownHostException.class);
add(SocketException.class);
add(SocketTimeoutException.class);
}
};
CustomRetryHandler(IClientConfig config) {
super(config);
}
@Override
protected List<Class<? extends Throwable>> getRetriableExceptions() {
return retriable;
}
@Override
protected List<Class<? extends Throwable>> getCircuitRelatedExceptions() {
return circuitRelated;
}
}
}
/*
* Copyright 2013-2017 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
*
* http://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.ribbon;
import java.net.ConnectException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RetryHandler;
import com.netflix.client.config.IClientConfig;
/**
* @author Tyler Van Gorder
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RibbonClientPreprocessorOverridesRetryTests.TestConfiguration.class, value = {
"customRetry.ribbon.MaxAutoRetries=0",
"customRetry.ribbon.MaxAutoRetriesNextServer=1",
"customRetry.ribbon.OkToRetryOnAllOperations=true" })
@DirtiesContext
public class RibbonClientPreprocessorOverridesRetryTests {
@Autowired
private SpringClientFactory factory;
@Test
public void customRetryIsConfigured() throws Exception {
RibbonLoadBalancerContext context = (RibbonLoadBalancerContext) this.factory
.getLoadBalancerContext("customRetry");
Assert.isInstanceOf(RetryRibbonConfiguration.CustomRetryHandler.class,
context.getRetryHandler());
Assert.isTrue(context.getRetryHandler().getMaxRetriesOnSameServer() == 0);
Assert.isTrue(context.getRetryHandler().getMaxRetriesOnNextServer() == 1);
Assert.isTrue(context.getRetryHandler()
.isCircuitTrippingException(new UnknownHostException("Unknown Host")));
}
@Configuration
@RibbonClient(name = "customRetry", configuration = RetryRibbonConfiguration.class)
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
RibbonAutoConfiguration.class })
protected static class TestConfiguration {
}
}
@Configuration
class RetryRibbonConfiguration {
@Bean
public RetryHandler retryHandler(IClientConfig config) {
return new CustomRetryHandler(config);
}
class CustomRetryHandler extends DefaultLoadBalancerRetryHandler {
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> retriable = new ArrayList() {
{
add(UnknownHostException.class);
add(ConnectException.class);
add(SocketTimeoutException.class);
}
};
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> circuitRelated = new ArrayList() {
{
add(UnknownHostException.class);
add(SocketException.class);
add(SocketTimeoutException.class);
}
};
CustomRetryHandler(IClientConfig config) {
super(config);
}
@Override
protected List<Class<? extends Throwable>> getRetriableExceptions() {
return retriable;
}
@Override
protected List<Class<? extends Throwable>> getCircuitRelatedExceptions() {
return circuitRelated;
}
}
}
......@@ -20,8 +20,10 @@ package org.springframework.cloud.netflix.zuul.filters.route;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletResponse;
......@@ -29,9 +31,13 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.Configurable;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -50,6 +56,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
......@@ -70,7 +77,8 @@ import static org.springframework.util.StreamUtils.copyToString;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SampleApplication.class,
webEnvironment = RANDOM_PORT,
properties = "server.servlet.contextPath: /app")
properties = {"server.servlet.contextPath: /app", "zuul.host.socket-timeout-millis=11000",
"zuul.host.connect-timeout-millis=2100"})
@DirtiesContext
public class SimpleHostRoutingFilterTests {
......@@ -87,12 +95,36 @@ public class SimpleHostRoutingFilterTests {
}
@Test
public void timeoutPropertiesAreApplied() {
setupContext();
CloseableHttpClient httpClient = getFilter().newClient();
Assertions.assertThat(httpClient).isInstanceOf(Configurable.class);
RequestConfig config = ((Configurable) httpClient).getConfig();
assertEquals(11000, config.getSocketTimeout());
assertEquals(2100, config.getConnectTimeout());
}
@Test
public void connectionPropertiesAreApplied() {
addEnvironment(this.context, "zuul.host.maxTotalConnections=100", "zuul.host.maxPerRouteConnections=10");
addEnvironment(this.context, "zuul.host.maxTotalConnections=100",
"zuul.host.maxPerRouteConnections=10", "zuul.host.timeToLive=5",
"zuul.host.timeUnit=SECONDS");
setupContext();
PoolingHttpClientConnectionManager connMgr = getFilter().newConnectionManager();
assertEquals(100, connMgr.getMaxTotal());
assertEquals(10, connMgr.getDefaultMaxPerRoute());
Object pool = getField(connMgr, "pool");
Long timeToLive = getField(pool, "timeToLive");
TimeUnit timeUnit = getField(pool, "tunit");
assertEquals(new Long(5), timeToLive);
assertEquals(TimeUnit.SECONDS, timeUnit);
}
protected <T> T getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
Object value = ReflectionUtils.getField(field, target);
return (T)value;
}
@Test
......
......@@ -16,8 +16,8 @@
<properties>
<archaius.version>0.7.4</archaius.version>
<eureka.version>1.6.2</eureka.version>
<feign.version>9.4.0</feign.version>
<hystrix.version>1.5.11</hystrix.version>
<feign.version>9.5.0</feign.version>
<hystrix.version>1.5.12</hystrix.version>
<ribbon.version>2.2.2</ribbon.version>
<servo.version>0.10.1</servo.version>
<zuul.version>1.3.0</zuul.version>
......@@ -239,11 +239,6 @@
</exclusions>
</dependency>
<dependency>
<groupId>javax-inject</groupId>
<artifactId>javax-inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-core</artifactId>
<version>${eureka.version}</version>
......
......@@ -22,9 +22,11 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.MalformedURLException;
import java.net.URL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
......@@ -53,6 +55,7 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertyResolver;
import org.springframework.util.StringUtils;
import com.netflix.appinfo.ApplicationInfoManager;
......@@ -62,7 +65,6 @@ import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.DiscoveryClient.DiscoveryClientOptionalArgs;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
/**
......@@ -82,18 +84,14 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI
@AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration")
public class EurekaClientAutoConfiguration {
@Value("${server.port:${SERVER_PORT:${PORT:8080}}}")
private int nonSecurePort;
@Value("${management.port:${MANAGEMENT_PORT:${server.port:${SERVER_PORT:${PORT:8080}}}}}")
private int managementPort;
@Autowired
private ConfigurableEnvironment env;
@Autowired(required = false)
private HealthCheckHandler healthCheckHandler;
public EurekaClientAutoConfiguration(ConfigurableEnvironment env) {
this.env = env;
}
@Bean
public HasFeatures eurekaFeature() {
return HasFeatures.namedFeature("Eureka Client", EurekaClient.class);
......@@ -120,17 +118,22 @@ public class EurekaClientAutoConfiguration {
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
String hostname = getProperty("eureka.instance.hostname");
boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
int nonSecurePort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));
int managementPort = Integer.valueOf(env.getProperty("management.port", String.valueOf(nonSecurePort)));
String managementContextPath = env.getProperty("management.context-path", env.getProperty("server.servlet.context-path", "/"));
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setNonSecurePort(this.nonSecurePort);
instance.setInstanceId(getDefaultInstanceId(this.env));
instance.setNonSecurePort(nonSecurePort);
instance.setInstanceId(getDefaultInstanceId(propertyResolver));
instance.setPreferIpAddress(preferIpAddress);
if (this.managementPort != this.nonSecurePort && this.managementPort != 0) {
if (managementPort != nonSecurePort && managementPort != 0) {
if (StringUtils.hasText(hostname)) {
instance.setHostname(hostname);
}
String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path");
String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path");
if (!managementContextPath.endsWith("/")) {
managementContextPath = managementContextPath + "/";
}
if (StringUtils.hasText(statusPageUrlPath)) {
instance.setStatusPageUrlPath(statusPageUrlPath);
}
......@@ -138,17 +141,15 @@ public class EurekaClientAutoConfiguration {
instance.setHealthCheckUrlPath(healthCheckUrlPath);
}
String scheme = instance.getSecurePortEnabled() ? "https" : "http";
instance.setStatusPageUrl(scheme + "://" + instance.getHostname() + ":"
+ this.managementPort + instance.getStatusPageUrlPath());
instance.setHealthCheckUrl(scheme + "://" + instance.getHostname() + ":"
+ this.managementPort + instance.getHealthCheckUrlPath());
URL base = new URL(scheme, instance.getHostname(), managementPort, managementContextPath);
instance.setStatusPageUrl(new URL(base, StringUtils.trimLeadingCharacter(instance.getStatusPageUrlPath(), '/')).toString());
instance.setHealthCheckUrl(new URL(base, StringUtils.trimLeadingCharacter(instance.getHealthCheckUrlPath(), '/')).toString());
}
return instance;
}
@Bean
public DiscoveryClient discoveryClient(EurekaInstanceConfig config,
EurekaClient client) {
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
return new EurekaDiscoveryClient(config, client);
}
......@@ -193,8 +194,7 @@ public class EurekaClientAutoConfiguration {
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config) {
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}
......@@ -222,8 +222,7 @@ public class EurekaClientAutoConfiguration {
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public EurekaClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config, EurekaInstanceConfig instance) {
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) {
manager.getInfo(); // force initialization
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
......@@ -233,8 +232,7 @@ public class EurekaClientAutoConfiguration {
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
......@@ -274,4 +272,14 @@ public class EurekaClientAutoConfiguration {
}
@Configuration
@ConditionalOnClass(Endpoint.class)
protected static class EurekaHealthIndicatorConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaHealthIndicator eurekaHealthIndicator(EurekaClient eurekaClient,
EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) {
return new EurekaHealthIndicator(eurekaClient, instanceConfig, clientConfig);
}
}
}
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2017 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.
......@@ -12,12 +12,12 @@
* 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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
......@@ -30,7 +30,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
......@@ -82,16 +81,6 @@ public class EurekaDiscoveryClientConfiguration {
}
}
@Configuration
@ConditionalOnClass(Endpoint.class)
protected static class EurekaHealthIndicatorConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaHealthIndicator eurekaHealthIndicator(EurekaClient eurekaClient,
EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) {
return new EurekaHealthIndicator(eurekaClient, instanceConfig, clientConfig);
}
}
@Configuration
@ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false)
......
......@@ -122,6 +122,54 @@ public class EurekaClientAutoConfigurationTests {
}
@Test
public void statusPageUrlPathAndManagementPortAndContextPath() {
EnvironmentTestUtils.addEnvironment(this.context, "server.port=8989",
"management.port=9999", "management.contextPath=/manage",
"eureka.instance.statusPageUrlPath=/myStatusPage");
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context
.getBean(EurekaInstanceConfigBean.class);
assertTrue("Wrong status page: " + instance.getStatusPageUrl(),
instance.getStatusPageUrl().endsWith(":9999/manage/myStatusPage"));
}
@Test
public void healthCheckUrlPathAndManagementPortAndContextPath() {
EnvironmentTestUtils.addEnvironment(this.context, "server.port=8989",
"management.port=9999", "management.contextPath=/manage",
"eureka.instance.healthCheckUrlPath=/myHealthCheck");
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context
.getBean(EurekaInstanceConfigBean.class);
assertTrue("Wrong health check: " + instance.getHealthCheckUrl(),
instance.getHealthCheckUrl().endsWith(":9999/manage/myHealthCheck"));
}
@Test
public void statusPageUrlPathAndManagementPortAndContextPathKebobCase() {
EnvironmentTestUtils.addEnvironment(this.context, "server.port=8989",
"management.port=9999", "management.context-path=/manage",
"eureka.instance.statusPageUrlPath=/myStatusPage");
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context
.getBean(EurekaInstanceConfigBean.class);
assertTrue("Wrong status page: " + instance.getStatusPageUrl(),
instance.getStatusPageUrl().endsWith(":9999/manage/myStatusPage"));
}
@Test
public void healthCheckUrlPathAndManagementPortAndContextPathKebobCase() {
EnvironmentTestUtils.addEnvironment(this.context, "server.port=8989",
"management.port=9999", "management.context-path=/manage",
"eureka.instance.healthCheckUrlPath=/myHealthCheck");
setupContext(RefreshAutoConfiguration.class);
EurekaInstanceConfigBean instance = this.context
.getBean(EurekaInstanceConfigBean.class);
assertTrue("Wrong health check: " + instance.getHealthCheckUrl(),
instance.getHealthCheckUrl().endsWith(":9999/manage/myHealthCheck"));
}
@Test
public void statusPageUrlPathAndManagementPortKabobCase() {
EnvironmentTestUtils.addEnvironment(this.context, "server.port=8989",
"management.port=9999",
......@@ -280,6 +328,12 @@ public class EurekaClientAutoConfigurationTests {
assertEquals("mytesteurekaappname", getInstanceConfig().getAppname());
}
@Test
public void eurekaHealthIndicatorCreated() {
setupContext();
this.context.getBean(EurekaHealthIndicator.class);
}
private void testNonSecurePort(String propName) {
addEnvironment(this.context, propName + ":8888");
setupContext();
......
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2017 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.
......@@ -19,11 +19,13 @@ package org.springframework.cloud.netflix.hystrix.stream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -31,12 +33,14 @@ import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = HystrixStreamTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "spring.jmx.enabled=true" })
properties = { "spring.jmx.enabled=true", "spring.application.name=mytestapp" })
@DirtiesContext
public class HystrixStreamTests {
......@@ -46,9 +50,13 @@ public class HystrixStreamTests {
@Autowired
private Application application;
@Autowired
private DiscoveryClient discoveryClient;
@EnableAutoConfiguration
@EnableCircuitBreaker
@RestController
@Configuration
public static class Application {
@HystrixCommand
......@@ -56,16 +64,16 @@ public class HystrixStreamTests {
public String hello() {
return "Hello World";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Test
public void contextLoads() {
this.application.hello();
//It is important that local service instance resolves for metrics
//origin details to be populated
ServiceInstance localServiceInstance = discoveryClient.getLocalServiceInstance();
assertThat(localServiceInstance).isNotNull();
assertThat(localServiceInstance.getServiceId()).isEqualTo("mytestapp");
this.task.gatherMetrics();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment