diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/ServoEnvironmentPostProcessor.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/ServoEnvironmentPostProcessor.java new file mode 100644 index 00000000..4a7cceb2 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/ServoEnvironmentPostProcessor.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2015 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.metrics; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.util.ClassUtils; + +/** + * @author Dave Syer + */ +public class ServoEnvironmentPostProcessor implements EnvironmentPostProcessor { + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { + if (ClassUtils.isPresent("com.netflix.servo.monitor.Monitors", null)) { + // Make spring AOP default to target class so RestTemplates can be customized + addDefaultProperty(environment, "spring.aop.proxyTargetClass", "true"); + } + } + + private void addDefaultProperty(ConfigurableEnvironment environment, String name, + String value) { + MutablePropertySources sources = environment.getPropertySources(); + Map map = null; + if (sources.contains("defaultProperties")) { + PropertySource source = sources.get("defaultProperties"); + if (source instanceof MapPropertySource) { + map = ((MapPropertySource) source).getSource(); + } + } + else { + map = new LinkedHashMap<>(); + sources.addLast(new MapPropertySource("defaultProperties", map)); + } + if (map != null) { + map.put(name, value); + } + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasConfiguration.java similarity index 98% rename from spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasAutoConfiguration.java rename to spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasConfiguration.java index 521cf913..df61f7df 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasConfiguration.java @@ -38,7 +38,7 @@ import com.netflix.servo.tag.BasicTagList; @Configuration @ConditionalOnClass(AtlasMetricObserver.class) @Import(ServoMetricsAutoConfiguration.class) -public class AtlasAutoConfiguration { +public class AtlasConfiguration { @Autowired(required = false) private Collection tagProviders; diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java index 3929d194..476e636e 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java @@ -29,6 +29,6 @@ import org.springframework.context.annotation.Import; @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Import(AtlasAutoConfiguration.class) +@Import(AtlasConfiguration.class) public @interface EnableAtlas { } diff --git a/spring-cloud-netflix-core/src/main/resources/META-INF/spring.factories b/spring-cloud-netflix-core/src/main/resources/META-INF/spring.factories index 88bc3a9c..709e2987 100644 --- a/spring-cloud-netflix-core/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-netflix-core/src/main/resources/META-INF/spring.factories @@ -11,3 +11,6 @@ org.springframework.cloud.netflix.metrics.servo.ServoMetricsAutoConfiguration org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\ org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration + +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.springframework.cloud.netflix.metrics.ServoEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java index b3a614e1..bc879307 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java @@ -1,11 +1,11 @@ /* * Copyright 2013-2015 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. @@ -20,6 +20,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; @@ -47,14 +48,16 @@ public class AtlasExporterTests { @Test public void exportMetricsAtPeriodicIntervals() { - MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); + MockRestServiceServer mockServer = MockRestServiceServer + .createServer(this.restTemplate); mockServer.expect(MockRestRequestMatchers.requestTo("atlas/api/v1/publish")) .andExpect(MockRestRequestMatchers.method(HttpMethod.POST)) - .andRespond(MockRestResponseCreators.withSuccess("{\"status\" : \"OK\"}", MediaType.APPLICATION_JSON)); + .andRespond(MockRestResponseCreators.withSuccess("{\"status\" : \"OK\"}", + MediaType.APPLICATION_JSON)); DynamicCounter.increment("counterThatWillBeSentToAtlas"); - atlasExporter.export(); + this.atlasExporter.export(); mockServer.verify(); } @@ -64,6 +67,13 @@ public class AtlasExporterTests { @Configuration @EnableAtlas class AtlasExporterConfiguration { + + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + @Bean public static PropertySourcesPlaceholderConfigurer properties() throws Exception { final PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer(); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactoryTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactoryTests.java index bdf17ea2..d3a4bc83 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactoryTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactoryTests.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; @@ -47,11 +48,11 @@ import org.springframework.web.client.RestTemplate; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; -import lombok.SneakyThrows; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import lombok.SneakyThrows; + /** * @author Spencer Gibb */ @@ -70,12 +71,13 @@ public class RibbonClientHttpRequestFactoryTests { @Test public void requestFactoryIsRibbon() { - assertTrue("wrong RequestFactory type", restTemplate.getRequestFactory() instanceof RibbonClientHttpRequestFactory); + assertTrue("wrong RequestFactory type", this.restTemplate + .getRequestFactory() instanceof RibbonClientHttpRequestFactory); } @Test public void vanillaRequestWorks() { - ResponseEntity response = restTemplate.getForEntity("http://simple/", + ResponseEntity response = this.restTemplate.getForEntity("http://simple/", String.class); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); assertEquals("wrong response body", "hello", response.getBody()); @@ -83,37 +85,41 @@ public class RibbonClientHttpRequestFactoryTests { @Test public void requestWithPathParamWorks() { - ResponseEntity response = restTemplate.getForEntity("http://simple/path/{param}", - String.class, "world"); + ResponseEntity response = this.restTemplate + .getForEntity("http://simple/path/{param}", String.class, "world"); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); assertEquals("wrong response body", "hello world", response.getBody()); } @Test public void requestWithEncodedPathParamWorks() { - ResponseEntity response = restTemplate.getForEntity("http://simple/path/{param}", - String.class, "world & everyone else"); + ResponseEntity response = this.restTemplate.getForEntity( + "http://simple/path/{param}", String.class, "world & everyone else"); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); - assertEquals("wrong response body", "hello world & everyone else", response.getBody()); + assertEquals("wrong response body", "hello world & everyone else", + response.getBody()); } @Test public void requestWithRequestParamWorks() { - ResponseEntity response = restTemplate.getForEntity("http://simple/request?param={param}", String.class, "world"); + ResponseEntity response = this.restTemplate.getForEntity( + "http://simple/request?param={param}", String.class, "world"); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); assertEquals("wrong response body", "hello world", response.getBody()); } @Test public void requestWithPostWorks() { - ResponseEntity response = restTemplate.postForEntity("http://simple/post", "world", String.class); + ResponseEntity response = this.restTemplate + .postForEntity("http://simple/post", "world", String.class); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); assertEquals("wrong response body", "hello world", response.getBody()); } @Test public void requestWithEmptyPostWorks() { - ResponseEntity response = restTemplate.postForEntity("http://simple/emptypost", "", String.class); + ResponseEntity response = this.restTemplate + .postForEntity("http://simple/emptypost", "", String.class); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); assertEquals("wrong response body", "hello empty", response.getBody()); } @@ -122,18 +128,18 @@ public class RibbonClientHttpRequestFactoryTests { @SneakyThrows public void requestWithHeaderWorks() { RequestEntity entity = RequestEntity.get(new URI("http://simple/header")) - .header("X-Param", "world") - .build(); - ResponseEntity response = restTemplate.exchange(entity, String.class); + .header("X-Param", "world").build(); + ResponseEntity response = this.restTemplate.exchange(entity, + String.class); assertEquals("wrong response code", HttpStatus.OK, response.getStatusCode()); assertEquals("wrong response body", "hello world", response.getBody()); } @Test public void invalidHostNameError() { - exceptionRule.expect(ResourceAccessException.class); - exceptionRule.expectMessage("Invalid hostname"); - restTemplate.getForEntity("http://simple_bad", String.class); + this.exceptionRule.expect(ResourceAccessException.class); + this.exceptionRule.expectMessage("Invalid hostname"); + this.restTemplate.getForEntity("http://simple_bad", String.class); } @Configuration @@ -142,6 +148,12 @@ public class RibbonClientHttpRequestFactoryTests { @RibbonClient(value = "simple", configuration = SimpleRibbonClientConfiguration.class) protected static class App { + @LoadBalanced + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } + @RequestMapping("/") public String hi() { return "hello"; @@ -149,17 +161,17 @@ public class RibbonClientHttpRequestFactoryTests { @RequestMapping("/path/{param}") public String hiParam(@PathVariable("param") String param) { - return "hello "+param; + return "hello " + param; } @RequestMapping("/request") public String hiRequest(@RequestParam("param") String param) { - return "hello "+param; + return "hello " + param; } @RequestMapping(value = "/post", method = RequestMethod.POST) public String hiPost(@RequestBody String param) { - return "hello "+param; + return "hello " + param; } @RequestMapping(value = "/emptypost", method = RequestMethod.POST) @@ -169,7 +181,7 @@ public class RibbonClientHttpRequestFactoryTests { @RequestMapping("/header") public String hiHeader(@RequestHeader("X-Param") String param) { - return "hello "+param; + return "hello " + param; } } }