Add basic cache metrics support for Infinispan

Closes gh-3066
pull/3077/merge
Stephane Nicoll 10 years ago
parent 28d2955d03
commit 18d7634947

@ -97,6 +97,11 @@
<artifactId>hibernate-validator</artifactId> <artifactId>hibernate-validator</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-spring4</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId> <artifactId>spring-messaging</artifactId>

@ -21,6 +21,7 @@ import javax.cache.Caching;
import com.hazelcast.core.IMap; import com.hazelcast.core.IMap;
import com.hazelcast.spring.cache.HazelcastCache; import com.hazelcast.spring.cache.HazelcastCache;
import net.sf.ehcache.Ehcache; import net.sf.ehcache.Ehcache;
import org.infinispan.spring.provider.SpringCache;
import org.springframework.boot.actuate.cache.CacheStatistics; import org.springframework.boot.actuate.cache.CacheStatistics;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider; import org.springframework.boot.actuate.cache.CacheStatisticsProvider;
@ -29,7 +30,8 @@ import org.springframework.boot.actuate.cache.DefaultCacheStatistics;
import org.springframework.boot.actuate.cache.EhCacheStatisticsProvider; import org.springframework.boot.actuate.cache.EhCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.GuavaCacheStatisticsProvider; import org.springframework.boot.actuate.cache.GuavaCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.HazelcastCacheStatisticsProvider; import org.springframework.boot.actuate.cache.HazelcastCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.JCacheStatisticsProvider; import org.springframework.boot.actuate.cache.InfinispanCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.JCacheCacheStatisticsProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
@ -61,8 +63,8 @@ public class CacheStatisticsAutoConfiguration {
static class JCacheCacheStatisticsProviderConfiguration { static class JCacheCacheStatisticsProviderConfiguration {
@Bean @Bean
public JCacheStatisticsProvider jCacheStatisticsProvider() { public JCacheCacheStatisticsProvider jCacheCacheStatisticsProvider() {
return new JCacheStatisticsProvider(); return new JCacheCacheStatisticsProvider();
} }
} }
@ -88,6 +90,17 @@ public class CacheStatisticsAutoConfiguration {
} }
} }
@Configuration
@ConditionalOnClass({ SpringCache.class })
static class InfinispanCacheStatisticsProviderConfiguration {
@Bean
public InfinispanCacheStatisticsProvider infinispanCacheStatisticsProvider() {
return new InfinispanCacheStatisticsProvider();
}
}
@Configuration @Configuration
@ConditionalOnClass(com.google.common.cache.Cache.class) @ConditionalOnClass(com.google.common.cache.Cache.class)
static class GuavaCacheStatisticsConfiguration { static class GuavaCacheStatisticsConfiguration {

@ -18,42 +18,44 @@ package org.springframework.boot.actuate.cache;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.management.AttributeNotFoundException; import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException; import javax.management.InstanceNotFoundException;
import javax.management.MBeanException; import javax.management.MBeanException;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException; import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.ReflectionException; import javax.management.ReflectionException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.jcache.JCacheCache;
/** /**
* {@link CacheStatisticsProvider} implementation for a JSR-107 compliant cache. * Base {@link CacheStatisticsProvider} implementation that uses JMX to
* retrieve the cache statistics.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.3.0 * @since 1.3.0
*/ */
public class JCacheStatisticsProvider implements CacheStatisticsProvider<JCacheCache> { public abstract class AbstractJmxCacheStatisticsProvider<C extends Cache>
implements CacheStatisticsProvider<C> {
private static final Logger logger = LoggerFactory private static final Logger logger = LoggerFactory
.getLogger(JCacheStatisticsProvider.class); .getLogger(AbstractJmxCacheStatisticsProvider.class);
private MBeanServer mBeanServer; private MBeanServer mBeanServer;
private Map<JCacheCache, ObjectName> caches = new ConcurrentHashMap<JCacheCache, ObjectName>(); private Map<String, ObjectNameWrapper> caches =
new ConcurrentHashMap<String, ObjectNameWrapper>();
@Override @Override
public CacheStatistics getCacheStatistics(CacheManager cacheManager, JCacheCache cache) { public CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache) {
try { try {
ObjectName objectName = getObjectName(cache); ObjectName objectName = internalGetObjectName(cache);
if (objectName != null) { if (objectName != null) {
return getCacheStatistics(objectName); return getCacheStatistics(objectName);
} }
@ -64,35 +66,33 @@ public class JCacheStatisticsProvider implements CacheStatisticsProvider<JCacheC
} }
} }
protected CacheStatistics getCacheStatistics(ObjectName objectName) { /**
MBeanServer mBeanServer = getMBeanServer(); * Return the {@link ObjectName} of the MBean that is managing the specified cache or
DefaultCacheStatistics statistics = new DefaultCacheStatistics(); * {@code null} if none is found.
Float hitPercentage = getAttribute(mBeanServer, objectName, "CacheHitPercentage", * @param cache the cache to handle
Float.class); * @return the object name of the cache statistics MBean
Float missPercentage = getAttribute(mBeanServer, objectName, */
"CacheMissPercentage", Float.class); protected abstract ObjectName getObjectName(C cache) throws MalformedObjectNameException;
if ((hitPercentage != null && missPercentage != null)
&& (hitPercentage > 0 || missPercentage > 0)) { /**
statistics.setHitRatio(hitPercentage / (double) 100); * Return the current {@link CacheStatistics} snapshot from the MBean identified by the
statistics.setMissRatio(missPercentage / (double) 100); * specified {@link ObjectName}.
} * @param objectName the object name of the cache statistics MBean
return statistics; * @return the current cache statistics
} */
protected abstract CacheStatistics getCacheStatistics(ObjectName objectName);
protected ObjectName getObjectName(JCacheCache cache) private ObjectName internalGetObjectName(C cache)
throws MalformedObjectNameException { throws MalformedObjectNameException {
if (this.caches.containsKey(cache)) { String cacheName = cache.getName();
return this.caches.get(cache); ObjectNameWrapper value = this.caches.get(cacheName);
} if (value != null) {
Set<ObjectInstance> instances = getMBeanServer().queryMBeans( return value.objectName;
new ObjectName("javax.cache:type=CacheStatistics,Cache="
+ cache.getName() + ",*"), null);
if (instances.size() == 1) {
ObjectName objectName = instances.iterator().next().getObjectName();
this.caches.put(cache, objectName);
return objectName;
} }
return null; // None or more than one ObjectName objectName = getObjectName(cache);
this.caches.put(cacheName, new ObjectNameWrapper(objectName));
return objectName;
} }
protected MBeanServer getMBeanServer() { protected MBeanServer getMBeanServer() {
@ -102,18 +102,17 @@ public class JCacheStatisticsProvider implements CacheStatisticsProvider<JCacheC
return this.mBeanServer; return this.mBeanServer;
} }
private static <T> T getAttribute(MBeanServer mBeanServer, ObjectName objectName, protected <T> T getAttribute(ObjectName objectName, String attributeName, Class<T> type) {
String attributeName, Class<T> type) {
try { try {
Object attribute = mBeanServer.getAttribute(objectName, attributeName); Object attribute = getMBeanServer().getAttribute(objectName, attributeName);
return type.cast(attribute); return type.cast(attribute);
} }
catch (MBeanException ex) { catch (MBeanException ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
catch (AttributeNotFoundException ex) { catch (AttributeNotFoundException ex) {
throw new IllegalStateException("Unexpected: jcache provider does not " throw new IllegalStateException("Unexpected: MBean with name '" + objectName + "' " +
+ "expose standard attribute " + attributeName, ex); "does not expose attribute with name " + attributeName, ex);
} }
catch (ReflectionException ex) { catch (ReflectionException ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
@ -124,4 +123,13 @@ public class JCacheStatisticsProvider implements CacheStatisticsProvider<JCacheC
} }
} }
private static class ObjectNameWrapper {
private final ObjectName objectName;
public ObjectNameWrapper(ObjectName objectName) {
this.objectName = objectName;
}
}
} }

@ -0,0 +1,63 @@
/*
* 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.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.infinispan.spring.provider.SpringCache;
/**
* {@link CacheStatisticsProvider} implementation for Infinispan.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
public class InfinispanCacheStatisticsProvider extends AbstractJmxCacheStatisticsProvider<SpringCache> {
@Override
protected ObjectName getObjectName(SpringCache cache) throws MalformedObjectNameException {
Set<ObjectInstance> instances = getMBeanServer().queryMBeans(
new ObjectName("org.infinispan:component=Statistics,type=Cache," +
"name=\"" + cache.getName() + "(local)\",*"), null);
if (instances.size() == 1) {
return instances.iterator().next().getObjectName();
}
return null; // None or more than one
}
protected CacheStatistics getCacheStatistics(ObjectName objectName) {
DefaultCacheStatistics statistics = new DefaultCacheStatistics();
Integer size = getAttribute(objectName, "numberOfEntries",
Integer.class);
if (size != null) {
statistics.setSize((long) size);
if (size > 0) { // Let's initialize the stats if we have some data
Double hitRatio = getAttribute(objectName, "hitRatio",
Double.class);
if ((hitRatio != null)) {
statistics.setHitRatio(hitRatio);
statistics.setMissRatio(1 - hitRatio);
}
}
}
return statistics;
}
}

@ -0,0 +1,61 @@
/*
* 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.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.springframework.cache.jcache.JCacheCache;
/**
* {@link CacheStatisticsProvider} implementation for a JSR-107 compliant cache.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
public class JCacheCacheStatisticsProvider extends AbstractJmxCacheStatisticsProvider<JCacheCache> {
protected ObjectName getObjectName(JCacheCache cache)
throws MalformedObjectNameException {
Set<ObjectInstance> instances = getMBeanServer().queryMBeans(
new ObjectName("javax.cache:type=CacheStatistics,Cache="
+ cache.getName() + ",*"), null);
if (instances.size() == 1) {
return instances.iterator().next().getObjectName();
}
return null; // None or more than one
}
protected CacheStatistics getCacheStatistics(ObjectName objectName) {
DefaultCacheStatistics statistics = new DefaultCacheStatistics();
Float hitPercentage = getAttribute(objectName, "CacheHitPercentage",
Float.class);
Float missPercentage = getAttribute(objectName,
"CacheMissPercentage", Float.class);
if ((hitPercentage != null && missPercentage != null)
&& (hitPercentage > 0 || missPercentage > 0)) {
statistics.setHitRatio(hitPercentage / (double) 100);
statistics.setMissRatio(missPercentage / (double) 100);
}
return statistics;
}
}

@ -17,14 +17,24 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import javax.cache.Caching; import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration; import javax.cache.configuration.MutableConfiguration;
import com.google.common.cache.CacheBuilder;
import com.hazelcast.cache.HazelcastCachingProvider;
import com.hazelcast.config.Config;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.spring.cache.HazelcastCacheManager;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.spring.provider.SpringEmbeddedCacheManager;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.CacheStatisticsAutoConfiguration;
import org.springframework.boot.actuate.cache.CacheStatistics; import org.springframework.boot.actuate.cache.CacheStatistics;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider; import org.springframework.boot.actuate.cache.CacheStatisticsProvider;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
@ -42,14 +52,6 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import com.google.common.cache.CacheBuilder;
import com.hazelcast.cache.HazelcastCachingProvider;
import com.hazelcast.config.Config;
import com.hazelcast.config.XmlConfigBuilder;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.spring.cache.HazelcastCacheManager;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -76,7 +78,7 @@ public class CacheStatisticsAutoConfigurationTests {
public void basicJCacheCacheStatistics() { public void basicJCacheCacheStatistics() {
load(JCacheCacheConfig.class); load(JCacheCacheConfig.class);
CacheStatisticsProvider provider = this.context.getBean( CacheStatisticsProvider provider = this.context.getBean(
"jCacheStatisticsProvider", CacheStatisticsProvider.class); "jCacheCacheStatisticsProvider", CacheStatisticsProvider.class);
doTestCoreStatistics(provider, false); doTestCoreStatistics(provider, false);
} }
@ -96,6 +98,14 @@ public class CacheStatisticsAutoConfigurationTests {
doTestCoreStatistics(provider, true); doTestCoreStatistics(provider, true);
} }
@Test
public void basicInfinispanCacheStatistics() {
load(InfinispanConfig.class);
CacheStatisticsProvider provider = this.context.getBean(
"infinispanCacheStatisticsProvider", CacheStatisticsProvider.class);
doTestCoreStatistics(provider, true);
}
@Test @Test
public void basicGuavaCacheStatistics() { public void basicGuavaCacheStatistics() {
load(GuavaConfig.class); load(GuavaConfig.class);
@ -246,6 +256,28 @@ public class CacheStatisticsAutoConfigurationTests {
} }
@Configuration
static class InfinispanConfig {
@Bean
public SpringEmbeddedCacheManager cacheManager() throws IOException {
return new SpringEmbeddedCacheManager(embeddedCacheManager());
}
@Bean
public EmbeddedCacheManager embeddedCacheManager() throws IOException {
Resource resource = new ClassPathResource("cache/test-infinispan.xml");
InputStream in = resource.getInputStream();
try {
return new DefaultCacheManager(in);
}
finally {
in.close();
}
}
}
@Configuration @Configuration
static class GuavaConfig { static class GuavaConfig {

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns="urn:infinispan:config:7.2">
<cache-container default-cache="default">
<local-cache name="books" statistics="true"/>
<local-cache name="players" statistics="true"/>
</cache-container>
</infinispan>

@ -2,7 +2,7 @@
<infinispan xmlns="urn:infinispan:config:7.2"> <infinispan xmlns="urn:infinispan:config:7.2">
<cache-container default-cache="default"> <cache-container default-cache="default">
<local-cache name="countries"> <local-cache name="countries" statistics="true">
<eviction max-entries="200"/> <eviction max-entries="200"/>
<expiration lifespan="600000"/> <expiration lifespan="600000"/>
</local-cache> </local-cache>

Loading…
Cancel
Save