From 0451f16accfff2e57a29dc37c618a5ae917d4f26 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 16 Apr 2015 12:47:32 -0700 Subject: [PATCH] Refactor CacheStatisticsProvider to use generics Update CacheStatisticsProvider to use a generic to indicate the type of cache supported. Also extract individual CacheStatisticsProvider implementations and made statistics an auto-configuration. See gh-2633 --- .../CacheStatisticsAutoConfiguration.java | 132 ++++++++++++ .../PublicMetricsAutoConfiguration.java | 7 +- .../cache/CacheStatisticsProvider.java | 8 +- .../cache/CacheStatisticsProviders.java | 62 ------ ...CacheStatisticsProvidersConfiguration.java | 204 ------------------ .../ConcurrentMapCacheStatisticsProvider.java | 39 ++++ .../cache/EhCacheStatisticsProvider.java | 46 ++++ .../cache/GuavaCacheStatisticsProvider.java | 44 ++++ .../HazelcastCacheStatisticsProvider.java | 46 ++++ ...der.java => JCacheStatisticsProvider.java} | 47 ++-- .../actuate/endpoint/CachePublicMetrics.java | 120 ++++++----- .../main/resources/META-INF/spring.factories | 3 +- ...acheStatisticsAutoConfigurationTests.java} | 44 ++-- .../PublicMetricsAutoConfigurationTests.java | 8 +- 14 files changed, 428 insertions(+), 382 deletions(-) create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfiguration.java delete mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProviders.java delete mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvidersConfiguration.java create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/GuavaCacheStatisticsProvider.java create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java rename spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/{JCacheCacheStatisticsProvider.java => JCacheStatisticsProvider.java} (74%) rename spring-boot-actuator/src/test/java/org/springframework/boot/actuate/{cache/CacheStatisticsProviderTests.java => autoconfigure/CacheStatisticsAutoConfigurationTests.java} (91%) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfiguration.java new file mode 100644 index 0000000000..bca4d6c8fa --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfiguration.java @@ -0,0 +1,132 @@ +/* + * Copyright 2012-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.boot.actuate.autoconfigure; + +import javax.cache.Caching; + +import net.sf.ehcache.Ehcache; + +import org.springframework.boot.actuate.cache.CacheStatistics; +import org.springframework.boot.actuate.cache.CacheStatisticsProvider; +import org.springframework.boot.actuate.cache.ConcurrentMapCacheStatisticsProvider; +import org.springframework.boot.actuate.cache.DefaultCacheStatistics; +import org.springframework.boot.actuate.cache.EhCacheStatisticsProvider; +import org.springframework.boot.actuate.cache.GuavaCacheStatisticsProvider; +import org.springframework.boot.actuate.cache.HazelcastCacheStatisticsProvider; +import org.springframework.boot.actuate.cache.JCacheStatisticsProvider; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.concurrent.ConcurrentMapCache; +import org.springframework.cache.jcache.JCacheCache; +import org.springframework.cache.support.NoOpCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.hazelcast.core.IMap; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for {@link CacheStatisticsProvider} + * beans. + * + * @author Stephane Nicoll + * @author Phillip Webb + * @since 1.3.0 + */ +@Configuration +@ConditionalOnBean(CacheManager.class) +public class CacheStatisticsAutoConfiguration { + + @Configuration + @ConditionalOnClass({ Caching.class, JCacheCache.class }) + static class JCacheCacheStatisticsProviderConfiguration { + + @Bean + public JCacheStatisticsProvider jCacheStatisticsProvider() { + return new JCacheStatisticsProvider(); + } + + } + + @Configuration + @ConditionalOnClass(Ehcache.class) + static class EhCacheCacheStatisticsProviderConfiguration { + + @Bean + public EhCacheStatisticsProvider ehCacheCacheStatisticsProvider() { + return new EhCacheStatisticsProvider(); + } + + } + + @Configuration + @ConditionalOnClass(IMap.class) + static class HazelcastCacheStatisticsConfiguration { + + @Bean + public HazelcastCacheStatisticsProvider hazelcastCacheStatisticsProvider() { + return new HazelcastCacheStatisticsProvider(); + } + } + + @Configuration + @ConditionalOnClass(com.google.common.cache.Cache.class) + static class GuavaCacheStatisticsConfiguration { + + @Bean + public GuavaCacheStatisticsProvider guavaCacheStatisticsProvider() { + return new GuavaCacheStatisticsProvider(); + } + + } + + @Configuration + @ConditionalOnClass(ConcurrentMapCache.class) + static class ConcurrentMapCacheStatisticsConfiguration { + + @Bean + public ConcurrentMapCacheStatisticsProvider concurrentMapCacheStatisticsProvider() { + return new ConcurrentMapCacheStatisticsProvider(); + } + + } + + @Configuration + @ConditionalOnClass(NoOpCacheManager.class) + static class NoOpCacheStatisticsConfiguration { + + private static final CacheStatistics NO_OP_STATS = new DefaultCacheStatistics(); + + @Bean + public CacheStatisticsProvider noOpCacheStatisticsProvider() { + return new CacheStatisticsProvider() { + @Override + public CacheStatistics getCacheStatistics(CacheManager cacheManager, + Cache cache) { + if (cacheManager instanceof NoOpCacheManager) { + return NO_OP_STATS; + } + return null; + } + }; + } + + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java index 182e2e8aed..ae5f0adf2e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java @@ -22,7 +22,6 @@ import javax.sql.DataSource; import org.apache.catalina.startup.Tomcat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.cache.CacheStatisticsProvider; -import org.springframework.boot.actuate.cache.CacheStatisticsProvidersConfiguration; import org.springframework.boot.actuate.endpoint.CachePublicMetrics; import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics; import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; @@ -45,7 +44,6 @@ import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetada import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link PublicMetrics}. @@ -56,9 +54,9 @@ import org.springframework.context.annotation.Import; * @since 1.2.0 */ @Configuration -@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class, - MetricRepositoryAutoConfiguration.class }) @AutoConfigureBefore(EndpointAutoConfiguration.class) +@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class, + MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class }) public class PublicMetricsAutoConfiguration { @Autowired(required = false) @@ -110,7 +108,6 @@ public class PublicMetricsAutoConfiguration { @Configuration @ConditionalOnClass(CacheManager.class) @ConditionalOnBean(CacheManager.class) - @Import(CacheStatisticsProvidersConfiguration.class) static class CacheStatisticsConfiguration { @Bean diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java index efa876c80c..d27e44dc8e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.java @@ -22,18 +22,20 @@ import org.springframework.cache.CacheManager; /** * Provide a {@link CacheStatistics} based on a {@link Cache}. * + * @param The {@link Cache} type * @author Stephane Nicoll + * @author Phillip Webb * @since 1.3.0 */ -public interface CacheStatisticsProvider { +public interface CacheStatisticsProvider { /** * Return the current {@link CacheStatistics} snapshot for the specified {@link Cache} * or {@code null} if the given cache could not be handled. - * @param cache the cache to handle * @param cacheManager the {@link CacheManager} handling this cache + * @param cache the cache to handle * @return the current cache statistics or {@code null} */ - CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager); + CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProviders.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProviders.java deleted file mode 100644 index cf85f6931d..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProviders.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-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.boot.actuate.cache; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; - -/** - * A {@link CacheStatisticsProvider} that returns the first {@link CacheStatistics} that - * can be retrieved by one of its delegates. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public class CacheStatisticsProviders implements CacheStatisticsProvider { - - private final List providers; - - /** - * Create a {@link CacheStatisticsProviders} instance with the collection of delegates - * to use. - * @param providers the cache statistics providers - */ - public CacheStatisticsProviders( - Collection providers) { - this.providers = (providers == null ? Collections - . emptyList() - : new ArrayList(providers)); - } - - @Override - public CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager) { - for (CacheStatisticsProvider provider : this.providers) { - CacheStatistics cacheStatistics = provider.getCacheStatistics(cache, - cacheManager); - if (cacheStatistics != null) { - return cacheStatistics; - } - } - return null; - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvidersConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvidersConfiguration.java deleted file mode 100644 index ee70e1edfc..0000000000 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvidersConfiguration.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2012-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.boot.actuate.cache; - -import java.util.concurrent.ConcurrentHashMap; - -import javax.cache.Caching; - -import net.sf.ehcache.Ehcache; -import net.sf.ehcache.statistics.StatisticsGateway; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.concurrent.ConcurrentMapCache; -import org.springframework.cache.ehcache.EhCacheCache; -import org.springframework.cache.guava.GuavaCache; -import org.springframework.cache.jcache.JCacheCache; -import org.springframework.cache.support.NoOpCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.google.common.cache.CacheStats; -import com.hazelcast.core.IMap; -import com.hazelcast.monitor.LocalMapStats; -import com.hazelcast.spring.cache.HazelcastCache; - -/** - * Register the {@link CacheStatisticsProvider} instances for the supported cache - * libraries. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@Configuration -@ConditionalOnBean(CacheManager.class) -public class CacheStatisticsProvidersConfiguration { - - @Configuration - @ConditionalOnClass({ Caching.class, JCacheCache.class }) - static class JCacheCacheStatisticsProviderConfiguration { - - @Bean - public CacheStatisticsProvider jCacheCacheStatisticsProvider() { - return new JCacheCacheStatisticsProvider(); - } - } - - @Configuration - @ConditionalOnClass(Ehcache.class) - static class EhCacheCacheStatisticsProviderConfiguration { - - @Bean - public CacheStatisticsProvider ehCacheCacheStatisticsProvider() { - return new CacheStatisticsProvider() { - @Override - public CacheStatistics getCacheStatistics(Cache cache, - CacheManager cacheManager) { - if (cache instanceof EhCacheCache) { - return getEhCacheStatistics((Ehcache) cache.getNativeCache()); - } - return null; - } - }; - } - - private CacheStatistics getEhCacheStatistics(Ehcache cache) { - StatisticsGateway statistics = cache.getStatistics(); - DefaultCacheStatistics stats = new DefaultCacheStatistics(); - stats.setSize(statistics.getSize()); - Double hitRatio = statistics.cacheHitRatio(); - if (!hitRatio.isNaN()) { - stats.setHitRatio(hitRatio); - stats.setMissRatio(1 - hitRatio); - } - return stats; - } - } - - @Configuration - @ConditionalOnClass(IMap.class) - static class HazelcastCacheStatisticsConfiguration { - - @Bean - public CacheStatisticsProvider hazelcastCacheStatisticsProvider() { - return new CacheStatisticsProvider() { - @Override - public CacheStatistics getCacheStatistics(Cache cache, - CacheManager cacheManager) { - if (cache instanceof HazelcastCache) { - return getHazelcastStatistics((IMap) cache.getNativeCache()); - } - return null; - } - }; - } - - private CacheStatistics getHazelcastStatistics(IMap cache) { - DefaultCacheStatistics stats = new DefaultCacheStatistics(); - LocalMapStats mapStats = cache.getLocalMapStats(); - stats.setSize(mapStats.getOwnedEntryCount()); - stats.setGetCacheCounts(mapStats.getHits(), mapStats.getGetOperationCount() - - mapStats.getHits()); - return stats; - } - } - - @Configuration - @ConditionalOnClass(com.google.common.cache.Cache.class) - static class GuavaCacheStatisticsConfiguration { - - @Bean - public CacheStatisticsProvider guavaCacheStatisticsProvider() { - return new CacheStatisticsProvider() { - @SuppressWarnings("unchecked") - @Override - public CacheStatistics getCacheStatistics(Cache cache, - CacheManager cacheManager) { - if (cache instanceof GuavaCache) { - return getGuavaStatistics((com.google.common.cache.Cache) cache - .getNativeCache()); - } - return null; - } - }; - } - - private CacheStatistics getGuavaStatistics( - com.google.common.cache.Cache cache) { - DefaultCacheStatistics stats = new DefaultCacheStatistics(); - stats.setSize(cache.size()); - CacheStats guavaStats = cache.stats(); - if (guavaStats.requestCount() > 0) { - stats.setHitRatio(guavaStats.hitRate()); - stats.setMissRatio(guavaStats.missRate()); - } - return stats; - } - - } - - @Configuration - @ConditionalOnClass(ConcurrentMapCache.class) - static class ConcurrentMapCacheStatisticsConfiguration { - - @Bean - public CacheStatisticsProvider concurrentMapCacheStatisticsProvider() { - return new CacheStatisticsProvider() { - @Override - public CacheStatistics getCacheStatistics(Cache cache, - CacheManager cacheManager) { - if (cache instanceof ConcurrentMapCache) { - return getConcurrentMapStatistics((ConcurrentHashMap) cache - .getNativeCache()); - } - return null; - } - }; - } - - private CacheStatistics getConcurrentMapStatistics(ConcurrentHashMap map) { - DefaultCacheStatistics stats = new DefaultCacheStatistics(); - stats.setSize((long) map.size()); - return stats; - } - } - - @Configuration - @ConditionalOnClass(NoOpCacheManager.class) - static class NoOpCacheStatisticsConfiguration { - - private static final CacheStatistics NO_OP_STATS = new DefaultCacheStatistics(); - - @Bean - public CacheStatisticsProvider noOpCacheStatisticsProvider() { - return new CacheStatisticsProvider() { - @Override - public CacheStatistics getCacheStatistics(Cache cache, - CacheManager cacheManager) { - if (cacheManager instanceof NoOpCacheManager) { - return NO_OP_STATS; - } - return null; - } - }; - } - } - -} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java new file mode 100644 index 0000000000..c48aeb25dd --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/ConcurrentMapCacheStatisticsProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-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.boot.actuate.cache; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.concurrent.ConcurrentMapCache; + +/** + * {@link CacheStatisticsProvider} implementation for {@link ConcurrentMapCache}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +public class ConcurrentMapCacheStatisticsProvider implements + CacheStatisticsProvider { + + @Override + public CacheStatistics getCacheStatistics(CacheManager cacheManager, + ConcurrentMapCache cache) { + DefaultCacheStatistics statistics = new DefaultCacheStatistics(); + statistics.setSize((long) cache.getNativeCache().size()); + return statistics; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java new file mode 100644 index 0000000000..8d72528233 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/EhCacheStatisticsProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-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.boot.actuate.cache; + +import net.sf.ehcache.statistics.StatisticsGateway; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.ehcache.EhCacheCache; + +/** + * {@link CacheStatisticsProvider} implementation for {@link EhCacheCache}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +public class EhCacheStatisticsProvider implements CacheStatisticsProvider { + + @Override + public CacheStatistics getCacheStatistics(CacheManager cacheManager, + EhCacheCache cache) { + DefaultCacheStatistics statistics = new DefaultCacheStatistics(); + StatisticsGateway ehCacheStatistics = cache.getNativeCache().getStatistics(); + statistics.setSize(ehCacheStatistics.getSize()); + Double hitRatio = ehCacheStatistics.cacheHitRatio(); + if (!hitRatio.isNaN()) { + statistics.setHitRatio(hitRatio); + statistics.setMissRatio(1 - hitRatio); + } + return statistics; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/GuavaCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/GuavaCacheStatisticsProvider.java new file mode 100644 index 0000000000..fb427bb812 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/GuavaCacheStatisticsProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-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.boot.actuate.cache; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.guava.GuavaCache; + +import com.google.common.cache.CacheStats; + +/** + * {@link CacheStatisticsProvider} implementation for {@link GuavaCache}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +public class GuavaCacheStatisticsProvider implements CacheStatisticsProvider { + + @Override + public CacheStatistics getCacheStatistics(CacheManager cacheManager, GuavaCache cache) { + DefaultCacheStatistics statistics = new DefaultCacheStatistics(); + statistics.setSize(cache.getNativeCache().size()); + CacheStats guavaStats = cache.getNativeCache().stats(); + if (guavaStats.requestCount() > 0) { + statistics.setHitRatio(guavaStats.hitRate()); + statistics.setMissRatio(guavaStats.missRate()); + } + return statistics; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java new file mode 100644 index 0000000000..746dd3367a --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/HazelcastCacheStatisticsProvider.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-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.boot.actuate.cache; + +import org.springframework.cache.CacheManager; + +import com.hazelcast.core.IMap; +import com.hazelcast.monitor.LocalMapStats; +import com.hazelcast.spring.cache.HazelcastCache; + +/** + * {@link CacheStatisticsProvider} implementation for {@link HazelcastCache}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +public class HazelcastCacheStatisticsProvider implements + CacheStatisticsProvider { + + @Override + public CacheStatistics getCacheStatistics(CacheManager cacheManager, + HazelcastCache cache) { + DefaultCacheStatistics statistics = new DefaultCacheStatistics(); + LocalMapStats mapStatistics = ((IMap) cache.getNativeCache()) + .getLocalMapStats(); + statistics.setSize(mapStatistics.getOwnedEntryCount()); + statistics.setGetCacheCounts(mapStatistics.getHits(), + mapStatistics.getGetOperationCount() - mapStatistics.getHits()); + return statistics; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheCacheStatisticsProvider.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheStatisticsProvider.java similarity index 74% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheCacheStatisticsProvider.java rename to spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheStatisticsProvider.java index 5cedca14b2..9cd74ee436 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheCacheStatisticsProvider.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/JCacheStatisticsProvider.java @@ -32,34 +32,26 @@ import javax.management.ReflectionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.jcache.JCacheCache; /** - * {@link CacheStatisticsProvider} implementation for a JSR-107 compliant cache. + * {@link CacheStatisticsProvider} implementation for {@link JCacheCache}. * * @author Stephane Nicoll * @since 1.3.0 */ -class JCacheCacheStatisticsProvider implements CacheStatisticsProvider { +public class JCacheStatisticsProvider implements CacheStatisticsProvider { private static final Logger logger = LoggerFactory - .getLogger(JCacheCacheStatisticsProvider.class); + .getLogger(JCacheStatisticsProvider.class); private MBeanServer mBeanServer; private Map caches = new ConcurrentHashMap(); @Override - public CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager) { - if (cache instanceof JCacheCache) { - return getCacheStatistics((JCacheCache) cache); - } - return null; - } - - protected CacheStatistics getCacheStatistics(JCacheCache cache) { + public CacheStatistics getCacheStatistics(CacheManager cacheManager, JCacheCache cache) { try { ObjectName objectName = getObjectName(cache); if (objectName != null) { @@ -67,24 +59,24 @@ class JCacheCacheStatisticsProvider implements CacheStatisticsProvider { } return null; } - catch (MalformedObjectNameException e) { - throw new IllegalStateException(e); + catch (MalformedObjectNameException ex) { + throw new IllegalStateException(ex); } } protected CacheStatistics getCacheStatistics(ObjectName objectName) { MBeanServer mBeanServer = getMBeanServer(); - DefaultCacheStatistics stats = new DefaultCacheStatistics(); + DefaultCacheStatistics statistics = new DefaultCacheStatistics(); Float hitPercentage = getAttribute(mBeanServer, objectName, "CacheHitPercentage", Float.class); Float missPercentage = getAttribute(mBeanServer, objectName, "CacheMissPercentage", Float.class); if ((hitPercentage != null && missPercentage != null) && (hitPercentage > 0 || missPercentage > 0)) { - stats.setHitRatio(hitPercentage / (double) 100); - stats.setMissRatio(missPercentage / (double) 100); + statistics.setHitRatio(hitPercentage / (double) 100); + statistics.setMissRatio(missPercentage / (double) 100); } - return stats; + return statistics; } protected ObjectName getObjectName(JCacheCache cache) @@ -92,7 +84,6 @@ class JCacheCacheStatisticsProvider implements CacheStatisticsProvider { if (this.caches.containsKey(cache)) { return this.caches.get(cache); } - Set instances = getMBeanServer().queryMBeans( new ObjectName("javax.cache:type=CacheStatistics,Cache=" + cache.getName() + ",*"), null); @@ -117,20 +108,20 @@ class JCacheCacheStatisticsProvider implements CacheStatisticsProvider { Object attribute = mBeanServer.getAttribute(objectName, attributeName); return type.cast(attribute); } - catch (MBeanException e) { - throw new IllegalStateException(e); + catch (MBeanException ex) { + throw new IllegalStateException(ex); } - catch (AttributeNotFoundException e) { + catch (AttributeNotFoundException ex) { throw new IllegalStateException("Unexpected: jcache provider does not " - + "expose standard attribute " + attributeName, e); + + "expose standard attribute " + attributeName, ex); } - catch (InstanceNotFoundException e) { - logger.warn("Cache statistics are no longer available", e); + catch (ReflectionException ex) { + throw new IllegalStateException(ex); } - catch (ReflectionException e) { - throw new IllegalStateException(e); + catch (InstanceNotFoundException ex) { + logger.warn("Cache statistics are no longer available", ex); + return null; } - return null; } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/CachePublicMetrics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/CachePublicMetrics.java index 70ded1aed8..5524755fbe 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/CachePublicMetrics.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/CachePublicMetrics.java @@ -16,22 +16,20 @@ package org.springframework.boot.actuate.endpoint; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import javax.annotation.PostConstruct; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.cache.CacheStatistics; import org.springframework.boot.actuate.cache.CacheStatisticsProvider; -import org.springframework.boot.actuate.cache.CacheStatisticsProviders; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; +import org.springframework.core.ResolvableType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; /** * A {@link PublicMetrics} implementation that provides cache statistics. @@ -41,82 +39,88 @@ import org.springframework.cache.CacheManager; */ public class CachePublicMetrics implements PublicMetrics { - @Autowired - private Collection providers; - @Autowired private Map cacheManagers; - private CacheStatisticsProvider cacheStatisticsProvider; - - @PostConstruct - public void initialize() { - this.cacheStatisticsProvider = new CacheStatisticsProviders(this.providers); - } + @Autowired + private Collection> statisticsProviders; @Override public Collection> metrics() { Collection> metrics = new HashSet>(); - Map> cacheManagerNamesByCacheName = getCacheManagerNamesByCacheName(); - for (Map.Entry entry : this.cacheManagers.entrySet()) { - String cacheManagerName = entry.getKey(); - CacheManager cacheManager = entry.getValue(); - for (String cacheName : cacheManager.getCacheNames()) { - Cache cache = cacheManager.getCache(cacheName); - CacheStatistics cacheStatistics = this.cacheStatisticsProvider - .getCacheStatistics(cache, cacheManager); - if (cacheStatistics != null) { - String prefix = cleanPrefix(createPrefix( - cacheManagerNamesByCacheName, cacheName, cacheManagerName)); - metrics.addAll(cacheStatistics.toMetrics(prefix)); - } - } + for (Map.Entry> entry : getCacheManagerBeans() + .entrySet()) { + addMetrics(metrics, entry.getKey(), entry.getValue()); } return metrics; } - private Map> getCacheManagerNamesByCacheName() { - Map> cacheManagerNamesByCacheName = new HashMap>(); + private MultiValueMap getCacheManagerBeans() { + MultiValueMap cacheManagerNamesByCacheName = new LinkedMultiValueMap(); for (Map.Entry entry : this.cacheManagers.entrySet()) { for (String cacheName : entry.getValue().getCacheNames()) { - List cacheManagerNames = cacheManagerNamesByCacheName - .get(cacheName); - if (cacheManagerNames == null) { - cacheManagerNames = new ArrayList(); - cacheManagerNamesByCacheName.put(cacheName, cacheManagerNames); - } - cacheManagerNames.add(entry.getKey()); + cacheManagerNamesByCacheName.add(cacheName, + new CacheManagerBean(entry.getKey(), entry.getValue())); } } return cacheManagerNamesByCacheName; } - /** - * Create the prefix to use for the specified cache. The generated prefix should be - * unique among those that will be generated for the given map of cache names - * @param cacheManagerNamesByCacheName a mapping of cache names to the names of the - * cache managers that have a cache with that name - * @param cacheName the name of the cache - * @param cacheManagerName the name of its cache manager - * @return a prefix to use for the specified cache - */ - protected String createPrefix(Map> cacheManagerNamesByCacheName, - String cacheName, String cacheManagerName) { - if (cacheManagerNamesByCacheName.get(cacheName).size() > 1) { - String target = cacheManagerName + "_" + cacheName; - return createPrefixFor(target); - } - else { - return createPrefixFor(cacheName); + private void addMetrics(Collection> metrics, String cacheName, + List cacheManagerBeans) { + for (CacheManagerBean cacheManagerBean : cacheManagerBeans) { + CacheManager cacheManager = cacheManagerBean.getCacheManager(); + Cache cache = cacheManager.getCache(cacheName); + CacheStatistics statistics = getCacheStatistics(cache, cacheManager); + if (statistics != null) { + String prefix = cacheName; + if (cacheManagerBeans.size() > 1) { + prefix = cacheManagerBean.getBeanName() + "_" + prefix; + } + prefix = "cache." + prefix + (prefix.endsWith(".") ? "" : "."); + metrics.addAll(statistics.toMetrics(prefix)); + } } } - protected String createPrefixFor(String name) { - return "cache." + name; + @SuppressWarnings({ "rawtypes", "unchecked" }) + private CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager) { + if (this.statisticsProviders != null) { + for (CacheStatisticsProvider provider : this.statisticsProviders) { + Class cacheType = ResolvableType.forClass( + CacheStatisticsProvider.class, provider.getClass()) + .resolveGeneric(); + if (cacheType.isInstance(cache)) { + CacheStatistics statistics = provider.getCacheStatistics( + cacheManager, cache); + if (statistics != null) { + return statistics; + } + } + } + } + return null; } - private String cleanPrefix(String prefix) { - return (prefix.endsWith(".") ? prefix : prefix + "."); + private static class CacheManagerBean { + + private final String beanName; + + private final CacheManager cacheManager; + + public CacheManagerBean(String beanName, CacheManager cacheManager) { + this.beanName = beanName; + this.cacheManager = cacheManager; + } + + public String getBeanName() { + return this.beanName; + } + + public CacheManager getCacheManager() { + return this.cacheManager; + } + } } diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index 6d4f6a7867..01f62773c1 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -1,5 +1,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.actuate.autoconfigure.AuditAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration,\ @@ -12,4 +13,4 @@ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration \ No newline at end of file +org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cache/CacheStatisticsProviderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfigurationTests.java similarity index 91% rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cache/CacheStatisticsProviderTests.java rename to spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfigurationTests.java index b90e5677db..c3128b72ca 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cache/CacheStatisticsProviderTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CacheStatisticsAutoConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.cache; +package org.springframework.boot.actuate.autoconfigure; import java.io.IOException; import java.util.Arrays; @@ -24,6 +24,9 @@ import javax.cache.configuration.MutableConfiguration; import org.junit.After; import org.junit.Test; +import org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration; +import org.springframework.boot.actuate.cache.CacheStatistics; +import org.springframework.boot.actuate.cache.CacheStatisticsProvider; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; @@ -51,9 +54,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; /** + * Tests for {@link CacheStatisticsAutoConfiguration}. + * * @author Stephane Nicoll */ -public class CacheStatisticsProviderTests { +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class CacheStatisticsAutoConfigurationTests { private AnnotationConfigApplicationContext context; @@ -70,7 +76,7 @@ public class CacheStatisticsProviderTests { public void basicJCacheCacheStatistics() { load(JCacheCacheConfig.class); CacheStatisticsProvider provider = this.context.getBean( - "jCacheCacheStatisticsProvider", CacheStatisticsProvider.class); + "jCacheStatisticsProvider", CacheStatisticsProvider.class); doTestCoreStatistics(provider, false); } @@ -104,12 +110,12 @@ public class CacheStatisticsProviderTests { CacheStatisticsProvider provider = this.context.getBean( "concurrentMapCacheStatisticsProvider", CacheStatisticsProvider.class); Cache books = getCache("books"); - CacheStatistics cacheStatistics = provider.getCacheStatistics(books, - this.cacheManager); + CacheStatistics cacheStatistics = provider.getCacheStatistics(this.cacheManager, + books); assertCoreStatistics(cacheStatistics, 0L, null, null); getOrCreate(books, "a", "b", "b", "a", "a"); - CacheStatistics updatedCacheStatistics = provider.getCacheStatistics(books, - this.cacheManager); + CacheStatistics updatedCacheStatistics = provider.getCacheStatistics( + this.cacheManager, books); assertCoreStatistics(updatedCacheStatistics, 2L, null, null); } @@ -119,24 +125,24 @@ public class CacheStatisticsProviderTests { CacheStatisticsProvider provider = this.context.getBean( "noOpCacheStatisticsProvider", CacheStatisticsProvider.class); Cache books = getCache("books"); - CacheStatistics cacheStatistics = provider.getCacheStatistics(books, - this.cacheManager); + CacheStatistics cacheStatistics = provider.getCacheStatistics(this.cacheManager, + books); assertCoreStatistics(cacheStatistics, null, null, null); getOrCreate(books, "a", "b", "b", "a", "a"); - CacheStatistics updatedCacheStatistics = provider.getCacheStatistics(books, - this.cacheManager); + CacheStatistics updatedCacheStatistics = provider.getCacheStatistics( + this.cacheManager, books); assertCoreStatistics(updatedCacheStatistics, null, null, null); } private void doTestCoreStatistics(CacheStatisticsProvider provider, boolean supportSize) { Cache books = getCache("books"); - CacheStatistics cacheStatistics = provider.getCacheStatistics(books, - this.cacheManager); + CacheStatistics cacheStatistics = provider.getCacheStatistics(this.cacheManager, + books); assertCoreStatistics(cacheStatistics, (supportSize ? 0L : null), null, null); getOrCreate(books, "a", "b", "b", "a", "a", "a"); - CacheStatistics updatedCacheStatistics = provider.getCacheStatistics(books, - this.cacheManager); + CacheStatistics updatedCacheStatistics = provider.getCacheStatistics( + this.cacheManager, books); assertCoreStatistics(updatedCacheStatistics, (supportSize ? 2L : null), 0.66D, 0.33D); } @@ -180,7 +186,7 @@ public class CacheStatisticsProviderTests { if (config.length > 0) { this.context.register(config); } - this.context.register(CacheStatisticsProvidersConfiguration.class); + this.context.register(CacheStatisticsAutoConfiguration.class); this.context.refresh(); this.cacheManager = this.context.getBean(CacheManager.class); } @@ -204,6 +210,7 @@ public class CacheStatisticsProviderTests { cacheManager.createCache("speakers", config); return cacheManager; } + } @Configuration @@ -219,6 +226,7 @@ public class CacheStatisticsProviderTests { return EhCacheManagerUtils.buildCacheManager(new ClassPathResource( "cache/test-ehcache.xml")); } + } @Configuration @@ -235,6 +243,7 @@ public class CacheStatisticsProviderTests { Config cfg = new XmlConfigBuilder(resource.getURL()).build(); return Hazelcast.newHazelcastInstance(cfg); } + } @Configuration @@ -247,6 +256,7 @@ public class CacheStatisticsProviderTests { cacheManager.setCacheNames(Arrays.asList("books", "speakers")); return cacheManager; } + } @Configuration @@ -256,6 +266,7 @@ public class CacheStatisticsProviderTests { public ConcurrentMapCacheManager cacheManager() { return new ConcurrentMapCacheManager("books", "speakers"); } + } @Configuration @@ -265,6 +276,7 @@ public class CacheStatisticsProviderTests { public NoOpCacheManager cacheManager() { return new NoOpCacheManager(); } + } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java index 285d73c478..fd0e35bf40 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java @@ -100,27 +100,22 @@ public class PublicMetricsAutoConfigurationTests { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( RichGaugeReaderConfig.class, MetricRepositoryAutoConfiguration.class, PublicMetricsAutoConfiguration.class); - RichGaugeReader richGaugeReader = context.getBean(RichGaugeReader.class); assertNotNull(richGaugeReader); given(richGaugeReader.findAll()).willReturn( Collections.singletonList(new RichGauge("bar", 3.7d))); - RichGaugeReaderPublicMetrics publicMetrics = context .getBean(RichGaugeReaderPublicMetrics.class); assertNotNull(publicMetrics); - Collection> metrics = publicMetrics.metrics(); assertNotNull(metrics); assertEquals(metrics.size(), 6); - assertHasMetric(metrics, new Metric("bar.val", 3.7d)); assertHasMetric(metrics, new Metric("bar.avg", 3.7d)); assertHasMetric(metrics, new Metric("bar.min", 3.7d)); assertHasMetric(metrics, new Metric("bar.max", 3.7d)); assertHasMetric(metrics, new Metric("bar.alpha", -1.d)); assertHasMetric(metrics, new Metric("bar.count", 1L)); - context.close(); } @@ -247,6 +242,7 @@ public class PublicMetricsAutoConfigurationTests { this.context.register(config); } this.context.register(DataSourcePoolMetadataProvidersConfiguration.class, + CacheStatisticsAutoConfiguration.class, PublicMetricsAutoConfiguration.class); this.context.refresh(); } @@ -352,6 +348,7 @@ public class PublicMetricsAutoConfigurationTests { public CacheManager cacheManager() { return new ConcurrentMapCacheManager("books", "speakers"); } + } @Configuration @@ -368,6 +365,7 @@ public class PublicMetricsAutoConfigurationTests { public CacheManager second() { return new ConcurrentMapCacheManager("users", "speakers"); } + } }