Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class MapBasedEntityCacheTest {
class MapBasedEntityCacheTest {

@Test
public void shouldInsertElement() {
void shouldInsertElement() {
// given
EntityCache<String, Object> cache = new MapBasedEntityCache<>();
var cache = new MapBasedEntityCache<>();

// and
String key = "key";
Object testObject = new Object();
var key = "key";
var testObject = new Object();

// when
Object previous = cache.put(key, testObject);
var previous = cache.put(key, testObject);

// then
assertNull(previous);
Expand All @@ -34,21 +33,21 @@ public void shouldInsertElement() {
}

@Test
public void shouldReplaceElement() {
void shouldReplaceElement() {
// given
String key = "key";
Object testObject = new Object();
var key = "key";
var testObject = new Object();

// and
ConcurrentMap<String, Object> precomputedCache = new ConcurrentHashMap<>();
var precomputedCache = new ConcurrentHashMap<String, Object>();
precomputedCache.put(key, testObject);
EntityCache<String, Object> cache = new MapBasedEntityCache<>(() -> precomputedCache);
var cache = new MapBasedEntityCache<>(() -> precomputedCache);

// and
Object newObject = new Object();
var newObject = new Object();

// when
Object previous = cache.put(key, newObject);
var previous = cache.put(key, newObject);

// then
assertSame(previous, testObject);
Expand All @@ -58,55 +57,51 @@ public void shouldReplaceElement() {
}

@Test
public void shouldComputeElement() throws Exception {
void shouldComputeElement() {
// given
EntityCache<String, Object> cache = new MapBasedEntityCache<>();
var cache = new MapBasedEntityCache<>();

// and
String key = "key";
Object testObject = new Object();
CacheLoader<String, Object, ?> loader = k -> testObject;
var key = "key";
var testObject = new Object();

// when
Object computed = cache.computeIfAbsent(key, loader);
var computed = cache.computeIfAbsent(key, k -> testObject);

// then
assertEquals(1L, cache.size());
assertSame(testObject, computed);
}

@Test
public void shouldNotComputeIfEntryForGivenKeyAlreadyExists() throws Exception {
void shouldNotComputeIfEntryForGivenKeyAlreadyExists() {
// given
String key = "key";
Object testObject = new Object();
var key = "key";
var testObject = new Object();

// and
ConcurrentMap<String, Object> precomputedCache = new ConcurrentHashMap<>();
var precomputedCache = new ConcurrentHashMap<String, Object>();
precomputedCache.put(key, testObject);
EntityCache<String, Object> cache = new MapBasedEntityCache<>(() -> precomputedCache);

// and
CacheLoader<String, Object, ?> loader = k -> new Object();
var cache = new MapBasedEntityCache<>(() -> precomputedCache);

// when
Object computed = cache.computeIfAbsent(key, loader);
var computed = cache.computeIfAbsent(key, k -> new Object());

// then
assertSame(computed, testObject);
assertEquals(1L, cache.size());
}

@Test
public void shouldInvalidateCache() {
void shouldInvalidateCache() {
// given
String key = "key";
Object testObject = new Object();
var key = "key";
var testObject = new Object();

// and
ConcurrentMap<String, Object> precomputedCache = new ConcurrentHashMap<>();
var precomputedCache = new ConcurrentHashMap<String, Object>();
precomputedCache.put(key, testObject);
EntityCache<String, Object> cache = new MapBasedEntityCache<>(() -> precomputedCache);
var cache = new MapBasedEntityCache<>(() -> precomputedCache);

// when
cache.invalidateAll();
Expand All @@ -116,18 +111,17 @@ public void shouldInvalidateCache() {
}

@Test
public void shouldRethrowAnExceptionIfCacheLoaderFails() {
void shouldRethrowAnExceptionIfCacheLoaderFails() {
// given
EntityCache<String, Object> cache = new MapBasedEntityCache<>();
var cache = new MapBasedEntityCache<>();

// and
String key = "key";
CacheLoader<String, Object, IOException> loader = k -> {
throw new IOException();
};
var key = "key";

// when
Executable compute = () -> cache.computeIfAbsent(key, loader);
var compute = (Executable) () -> cache.computeIfAbsent(key, k -> {
throw new IOException();
});

// then
assertThrows(IOException.class, compute);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,94 +6,92 @@
import org.junit.jupiter.params.provider.ArgumentsSource;
import pl.allegro.tech.jsoncache.CacheableEntity;
import pl.allegro.tech.jsoncache.keybuilder.stategy.CacheKeyBuilderStrategy;
import pl.allegro.tech.jsoncache.support.CacheableEntityBuilder;
import pl.allegro.tech.jsoncache.keybuilder.support.KeyBuilderStrategies;
import pl.allegro.tech.jsoncache.support.CacheableEntityBuilder;

import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class CacheKeyBuilderFactoryTest {
class CacheKeyBuilderFactoryTest {

@Test
public void givenKeyBuilderFactoryWithoutStrategiesAndEntityMetadataWhenRequestedForKeyBuilderNoneIsReturned() {
void givenKeyBuilderFactoryWithoutStrategiesAndEntityMetadataWhenRequestedForKeyBuilderNoneIsReturned() {
// given
CacheKeyBuilderFactory factory = new CacheKeyBuilderFactory(List.of());
var factory = new CacheKeyBuilderFactory(List.of());

// and
CacheableEntity entityMetadata = new CacheableEntityBuilder()
var entityMetadata = new CacheableEntityBuilder()
.withKeyComponents("a", "b", "c")
.build();

// when
Optional<? extends CacheKeyBuilder<?, ?>> keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);
var keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);

// then
assertTrue(keyBuilder::isEmpty);
}

@ParameterizedTest
@ArgumentsSource(KeyBuilderStrategies.StrategyMatchingMetadata.class)
public void givenKeyBuilderFactoryWithStrategyAndEntityMetadataWhenRequestedForKeyBuilderMatchingOneIsReturned(
void givenKeyBuilderFactoryWithStrategyAndEntityMetadataWhenRequestedForKeyBuilderMatchingOneIsReturned(
CacheKeyBuilderStrategy<?, ?> strategy,
CacheableEntity entityMetadata
) {
// given
CacheKeyBuilderFactory factory = new CacheKeyBuilderFactory(List.of(strategy));
var factory = new CacheKeyBuilderFactory(List.of(strategy));

// when
Optional<? extends CacheKeyBuilder<?, ?>> keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);
var keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);

// then
assertTrue(keyBuilder::isPresent);
}

@Test
public void givenKeyBuilderFactoryWithStrategiesAndInvalidEntityMetadataWhenRequestedForKeyBuilderNoneIsReturned() {
void givenKeyBuilderFactoryWithStrategiesAndInvalidEntityMetadataWhenRequestedForKeyBuilderNoneIsReturned() {
// given
CacheKeyBuilderFactory factory = new CacheKeyBuilderFactory(List.of(
var factory = new CacheKeyBuilderFactory(List.of(
KeyBuilderStrategies.DUMMY_COMPONENT_STRATEGY,
KeyBuilderStrategies.DUMMY_TEMPLATE_STRATEGY
));

// and
CacheableEntity entityMetadata = new CacheableEntityBuilder().build();
var entityMetadata = new CacheableEntityBuilder().build();

// when
Optional<? extends CacheKeyBuilder<?, ?>> keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);
var keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);

// then
assertTrue(keyBuilder::isEmpty);
}

@Test
public void givenKeyBuilderFactoryWithStrategiesWhenBothSupportEntityMetadataThenKeyBuilderForFirstOneIsReturned()
throws Exception {
void givenKeyBuilderFactoryWithStrategiesWhenBothSupportEntityMetadataThenKeyBuilderForFirstOneIsReturned() throws Exception {
// given
CacheKeyBuilderFactory factory = new CacheKeyBuilderFactory(List.of(
var factory = new CacheKeyBuilderFactory(List.of(
KeyBuilderStrategies.DUMMY_COMPONENT_STRATEGY,
KeyBuilderStrategies.DUMMY_TEMPLATE_STRATEGY
));

// and
CacheableEntity entityMetadata = new CacheableEntityBuilder()
var entityMetadata = new CacheableEntityBuilder()
.withKeyComponents("a", "b", "c")
.withKeyTemplate("{{a}}-{{b}}-{{c}}")
.build();

// when
Optional<? extends CacheKeyBuilder<?, Object>> keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);
var keyBuilder = factory.findCacheKeyBuilderFor(entityMetadata, Object.class);

// then
assertTrue(keyBuilder::isPresent);

Object keySource = new Object();
Object expectedKey = KeyBuilderStrategies.DUMMY_COMPONENT_STRATEGY
var keySource = new Object();
var expectedKey = KeyBuilderStrategies.DUMMY_COMPONENT_STRATEGY
.prepareBuilder(entityMetadata)
.buildKey(keySource);
Object actualKey = keyBuilder.orElseGet(Assertions::fail)
var actualKey = keyBuilder.orElseGet(Assertions::fail)
.buildKey(new Object());

assertEquals(expectedKey, actualKey);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package pl.allegro.tech.jsoncache.jackson.keybuilder.strategy;

import tools.jackson.databind.JsonNode;
import pl.allegro.tech.jsoncache.keybuilder.KeyPartMissingException;
import pl.allegro.tech.jsoncache.keybuilder.stategy.KeyComponentBasedStrategy;
import tools.jackson.databind.JsonNode;

import java.util.Optional;

/**
* Strategy for retrieving key parts directly from {@link JsonNode json nodes}.
* Nested components can be extracted using dot notation, e.g. "user.name" will extract "name" from nested "user" object.
*/
public class JsonComponentExtractingStrategy extends KeyComponentBasedStrategy<JsonNode> {

@Override
protected String extractKeyPart(JsonNode value, String component) throws KeyPartMissingException {
return value.optional(component)
return findComponentNode(value, component)
.filter(JsonNode::isValueNode)
.map(JsonNode::asString)
.orElseThrow(() -> new KeyPartMissingException(value, component));
Expand All @@ -22,4 +25,16 @@ protected Class<? extends JsonNode> supportedValueType() {
return JsonNode.class;
}

private Optional<JsonNode> findComponentNode(JsonNode value, String component) {
if (component.indexOf('.') == -1) {
// no nesting - just flat component
return value.optional(component);
}

for (var fragment : component.split("\\.")) {
value = value.path(fragment);
}
return value.asOptional();
}

}
Loading