Skip to content

Commit

Permalink
update ehCache
Browse files Browse the repository at this point in the history
Signed-off-by: Pavel Jareš <[email protected]>
  • Loading branch information
pj892031 committed Sep 21, 2023
1 parent 6066e19 commit 711e557
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 109 deletions.
1 change: 1 addition & 0 deletions common-service-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ dependencies {
exclude group: "org.springframework", module: "spring-test"
}
testImplementation libraries.spring_test
testImplementation libraries.spring_context_support
testImplementation libraries.json_smart
testImplementation(libraries.eh_cache)
testImplementation(libraries.jackson_core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@

package org.zowe.apiml.util;

import net.sf.ehcache.Element;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.zowe.apiml.cache.CompositeKey;

import java.util.List;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
* This utils offer base operation with cache, which can be shared to multiple codes.
Expand Down Expand Up @@ -61,18 +63,13 @@ public void evictSubset(CacheManager cacheManager, String cacheName, Predicate<C
final Cache cache = cacheManager.getCache(cacheName);
if (cache == null) throw new IllegalArgumentException("Unknown cache " + cacheName);
final Object nativeCache = cache.getNativeCache();
if (nativeCache instanceof net.sf.ehcache.Cache) {
final net.sf.ehcache.Cache ehCache = (net.sf.ehcache.Cache) nativeCache;

for (final Object key : ehCache.getKeys()) {
if (key instanceof CompositeKey) {
// if entry is compositeKey and first param is different, skip it (be sure this is not to evict)
final CompositeKey compositeKey = ((CompositeKey) key);
if (!keyPredicate.test(compositeKey)) continue;
}
// if key is not composite key (unknown for evict) or has same serviceId, evict record
ehCache.remove(key);
}
if (nativeCache instanceof javax.cache.Cache) {
Spliterator<javax.cache.Cache.Entry<Object, Object>> spliterator = ((javax.cache.Cache<Object, Object>) nativeCache).spliterator();
Set<Object> keysToRemove = StreamSupport.stream(spliterator, true)
.filter(e -> !(e.getKey() instanceof CompositeKey) || keyPredicate.test((CompositeKey) e.getKey()))
.map(e -> e.getKey())
.collect(Collectors.toSet());
((javax.cache.Cache<Object, Object>) nativeCache).removeAll(keysToRemove);
} else {
// in case of using different cache manager, evict all records for sure
cache.clear();
Expand All @@ -93,14 +90,9 @@ public <T> List<T> getAllRecords(CacheManager cacheManager, String cacheName) {
if (cache == null) throw new IllegalArgumentException("Unknown cache " + cacheName);

final Object nativeCache = cache.getNativeCache();
if (nativeCache instanceof net.sf.ehcache.Cache) {
final net.sf.ehcache.Cache ehCache = (net.sf.ehcache.Cache) nativeCache;

return (List<T>) ehCache.getAll(ehCache.getKeys())
.values()
.stream()
.map(Element::getObjectValue)
.collect(Collectors.toList());
if (nativeCache instanceof javax.cache.Cache) {
Spliterator<javax.cache.Cache.Entry<Object, T>> spliterator = ((javax.cache.Cache<Object, T>) nativeCache).spliterator();
return StreamSupport.stream(spliterator, true).map(e -> e.getValue()).collect(Collectors.toList());
} else {
throw new IllegalArgumentException("Unsupported type of cache : " + nativeCache.getClass());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,16 @@

package org.zowe.apiml.util;

import net.sf.ehcache.Element;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.zowe.apiml.cache.CompositeKey;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class CacheUtilsTest {

Expand All @@ -49,6 +31,25 @@ void setUp() {
underTest = new CacheUtils();
}

private javax.cache.Cache.Entry<Object, Object> createEntry(Object key, Object value) {
return new javax.cache.Cache.Entry<Object, Object>() {
@Override
public Object getKey() {
return key;
}

@Override
public Object getValue() {
return value;
}

@Override
public <T> T unwrap(Class<T> clazz) {
return (T) value;
}
};
}

@Test
void testEvictSubset() {
CacheManager cacheManager = mock(CacheManager.class);
Expand All @@ -60,16 +61,22 @@ void testEvictSubset() {

Cache cache2 = mock(Cache.class);
when(cacheManager.getCache("cache2")).thenReturn(cache2);
net.sf.ehcache.Cache ehCache2 = mock(net.sf.ehcache.Cache.class);
javax.cache.Cache ehCache2 = mock(javax.cache.Cache.class);

when(cache2.getNativeCache()).thenReturn(ehCache2);
List<Object> keys = Arrays.asList(
"abc", // not composite key
"abc",
new CompositeKey("test", 5),
new CompositeKey("next", 10),
new CompositeKey("next", 15)
);
when(ehCache2.getKeys()).thenReturn(keys);
List<javax.cache.Cache.Entry<Object, Object>> values = Arrays.asList(
createEntry(keys.get(0), "A"),
createEntry(keys.get(1), "B"),
createEntry(keys.get(2), "C"),
createEntry(keys.get(3), "D")
);
when(ehCache2.spliterator()).thenAnswer(invocation -> values.spliterator());

try {
underTest.evictSubset(cacheManager, "missing", x -> true);
Expand All @@ -79,34 +86,20 @@ void testEvictSubset() {
assertTrue(e.getMessage().contains("missing"));
}

// not EhCache - clean all, dont use keyPredicate
// not EhCache - clean all, do not use keyPredicate
verify(cache1, never()).clear();
underTest.evictSubset(cacheManager, "cache1", x -> false);
verify(cache1, times(1)).clear();

final Answer<Boolean> answer = invocation -> {
removeCounter++;
return true;
};

doAnswer(answer).when(ehCache2).remove(any(Serializable.class));
doAnswer(answer).when(ehCache2).remove((Object) any());

assertEquals(0, removeCounter);
// in all cases remove entries without CompositeKey
underTest.evictSubset(cacheManager, "cache2", x -> false);
assertEquals(1, removeCounter);
verify(ehCache2, times(1)).remove(keys.get(0));
verify(ehCache2, times(1)).removeAll(Collections.singleton(keys.get(0)));

underTest.evictSubset(cacheManager, "cache2", x -> x.equals(0, "test"));
assertEquals(3, removeCounter);
verify(ehCache2, times(2)).remove(keys.get(0));
verify(ehCache2, times(1)).remove(keys.get(1));
verify(ehCache2, times(1)).removeAll(new HashSet(Arrays.asList(keys.get(0), keys.get(1))));

underTest.evictSubset(cacheManager, "cache2", x -> (Integer) x.get(1) > 10);
assertEquals(5, removeCounter);
verify(ehCache2, times(3)).remove(keys.get(0));
verify(ehCache2, times(1)).remove(keys.get(3));
verify(ehCache2, times(1)).removeAll(new HashSet(Arrays.asList(keys.get(0), keys.get(3))));
}

@Test
Expand All @@ -132,30 +125,21 @@ void givenUnsupportedCacheManager_whenGetAllRecords_thenThrowsException() {
assertTrue(iae.getMessage().startsWith("Unsupported type of cache : "));
}

private Map<Object, Element> convert(Map<Integer, String> in) {
Map<Object, Element> out = new HashMap<>();
for (Map.Entry<Integer, String> entry : in.entrySet()) {
out.put(entry.getKey(), new Element(entry.getKey(), entry.getValue()));
}
return out;
}

@Test
void givenValidCacheManager_whenGetAllRecords_thenReadAllStoredRecords() {
CacheManager cacheManager = mock(CacheManager.class);
Cache cache = mock(Cache.class);
net.sf.ehcache.Cache ehCache = mock(net.sf.ehcache.Cache.class);
javax.cache.Cache ehCache = mock(javax.cache.Cache.class);

Map<Integer, String> entries = new HashMap<>();
entries.put(1, "a");
entries.put(2, "b");
entries.put(3, "c");
List<Object> keys = new ArrayList<>(entries.keySet());
List entries = Arrays.asList(
createEntry(1, "a"),
createEntry(2, "b"),
createEntry(3, "c")
);

when(cacheManager.getCache("knownCacheName")).thenReturn(cache);
when(cache.getNativeCache()).thenReturn(ehCache);
when(ehCache.getKeys()).thenReturn(keys);
when(ehCache.getAll(keys)).thenReturn(convert(entries));
when(ehCache.spliterator()).thenAnswer(invocation -> entries.spliterator());

Collection<String> values = underTest.getAllRecords(cacheManager, "knownCacheName");
assertNotNull(values);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.cache.jcache.JCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
Expand Down Expand Up @@ -48,18 +48,17 @@ public void afterPropertiesSet() {
}

@Bean
public CacheManager cacheManager() {
net.sf.ehcache.CacheManager cache = ehCacheCacheManager().getObject();
assert cache != null;
return new EhCacheCacheManager(cache);
public JCacheManagerFactoryBean cacheManagerFactoryBean() throws Exception {
JCacheManagerFactoryBean jCacheManagerFactoryBean = new JCacheManagerFactoryBean();
jCacheManagerFactoryBean.setCacheManagerUri(new ClassPathResource("ehcache.xml").getURI());
return jCacheManagerFactoryBean;
}

@Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
cmfb.setShared(true);
return cmfb;
public CacheManager cacheManager() throws Exception {
final JCacheCacheManager jCacheCacheManager = new JCacheCacheManager();
jCacheCacheManager.setCacheManager(cacheManagerFactoryBean().getObject());
return jCacheCacheManager;
}

@Bean(CacheConfig.COMPOSITE_KEY_GENERATOR)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public Authentication getAuthentication(String serviceId) {

@Override
@CacheEvict(value = CACHE_BY_AUTHENTICATION, condition = "#result != null && #result.isExpired()")
@Cacheable(CACHE_BY_AUTHENTICATION)
@Cacheable(value = CACHE_BY_AUTHENTICATION, condition = "#result != null")
public AuthenticationCommand getAuthenticationCommand(Authentication authentication, AuthSource authSource) {
final IAuthenticationScheme scheme = authenticationSchemeFactory.getSchema(authentication.getScheme());
return scheme.createCommand(authentication, authSource);
Expand Down
3 changes: 0 additions & 3 deletions gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ spring:
output:
ansi:
enabled: detect
cache:
ehcache:
config: classpath:ehcache.xml
main:
banner-mode: ${apiml.banner:"off"}
allow-circular-references: true
Expand Down
86 changes: 74 additions & 12 deletions gateway-service/src/main/resources/ehcache.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

<diskStore path="ehcache.disk.store.dir" />
<persistence directory="${ehcache.disk.store.dir}"/>

<defaultCache maxEntriesLocalHeap="0" eternal="false" timeToIdleSeconds="60" timeToLiveSeconds="60" />
<thread-pools>
<thread-pool alias="defaultDiskPool" min-size="1" max-size="3" default="true"/>
</thread-pools>

<cache name="invalidatedJwtTokens" diskPersistent="true" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LRU" transactionalMode="off" maxBytesLocalHeap="1048576" />
<cache name="validationJwtToken" diskPersistent="false" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache name="serviceAuthenticationByServiceId" diskPersistent="false" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache name="serviceAuthenticationByAuthentication" diskPersistent="false" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache name="zosmfInfo" diskPersistent="false" maxEntriesLocalHeap="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache name="zosmfServiceImplementation" diskPersistent="false" maxEntriesLocalHeap="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache name="zosmfAuthenticationEndpoint" diskPersistent="false" maxEntriesLocalHeap="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache name="zosmfJwtEndpoint" diskPersistent="false" maxEntriesLocalHeap="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU" transactionalMode="off" />
<cache alias="invalidatedJwtTokens">
<key-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.String</key-type>
<value-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.Boolean</value-type>
<expiry>
<ttl unit="days">1</ttl>
</expiry>
<resources>
<heap unit="MB">1</heap>
<disk unit="MB" persistent="true">10</disk>
</resources>
</cache>

</ehcache>
<cache alias="validationJwtToken">
<key-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.String</key-type>
<value-type copier="org.ehcache.impl.copy.SerializingCopier">org.zowe.apiml.security.common.token.TokenAuthentication</value-type>
<expiry>
<ttl unit="days">1</ttl>
</expiry>
<heap unit="entries">1000</heap>
</cache>

<cache alias="serviceAuthenticationByServiceId">
<key-type copier="org.ehcache.impl.copy.IdentityCopier">org.zowe.apiml.cache.CompositeKey</key-type>
<value-type copier="org.ehcache.impl.copy.IdentityCopier">org.zowe.apiml.gateway.security.service.schema.AuthenticationCommand</value-type>
<expiry>
<ttl unit="days">1</ttl>
</expiry>
<heap unit="entries">1000</heap>
</cache>

<cache alias="serviceAuthenticationByAuthentication">
<key-type copier="org.ehcache.impl.copy.IdentityCopier">org.springframework.cache.interceptor.SimpleKey</key-type>
<value-type copier="org.ehcache.impl.copy.IdentityCopier">org.zowe.apiml.gateway.security.service.schema.AuthenticationCommand</value-type>
<expiry>
<ttl unit="days">1</ttl>
</expiry>
<heap unit="entries">1000</heap>
</cache>

<cache alias="zosmfInfo">
<key-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.String</key-type>
<value-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.String</value-type>
<expiry>
<ttl unit="hours">1</ttl>
</expiry>
<heap unit="entries">10</heap>
</cache>

<cache alias="zosmfAuthenticationEndpoint">
<key-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.String</key-type>
<value-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.Boolean</value-type>
<expiry>
<ttl unit="hours">1</ttl>
</expiry>
<heap unit="entries">10</heap>
</cache>

<cache alias="zosmfJwtEndpoint">
<key-type copier="org.ehcache.impl.copy.SerializingCopier">org.springframework.http.HttpHeaders</key-type>
<value-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.Boolean</value-type>
<expiry>
<ttl unit="hours">1</ttl>
</expiry>
<heap unit="entries">10</heap>
</cache>

</config>
Loading

0 comments on commit 711e557

Please sign in to comment.