diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml
index 924506687e..caa62a59fe 100644
--- a/spring-boot-actuator/pom.xml
+++ b/spring-boot-actuator/pom.xml
@@ -41,6 +41,21 @@
spring-context
+
+ com.google.guava
+ guava
+ true
+
+
+ com.hazelcast
+ hazelcast
+ true
+
+
+ com.hazelcast
+ hazelcast-spring
+ true
+ com.sun.mailjavax.mail
@@ -51,11 +66,21 @@
metrics-coretrue
+
+ javax.cache
+ cache-api
+ true
+ javax.servletjavax.servlet-apitrue
+
+ net.sf.ehcache
+ ehcache
+ true
+ org.apache.activemqactivemq-broker
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 a70ecd13f2..182e2e8aed 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * 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.
@@ -21,6 +21,9 @@ 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;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
@@ -33,13 +36,16 @@ import org.springframework.boot.actuate.metrics.rich.RichGaugeReader;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
+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}.
@@ -50,7 +56,7 @@ import org.springframework.context.annotation.Configuration;
* @since 1.2.0
*/
@Configuration
-@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
+@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class,
MetricRepositoryAutoConfiguration.class })
@AutoConfigureBefore(EndpointAutoConfiguration.class)
public class PublicMetricsAutoConfiguration {
@@ -75,6 +81,7 @@ public class PublicMetricsAutoConfiguration {
return new RichGaugeReaderPublicMetrics(richGaugeReader);
}
+ @Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
static class DataSourceMetricsConfiguration {
@@ -88,6 +95,7 @@ public class PublicMetricsAutoConfiguration {
}
+ @Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
static class TomcatMetricsConfiguration {
@@ -99,4 +107,19 @@ public class PublicMetricsAutoConfiguration {
}
+ @Configuration
+ @ConditionalOnClass(CacheManager.class)
+ @ConditionalOnBean(CacheManager.class)
+ @Import(CacheStatisticsProvidersConfiguration.class)
+ static class CacheStatisticsConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnBean(CacheStatisticsProvider.class)
+ public CachePublicMetrics cachePublicMetrics() {
+ return new CachePublicMetrics();
+ }
+
+ }
+
}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatistics.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatistics.java
new file mode 100644
index 0000000000..f6cc96e1f9
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatistics.java
@@ -0,0 +1,66 @@
+/*
+ * 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.Collection;
+
+import org.springframework.boot.actuate.metrics.Metric;
+
+/**
+ * Snapshot of the statistics of a given cache. {@code CacheStatistics} instances have a
+ * very short life as it represents the statistics of a cache at one particular point in
+ * time.
+ *
+ * @author Stephane Nicoll
+ * @since 1.3.0
+ */
+public interface CacheStatistics {
+
+ /**
+ * Generate the relevant {@link Metric} instances based on the specified prefix.
+ * @param prefix the metrics prefix (ends with '.')
+ * @return the metrics corresponding to this instance
+ */
+ Collection> toMetrics(String prefix);
+
+ /**
+ * Return the size of the cache or {@code null} if that information is not available.
+ * @return the size of the cache or {@code null}
+ */
+ Long getSize();
+
+ /**
+ * Return the ratio of cache requests which were hits as a value between 0 and 1 where
+ * 0 means that the hit ratio is 0% and 1 means it is 100%.
+ *
+ * This may also return {@code null} if the cache-specifics statistics does not
+ * provide the necessary information
+ * @return the hit ratio or {@code null}
+ */
+ Double getHitRatio();
+
+ /**
+ * Return the ratio of cache requests which were misses as value between 0 and 1 where
+ * 0 means that the miss ratio is 0% and 1 means it is 100%.
+ *
+ * This may also return {@code null} if the cache-specifics statistics does not
+ * provide the necessary information
+ * @return the miss ratio or {@code null}
+ */
+ Double getMissRatio();
+
+}
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
new file mode 100644
index 0000000000..efa876c80c
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvider.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.Cache;
+import org.springframework.cache.CacheManager;
+
+/**
+ * Provide a {@link CacheStatistics} based on a {@link Cache}.
+ *
+ * @author Stephane Nicoll
+ * @since 1.3.0
+ */
+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
+ * @return the current cache statistics or {@code null}
+ */
+ CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager);
+
+}
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
new file mode 100644
index 0000000000..cf85f6931d
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProviders.java
@@ -0,0 +1,62 @@
+/*
+ * 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 extends CacheStatisticsProvider> 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
new file mode 100644
index 0000000000..ee70e1edfc
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cache/CacheStatisticsProvidersConfiguration.java
@@ -0,0 +1,204 @@
+/*
+ * 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