Skip to content

Commit

Permalink
Introduce cache injection and NoOpCache
Browse files Browse the repository at this point in the history
  • Loading branch information
gwenneg committed Nov 25, 2020
1 parent 31ea16f commit 35212e5
Show file tree
Hide file tree
Showing 28 changed files with 656 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
public class CacheConfig {

/**
* Whether or not the cache extension is enabled. If the extension is disabled, the caching annotations will have no effect
* at run time.
* Whether or not the cache extension is enabled.
*/
@ConfigItem(defaultValue = "true")
public boolean enabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import io.quarkus.cache.CacheInvalidate;
import io.quarkus.cache.CacheInvalidateAll;
import io.quarkus.cache.CacheKey;
import io.quarkus.cache.CacheName;
import io.quarkus.cache.CacheResult;

public class CacheDeploymentConstants {

// API annotations names.
public static final DotName CACHE_NAME = dotName(CacheName.class);
public static final DotName CACHE_INVALIDATE_ALL = dotName(CacheInvalidateAll.class);
public static final DotName CACHE_INVALIDATE_ALL_LIST = dotName(CacheInvalidateAll.List.class);
public static final DotName CACHE_INVALIDATE = dotName(CacheInvalidate.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.quarkus.cache.deployment.CacheDeploymentConstants.API_METHODS_ANNOTATIONS;
import static io.quarkus.cache.deployment.CacheDeploymentConstants.API_METHODS_ANNOTATIONS_LISTS;
import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_NAME;
import static io.quarkus.cache.deployment.CacheDeploymentConstants.CACHE_NAME_PARAM;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
import static org.jboss.jandex.AnnotationTarget.Kind.METHOD;
Expand All @@ -11,17 +12,19 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BooleanSupplier;

import javax.enterprise.inject.spi.DeploymentException;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.MethodInfo;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.AutoInjectAnnotationBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.ValidationPhaseBuildItem;
import io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem;
Expand All @@ -33,6 +36,7 @@
import io.quarkus.cache.runtime.CacheResultInterceptor;
import io.quarkus.cache.runtime.caffeine.CaffeineCacheBuildRecorder;
import io.quarkus.cache.runtime.caffeine.CaffeineCacheInfo;
import io.quarkus.cache.runtime.noop.NoOpCacheBuildRecorder;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
Expand All @@ -46,20 +50,25 @@ FeatureBuildItem feature() {
return new FeatureBuildItem(Feature.CACHE);
}

@BuildStep(onlyIf = CacheEnabled.class)
@BuildStep
AutoInjectAnnotationBuildItem autoInjectCacheName() {
return new AutoInjectAnnotationBuildItem(CACHE_NAME);
}

@BuildStep
AnnotationsTransformerBuildItem annotationsTransformer() {
return new AnnotationsTransformerBuildItem(new CacheAnnotationsTransformer());
}

@BuildStep(onlyIf = CacheEnabled.class)
@BuildStep
List<AdditionalBeanBuildItem> additionalBeans() {
return Arrays.asList(
new AdditionalBeanBuildItem(CacheInvalidateAllInterceptor.class),
new AdditionalBeanBuildItem(CacheInvalidateInterceptor.class),
new AdditionalBeanBuildItem(CacheResultInterceptor.class));
}

@BuildStep(onlyIf = CacheEnabled.class)
@BuildStep
ValidationErrorBuildItem validateBeanDeployment(ValidationPhaseBuildItem validationPhase) {
AnnotationStore annotationStore = validationPhase.getContext().get(Key.ANNOTATION_STORE);
List<Throwable> throwables = new ArrayList<>();
Expand All @@ -75,26 +84,30 @@ ValidationErrorBuildItem validateBeanDeployment(ValidationPhaseBuildItem validat
return new ValidationErrorBuildItem(throwables.toArray(new Throwable[0]));
}

@BuildStep(onlyIf = CacheEnabled.class)
@BuildStep
@Record(STATIC_INIT)
void recordCachesBuild(CombinedIndexBuildItem combinedIndex, BeanContainerBuildItem beanContainer, CacheConfig config,
CaffeineCacheBuildRecorder caffeineRecorder,
CaffeineCacheBuildRecorder caffeineRecorder, NoOpCacheBuildRecorder noOpRecorder,
List<AdditionalCacheNameBuildItem> additionalCacheNames) {
Set<String> cacheNames = getCacheNames(combinedIndex.getIndex());
for (AdditionalCacheNameBuildItem additionalCacheName : additionalCacheNames) {
cacheNames.add(additionalCacheName.getName());
}
switch (config.type) {
case CacheDeploymentConstants.CAFFEINE_CACHE_TYPE:
Set<CaffeineCacheInfo> cacheInfos = CaffeineCacheInfoBuilder.build(cacheNames, config);
caffeineRecorder.buildCaches(beanContainer.getValue(), cacheInfos);
break;
default:
throw new DeploymentException("Unknown cache type: " + config.type);
Set<String> cacheNames = getCacheNames(combinedIndex.getIndex(), additionalCacheNames);
validateCacheNameAnnotations(combinedIndex.getIndex(), cacheNames);
if (cacheNames.size() > 0) {
if (config.enabled) {
switch (config.type) {
case CacheDeploymentConstants.CAFFEINE_CACHE_TYPE:
Set<CaffeineCacheInfo> cacheInfos = CaffeineCacheInfoBuilder.build(cacheNames, config);
caffeineRecorder.buildCaches(beanContainer.getValue(), cacheInfos);
break;
default:
throw new DeploymentException("Unknown cache type: " + config.type);
}
} else {
noOpRecorder.buildCaches(beanContainer.getValue(), cacheNames);
}
}
}

private Set<String> getCacheNames(IndexView index) {
private Set<String> getCacheNames(IndexView index, List<AdditionalCacheNameBuildItem> additionalCacheNames) {
Set<String> cacheNames = new HashSet<>();
for (DotName cacheAnnotation : API_METHODS_ANNOTATIONS) {
for (AnnotationInstance annotation : index.getAnnotations(cacheAnnotation)) {
Expand All @@ -112,15 +125,29 @@ private Set<String> getCacheNames(IndexView index) {
}
}
}
for (AdditionalCacheNameBuildItem additionalCacheName : additionalCacheNames) {
cacheNames.add(additionalCacheName.getName());
}
return cacheNames;
}

private static class CacheEnabled implements BooleanSupplier {

CacheConfig config;

public boolean getAsBoolean() {
return config.enabled;
private void validateCacheNameAnnotations(IndexView index, Set<String> cacheNames) {
for (AnnotationInstance cacheNameAnnotation : index.getAnnotations(CACHE_NAME)) {
AnnotationTarget target = cacheNameAnnotation.target();
if (target.kind() == Kind.FIELD || target.kind() == Kind.METHOD_PARAMETER) {
String cacheName = cacheNameAnnotation.value().asString();
if (!cacheNames.contains(cacheName)) {
String declaringClass;
if (target.kind() == Kind.FIELD) {
declaringClass = target.asField().declaringClass().name().toString();
} else {
declaringClass = target.asMethodParameter().method().declaringClass().name().toString();
}
throw new DeploymentException(
"A field or method parameter is annotated with @CacheName(\"" + cacheName + "\") in the "
+ declaringClass + " class but there is no cache with this name in the application");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.cache.CacheManager;
import io.quarkus.cache.CacheResult;
import io.quarkus.cache.runtime.CacheRepository;
import io.quarkus.cache.runtime.caffeine.CaffeineCache;
import io.quarkus.test.QuarkusUnitTest;

Expand All @@ -29,11 +29,11 @@ public class CacheConfigTest {
private static final String CACHE_NAME = "test-cache";

@Inject
CacheRepository cacheRepository;
CacheManager cacheManager;

@Test
public void testConfig() {
CaffeineCache cache = (CaffeineCache) cacheRepository.getCache(CACHE_NAME);
CaffeineCache cache = (CaffeineCache) cacheManager.getCache(CACHE_NAME).get();
assertEquals(10, cache.getInitialCapacity());
assertEquals(100L, cache.getMaximumSize());
assertEquals(Duration.ofSeconds(30L), cache.getExpireAfterWrite());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.fail;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.spi.DeploymentException;

import org.jboss.shrinkwrap.api.ShrinkWrap;
Expand All @@ -10,18 +11,29 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.cache.CacheResult;
import io.quarkus.test.QuarkusUnitTest;

public class UnknownCacheTypeTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addAsResource(new StringAsset("quarkus.cache.type=i_am_an_unknown_cache_type"), "application.properties"))
.addAsResource(new StringAsset("quarkus.cache.type=i_am_an_unknown_cache_type"), "application.properties")
.addClass(CachedService.class))
.setExpectedException(DeploymentException.class);

@Test
public void shouldNotBeInvoked() {
fail("This method should not be invoked");
}

@ApplicationScoped
static class CachedService {

@CacheResult(cacheName = "test-cache")
public Object cachedMethod(String key) {
return new Object();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.cache.test.deployment;

import static org.junit.jupiter.api.Assertions.fail;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.spi.DeploymentException;
import javax.inject.Inject;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheName;
import io.quarkus.cache.CacheResult;
import io.quarkus.test.QuarkusUnitTest;

public class UnknownFieldCacheNameTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(CachedService.class, TestBean.class))
.setExpectedException(DeploymentException.class);

@Test
public void shouldNotBeInvoked() {
fail("This method should not be invoked");
}

@ApplicationScoped
static class CachedService {

@CacheResult(cacheName = "test-cache")
public Object getObject(Object key) {
return new Object();
}
}

@Dependent
static class TestBean {

@Inject
@CacheName("unknown-cache")
Cache cache;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.cache.test.deployment;

import static org.junit.jupiter.api.Assertions.fail;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.spi.DeploymentException;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.cache.Cache;
import io.quarkus.cache.CacheName;
import io.quarkus.cache.CacheResult;
import io.quarkus.test.QuarkusUnitTest;

public class UnknownMethodParameterCacheNameTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(CachedService.class, TestBean.class))
.setExpectedException(DeploymentException.class);

@Test
public void shouldNotBeInvoked() {
fail("This method should not be invoked");
}

@ApplicationScoped
static class CachedService {

@CacheResult(cacheName = "test-cache")
public Object getObject(Object key) {
return new Object();
}
}

@Dependent
static class TestBean {

public void unknownCacheName(@CacheName("unknown-cache") Cache cache) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.junit.jupiter.api.Test;

import io.quarkus.cache.runtime.AbstractCache;
import io.quarkus.cache.runtime.CacheInterceptor;
import io.quarkus.cache.runtime.CompositeCacheKey;
import io.quarkus.cache.runtime.DefaultCacheKey;
Expand All @@ -21,7 +22,7 @@ public void testDefaultKey() {
cacheInfo.name = "test-cache";
CaffeineCache cache = new CaffeineCache(cacheInfo);

DefaultCacheKey expectedKey = new DefaultCacheKey(cache.getName());
DefaultCacheKey expectedKey = new DefaultCacheKey(cacheInfo.name);
Object actualKey = getCacheKey(cache, new short[] {}, new Object[] {});
assertEquals(expectedKey, actualKey);
}
Expand Down Expand Up @@ -60,7 +61,7 @@ public void testImplicitCompositeKey() {
assertEquals(expectedKey, actualKey);
}

private Object getCacheKey(CaffeineCache cache, short[] cacheKeyParameterPositions, Object[] methodParameterValues) {
private Object getCacheKey(AbstractCache cache, short[] cacheKeyParameterPositions, Object[] methodParameterValues) {
return TEST_CACHE_INTERCEPTOR.getCacheKey(cache, cacheKeyParameterPositions, methodParameterValues);
}

Expand All @@ -71,7 +72,7 @@ private Object getCacheKey(short[] cacheKeyParameterPositions, Object[] methodPa
// This inner class changes the CacheInterceptor#getCacheKey method visibility to public.
private static class TestCacheInterceptor extends CacheInterceptor {
@Override
public Object getCacheKey(CaffeineCache cache, short[] cacheKeyParameterPositions, Object[] methodParameterValues) {
public Object getCacheKey(AbstractCache cache, short[] cacheKeyParameterPositions, Object[] methodParameterValues) {
return super.getCacheKey(cache, cacheKeyParameterPositions, methodParameterValues);
}
}
Expand Down
Loading

0 comments on commit 35212e5

Please sign in to comment.