Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce cache injection and NoOpCache #13296

Merged
merged 1 commit into from
Dec 11, 2020
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 @@ -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,12 +8,15 @@
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;
import io.quarkus.cache.runtime.CacheKeyParameterPositions;
import io.quarkus.cache.runtime.CacheProducer;

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 All @@ -24,8 +27,8 @@ public class CacheDeploymentConstants {
CACHE_RESULT, CACHE_INVALIDATE, CACHE_INVALIDATE_ALL);
public static final List<DotName> API_METHODS_ANNOTATIONS_LISTS = Arrays.asList(
CACHE_INVALIDATE_LIST, CACHE_INVALIDATE_ALL_LIST);
public static final DotName CACHE_KEY_PARAMETER_POSITIONS = DotName
.createSimple(CacheKeyParameterPositions.class.getName());
public static final DotName CACHE_KEY_PARAMETER_POSITIONS = dotName(CacheKeyParameterPositions.class);
public static final DotName CACHE_PRODUCER = dotName(CacheProducer.class);

// Annotations parameters.
public static final String CACHE_NAME_PARAM = "cacheName";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

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.cache.deployment.CacheDeploymentConstants.CACHE_PRODUCER;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
import static org.jboss.jandex.AnnotationTarget.Kind.METHOD;

Expand All @@ -11,17 +13,20 @@
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.ClassInfo;
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 +38,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 +52,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 +86,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 +127,36 @@ 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)) {
ClassInfo declaringClass;
if (target.kind() == Kind.FIELD) {
declaringClass = target.asField().declaringClass();
} else {
declaringClass = target.asMethodParameter().method().declaringClass();
}
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");
}
} else if (target.kind() == Kind.METHOD) {
ClassInfo declaringClass = target.asMethod().declaringClass();
if (!CACHE_PRODUCER.equals(declaringClass.name())) {
throw new DeploymentException(
"The @CacheName annotation is not allowed on a method: [class= "
+ declaringClass + ", method= " + target.asMethod().name() + "]");
}
}
}
}
}
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
@@ -0,0 +1,36 @@
package io.quarkus.cache.test.deployment;

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;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

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

public class MethodCacheNameTest {

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

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

@ApplicationScoped
static class CachedService {

@CacheName("illegal-annotation-target")
public Object getObject(Object key) {
return new Object();
}
}
}
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,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 UnknownConstructorParameterCacheNameTest {

@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 TestBean(@CacheName("unknown-cache") Cache cache) {
}
}
}
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;
}
}
Loading