Polish "Add cache actuator endpoint"
This commit improves the initial proposal by providing a by name read operation that returns the detail of a particular cache. It also adds more tests and complete API documentation for the feature. Closes gh-12216
This commit is contained in:
parent
1a57673345
commit
fb8a5a9864
|
@ -0,0 +1,102 @@
|
|||
[[caches]]
|
||||
= Caches (`caches`)
|
||||
|
||||
The `caches` endpoint provides access to the application's caches.
|
||||
|
||||
|
||||
|
||||
[[caches-all]]
|
||||
== Retrieving All Caches
|
||||
|
||||
To retrieve the application's caches, make a `GET` request to `/actuator/caches`, as
|
||||
shown in the following curl-based example:
|
||||
|
||||
include::{snippets}caches/all/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}caches/all/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[caches-all-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's caches. The following table describes
|
||||
the structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}caches/all/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[caches-named]]
|
||||
== Retrieving Caches by Name
|
||||
|
||||
To retrieve a cache by name, make a `GET` request to `/actuator/caches/{name}`,
|
||||
as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}caches/named/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves information about the cache named `cities`. The
|
||||
resulting response is similar to the following:
|
||||
|
||||
include::{snippets}caches/named/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[caches-named-request-structure]]
|
||||
=== Request Structure
|
||||
|
||||
If the requested name is specific enough to identify a single cache, no extra parameter is
|
||||
required. Otherwise, the `cacheManager` must be specified. The following table shows the
|
||||
supported query parameters:
|
||||
|
||||
[cols="2,4"]
|
||||
include::{snippets}caches/named/request-parameters.adoc[]
|
||||
|
||||
|
||||
|
||||
[[caches-named-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the requested cache. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}caches/named/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[caches-evict-all]]
|
||||
== Evict All Caches
|
||||
|
||||
To clear all available caches, make a `DELETE` request to `/actuator/caches` as shown in
|
||||
the following curl-based example:
|
||||
|
||||
include::{snippets}caches/evict-all/curl-request.adoc[]
|
||||
|
||||
|
||||
|
||||
[[caches-evict-named]]
|
||||
== Evict a Cache by Name
|
||||
|
||||
To evict a particular cache, make a `DELETE` request to `/actuator/caches/{name}` as shown
|
||||
in the following curl-based example:
|
||||
|
||||
include::{snippets}caches/evict-named/curl-request.adoc[]
|
||||
|
||||
NOTE: As there are two caches named `countries`, the `cacheManager` has to be provided to
|
||||
specify which `Cache` should be cleared.
|
||||
|
||||
|
||||
|
||||
[[caches-evict-named-request-structure]]
|
||||
=== Request Structure
|
||||
|
||||
If the requested name is specific enough to identify a single cache, no extra parameter is
|
||||
required. Otherwise, the `cacheManager` must be specified. The following table shows the
|
||||
supported query parameters:
|
||||
|
||||
[cols="2,4"]
|
||||
include::{snippets}caches/evict-named/request-parameters.adoc[]
|
|
@ -51,6 +51,7 @@ https://en.wikipedia.org/wiki/ISO_8601[ISO 8601].
|
|||
|
||||
include::endpoints/auditevents.adoc[leveloffset=+1]
|
||||
include::endpoints/beans.adoc[leveloffset=+1]
|
||||
include::endpoints/caches.adoc[leveloffset=+1]
|
||||
include::endpoints/conditions.adoc[leveloffset=+1]
|
||||
include::endpoints/configprops.adoc[leveloffset=+1]
|
||||
include::endpoints/env.adoc[leveloffset=+1]
|
||||
|
|
|
@ -16,8 +16,13 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.cache;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.cache.CachesEndpoint;
|
||||
import org.springframework.boot.actuate.cache.CachesEndpointWebExtension;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
|
||||
|
@ -25,7 +30,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -33,7 +37,8 @@ import org.springframework.context.annotation.Configuration;
|
|||
* {@link EnableAutoConfiguration Auto-configuration} for {@link CachesEndpoint}.
|
||||
*
|
||||
* @author Johannes Edmeier
|
||||
* @since 2.0.0
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(CacheManager.class)
|
||||
|
@ -41,11 +46,20 @@ import org.springframework.context.annotation.Configuration;
|
|||
public class CachesEndpointAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(CacheManager.class)
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public CachesEndpoint cachesEndpoint(ApplicationContext context) {
|
||||
return new CachesEndpoint(context);
|
||||
public CachesEndpoint cachesEndpoint(
|
||||
ObjectProvider<Map<String, CacheManager>> cacheManagers) {
|
||||
return new CachesEndpoint(cacheManagers.getIfAvailable(LinkedHashMap::new));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(CachesEndpoint.class)
|
||||
public CachesEndpointWebExtension cachesEndpointWebExtension(
|
||||
CachesEndpoint cachesEndpoint) {
|
||||
return new CachesEndpointWebExtension(cachesEndpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
|
@ -32,23 +32,31 @@ import static org.mockito.Mockito.mock;
|
|||
* Tests for {@link CachesEndpointAutoConfiguration}.
|
||||
*
|
||||
* @author Johannes Edmeier
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CachesEndpointAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(
|
||||
AutoConfigurations.of(CachesEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(CacheConfiguration.class);
|
||||
AutoConfigurations.of(CachesEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void runShouldHaveEndpointBean() {
|
||||
this.contextRunner
|
||||
.run((context) -> assertThat(context).hasSingleBean(CachesEndpoint.class));
|
||||
this.contextRunner.withUserConfiguration(CacheConfiguration.class)
|
||||
.run((context) ->
|
||||
assertThat(context).hasSingleBean(CachesEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runWithoutCacheManagerShouldHaveEndpointBean() {
|
||||
this.contextRunner.run((context) ->
|
||||
assertThat(context).hasSingleBean(CachesEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpointBean() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.caches.enabled:false")
|
||||
.withUserConfiguration(CacheConfiguration.class)
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(CachesEndpoint.class));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.endpoint.web.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.cache.CachesEndpoint;
|
||||
import org.springframework.boot.actuate.cache.CachesEndpointWebExtension;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.restdocs.request.ParameterDescriptor;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link CachesEndpoint}
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CachesEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
|
||||
|
||||
private static final List<FieldDescriptor> levelFields = Arrays.asList(
|
||||
fieldWithPath("name").description("Cache name."),
|
||||
fieldWithPath("cacheManager").description("Cache manager name."),
|
||||
fieldWithPath("target").description(
|
||||
"Fully qualified name of the native cache."));
|
||||
|
||||
private static final List<ParameterDescriptor> requestParameters = Collections.singletonList(
|
||||
parameterWithName("cacheManager")
|
||||
.description("Name of the cacheManager to qualify the cache. May be "
|
||||
+ "omitted if the cache name is unique.")
|
||||
.optional());
|
||||
|
||||
@Test
|
||||
public void allCaches() throws Exception {
|
||||
this.mockMvc.perform(get("/actuator/caches")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("caches/all", responseFields(
|
||||
fieldWithPath("cacheManagers")
|
||||
.description("Cache managers keyed by id."),
|
||||
fieldWithPath("cacheManagers.*")
|
||||
.description("Caches in the application context keyed by "
|
||||
+ "name."))
|
||||
.andWithPrefix("cacheManagers.*.*.", fieldWithPath("target")
|
||||
.description(
|
||||
"Fully qualified name of the native cache."))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedCache() throws Exception {
|
||||
this.mockMvc.perform(get("/actuator/caches/cities")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("caches/named",
|
||||
requestParameters(requestParameters),
|
||||
responseFields(levelFields)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictAllCaches() throws Exception {
|
||||
this.mockMvc.perform(delete("/actuator/caches")).andExpect(status().isNoContent())
|
||||
.andDo(MockMvcRestDocumentation.document("caches/evict-all"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictNamedCache() throws Exception {
|
||||
this.mockMvc.perform(
|
||||
delete("/actuator/caches/countries?cacheManager=anotherCacheManager"))
|
||||
.andExpect(status().isNoContent()).andDo(
|
||||
MockMvcRestDocumentation.document("caches/evict-named",
|
||||
requestParameters(requestParameters)));
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public CachesEndpoint endpoint() {
|
||||
Map<String, CacheManager> cacheManagers = new HashMap<>();
|
||||
cacheManagers.put("cacheManager", new ConcurrentMapCacheManager(
|
||||
"countries", "cities"));
|
||||
cacheManagers.put("anotherCacheManager", new ConcurrentMapCacheManager(
|
||||
"countries"));
|
||||
return new CachesEndpoint(cacheManagers);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CachesEndpointWebExtension endpointWebExtension() {
|
||||
return new CachesEndpointWebExtension(endpoint());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
|
@ -17,103 +17,202 @@
|
|||
package org.springframework.boot.actuate.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* {@link Endpoint} to expose cache operations.
|
||||
* {@link Endpoint} to expose available {@link Cache caches}.
|
||||
*
|
||||
* @author Johannes Edmeuer
|
||||
* @since 2.0.2
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@Endpoint(id = "caches")
|
||||
public class CachesEndpoint {
|
||||
private final ApplicationContext context;
|
||||
|
||||
public CachesEndpoint(ApplicationContext context) {
|
||||
this.context = context;
|
||||
private final Map<String, CacheManager> cacheManagers;
|
||||
|
||||
/**
|
||||
* Create a new endpoint with the {@link CacheManager} instances to use.
|
||||
* @param cacheManagers the cache managers to use, indexed by name
|
||||
*/
|
||||
public CachesEndpoint(Map<String, CacheManager> cacheManagers) {
|
||||
this.cacheManagers = new LinkedHashMap<>(cacheManagers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link CachesReport} of all available {@link Cache caches}.
|
||||
* @return a caches reports
|
||||
*/
|
||||
@ReadOperation
|
||||
public CachesDescriptor caches() {
|
||||
List<CacheDescriptor> caches = new ArrayList<>();
|
||||
this.context.getBeansOfType(CacheManager.class)
|
||||
.forEach((name, cacheManager) -> caches.addAll(
|
||||
getCacheDescriptors(name, cacheManager.getCacheNames())));
|
||||
return new CachesDescriptor(caches);
|
||||
public CachesReport caches() {
|
||||
Map<String, Map<String, CacheDescriptor>> descriptors = new LinkedHashMap<>();
|
||||
getCacheEntries((name) -> true, (cacheManager) -> true).forEach((entry) -> {
|
||||
Map<String, CacheDescriptor> cmDescriptors = descriptors.computeIfAbsent(
|
||||
entry.getCacheManager(), (key) -> new LinkedHashMap<>());
|
||||
String cache = entry.getName();
|
||||
cmDescriptors.put(cache, new CacheDescriptor(entry.getTarget()));
|
||||
});
|
||||
return new CachesReport(descriptors);
|
||||
}
|
||||
|
||||
private Collection<? extends CacheDescriptor> getCacheDescriptors(String cacheManager,
|
||||
Collection<String> cacheNames) {
|
||||
return cacheNames.stream().map(cacheName -> new CacheDescriptor(cacheName, cacheManager)).collect(toList());
|
||||
/**
|
||||
* Return a {@link CacheDescriptor} for the specified cache.
|
||||
* @param cache then name of the cache
|
||||
* @param cacheManager the name of the cacheManager (can be {@code null}
|
||||
* @return the descriptor of the cache or {@code null} if no such cache exists
|
||||
* @throws NonUniqueCacheException if more than one cache with that name exist and no
|
||||
* {@code cacheManager} was provided to identify a unique candidate
|
||||
*/
|
||||
@ReadOperation
|
||||
public CacheEntry cache(@Selector String cache, @Nullable String cacheManager) {
|
||||
return extractUniqueCacheEntry(cache, getCacheEntries(
|
||||
(name) -> name.equals(cache), safeEqual(cacheManager)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all the available {@link Cache caches}.
|
||||
*/
|
||||
@DeleteOperation
|
||||
public void clearCaches(@Nullable String cacheManager, @Nullable String cacheName) {
|
||||
if (cacheManager == null) {
|
||||
this.context.getBeansOfType(CacheManager.class)
|
||||
.forEach((name, manager) -> this.clearCaches(manager, cacheName));
|
||||
} else {
|
||||
this.clearCaches(this.context.getBean(cacheManager, CacheManager.class), cacheName);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearCaches(CacheManager cacheManager, String cacheName) {
|
||||
if (cacheName == null) {
|
||||
cacheManager.getCacheNames().forEach(cn -> cacheManager.getCache(cn).clear());
|
||||
} else {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
public void clearCaches() {
|
||||
getCacheEntries((name) -> true, (cacheManagerName) -> true)
|
||||
.forEach(this::clearCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of an application context's caches, primarily
|
||||
* intended for serialization to JSON.
|
||||
* Clear the specific {@link Cache}.
|
||||
* @param cache then name of the cache
|
||||
* @param cacheManager the name of the cacheManager (can be {@code null}
|
||||
* @return {@code true} if the cache was cleared or {@code false} if no such cache exists
|
||||
* @throws NonUniqueCacheException if more than one cache with that name exist and no
|
||||
*/
|
||||
public static final class CachesDescriptor {
|
||||
private final List<CacheDescriptor> caches;
|
||||
@DeleteOperation
|
||||
public boolean clearCache(@Selector String cache, @Nullable String cacheManager) {
|
||||
CacheEntry entry = extractUniqueCacheEntry(cache, getCacheEntries(
|
||||
(name) -> name.equals(cache), safeEqual(cacheManager)));
|
||||
return (entry != null && clearCache(entry));
|
||||
}
|
||||
|
||||
private CachesDescriptor(List<CacheDescriptor> caches) {
|
||||
this.caches = caches;
|
||||
}
|
||||
private List<CacheEntry> getCacheEntries(
|
||||
Predicate<String> cacheNamePredicate,
|
||||
Predicate<String> cacheManagerNamePredicate) {
|
||||
List<CacheEntry> entries = new ArrayList<>();
|
||||
this.cacheManagers.keySet().stream().filter(cacheManagerNamePredicate)
|
||||
.forEach((cacheManagerName) -> entries.addAll(
|
||||
getCacheEntries(cacheManagerName, cacheNamePredicate)));
|
||||
return entries;
|
||||
}
|
||||
|
||||
public List<CacheDescriptor> getCaches() {
|
||||
return this.caches;
|
||||
private List<CacheEntry> getCacheEntries(String cacheManagerName,
|
||||
Predicate<String> cacheNamePredicate) {
|
||||
CacheManager cacheManager = this.cacheManagers.get(cacheManagerName);
|
||||
List<CacheEntry> entries = new ArrayList<>();
|
||||
cacheManager.getCacheNames().stream().filter(cacheNamePredicate)
|
||||
.map(cacheManager::getCache).filter(Objects::nonNull)
|
||||
.forEach((cache) -> entries.add(
|
||||
new CacheEntry(cache, cacheManagerName)));
|
||||
return entries;
|
||||
}
|
||||
|
||||
private CacheEntry extractUniqueCacheEntry(String cache,
|
||||
List<CacheEntry> entries) {
|
||||
if (entries.size() > 1) {
|
||||
throw new NonUniqueCacheException(cache, entries.stream()
|
||||
.map(CacheEntry::getCacheManager).distinct()
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
return (entries.isEmpty() ? null : entries.get(0));
|
||||
}
|
||||
|
||||
private boolean clearCache(CacheEntry entry) {
|
||||
String cacheName = entry.getName();
|
||||
Cache cache = this.cacheManagers.get(entry.getCacheManager()).getCache(cacheName);
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Predicate<String> safeEqual(String name) {
|
||||
return (name != null ? ((requested) -> requested.equals(name))
|
||||
: ((requested) -> true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a {@link Cache}, primarily intended for serialization to
|
||||
* JSON.
|
||||
* A report of available {@link Cache caches}, primarily intended for serialization
|
||||
* to JSON.
|
||||
*/
|
||||
public static final class CacheDescriptor {
|
||||
public static final class CachesReport {
|
||||
|
||||
private final Map<String, Map<String, CacheDescriptor>> cacheManagers;
|
||||
|
||||
public CachesReport(Map<String, Map<String, CacheDescriptor>> cacheManagers) {
|
||||
this.cacheManagers = cacheManagers;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, CacheDescriptor>> getCacheManagers() {
|
||||
return this.cacheManagers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic description of a {@link Cache}, primarily intended for serialization to JSON.
|
||||
*/
|
||||
public static class CacheDescriptor {
|
||||
|
||||
private final String target;
|
||||
|
||||
public CacheDescriptor(String target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fully qualified name of the native cache.
|
||||
* @return the fully qualified name of the native cache
|
||||
*/
|
||||
public String getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of a {@link Cache}, primarily intended for serialization to JSON.
|
||||
*/
|
||||
public static final class CacheEntry extends CacheDescriptor {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String cacheManager;
|
||||
|
||||
public CacheDescriptor(String name, String cacheManager) {
|
||||
this.name = name;
|
||||
public CacheEntry(Cache cache, String cacheManager) {
|
||||
super(cache.getNativeCache().getClass().getName());
|
||||
this.name = cache.getName();
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getCacheManager() {
|
||||
return cacheManager;
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.boot.actuate.cache.CachesEndpoint.CacheEntry;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link EndpointWebExtension} for the {@link CachesEndpoint}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@EndpointWebExtension(endpoint = CachesEndpoint.class)
|
||||
public class CachesEndpointWebExtension {
|
||||
|
||||
private final CachesEndpoint delegate;
|
||||
|
||||
public CachesEndpointWebExtension(CachesEndpoint delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<CacheEntry> cache(@Selector String cache,
|
||||
@Nullable String cacheManager) {
|
||||
try {
|
||||
CacheEntry entry = this.delegate.cache(cache, cacheManager);
|
||||
int status = (entry != null ? WebEndpointResponse.STATUS_OK
|
||||
: WebEndpointResponse.STATUS_NOT_FOUND);
|
||||
return new WebEndpointResponse<>(entry, status);
|
||||
}
|
||||
catch (NonUniqueCacheException ex) {
|
||||
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteOperation
|
||||
public WebEndpointResponse<Void> clearCache(@Selector String cache,
|
||||
@Nullable String cacheManager) {
|
||||
try {
|
||||
boolean cleared = this.delegate.clearCache(cache, cacheManager);
|
||||
int status = (cleared ? WebEndpointResponse.STATUS_NO_CONTENT
|
||||
: WebEndpointResponse.STATUS_NOT_FOUND);
|
||||
return new WebEndpointResponse<>(status);
|
||||
}
|
||||
catch (NonUniqueCacheException ex) {
|
||||
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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 java.util.Collections;
|
||||
|
||||
/**
|
||||
* Exception thrown when multiple caches exist with the same name.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class NonUniqueCacheException extends RuntimeException {
|
||||
|
||||
private final String cacheName;
|
||||
|
||||
private final Collection<String> cacheManagerNames;
|
||||
|
||||
public NonUniqueCacheException(String cacheName,
|
||||
Collection<String> cacheManagerNames) {
|
||||
super(String.format("Multiple caches named %s found, specify the 'cacheManager' "
|
||||
+ "to use: %s", cacheName, cacheManagerNames));
|
||||
this.cacheName = cacheName;
|
||||
this.cacheManagerNames = Collections.unmodifiableCollection(cacheManagerNames);
|
||||
}
|
||||
|
||||
public String getCacheName() {
|
||||
return this.cacheName;
|
||||
}
|
||||
|
||||
public Collection<String> getCacheManagerNames() {
|
||||
return this.cacheManagerNames;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
|
@ -36,6 +36,11 @@ public final class WebEndpointResponse<T> {
|
|||
*/
|
||||
public static final int STATUS_OK = 200;
|
||||
|
||||
/**
|
||||
* {@code 204 No Content}.
|
||||
*/
|
||||
public static final int STATUS_NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* {@code 400 Bad Request}.
|
||||
*/
|
||||
|
|
|
@ -16,102 +16,201 @@
|
|||
|
||||
package org.springframework.boot.actuate.cache;
|
||||
|
||||
import org.junit.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.cache.CachesEndpoint.CacheDescriptor;
|
||||
import org.springframework.boot.actuate.cache.CachesEndpoint.CacheEntry;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link CachesEndpoint}.
|
||||
*
|
||||
* @author Johannes Edmeier
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CachesEndpointTests {
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
|
||||
AutoConfigurations.of(CacheAutoConfiguration.class));
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void cacheReportIsReturned() {
|
||||
//@formatter:off
|
||||
this.contextRunner.withUserConfiguration(Config.class)
|
||||
.run(context -> assertThat(context.getBean(CachesEndpoint.class).caches().getCaches())
|
||||
.hasSize(2)
|
||||
.anySatisfy(cache -> {
|
||||
assertThat(cache.getName()).isEqualTo("first");
|
||||
assertThat(cache.getCacheManager()).isEqualTo("cacheManager");
|
||||
}).anySatisfy(cache -> {
|
||||
assertThat(cache.getName()).isEqualTo("second");
|
||||
assertThat(cache.getCacheManager()).isEqualTo("cacheManager");
|
||||
})
|
||||
);
|
||||
//@formatter:on
|
||||
public void allCachesWithSingleCacheManager() {
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", new ConcurrentMapCacheManager("a", "b")));
|
||||
Map<String, Map<String, CacheDescriptor>> allDescriptors = endpoint.caches()
|
||||
.getCacheManagers();
|
||||
assertThat(allDescriptors).containsOnlyKeys("test");
|
||||
Map<String, CacheDescriptor> descriptors = allDescriptors.get("test");
|
||||
assertThat(descriptors).containsOnlyKeys("a", "b");
|
||||
assertThat(descriptors.get("a").getTarget()).isEqualTo(
|
||||
ConcurrentHashMap.class.getName());
|
||||
assertThat(descriptors.get("b").getTarget()).isEqualTo(
|
||||
ConcurrentHashMap.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheIsCleared() {
|
||||
this.contextRunner.withUserConfiguration(Config.class).run((context) -> {
|
||||
Cache firstCache = context.getBean("firstCache", Cache.class);
|
||||
firstCache.put("key", "vale");
|
||||
Cache secondCache = context.getBean("secondCache", Cache.class);
|
||||
secondCache.put("key", "value");
|
||||
context.getBean(CachesEndpoint.class).clearCaches(null, null);
|
||||
assertThat(firstCache.get("key", String.class)).isNull();
|
||||
assertThat(secondCache.get("key", String.class)).isNull();
|
||||
});
|
||||
public void allCachesWithSeveralCacheManagers() {
|
||||
Map<String, CacheManager> cacheManagers = new LinkedHashMap<>();
|
||||
cacheManagers.put("test", new ConcurrentMapCacheManager("a", "b"));
|
||||
cacheManagers.put("another", new ConcurrentMapCacheManager("a", "c"));
|
||||
CachesEndpoint endpoint = new CachesEndpoint(cacheManagers);
|
||||
Map<String, Map<String, CacheDescriptor>> allDescriptors = endpoint.caches()
|
||||
.getCacheManagers();
|
||||
assertThat(allDescriptors).containsOnlyKeys("test", "another");
|
||||
assertThat(allDescriptors.get("test")).containsOnlyKeys("a", "b");
|
||||
assertThat(allDescriptors.get("another")).containsOnlyKeys("a", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedCacheIsCleared() {
|
||||
this.contextRunner.withUserConfiguration(Config.class).run((context) -> {
|
||||
Cache firstCache = context.getBean("firstCache", Cache.class);
|
||||
firstCache.put("key", "value");
|
||||
Cache secondCache = context.getBean("secondCache", Cache.class);
|
||||
secondCache.put("key", "value");
|
||||
context.getBean(CachesEndpoint.class).clearCaches(null, "first");
|
||||
assertThat(firstCache.get("key", String.class)).isNull();
|
||||
assertThat(secondCache.get("key", String.class)).isEqualTo("value");
|
||||
});
|
||||
public void namedCacheWithSingleCacheManager() {
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", new ConcurrentMapCacheManager("b", "a")));
|
||||
CacheEntry entry = endpoint.cache("a", null);
|
||||
assertThat(entry).isNotNull();
|
||||
assertThat(entry.getCacheManager()).isEqualTo("test");
|
||||
assertThat(entry.getName()).isEqualTo("a");
|
||||
assertThat(entry.getTarget()).isEqualTo(ConcurrentHashMap.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknwonCache() {
|
||||
this.contextRunner.withUserConfiguration(Config.class).run((context) -> {
|
||||
Cache firstCache = context.getBean("firstCache", Cache.class);
|
||||
firstCache.put("key", "value");
|
||||
Cache secondCache = context.getBean("secondCache", Cache.class);
|
||||
secondCache.put("key", "value");
|
||||
context.getBean(CachesEndpoint.class).clearCaches(null, "UNKNWON");
|
||||
assertThat(firstCache.get("key", String.class)).isEqualTo("value");
|
||||
assertThat(secondCache.get("key", String.class)).isEqualTo("value");
|
||||
});
|
||||
public void namedCacheWithSeveralCacheManagers() {
|
||||
Map<String, CacheManager> cacheManagers = new LinkedHashMap<>();
|
||||
cacheManagers.put("test", new ConcurrentMapCacheManager("b", "dupe-cache"));
|
||||
cacheManagers.put("another", new ConcurrentMapCacheManager("c", "dupe-cache"));
|
||||
CachesEndpoint endpoint = new CachesEndpoint(cacheManagers);
|
||||
this.thrown.expect(NonUniqueCacheException.class);
|
||||
this.thrown.expectMessage("dupe-cache");
|
||||
this.thrown.expectMessage("test");
|
||||
this.thrown.expectMessage("another");
|
||||
endpoint.cache("dupe-cache", null);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public static class Config {
|
||||
@Bean
|
||||
public Cache firstCache() {
|
||||
return new ConcurrentMapCache("first");
|
||||
}
|
||||
@Test
|
||||
public void namedCacheWithUnknownCache() {
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", new ConcurrentMapCacheManager("b", "a")));
|
||||
CacheEntry entry = endpoint.cache("unknown", null);
|
||||
assertThat(entry).isNull();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Cache secondCache() {
|
||||
return new ConcurrentMapCache("second");
|
||||
}
|
||||
@Test
|
||||
public void namedCacheWithWrongCacheManager() {
|
||||
Map<String, CacheManager> cacheManagers = new LinkedHashMap<>();
|
||||
cacheManagers.put("test", new ConcurrentMapCacheManager("b", "a"));
|
||||
cacheManagers.put("another", new ConcurrentMapCacheManager("c", "a"));
|
||||
CachesEndpoint endpoint = new CachesEndpoint(cacheManagers);
|
||||
CacheEntry entry = endpoint.cache("c", "test");
|
||||
assertThat(entry).isNull();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CachesEndpoint endpoint(ApplicationContext context) {
|
||||
return new CachesEndpoint(context);
|
||||
}
|
||||
@Test
|
||||
public void namedCacheWithSeveralCacheManagersWithCacheManagerFilter() {
|
||||
Map<String, CacheManager> cacheManagers = new LinkedHashMap<>();
|
||||
cacheManagers.put("test", new ConcurrentMapCacheManager("b", "a"));
|
||||
cacheManagers.put("another", new ConcurrentMapCacheManager("c", "a"));
|
||||
CachesEndpoint endpoint = new CachesEndpoint(cacheManagers);
|
||||
CacheEntry entry = endpoint.cache("a", "test");
|
||||
assertThat(entry).isNotNull();
|
||||
assertThat(entry.getCacheManager()).isEqualTo("test");
|
||||
assertThat(entry.getName()).isEqualTo("a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearAllCaches() {
|
||||
Cache a = mockCache("a");
|
||||
Cache b = mockCache("b");
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", cacheManager(a, b)));
|
||||
endpoint.clearCaches();
|
||||
verify(a).clear();
|
||||
verify(b).clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearCache() {
|
||||
Cache a = mockCache("a");
|
||||
Cache b = mockCache("b");
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", cacheManager(a, b)));
|
||||
assertThat(endpoint.clearCache("a", null)).isTrue();
|
||||
verify(a).clear();
|
||||
verify(b, never()).clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearCacheWithSeveralCacheManagers() {
|
||||
Map<String, CacheManager> cacheManagers = new LinkedHashMap<>();
|
||||
cacheManagers.put("test", cacheManager(mockCache("dupe-cache"), mockCache("b")));
|
||||
cacheManagers.put("another", cacheManager(mockCache("dupe-cache")));
|
||||
CachesEndpoint endpoint = new CachesEndpoint(cacheManagers);
|
||||
|
||||
this.thrown.expectMessage("dupe-cache");
|
||||
this.thrown.expectMessage("test");
|
||||
this.thrown.expectMessage("another");
|
||||
endpoint.clearCache("dupe-cache", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearCacheWithSeveralCacheManagersWithCacheManagerFilter() {
|
||||
Map<String, CacheManager> cacheManagers = new LinkedHashMap<>();
|
||||
Cache a = mockCache("a");
|
||||
Cache b = mockCache("b");
|
||||
cacheManagers.put("test", cacheManager(a, b));
|
||||
Cache anotherA = mockCache("a");
|
||||
cacheManagers.put("another", cacheManager(anotherA));
|
||||
CachesEndpoint endpoint = new CachesEndpoint(cacheManagers);
|
||||
assertThat(endpoint.clearCache("a", "another")).isTrue();
|
||||
verify(a, never()).clear();
|
||||
verify(anotherA).clear();
|
||||
verify(b, never()).clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearCacheWithUnknownCache() {
|
||||
Cache a = mockCache("a");
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", cacheManager(a)));
|
||||
assertThat(endpoint.clearCache("unknown", null)).isFalse();
|
||||
verify(a, never()).clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearCacheWithUnknownCacheManager() {
|
||||
Cache a = mockCache("a");
|
||||
CachesEndpoint endpoint = new CachesEndpoint(Collections.singletonMap(
|
||||
"test", cacheManager(a)));
|
||||
assertThat(endpoint.clearCache("a", "unknown")).isFalse();
|
||||
verify(a, never()).clear();
|
||||
}
|
||||
|
||||
private CacheManager cacheManager(Cache... caches) {
|
||||
SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
cacheManager.setCaches(Arrays.asList(caches));
|
||||
cacheManager.afterPropertiesSet();
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
private Cache mockCache(String name) {
|
||||
Cache cache = mock(Cache.class);
|
||||
given(cache.getName()).willReturn(name);
|
||||
given(cache.getNativeCache()).willReturn(new Object());
|
||||
return cache;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointRunners;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link CachesEndpoint} exposed by Jersey, Spring MVC, and WebFlux.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@RunWith(WebEndpointRunners.class)
|
||||
public class CachesEndpointWebIntegrationTests {
|
||||
|
||||
private static WebTestClient client;
|
||||
|
||||
private static ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void allCaches() {
|
||||
client.get().uri("/actuator/caches").exchange().expectStatus().isOk().expectBody()
|
||||
.jsonPath("cacheManagers.one.a.target").isEqualTo(
|
||||
ConcurrentHashMap.class.getName())
|
||||
.jsonPath("cacheManagers.one.b.target").isEqualTo(
|
||||
ConcurrentHashMap.class.getName())
|
||||
.jsonPath("cacheManagers.two.a.target").isEqualTo(
|
||||
ConcurrentHashMap.class.getName())
|
||||
.jsonPath("cacheManagers.two.c.target").isEqualTo(
|
||||
ConcurrentHashMap.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedCache() {
|
||||
client.get().uri("/actuator/caches/b").exchange().expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("name").isEqualTo("b")
|
||||
.jsonPath("cacheManager").isEqualTo("one")
|
||||
.jsonPath("target").isEqualTo(ConcurrentHashMap.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedCacheWithUnknownName() {
|
||||
client.get().uri("/actuator/caches/does-not-exist").exchange().expectStatus()
|
||||
.isNotFound();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedCacheWithNonUniqueName() {
|
||||
client.get().uri("/actuator/caches/a").exchange().expectStatus()
|
||||
.isBadRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearNamedCache() {
|
||||
Cache b = context.getBean("one", CacheManager.class).getCache("b");
|
||||
b.put("test", "value");
|
||||
client.delete().uri("/actuator/caches/b").exchange().expectStatus().isNoContent();
|
||||
assertThat(b.get("test")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanNamedCacheWithUnknownName() {
|
||||
client.delete().uri("/actuator/caches/does-not-exist").exchange().expectStatus()
|
||||
.isNotFound();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearNamedCacheWithNonUniqueName() {
|
||||
client.get().uri("/actuator/caches/a").exchange().expectStatus()
|
||||
.isBadRequest();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public CacheManager one() {
|
||||
return new ConcurrentMapCacheManager("a", "b");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManager two() {
|
||||
return new ConcurrentMapCacheManager("a", "c");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CachesEndpoint endpoint(Map<String, CacheManager> cacheManagers) {
|
||||
return new CachesEndpoint(cacheManagers);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CachesEndpointWebExtension cachesEndpointWebExtension(
|
||||
CachesEndpoint endpoint) {
|
||||
return new CachesEndpointWebExtension(endpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1182,6 +1182,10 @@ content into your application. Rather, pick only the properties that you need.
|
|||
management.endpoint.beans.cache.time-to-live=0ms # Maximum time that a response can be cached.
|
||||
management.endpoint.beans.enabled=true # Whether to enable the beans endpoint.
|
||||
|
||||
# CACHES ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/cache/CachesEndpoint.{sc-ext}[CachesEndpoint])
|
||||
management.endpoint.caches.cache.time-to-live=0ms # Maximum time that a response can be cached.
|
||||
management.endpoint.caches.enabled=true # Whether to enable the caches endpoint.
|
||||
|
||||
# CONDITIONS REPORT ENDPOINT ({sc-spring-boot-actuator-autoconfigure}/condition/ConditionsReportEndpoint.{sc-ext}[ConditionsReportEndpoint])
|
||||
management.endpoint.conditions.cache.time-to-live=0ms # Maximum time that a response can be cached.
|
||||
management.endpoint.conditions.enabled=true # Whether to enable the conditions endpoint.
|
||||
|
|
|
@ -75,6 +75,10 @@ The following technology-agnostic endpoints are available:
|
|||
|Displays a complete list of all the Spring beans in your application.
|
||||
|Yes
|
||||
|
||||
|`caches`
|
||||
|Exposes available caches.
|
||||
|Yes
|
||||
|
||||
|`conditions`
|
||||
|Shows the conditions that were evaluated on configuration and auto-configuration
|
||||
classes and the reasons why they did or did not match.
|
||||
|
|
Loading…
Reference in New Issue