diff --git a/docs/pom.xml b/docs/pom.xml index 750a0699..7225f151 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT spring-cloud-netflix-docs pom diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 53961116..bd65af99 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -1012,6 +1012,30 @@ static class HystrixClientFallback implements HystrixClient { } ---- +If one needs access to the cause that made the fallback trigger, one can use the `fallbackFactory` attribute inside `@FeignClient`. + +[source,java,indent=0] +---- +@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class) +protected interface HystrixClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") + Hello iFailSometimes(); +} + +@Component +static class HystrixClientFallbackFactory implements FallbackFactory { + @Override + public HystrixClient create(Throwable cause) { + return new HystrixClientWithFallBackFactory() { + @Override + public Hello iFailSometimes() { + return new Hello("fallback; reason was: " + cause.getMessage()); + } + }; + } +} +---- + WARNING: There is a limitation with the implementation of fallbacks in Feign and how Hystrix fallbacks work. Fallbacks are currently not supported for methods that return `com.netflix.hystrix.HystrixCommand` and `rx.Observable`. [[spring-cloud-feign-inheritance]] @@ -1363,7 +1387,7 @@ path rendering the `users` path unreachable. === Zuul Http Client The default HTTP client used by zuul is now backed by the Apache HTTP Client instead of the -deprecated Ribbon `RestClient. To use `RestClient` or to use the `okhttp3.OkHttpClient` set +deprecated Ribbon `RestClient`. To use `RestClient` or to use the `okhttp3.OkHttpClient` set `ribbon.restclient.enabled=true` or `ribbon.okhttp.enabled=true` respectively. === Cookies and Sensitive Headers @@ -1734,6 +1758,18 @@ If Spring AOP is enabled and `org.aspectj:aspectjweaver` is present on your runt 3. URI, sanitized for Atlas 4. Client name +WARNING: Avoid using hardcoded url parameters within `RestTemplate`. When targeting dynamic endpoints use URL variables. This will avoid potential "GC Overhead Limit Reached" issues where `ServoMonitorCache` treats each url as a unique key. + +[source,java,indent=0] +---- +// recommended +String orderid = "1"; +restTemplate.getForObject("http://testeurekabrixtonclient/orders/{orderid}", String.class, orderid) + +// avoid +restTemplate.getForObject("http://testeurekabrixtonclient/orders/1", String.class) +---- + [[netflix-metrics-spectator]] === Metrics Collection: Spectator diff --git a/pom.xml b/pom.xml index b0994d49..ef239e18 100644 --- a/pom.xml +++ b/pom.xml @@ -3,14 +3,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT pom Spring Cloud Netflix Spring Cloud Netflix org.springframework.cloud spring-cloud-build - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT @@ -24,8 +24,8 @@ ${basedir} 4.0.27.Final 2.7.3 - 1.1.4.BUILD-SNAPSHOT - 1.2.1.BUILD-SNAPSHOT + 1.1.5.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT Brooklyn.BUILD-SNAPSHOT diff --git a/spring-cloud-netflix-core/pom.xml b/spring-cloud-netflix-core/pom.xml index 8f6985cc..3343772a 100644 --- a/spring-cloud-netflix-core/pom.xml +++ b/spring-cloud-netflix-core/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-core diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClient.java index 8c2e361f..1dc139a1 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClient.java @@ -90,6 +90,16 @@ public @interface FeignClient { */ Class fallback() default void.class; + /** + * Define a fallback factory for the specified Feign client interface. The fallback + * factory must produce instances of fallback classes that implement the interface + * annotated by {@link FeignClient}. The fallback factory must be a valid spring + * bean. + * + * @see feign.hystrix.FallbackFactory for details. + */ + Class fallbackFactory() default void.class; + /** * Path prefix to be used by all method-level mappings. Can be used with or without * @RibbonClient. diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java index fa345d88..e5fcffc7 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientFactoryBean.java @@ -51,9 +51,9 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = false) class FeignClientFactoryBean implements FactoryBean, InitializingBean, ApplicationContextAware { - - @Autowired - private Targeter targeter; + /*********************************** + * WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition. + ***********************************/ private Class type; @@ -69,6 +69,8 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, private Class fallback = void.class; + private Class fallbackFactory = void.class; + @Override public void afterPropertiesSet() throws Exception { Assert.hasText(this.name, "Name must be set"); @@ -144,6 +146,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); + Targeter targeter = get(context, Targeter.class); return targeter.target(this, builder, context, target); } @@ -181,6 +184,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } builder.client(client); } + Targeter targeter = get(context, Targeter.class); return targeter.target(this, builder, context, new HardCodedTarget<>( this.type, this.name, url)); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsRegistrar.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsRegistrar.java index 9020c6e7..53c34ef5 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsRegistrar.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/FeignClientsRegistrar.java @@ -179,6 +179,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, definition.addPropertyValue("type", className); definition.addPropertyValue("decode404", attributes.get("decode404")); definition.addPropertyValue("fallback", attributes.get("fallback")); + definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory")); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); String alias = name + "FeignClient"; diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java index 5b9ed8d8..66106c70 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java @@ -19,6 +19,9 @@ package org.springframework.cloud.netflix.feign; import feign.Feign; import feign.Target; +import feign.hystrix.FallbackFactory; +import feign.hystrix.HystrixFeign; +import org.springframework.util.Assert; /** * @author Spencer Gibb @@ -29,26 +32,67 @@ class HystrixTargeter implements Targeter { @Override public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget target) { - if (factory.getFallback() == void.class - || !(feign instanceof feign.hystrix.HystrixFeign.Builder)) { + if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } + feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; + Class fallback = factory.getFallback(); + if (fallback != void.class) { + return targetWithFallback(factory.getName(), context, target, builder, fallback); + } + Class fallbackFactory = factory.getFallbackFactory(); + if (fallbackFactory != void.class) { + return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory); + } - Object fallbackInstance = context.getInstance(factory.getName(), factory.getFallback()); + return feign.target(target); + } + + private T targetWithFallbackFactory(String feignClientName, FeignContext context, + Target.HardCodedTarget target, + HystrixFeign.Builder builder, + Class fallbackFactoryClass) { + FallbackFactory fallbackFactory = (FallbackFactory) + getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class); + /* We take a sample fallback from the fallback factory to check if it returns a fallback + that is compatible with the annotated feign interface. */ + Object exampleFallback = fallbackFactory.create(new RuntimeException()); + Assert.notNull(exampleFallback, + String.format( + "Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!", + feignClientName)); + if (!target.type().isAssignableFrom(exampleFallback.getClass())) { + throw new IllegalStateException( + String.format( + "Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'", + feignClientName, exampleFallback.getClass(), target.type())); + } + return builder.target(target, fallbackFactory); + } + + + private T targetWithFallback(String feignClientName, FeignContext context, + Target.HardCodedTarget target, + HystrixFeign.Builder builder, Class fallback) { + T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type()); + return builder.target(target, fallbackInstance); + } + + private T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, + Class beanType, Class targetType) { + Object fallbackInstance = context.getInstance(feignClientName, beanType); if (fallbackInstance == null) { throw new IllegalStateException(String.format( - "No fallback instance of type %s found for feign client %s", - factory.getFallback(), factory.getName())); + "No " + fallbackMechanism + " instance of type %s found for feign client %s", + beanType, feignClientName)); } - if (!target.type().isAssignableFrom(factory.getFallback())) { + if (!targetType.isAssignableFrom(beanType)) { throw new IllegalStateException( String.format( - "Incompatible fallback instance. Fallback of type %s is not assignable to %s for feign client %s", - factory.getFallback(), target.type(), factory.getName())); + "Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s", + beanType, targetType, feignClientName)); } - - feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; - return builder.target(target, (T) fallbackInstance); + return (T) fallbackInstance; } } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java index 96ef9ae2..ca6ddfd8 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java @@ -52,11 +52,10 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient{ RibbonLoadBalancerContext context = this.clientFactory .getLoadBalancerContext(serviceId); Server server = new Server(instance.getHost(), instance.getPort()); - boolean secure = isSecure(server, serviceId); - URI uri = original; - if (secure) { - uri = UriComponentsBuilder.fromUri(uri).scheme("https").build().toUri(); - } + IClientConfig clientConfig = clientFactory.getClientConfig(serviceId); + ServerIntrospector serverIntrospector = serverIntrospector(serviceId); + URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig, + serverIntrospector, server); return context.reconstructURIWithServer(server, uri); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java index cc5a5d0b..81f46576 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java @@ -24,8 +24,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.web.ErrorController; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; -import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.cloud.client.discovery.event.HeartbeatEvent; import org.springframework.cloud.client.discovery.event.HeartbeatMonitor; diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java index 0f99a99e..85bdb599 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelper.java @@ -249,6 +249,12 @@ public class ProxyRequestHelper { MultiValueMap headers) { } + /** + * Get url encoded query string. Pay special attention to single parameters with no values + * and parameter names with colon (:) from use of UriTemplate. + * @param params Un-encoded request parameters + * @return + */ public String getQueryString(MultiValueMap params) { if (params.isEmpty()) { return ""; @@ -260,10 +266,21 @@ public class ProxyRequestHelper { for (String value : params.get(param)) { query.append("&"); query.append(param); - if (!"".equals(value)) { - singles.put(param + i, value); + if (!"".equals(value)) { // don't add =, if original is ?wsdl, output is not ?wsdl= + String key = param; + // if form feed is already part of param name double + // since form feed is used as the colon replacement below + if (key.contains("\f")) { + key = (key.replaceAll("\f", "\f\f")); + } + // colon is special to UriTemplate + if (key.contains(":")) { + key = key.replaceAll(":", "\f"); + } + key = key + i; + singles.put(key, value); query.append("={"); - query.append(param + i); + query.append(key); query.append("}"); } i++; diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java index b621f59d..59c37dab 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java @@ -103,7 +103,7 @@ public class ZuulProperties { private Set ignoredHeaders = new LinkedHashSet<>(); /** - * SECURITY_HEADERS are added to ignored headers if spring security is on the classpath and ignoreSecurityHeaders = true + * Flag to say that SECURITY_HEADERS are added to ignored headers if spring security is on the classpath. * By setting ignoreSecurityHeaders to false we can switch off this default behaviour. This should be used together with * disabling the default spring security headers * see https://docs.spring.io/spring-security/site/docs/current/reference/html/headers.html#default-security-headers diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/FormBodyWrapperFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/FormBodyWrapperFilter.java index 22681762..500ee5a2 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/FormBodyWrapperFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre/FormBodyWrapperFilter.java @@ -22,16 +22,14 @@ import java.io.OutputStream; import java.lang.reflect.Field; import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; import java.util.Set; +import java.util.Map.Entry; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestWrapper; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.Part; -import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; @@ -44,6 +42,7 @@ import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.servlet.DispatcherServlet; @@ -65,7 +64,7 @@ public class FormBodyWrapperFilter extends ZuulFilter { this.requestField = ReflectionUtils.findField(HttpServletRequestWrapper.class, "req", HttpServletRequest.class); this.servletRequestField = ReflectionUtils.findField(ServletRequestWrapper.class, - "request", ServletRequest.class); + "request", ServletRequest.class); Assert.notNull(this.requestField, "HttpServletRequestWrapper.req field not found"); Assert.notNull(this.servletRequestField, @@ -121,7 +120,7 @@ public class FormBodyWrapperFilter extends ZuulFilter { .getField(this.requestField, request); wrapper = new FormBodyRequestWrapper(wrapped); ReflectionUtils.setField(this.requestField, request, wrapper); - if(request instanceof ServletRequestWrapper) { + if (request instanceof ServletRequestWrapper) { ReflectionUtils.setField(this.servletRequestField, request, wrapper); } } @@ -170,7 +169,7 @@ public class FormBodyWrapperFilter extends ZuulFilter { } return this.contentLength; } - + @Override public long getContentLengthLong() { return getContentLength(); @@ -233,7 +232,7 @@ public class FormBodyWrapperFilter extends ZuulFilter { Set result = new HashSet<>(); String query = this.request.getQueryString(); if (query != null) { - for (String value : StringUtils.split(query, "&")) { + for (String value : StringUtils.tokenizeToStringArray(query, "&")) { if (value.contains("=")) { value = value.substring(0, value.indexOf("=")); } diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java index cdefd022..a76a5dd5 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilter.java @@ -63,6 +63,7 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; import org.apache.http.protocol.HttpContext; import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper; @@ -271,34 +272,13 @@ public class SimpleHostRoutingFilter extends ZuulFilter { URL host = RequestContext.getCurrentContext().getRouteHost(); HttpHost httpHost = getHttpHost(host); uri = StringUtils.cleanPath((host.getPath() + uri).replaceAll("/{2,}", "/")); - HttpRequest httpRequest; int contentLength = request.getContentLength(); InputStreamEntity entity = new InputStreamEntity(requestEntity, contentLength, request.getContentType() != null ? ContentType.create(request.getContentType()) : null); - switch (verb.toUpperCase()) { - case "POST": - HttpPost httpPost = new HttpPost(uri + this.helper.getQueryString(params)); - httpRequest = httpPost; - httpPost.setEntity(entity); - break; - case "PUT": - HttpPut httpPut = new HttpPut(uri + this.helper.getQueryString(params)); - httpRequest = httpPut; - httpPut.setEntity(entity); - break; - case "PATCH": - HttpPatch httpPatch = new HttpPatch(uri + this.helper.getQueryString(params)); - httpRequest = httpPatch; - httpPatch.setEntity(entity); - break; - default: - httpRequest = new BasicHttpRequest(verb, - uri + this.helper.getQueryString(params)); - log.debug(uri + this.helper.getQueryString(params)); - } + + HttpRequest httpRequest = buildHttpRequest(verb, uri, entity, headers, params); try { - httpRequest.setHeaders(convertHeaders(headers)); log.debug(httpHost.getHostName() + " " + httpHost.getPort() + " " + httpHost.getSchemeName()); HttpResponse zuulResponse = forwardRequest(httpclient, httpHost, httpRequest); @@ -314,6 +294,43 @@ public class SimpleHostRoutingFilter extends ZuulFilter { } } + protected HttpRequest buildHttpRequest (String verb, String uri, InputStreamEntity entity, + MultiValueMap headers, MultiValueMap params) + { + HttpRequest httpRequest; + + switch (verb.toUpperCase()) { + case "POST": + HttpPost httpPost = new HttpPost(uri + this.helper.getQueryString(params)); + httpRequest = httpPost; + httpPost.setEntity(entity); + break; + case "PUT": + HttpPut httpPut = new HttpPut(uri + this.helper.getQueryString(params)); + httpRequest = httpPut; + httpPut.setEntity(entity); + break; + case "PATCH": + HttpPatch httpPatch = new HttpPatch(uri + this.helper.getQueryString(params)); + httpRequest = httpPatch; + httpPatch.setEntity(entity); + break; + case "DELETE": + BasicHttpEntityEnclosingRequest entityRequest = new BasicHttpEntityEnclosingRequest(verb, + uri + this.helper.getQueryString(params)); + httpRequest = entityRequest; + entityRequest.setEntity(entity); + break; + default: + httpRequest = new BasicHttpRequest(verb, + uri + this.helper.getQueryString(params)); + log.debug(uri + this.helper.getQueryString(params)); + } + + httpRequest.setHeaders(convertHeaders(headers)); + return httpRequest; + } + private MultiValueMap revertHeaders(Header[] headers) { MultiValueMap map = new LinkedMultiValueMap(); for (Header header : headers) { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/invalid/FeignClientValidationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/invalid/FeignClientValidationTests.java index 178fc643..bd7d235f 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/invalid/FeignClientValidationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/invalid/FeignClientValidationTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.netflix.feign.invalid; +import feign.hystrix.FallbackFactory; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -42,10 +43,7 @@ public class FeignClientValidationTests { @Test public void testNameAndValue() { this.expected.expectMessage("only one is permitted"); - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - NameAndValueConfiguration.class); - assertNotNull(context.getBean(NameAndValueConfiguration.Client.class)); - context.close(); + new AnnotationConfigApplicationContext(NameAndValueConfiguration.class); } @Configuration @@ -86,10 +84,7 @@ public class FeignClientValidationTests { @Test public void testNotLegalHostname() { this.expected.expectMessage("not legal hostname (foo_bar)"); - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - BadHostnameConfiguration.class); - assertNotNull(context.getBean(BadHostnameConfiguration.Client.class)); - context.close(); + new AnnotationConfigApplicationContext(BadHostnameConfiguration.class); } @Configuration @@ -107,11 +102,12 @@ public class FeignClientValidationTests { @Test public void testMissingFallback() { - this.expected.expectMessage("No fallback instance of type"); + try ( AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - MissingFallbackConfiguration.class); - assertNotNull(context.getBean(MissingFallbackConfiguration.Client.class)); - context.close(); + MissingFallbackConfiguration.class)) { + this.expected.expectMessage("No fallback instance of type"); + assertNotNull(context.getBean(MissingFallbackConfiguration.Client.class)); + } } @Configuration @@ -136,11 +132,11 @@ public class FeignClientValidationTests { @Test public void testWrongFallbackType() { - this.expected.expectMessage("Incompatible fallback instance"); - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - WrongFallbackTypeConfiguration.class); - assertNotNull(context.getBean(WrongFallbackTypeConfiguration.Client.class)); - context.close(); + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WrongFallbackTypeConfiguration.class)) { + this.expected.expectMessage("Incompatible fallback instance"); + assertNotNull(context.getBean(WrongFallbackTypeConfiguration.Client.class)); + } } @Configuration @@ -163,4 +159,98 @@ public class FeignClientValidationTests { } } + + @Test + public void testMissingFallbackFactory() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + MissingFallbackFactoryConfiguration.class)) { + this.expected.expectMessage("No fallbackFactory instance of type"); + assertNotNull(context.getBean(MissingFallbackFactoryConfiguration.Client.class)); + } + } + + @Configuration + @Import(FeignAutoConfiguration.class) + @EnableFeignClients(clients = MissingFallbackFactoryConfiguration.Client.class) + protected static class MissingFallbackFactoryConfiguration { + + @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = ClientFallback.class) + interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") + String get(); + } + + class ClientFallback implements FallbackFactory { + + @Override + public Client create(Throwable cause) { + return null; + } + } + } + + @Test + public void testWrongFallbackFactoryType() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WrongFallbackFactoryTypeConfiguration.class)) { + this.expected.expectMessage("Incompatible fallbackFactory instance"); + assertNotNull(context.getBean(WrongFallbackFactoryTypeConfiguration.Client.class)); + } + } + + @Configuration + @Import(FeignAutoConfiguration.class) + @EnableFeignClients(clients = WrongFallbackFactoryTypeConfiguration.Client.class) + protected static class WrongFallbackFactoryTypeConfiguration { + + @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = Dummy.class) + interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") + String get(); + } + + @Bean + Dummy dummy() { + return new Dummy(); + } + + class Dummy { + } + + } + + @Test + public void testWrongFallbackFactoryGenericType() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WrongFallbackFactoryGenericTypeConfiguration.class)) { + this.expected.expectMessage("Incompatible fallbackFactory instance"); + assertNotNull(context.getBean(WrongFallbackFactoryGenericTypeConfiguration.Client.class)); + } + } + + @Configuration + @Import(FeignAutoConfiguration.class) + @EnableFeignClients(clients = WrongFallbackFactoryGenericTypeConfiguration.Client.class) + protected static class WrongFallbackFactoryGenericTypeConfiguration { + + @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = ClientFallback.class) + interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") + String get(); + } + + @Bean + ClientFallback dummy() { + return new ClientFallback(); + } + + class ClientFallback implements FallbackFactory { + + @Override + public String create(Throwable cause) { + return "tryinToTrickYa"; + } + } + + } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java index 49f3b831..366e6454 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java @@ -74,6 +74,7 @@ import feign.Client; import feign.Logger; import feign.RequestInterceptor; import feign.RequestTemplate; +import feign.hystrix.FallbackFactory; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -115,6 +116,9 @@ public class FeignClientTests { @Autowired HystrixClient hystrixClient; + @Autowired + private HystrixClientWithFallBackFactory hystrixClientWithFallBackFactory; + @Autowired @Qualifier("localapp3FeignClient") HystrixClient namedHystrixClient; @@ -237,6 +241,27 @@ public class FeignClientTests { Future failFuture(); } + @FeignClient(name = "localapp4", fallbackFactory = HystrixClientFallbackFactory.class) + protected interface HystrixClientWithFallBackFactory { + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Hello fail(); + } + + static class HystrixClientFallbackFactory implements FallbackFactory { + + @Override + public HystrixClientWithFallBackFactory create(final Throwable cause) { + return new HystrixClientWithFallBackFactory() { + @Override + public Hello fail() { + assertNotNull("Cause was null", cause); + return new Hello("Hello from the fallback side: " + cause.getMessage()); + } + }; + } + } + static class HystrixClientFallback implements HystrixClient { @Override public Hello fail() { @@ -268,13 +293,15 @@ public class FeignClientTests { @EnableAutoConfiguration @RestController @EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class, - DecodingTestClient.class, - HystrixClient.class }, defaultConfiguration = TestDefaultFeignConfig.class) + DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class }, + defaultConfiguration = TestDefaultFeignConfig.class) @RibbonClients({ @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), }) + @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class) + }) protected static class Application { // needs to be in parent context to test multiple HystrixClient beans @@ -283,6 +310,11 @@ public class FeignClientTests { return new HystrixClientFallback(); } + @Bean + public HystrixClientFallbackFactory hystrixClientFallbackFactory() { + return new HystrixClientFallbackFactory(); + } + @Bean FeignFormatterRegistrar feignFormatterRegistrar() { return new FeignFormatterRegistrar() { @@ -584,6 +616,15 @@ public class FeignClientTests { assertEquals("message was wrong", "fallbackfuture", hello.getMessage()); } + @Test + public void testHystrixClientWithFallBackFactory() throws Exception { + Hello hello = hystrixClientWithFallBackFactory.fail(); + assertNotNull("hello was null", hello); + assertNotNull("hello#message was null", hello.getMessage()); + assertTrue("hello#message did not contain the cause (status code) of the fallback invocation", + hello.getMessage().contains("500")); + } + @Test public void namedFeignClientWorks() { assertNotNull("namedHystrixClient was null", this.namedHystrixClient); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java index e224a134..905cc6ac 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClientTests.java @@ -29,6 +29,7 @@ import org.mockito.MockitoAnnotations; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer; +import org.springframework.web.util.DefaultUriTemplateHandler; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.IClientConfig; @@ -107,6 +108,31 @@ public class RibbonLoadBalancerClientTests { assertEquals(server.getPort(), uri.getPort()); } + @Test + public void testReconstructSecureUriWithSpecialCharsPath() { + testReconstructUriWithPath("https", "/foo=|"); + } + + @Test + public void testReconstructUnsecureUriWithSpecialCharsPath() { + testReconstructUriWithPath("http", "/foo=|"); + } + + private void testReconstructUriWithPath(String scheme, String path) { + RibbonServer server = getRibbonServer(); + IClientConfig config = mock(IClientConfig.class); + when(config.get(CommonClientConfigKey.IsSecure)).thenReturn(true); + when(clientFactory.getClientConfig(server.getServiceId())).thenReturn(config); + + RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server); + ServiceInstance serviceInstance = client.choose(server.getServiceId()); + + URI expanded = new DefaultUriTemplateHandler() + .expand(scheme + "://" + server.getServiceId() + path); + URI reconstructed = client.reconstructURI(serviceInstance, expanded); + assertEquals(expanded.getPath(), reconstructed.getPath()); + } + @Test @SneakyThrows public void testReconstructUriWithSecureClientConfig() { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonUtilsTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonUtilsTests.java index 53b1bd8d..92548e7f 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonUtilsTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonUtilsTests.java @@ -17,9 +17,6 @@ package org.springframework.cloud.netflix.ribbon; -import static org.hamcrest.Matchers.is; -import static org.springframework.cloud.netflix.ribbon.RibbonUtils.*; - import java.net.URI; import java.net.URISyntaxException; import java.util.Map; @@ -31,6 +28,10 @@ import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.loadbalancer.Server; +import static org.hamcrest.Matchers.is; +import static org.springframework.cloud.netflix.ribbon.RibbonUtils.isSecure; +import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToHttpsIfNeeded; + /** * @author Spencer Gibb * @author Jacques-Etienne Beaudet diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java index a3f6f6ab..4d211dc0 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/ProxyRequestHelperTests.java @@ -259,6 +259,28 @@ public class ProxyRequestHelperTests { assertThat(queryString, is("?wsdl")); } + @Test + public void getQueryStringEncoded() { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("foo", "weird#chars"); + + String queryString = new ProxyRequestHelper().getQueryString(params); + + assertThat(queryString, is("?foo=weird%23chars")); + } + + @Test + public void getQueryParamNameWithColon() { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("foo:bar", "baz"); + params.add("foobar", "bam"); + params.add("foo\fbar", "bat"); // form feed is the colon replacement char + + String queryString = new ProxyRequestHelper().getQueryString(params); + + assertThat(queryString, is("?foo:bar=baz&foobar=bam&foo%0Cbar=bat")); + } + @Test public void buildZuulRequestURIWithUTF8() throws Exception { String encodedURI = "/resource/esp%C3%A9cial-char"; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java index 7275e36d..faf5871e 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/SimpleHostRoutingFilterTests.java @@ -16,6 +16,11 @@ package org.springframework.cloud.netflix.zuul.filters.route; +import java.io.ByteArrayInputStream; + +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpRequest; +import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.junit.After; import org.junit.Test; @@ -26,6 +31,7 @@ import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.util.LinkedMultiValueMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -80,6 +86,18 @@ public class SimpleHostRoutingFilterTests { assertEquals(20, connMgr.getDefaultMaxPerRoute()); } + @Test + public void deleteRequestBuiltWithBody() { + setupContext(); + InputStreamEntity inputStreamEntity = new InputStreamEntity(new ByteArrayInputStream(new byte[]{1})); + HttpRequest httpRequest = getFilter().buildHttpRequest("DELETE", "uri", inputStreamEntity, + new LinkedMultiValueMap(), new LinkedMultiValueMap()); + + assertTrue(httpRequest instanceof HttpEntityEnclosingRequest); + HttpEntityEnclosingRequest httpEntityEnclosingRequest = (HttpEntityEnclosingRequest) httpRequest; + assertTrue(httpEntityEnclosingRequest.getEntity() != null); + } + private void setupContext() { this.context.register(PropertyPlaceholderAutoConfiguration.class, TestConfiguration.class); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java index 826a574b..7b1a4d8b 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/restclient/RestClientRibbonCommandIntegrationTests.java @@ -186,6 +186,17 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase { assertEquals("/query?foo=weird#chars", result.getBody()); } + @Test + public void simpleHostRouteWithColonParamNames() { + this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/"); + this.endpoint.reset(); + ResponseEntity result = new TestRestTemplate().exchange( + "http://localhost:" + this.port + "/self/colonquery?foo:bar={foobar0}&foobar={foobar1}", HttpMethod.GET, + new HttpEntity<>((Void) null), String.class, "baz", "bam"); + assertEquals(HttpStatus.OK, result.getStatusCode()); + assertEquals("/colonquery?foo:bar=baz&foobar=bam", result.getBody()); + } + @Test public void simpleHostRouteWithContentType() { this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/"); @@ -295,10 +306,15 @@ public class RestClientRibbonCommandIntegrationTests extends ZuulProxyTestBase { } @RequestMapping("/query") - public String addQuery(HttpServletRequest request, @RequestParam String foo) { + public String query(HttpServletRequest request, @RequestParam String foo) { return request.getRequestURI() + "?foo=" + foo; } + @RequestMapping("/colonquery") + public String colonQuery(HttpServletRequest request, @RequestParam(name = "foo:bar") String foobar0, @RequestParam(name = "foobar") String foobar1) { + return request.getRequestURI() + "?foo:bar=" + foobar0 + "&foobar=" + foobar1; + } + @RequestMapping("/matrix/{name}/{another}") public String matrix(@PathVariable("name") String name, @MatrixVariable(value = "p", pathVar = "name") int p, diff --git a/spring-cloud-netflix-dependencies/pom.xml b/spring-cloud-netflix-dependencies/pom.xml index 99e3cb2d..c40c98c6 100644 --- a/spring-cloud-netflix-dependencies/pom.xml +++ b/spring-cloud-netflix-dependencies/pom.xml @@ -5,11 +5,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT spring-cloud-netflix-dependencies - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT pom spring-cloud-netflix-dependencies Spring Cloud Netflix Dependencies @@ -17,10 +17,10 @@ 0.7.4 1.4.11 9.3.1 - 1.5.5 + 1.5.6 2.2.0 0.10.1 - 1.2.2 + 1.3.0 1.1.10 1.7 1.0.0 @@ -270,6 +270,10 @@ jackson-dataformat-xml com.fasterxml.jackson.dataformat + + * + com.amazonaws + diff --git a/spring-cloud-netflix-eureka-client/pom.xml b/spring-cloud-netflix-eureka-client/pom.xml index 6d6bdcd9..a30f0e75 100644 --- a/spring-cloud-netflix-eureka-client/pom.xml +++ b/spring-cloud-netflix-eureka-client/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-eureka-client diff --git a/spring-cloud-netflix-eureka-server/pom.xml b/spring-cloud-netflix-eureka-server/pom.xml index 74b41f12..4c1d4190 100644 --- a/spring-cloud-netflix-eureka-server/pom.xml +++ b/spring-cloud-netflix-eureka-server/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-eureka-server diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java index 0e2f81ed..e5fae161 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerConfiguration.java @@ -71,7 +71,7 @@ import com.sun.jersey.spi.container.servlet.ServletContainer; @Configuration @Import(EurekaServerInitializerConfiguration.class) @EnableDiscoveryClient -@EnableConfigurationProperties(EurekaDashboardProperties.class) +@EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class }) @PropertySource("classpath:/eureka/server.properties") public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { /** @@ -92,17 +92,9 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { @Autowired private EurekaClient eurekaClient; - /* - * Setting expectedNumberOfRenewsPerMin to non-zero to ensure that even an isolated - * server can adjust its eviction policy to the number of registrations (when it's - * zero, even a successful registration won't reset the rate threshold in - * InstanceRegistry.register()). - */ - @Value("${eureka.server.expectedNumberOfRenewsPerMin:1}") - private int expectedNumberOfRenewsPerMin; + @Autowired + private InstanceRegistryProperties instanceRegistryProperties; - @Value("${eureka.server.defaultOpenForTrafficCount:1}") - private int defaultOpenForTrafficCount; public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson(); @Bean @@ -166,8 +158,9 @@ public class EurekaServerConfiguration extends WebMvcConfigurerAdapter { ServerCodecs serverCodecs) { this.eurekaClient.getApplications(); // force initialization return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, - serverCodecs, this.eurekaClient, this.expectedNumberOfRenewsPerMin, - this.defaultOpenForTrafficCount); + serverCodecs, this.eurekaClient, + this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(), + this.instanceRegistryProperties.getDefaultOpenForTrafficCount()); } @Bean diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java index 05c5676f..3faaef7e 100644 --- a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistry.java @@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.eureka.server; import java.util.List; +import com.netflix.eureka.lease.Lease; import org.springframework.beans.BeansException; import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent; import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent; @@ -35,6 +36,7 @@ import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl; import com.netflix.eureka.resources.ServerCodecs; import lombok.extern.apachecommons.CommonsLog; +import org.springframework.context.ApplicationEvent; /** * @author Spencer Gibb @@ -64,8 +66,8 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl /** * If * {@link PeerAwareInstanceRegistryImpl#openForTraffic(ApplicationInfoManager, int)} - * is called with a zero * argument, it means that leases are not automatically * - * cancelled if the instance * hasn't sent any renewals recently. This happens for a + * is called with a zero argument, it means that leases are not automatically + * cancelled if the instance hasn't sent any renewals recently. This happens for a * standalone server. It seems like a bad default, so we set it to the smallest * non-zero value we can, so that any instances that subsequently register can bump up * the threshold. @@ -78,37 +80,27 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl @Override public void register(InstanceInfo info, int leaseDuration, boolean isReplication) { - if (log.isDebugEnabled()) { - log.debug("register " + info.getAppName() + ", vip " + info.getVIPAddress() - + ", leaseDuration " + leaseDuration + ", isReplication " - + isReplication); - } - // TODO: what to publish from info (whole object?) - this.ctxt.publishEvent(new EurekaInstanceRegisteredEvent(this, info, - leaseDuration, isReplication)); - + handleRegistration(info, leaseDuration, isReplication); super.register(info, leaseDuration, isReplication); } @Override - public boolean cancel(String appName, String serverId, boolean isReplication) { - if (log.isDebugEnabled()) { - log.debug("cancel " + appName + " serverId " + serverId + ", isReplication {}" - + isReplication); - } - this.ctxt.publishEvent( - new EurekaInstanceCanceledEvent(this, appName, serverId, isReplication)); + public void register(final InstanceInfo info, final boolean isReplication) { + handleRegistration(info, resolveInstanceLeaseDuration(info), isReplication); + super.register(info, isReplication); + } + @Override + public boolean cancel(String appName, String serverId, boolean isReplication) { + handleCancelation(appName, serverId, isReplication); return super.cancel(appName, serverId, isReplication); } @Override public boolean renew(final String appName, final String serverId, boolean isReplication) { - if (log.isDebugEnabled()) { - log.debug("renew " + appName + " serverId " + serverId + ", isReplication {}" - + isReplication); - } + log("renew " + appName + " serverId " + serverId + ", isReplication {}" + + isReplication); List applications = getSortedApplications(); for (Application input : applications) { if (input.getName().equals(appName)) { @@ -119,11 +111,49 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl break; } } - this.ctxt.publishEvent(new EurekaInstanceRenewedEvent(this, appName, - serverId, instance, isReplication)); + publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId, + instance, isReplication)); break; } } return super.renew(appName, serverId, isReplication); } + + @Override + protected boolean internalCancel(String appName, String id, boolean isReplication) { + handleCancelation(appName, id, isReplication); + return super.internalCancel(appName, id, isReplication); + } + + private void handleCancelation(String appName, String id, boolean isReplication) { + log("cancel " + appName + ", serverId " + id + ", isReplication " + isReplication); + publishEvent(new EurekaInstanceCanceledEvent(this, appName, id, isReplication)); + } + + private void handleRegistration(InstanceInfo info, int leaseDuration, + boolean isReplication) { + log("register " + info.getAppName() + ", vip " + info.getVIPAddress() + + ", leaseDuration " + leaseDuration + ", isReplication " + + isReplication); + publishEvent(new EurekaInstanceRegisteredEvent(this, info, leaseDuration, + isReplication)); + } + + private void log(String message) { + if (log.isDebugEnabled()) { + log.debug(message); + } + } + + private void publishEvent(ApplicationEvent applicationEvent) { + this.ctxt.publishEvent(applicationEvent); + } + + private int resolveInstanceLeaseDuration(final InstanceInfo info) { + int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; + if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { + leaseDuration = info.getLeaseInfo().getDurationInSecs(); + } + return leaseDuration; + } } diff --git a/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryProperties.java b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryProperties.java new file mode 100644 index 00000000..87a0ebac --- /dev/null +++ b/spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryProperties.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2016 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.eureka.server; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import static org.springframework.cloud.netflix.eureka.server.InstanceRegistryProperties.PREFIX; + +/** + * @author Spencer Gibb + */ +@ConfigurationProperties(PREFIX) +public class InstanceRegistryProperties { + + public static final String PREFIX = "eureka.instance.registry"; + + + /* Default number of expected renews per minute, defaults to 1. + * Setting expectedNumberOfRenewsPerMin to non-zero to ensure that even an isolated + * server can adjust its eviction policy to the number of registrations (when it's + * zero, even a successful registration won't reset the rate threshold in + * InstanceRegistry.register()). + */ + @Value("${eureka.server.expectedNumberOfRenewsPerMin:1}") // for backwards compatibility + private int expectedNumberOfRenewsPerMin = 1; + + /** Value used in determining when leases are cancelled, default to 1 for standalone. + * Should be set to 0 for peer replicated eurekas */ + @Value("${eureka.server.defaultOpenForTrafficCount:1}") // for backwards compatibility + private int defaultOpenForTrafficCount = 1; + + public int getExpectedNumberOfRenewsPerMin() { + return expectedNumberOfRenewsPerMin; + } + + public void setExpectedNumberOfRenewsPerMin(int expectedNumberOfRenewsPerMin) { + this.expectedNumberOfRenewsPerMin = expectedNumberOfRenewsPerMin; + } + + public int getDefaultOpenForTrafficCount() { + return defaultOpenForTrafficCount; + } + + public void setDefaultOpenForTrafficCount(int defaultOpenForTrafficCount) { + this.defaultOpenForTrafficCount = defaultOpenForTrafficCount; + } +} diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryTest.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryTest.java new file mode 100644 index 00000000..9689e779 --- /dev/null +++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/InstanceRegistryTest.java @@ -0,0 +1,186 @@ +package org.springframework.cloud.netflix.eureka.server; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import com.netflix.discovery.shared.Application; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.netflix.eureka.server.InstanceRegistryTest.TestApplication; +import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent; +import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent; +import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRenewedEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.LeaseInfo; +import com.netflix.eureka.registry.PeerAwareInstanceRegistry; + +/** + * @author Bartlomiej Slota + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = TestApplication.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + value = {"spring.application.name=eureka", "logging.level.org.springframework." + + "cloud.netflix.eureka.server.InstanceRegistry=DEBUG"}) +public class InstanceRegistryTest { + + private final List applicationEvents = new LinkedList<>(); + private static final String APP_NAME = "MY-APP-NAME"; + private static final String HOST_NAME = "my-host-name"; + + @SpyBean(PeerAwareInstanceRegistry.class) + private InstanceRegistry instanceRegistry; + + @MockBean + private ApplicationListener + instanceRegisteredEventListenerMock; + + @MockBean + private ApplicationListener + instanceCanceledEventListenerMock; + + @MockBean + private ApplicationListener instanceRenewedEventListener; + + @Before + public void setup() { + applicationEvents.clear(); + Answer applicationListenerAnswer = prepareListenerMockAnswer(); + doAnswer(applicationListenerAnswer).when(instanceRegisteredEventListenerMock) + .onApplicationEvent(isA(EurekaInstanceRegisteredEvent.class)); + doAnswer(applicationListenerAnswer).when(instanceCanceledEventListenerMock) + .onApplicationEvent(isA(EurekaInstanceCanceledEvent.class)); + doAnswer(applicationListenerAnswer).when(instanceRenewedEventListener) + .onApplicationEvent(isA(EurekaInstanceRenewedEvent.class)); + } + + + @Test + public void testRegister() throws Exception { + // creating instance info + final LeaseInfo leaseInfo = getLeaseInfo(); + final InstanceInfo instanceInfo = getInstanceInfo(leaseInfo); + // calling tested method + instanceRegistry.register(instanceInfo, false); + // event of proper type is registered + assertEquals(1, applicationEvents.size()); + assertTrue(applicationEvents.get(0) instanceof EurekaInstanceRegisteredEvent); + // event details are correct + final EurekaInstanceRegisteredEvent registeredEvent = + (EurekaInstanceRegisteredEvent) (applicationEvents.get(0)); + assertEquals(instanceInfo, registeredEvent.getInstanceInfo()); + assertEquals(leaseInfo.getDurationInSecs(), registeredEvent.getLeaseDuration()); + assertEquals(instanceRegistry, registeredEvent.getSource()); + assertFalse(registeredEvent.isReplication()); + } + + @Test + public void testDefaultLeaseDurationRegisterEvent() throws Exception { + // creating instance info + final InstanceInfo instanceInfo = getInstanceInfo(null); + // calling tested method + instanceRegistry.register(instanceInfo, false); + // instance info duration is set to default + final EurekaInstanceRegisteredEvent registeredEvent = + (EurekaInstanceRegisteredEvent) (applicationEvents.get(0)); + assertEquals(LeaseInfo.DEFAULT_LEASE_DURATION, + registeredEvent.getLeaseDuration()); + } + + @Test + public void testInternalCancel() throws Exception { + // calling tested method + instanceRegistry.internalCancel(APP_NAME, HOST_NAME, false); + // event of proper type is registered + assertEquals(1, applicationEvents.size()); + assertTrue(applicationEvents.get(0) instanceof EurekaInstanceCanceledEvent); + // event details are correct + final EurekaInstanceCanceledEvent registeredEvent = + (EurekaInstanceCanceledEvent) (applicationEvents.get(0)); + assertEquals(APP_NAME, registeredEvent.getAppName()); + assertEquals(HOST_NAME, registeredEvent.getServerId()); + assertEquals(instanceRegistry, registeredEvent.getSource()); + assertFalse(registeredEvent.isReplication()); + } + + @Test + public void testRenew() throws Exception { + // creating application list + final LeaseInfo leaseInfo = getLeaseInfo(); + final InstanceInfo instanceInfo = getInstanceInfo(leaseInfo); + final List instances = new ArrayList<>(); + instances.add(instanceInfo); + final Application application = new Application(APP_NAME, instances); + final List applications = new ArrayList<>(); + applications.add(application); + // stubbing applications list + doReturn(applications).when(instanceRegistry).getSortedApplications(); + // calling tested method + instanceRegistry.renew(APP_NAME, HOST_NAME, false); + // event of proper type is registered + assertEquals(1, applicationEvents.size()); + assertTrue(applicationEvents.get(0) instanceof EurekaInstanceRenewedEvent); + // event details are correct + final EurekaInstanceRenewedEvent registeredEvent = (EurekaInstanceRenewedEvent) + (applicationEvents.get(0)); + assertEquals(APP_NAME, registeredEvent.getAppName()); + assertEquals(HOST_NAME, registeredEvent.getServerId()); + assertEquals(instanceRegistry, registeredEvent.getSource()); + assertEquals(instanceInfo, registeredEvent.getInstanceInfo()); + assertFalse(registeredEvent.isReplication()); + } + + @Configuration + @EnableAutoConfiguration + @EnableEurekaServer + protected static class TestApplication { + public static void main(String[] args) { + new SpringApplicationBuilder(TestApplication.class).run(args); + } + } + + private LeaseInfo getLeaseInfo() { + LeaseInfo.Builder leaseBuilder = LeaseInfo.Builder.newBuilder(); + leaseBuilder.setRenewalIntervalInSecs(10); + leaseBuilder.setDurationInSecs(15); + return leaseBuilder.build(); + } + + private InstanceInfo getInstanceInfo(LeaseInfo leaseInfo) { + InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(); + builder.setAppName(APP_NAME); + builder.setHostName(HOST_NAME); + builder.setPort(8008); + builder.setLeaseInfo(leaseInfo); + return builder.build(); + } + + private Answer prepareListenerMockAnswer() { + return new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return applicationEvents + .add((ApplicationEvent) invocation.getArguments()[0]); + } + }; + } +} \ No newline at end of file diff --git a/spring-cloud-netflix-hystrix-amqp/pom.xml b/spring-cloud-netflix-hystrix-amqp/pom.xml index b3dcd037..34442fc8 100644 --- a/spring-cloud-netflix-hystrix-amqp/pom.xml +++ b/spring-cloud-netflix-hystrix-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-hystrix-amqp diff --git a/spring-cloud-netflix-hystrix-dashboard/pom.xml b/spring-cloud-netflix-hystrix-dashboard/pom.xml index 64d5f719..e0a0528b 100644 --- a/spring-cloud-netflix-hystrix-dashboard/pom.xml +++ b/spring-cloud-netflix-hystrix-dashboard/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. diff --git a/spring-cloud-netflix-hystrix-dashboard/src/main/java/org/springframework/cloud/netflix/hystrix/dashboard/HystrixDashboardConfiguration.java b/spring-cloud-netflix-hystrix-dashboard/src/main/java/org/springframework/cloud/netflix/hystrix/dashboard/HystrixDashboardConfiguration.java index 38569b63..4d05b504 100644 --- a/spring-cloud-netflix-hystrix-dashboard/src/main/java/org/springframework/cloud/netflix/hystrix/dashboard/HystrixDashboardConfiguration.java +++ b/spring-cloud-netflix-hystrix-dashboard/src/main/java/org/springframework/cloud/netflix/hystrix/dashboard/HystrixDashboardConfiguration.java @@ -40,8 +40,8 @@ import org.apache.http.params.HttpParams; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration; -import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -53,7 +53,6 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; * @author Dave Syer * @author Roy Clarkson */ -@SuppressWarnings("deprecation") @Configuration @EnableConfigurationProperties(HystrixDashboardProperties.class) public class HystrixDashboardConfiguration { @@ -263,6 +262,7 @@ public class HystrixDashboardConfiguration { } } + @SuppressWarnings("deprecation") private static class ProxyConnectionManager { private final static PoolingClientConnectionManager threadSafeConnectionManager = new PoolingClientConnectionManager(); diff --git a/spring-cloud-netflix-hystrix-stream/pom.xml b/spring-cloud-netflix-hystrix-stream/pom.xml index dd4c8f51..1e5715a2 100644 --- a/spring-cloud-netflix-hystrix-stream/pom.xml +++ b/spring-cloud-netflix-hystrix-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-hystrix-stream diff --git a/spring-cloud-netflix-sidecar/pom.xml b/spring-cloud-netflix-sidecar/pom.xml index c39d4005..e919210c 100644 --- a/spring-cloud-netflix-sidecar/pom.xml +++ b/spring-cloud-netflix-sidecar/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-sidecar diff --git a/spring-cloud-netflix-spectator/pom.xml b/spring-cloud-netflix-spectator/pom.xml index 55715ac6..2d193b84 100644 --- a/spring-cloud-netflix-spectator/pom.xml +++ b/spring-cloud-netflix-spectator/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-spectator diff --git a/spring-cloud-netflix-turbine-stream/pom.xml b/spring-cloud-netflix-turbine-stream/pom.xml index 7fdeef11..9328f905 100644 --- a/spring-cloud-netflix-turbine-stream/pom.xml +++ b/spring-cloud-netflix-turbine-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-turbine-stream diff --git a/spring-cloud-netflix-turbine/pom.xml b/spring-cloud-netflix-turbine/pom.xml index f4ba02b1..4af74b88 100644 --- a/spring-cloud-netflix-turbine/pom.xml +++ b/spring-cloud-netflix-turbine/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-netflix-turbine diff --git a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java index 0c58625c..8c23c86f 100644 --- a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java +++ b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java @@ -19,8 +19,8 @@ package org.springframework.cloud.netflix.turbine; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.actuator.HasFeatures; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; diff --git a/spring-cloud-starter-archaius/pom.xml b/spring-cloud-starter-archaius/pom.xml index 29c05ed3..fa0b7fa9 100644 --- a/spring-cloud-starter-archaius/pom.xml +++ b/spring-cloud-starter-archaius/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-archaius diff --git a/spring-cloud-starter-atlas/pom.xml b/spring-cloud-starter-atlas/pom.xml index ef07240e..1add805c 100644 --- a/spring-cloud-starter-atlas/pom.xml +++ b/spring-cloud-starter-atlas/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-atlas diff --git a/spring-cloud-starter-eureka-server/pom.xml b/spring-cloud-starter-eureka-server/pom.xml index 281d6840..a7b34608 100644 --- a/spring-cloud-starter-eureka-server/pom.xml +++ b/spring-cloud-starter-eureka-server/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-eureka-server diff --git a/spring-cloud-starter-eureka/pom.xml b/spring-cloud-starter-eureka/pom.xml index 5db347ea..feb3560a 100644 --- a/spring-cloud-starter-eureka/pom.xml +++ b/spring-cloud-starter-eureka/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-eureka diff --git a/spring-cloud-starter-feign/pom.xml b/spring-cloud-starter-feign/pom.xml index cfced5bb..ba5bb310 100644 --- a/spring-cloud-starter-feign/pom.xml +++ b/spring-cloud-starter-feign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-feign diff --git a/spring-cloud-starter-hystrix-dashboard/pom.xml b/spring-cloud-starter-hystrix-dashboard/pom.xml index ef126ed6..87069df1 100644 --- a/spring-cloud-starter-hystrix-dashboard/pom.xml +++ b/spring-cloud-starter-hystrix-dashboard/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-hystrix-dashboard diff --git a/spring-cloud-starter-hystrix/pom.xml b/spring-cloud-starter-hystrix/pom.xml index 220ad2e3..7c90a6f1 100644 --- a/spring-cloud-starter-hystrix/pom.xml +++ b/spring-cloud-starter-hystrix/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-hystrix diff --git a/spring-cloud-starter-ribbon/pom.xml b/spring-cloud-starter-ribbon/pom.xml index 533583c0..4a31be7c 100644 --- a/spring-cloud-starter-ribbon/pom.xml +++ b/spring-cloud-starter-ribbon/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-ribbon diff --git a/spring-cloud-starter-spectator/pom.xml b/spring-cloud-starter-spectator/pom.xml index ee66b0eb..5ed2f4ec 100644 --- a/spring-cloud-starter-spectator/pom.xml +++ b/spring-cloud-starter-spectator/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-spectator diff --git a/spring-cloud-starter-turbine-amqp/pom.xml b/spring-cloud-starter-turbine-amqp/pom.xml index 15e5a043..0820167f 100644 --- a/spring-cloud-starter-turbine-amqp/pom.xml +++ b/spring-cloud-starter-turbine-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-turbine-amqp diff --git a/spring-cloud-starter-turbine-stream/pom.xml b/spring-cloud-starter-turbine-stream/pom.xml index ef45f833..1a7edbcb 100644 --- a/spring-cloud-starter-turbine-stream/pom.xml +++ b/spring-cloud-starter-turbine-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-turbine-stream diff --git a/spring-cloud-starter-turbine/pom.xml b/spring-cloud-starter-turbine/pom.xml index 3639be91..5f6ed33a 100644 --- a/spring-cloud-starter-turbine/pom.xml +++ b/spring-cloud-starter-turbine/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-turbine diff --git a/spring-cloud-starter-zuul/pom.xml b/spring-cloud-starter-zuul/pom.xml index 8b664ac8..fb397c1d 100644 --- a/spring-cloud-starter-zuul/pom.xml +++ b/spring-cloud-starter-zuul/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-netflix - 1.2.1.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-zuul