From 7bbc1700c8b66009a72517d1e87370765ca45733 Mon Sep 17 00:00:00 2001 From: Daniel Bobbert Date: Thu, 29 Feb 2024 09:37:06 +0100 Subject: [PATCH] Honor Cache and NoCache annotations on implementations of a JaxRS interface --- .../scanning/CacheControlScanner.java | 44 ++++++-- ...heOnClassAndMethodsImplementationTest.java | 84 ++++++++++++++ .../CacheOnClassAndMethodsInterfaceTest.java | 84 ++++++++++++++ .../CacheOnMethodsImplementationTest.java | 84 ++++++++++++++ .../cache/CacheOnMethodsInterfaceTest.java | 84 ++++++++++++++ ...ImplementationOverridingInterfaceTest.java | 96 ++++++++++++++++ ...heOnClassAndMethodsImplementationTest.java | 84 ++++++++++++++ ...NoCacheOnClassAndMethodsInterfaceTest.java | 84 ++++++++++++++ .../NoCacheOnMethodsImplementationTest.java | 103 ++++++++++++++++++ .../cache/NoCacheOnMethodsInterfaceTest.java | 103 ++++++++++++++++++ 10 files changed, 840 insertions(+), 10 deletions(-) create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsImplementationTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsInterfaceTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsImplementationTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsInterfaceTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/ImplementationOverridingInterfaceTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsImplementationTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsInterfaceTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsImplementationTest.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsInterfaceTest.java diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java index f89391bb8764ed..150b86533e5367 100644 --- a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java @@ -1,6 +1,6 @@ package org.jboss.resteasy.reactive.server.processor.scanning; -import java.util.Arrays; +import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; import java.util.Map; @@ -10,6 +10,7 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; import org.jboss.resteasy.reactive.Cache; import org.jboss.resteasy.reactive.NoCache; import org.jboss.resteasy.reactive.common.processor.EndpointIndexer; @@ -27,32 +28,55 @@ public class CacheControlScanner implements MethodScanner { @Override public List scan(MethodInfo method, ClassInfo actualEndpointClass, Map methodContext) { + + ClassInfo currentClassInfo = method.declaringClass(); + + // Check whether the actualEndpointClass is implementing an interface containing the method + // In this case, allow annotations on the implementation itself to take precedence over annotations on the + // interface. + if (!actualEndpointClass.equals(currentClassInfo) && Modifier.isInterface(currentClassInfo.flags())) { + MethodInfo actualMethod = actualEndpointClass.method(method.name(), method.parameterTypes().toArray(new Type[0])); + + // first check bean implementation + List customizers = doScan(actualMethod, actualEndpointClass, methodContext); + if (customizers.isEmpty()) { + // if no annotations where found on implementation, check the interface + customizers = doScan(method, currentClassInfo, methodContext); + } + return customizers; + } else { + return doScan(method, actualEndpointClass, methodContext); + } + } + + private List doScan(MethodInfo methodInfo, ClassInfo classInfo, + Map methodContext) { AnnotationStore annotationStore = (AnnotationStore) methodContext.get(EndpointIndexer.METHOD_CONTEXT_ANNOTATION_STORE); - ExtendedCacheControl cacheControl = noCacheToCacheControl(annotationStore.getAnnotation(method, NO_CACHE)); + ExtendedCacheControl cacheControl = noCacheToCacheControl(annotationStore.getAnnotation(methodInfo, NO_CACHE)); if (cacheControl != null) { - if (method.annotation(CACHE) != null) { + if (methodInfo.annotation(CACHE) != null) { throw new IllegalStateException( "A resource method cannot be simultaneously annotated with '@Cache' and '@NoCache'. Offending method is '" - + method.name() + "' of class '" + method.declaringClass().name() + "'"); + + methodInfo.name() + "' of class '" + methodInfo.declaringClass().name() + "'"); } return cacheControlToCustomizerList(cacheControl); } else { - cacheControl = noCacheToCacheControl(annotationStore.getAnnotation(actualEndpointClass, NO_CACHE)); + cacheControl = noCacheToCacheControl(annotationStore.getAnnotation(classInfo, NO_CACHE)); if (cacheControl != null) { - if (actualEndpointClass.declaredAnnotation(CACHE) != null) { + if (classInfo.declaredAnnotation(CACHE) != null) { throw new IllegalStateException( "A resource class cannot be simultaneously annotated with '@Cache' and '@NoCache'. Offending class is '" - + actualEndpointClass.name() + "'"); + + classInfo.name() + "'"); } return cacheControlToCustomizerList(cacheControl); } } - cacheControl = cacheToCacheControl(method.annotation(CACHE)); + cacheControl = cacheToCacheControl(methodInfo.annotation(CACHE)); if (cacheControl != null) { return cacheControlToCustomizerList(cacheControl); } else { - cacheControl = cacheToCacheControl(actualEndpointClass.declaredAnnotation(CACHE)); + cacheControl = cacheToCacheControl(classInfo.declaredAnnotation(CACHE)); if (cacheControl != null) { return cacheControlToCustomizerList(cacheControl); } @@ -72,7 +96,7 @@ private ExtendedCacheControl noCacheToCacheControl(AnnotationInstance noCacheIns if (fieldsValue != null) { String[] fields = fieldsValue.asStringArray(); if ((fields != null) && (fields.length > 0)) { - cacheControl.getNoCacheFields().addAll(Arrays.asList(fields)); + cacheControl.getNoCacheFields().addAll(List.of(fields)); } } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsImplementationTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsImplementationTest.java new file mode 100644 index 00000000000000..034de1d33ae74e --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsImplementationTest.java @@ -0,0 +1,84 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.Cache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class CacheOnClassAndMethodsImplementationTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(ResourceWithCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "no-store"); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", "no-cache, no-transform, proxy-revalidate, s-maxage=100"); + } + + @Path("test") + public interface IResourceWithCache { + + @Path("with") + @GET + String with(); + + @Path("without") + @GET + String without(); + } + + @Cache(sMaxAge = 100, noTransform = true, proxyRevalidate = true, noCache = true) + public static class ResourceWithCache implements IResourceWithCache { + + @Override + @Cache(noStore = true) + public String with() { + return "with"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsInterfaceTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsInterfaceTest.java new file mode 100644 index 00000000000000..4838a76447741c --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnClassAndMethodsInterfaceTest.java @@ -0,0 +1,84 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.Cache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class CacheOnClassAndMethodsInterfaceTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(ResourceWithCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "no-store"); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", "no-cache, no-transform, proxy-revalidate, s-maxage=100"); + } + + @Path("test") + @Cache(sMaxAge = 100, noTransform = true, proxyRevalidate = true, noCache = true) + public interface IResourceWithCache { + + @Path("with") + @Cache(noStore = true) + @GET + String with(); + + @Path("without") + @GET + String without(); + } + + public static class ResourceWithCache implements IResourceWithCache { + + @Override + public String with() { + return "with"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsImplementationTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsImplementationTest.java new file mode 100644 index 00000000000000..58ee751ffe012d --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsImplementationTest.java @@ -0,0 +1,84 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.Cache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class CacheOnMethodsImplementationTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithCache.class, ResourceWithCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "must-revalidate, no-store, max-age=100, private"); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", nullValue()); + } + + @Path("test") + public interface IResourceWithCache { + + @Path("with") + @GET + String with(); + + @Path("without") + @GET + String without(); + } + + public static class ResourceWithCache implements IResourceWithCache { + + @Override + @Cache(maxAge = 100, noStore = true, mustRevalidate = true, isPrivate = true) + public String with() { + return "with"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsInterfaceTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsInterfaceTest.java new file mode 100644 index 00000000000000..d5dcaf574eed85 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/CacheOnMethodsInterfaceTest.java @@ -0,0 +1,84 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.Cache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class CacheOnMethodsInterfaceTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithCache.class, ResourceWithCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "must-revalidate, no-store, max-age=100, private"); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", nullValue()); + } + + @Path("test") + public interface IResourceWithCache { + + @Path("with") + @GET + @Cache(maxAge = 100, noStore = true, mustRevalidate = true, isPrivate = true) + String with(); + + @Path("without") + @GET + String without(); + } + + public static class ResourceWithCache implements IResourceWithCache { + + @Override + public String with() { + return "with"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/ImplementationOverridingInterfaceTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/ImplementationOverridingInterfaceTest.java new file mode 100644 index 00000000000000..97127255a3d0c0 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/ImplementationOverridingInterfaceTest.java @@ -0,0 +1,96 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.Cache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class ImplementationOverridingInterfaceTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithCache.class, ResourceWithCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "must-revalidate, no-store, max-age=100, private"); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", nullValue()); + } + + @Path("test") + public interface IResourceWithCache { + + @Path("with") + @GET + String with(); + + @Path("with") + @GET + @Cache(maxAge = 100, noStore = true, mustRevalidate = true, isPrivate = true) + String overridden(); + + @Path("without") + @GET + String without(); + } + + @ApplicationScoped + public static class ResourceWithCache implements IResourceWithCache { + + @Override + @Cache(maxAge = 100, noStore = true, mustRevalidate = true, isPrivate = true) + public String with() { + return "with"; + } + + @Override + @NoCache + public String overridden() { + return "overridden"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsImplementationTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsImplementationTest.java new file mode 100644 index 00000000000000..80c4fba5818f1a --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsImplementationTest.java @@ -0,0 +1,84 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.NoCache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class NoCacheOnClassAndMethodsImplementationTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithNoCache.class, ResourceWithNoCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "no-cache=\"f1\", no-cache=\"f2\""); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", "no-cache=\"f1\""); + } + + @Path("test") + public interface IResourceWithNoCache { + + @Path("with") + @GET + String with(); + + @Path("without") + @GET + String without(); + } + + @NoCache(fields = "f1") + public static class ResourceWithNoCache implements IResourceWithNoCache { + + @Override + @NoCache(fields = { "f1", "f2" }) + public String with() { + return "with"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsInterfaceTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsInterfaceTest.java new file mode 100644 index 00000000000000..7070ce972a6a7a --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnClassAndMethodsInterfaceTest.java @@ -0,0 +1,84 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.NoCache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class NoCacheOnClassAndMethodsInterfaceTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithNoCache.class, ResourceWithNoCache.class); + } + }); + + @Test + public void testWith() { + RestAssured.get("/test/with") + .then() + .statusCode(200) + .body(equalTo("with")) + .header("Cache-Control", "no-cache=\"f1\", no-cache=\"f2\""); + } + + @Test + public void testWithout() { + RestAssured.get("/test/without") + .then() + .statusCode(200) + .body(equalTo("without")) + .header("Cache-Control", "no-cache=\"f1\""); + } + + @NoCache(fields = "f1") + @Path("test") + public interface IResourceWithNoCache { + + @Path("with") + @GET + @NoCache(fields = { "f1", "f2" }) + String with(); + + @Path("without") + @GET + String without(); + } + + public static class ResourceWithNoCache implements IResourceWithNoCache { + + @Override + public String with() { + return "with"; + } + + @Override + public String without() { + return "without"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsImplementationTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsImplementationTest.java new file mode 100644 index 00000000000000..24b5893ab77d7b --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsImplementationTest.java @@ -0,0 +1,103 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.NoCache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class NoCacheOnMethodsImplementationTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithNoCache.class, ResourceWithNoCache.class); + } + }); + + @Test + public void testWithFields() { + RestAssured.get("/test/withFields") + .then() + .statusCode(200) + .body(equalTo("withFields")) + .header("Cache-Control", "no-cache=\"f1\", no-cache=\"f2\""); + } + + @Test + public void testWithoutFields() { + RestAssured.get("/test/withoutFields") + .then() + .statusCode(200) + .body(equalTo("withoutFields")) + .header("Cache-Control", "no-cache"); + } + + @Test + public void testWithoutAnnotation() { + RestAssured.get("/test/withoutAnnotation") + .then() + .statusCode(200) + .body(equalTo("withoutAnnotation")) + .header("Cache-Control", nullValue()); + } + + @Path("test") + public interface IResourceWithNoCache { + + @Path("withFields") + @GET + String withFields(); + + @Path("withoutFields") + @GET + String withoutFields(); + + @Path("withoutAnnotation") + @GET + String withoutAnnotation(); + } + + public static class ResourceWithNoCache implements IResourceWithNoCache { + + @Override + @NoCache(fields = { "f1", "f2" }) + public String withFields() { + return "withFields"; + } + + @Override + @NoCache + public String withoutFields() { + return "withoutFields"; + } + + @Override + public String withoutAnnotation() { + return "withoutAnnotation"; + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsInterfaceTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsInterfaceTest.java new file mode 100644 index 00000000000000..8e507a700eb8f3 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/cache/NoCacheOnMethodsInterfaceTest.java @@ -0,0 +1,103 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.NoCache; +import org.jboss.resteasy.reactive.server.processor.ResteasyReactiveDeploymentManager; +import org.jboss.resteasy.reactive.server.processor.scanning.CacheControlScanner; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.restassured.RestAssured; + +public class NoCacheOnMethodsInterfaceTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .addScanCustomizer(new Consumer() { + @Override + public void accept(ResteasyReactiveDeploymentManager.ScanStep scanStep) { + scanStep.addMethodScanner(new CacheControlScanner()); + } + }) + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(IResourceWithNoCache.class, ResourceWithNoCache.class); + } + }); + + @Test + public void testWithFields() { + RestAssured.get("/test/withFields") + .then() + .statusCode(200) + .body(equalTo("withFields")) + .header("Cache-Control", "no-cache=\"f1\", no-cache=\"f2\""); + } + + @Test + public void testWithoutFields() { + RestAssured.get("/test/withoutFields") + .then() + .statusCode(200) + .body(equalTo("withoutFields")) + .header("Cache-Control", "no-cache"); + } + + @Test + public void testWithoutAnnotation() { + RestAssured.get("/test/withoutAnnotation") + .then() + .statusCode(200) + .body(equalTo("withoutAnnotation")) + .header("Cache-Control", nullValue()); + } + + @Path("test") + public interface IResourceWithNoCache { + + @Path("withFields") + @GET + @NoCache(fields = { "f1", "f2" }) + String withFields(); + + @Path("withoutFields") + @GET + @NoCache + String withoutFields(); + + @Path("withoutAnnotation") + @GET + String withoutAnnotation(); + } + + public static class ResourceWithNoCache implements IResourceWithNoCache { + + @Override + public String withFields() { + return "withFields"; + } + + @Override + public String withoutFields() { + return "withoutFields"; + } + + @Override + public String withoutAnnotation() { + return "withoutAnnotation"; + } + } +}