Add basic cache metrics support for Infinispan

Closes gh-3066
This commit is contained in:
Stephane Nicoll 2015-06-01 17:41:59 +02:00
parent 28d2955d03
commit 18d7634947
8 changed files with 247 additions and 56 deletions

View File

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

View File

@ -21,6 +21,7 @@ import javax.cache.Caching;
import com.hazelcast.core.IMap;
import com.hazelcast.spring.cache.HazelcastCache;
import net.sf.ehcache.Ehcache;
import org.infinispan.spring.provider.SpringCache;
import org.springframework.boot.actuate.cache.CacheStatistics;
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.GuavaCacheStatisticsProvider;
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.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
@ -61,8 +63,8 @@ public class CacheStatisticsAutoConfiguration {
static class JCacheCacheStatisticsProviderConfiguration {
@Bean
public JCacheStatisticsProvider jCacheStatisticsProvider() {
return new JCacheStatisticsProvider();
public JCacheCacheStatisticsProvider jCacheCacheStatisticsProvider() {
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
@ConditionalOnClass(com.google.common.cache.Cache.class)
static class GuavaCacheStatisticsConfiguration {

View File

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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -17,14 +17,24 @@
package org.springframework.boot.actuate.autoconfigure;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import javax.cache.Caching;
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.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;
@ -42,14 +52,6 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
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.assertNotNull;
@ -76,7 +78,7 @@ public class CacheStatisticsAutoConfigurationTests {
public void basicJCacheCacheStatistics() {
load(JCacheCacheConfig.class);
CacheStatisticsProvider provider = this.context.getBean(
"jCacheStatisticsProvider", CacheStatisticsProvider.class);
"jCacheCacheStatisticsProvider", CacheStatisticsProvider.class);
doTestCoreStatistics(provider, false);
}
@ -96,6 +98,14 @@ public class CacheStatisticsAutoConfigurationTests {
doTestCoreStatistics(provider, true);
}
@Test
public void basicInfinispanCacheStatistics() {
load(InfinispanConfig.class);
CacheStatisticsProvider provider = this.context.getBean(
"infinispanCacheStatisticsProvider", CacheStatisticsProvider.class);
doTestCoreStatistics(provider, true);
}
@Test
public void basicGuavaCacheStatistics() {
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
static class GuavaConfig {

View File

@ -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>

View File

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