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..5e90a1ab7c95ff 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,5 +1,6 @@ package org.jboss.resteasy.reactive.server.processor.scanning; +import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -10,6 +11,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 +29,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 = scanImpl(actualMethod, actualEndpointClass, methodContext); + if (customizers.isEmpty()) { + // if no annotations where found on implementation, check the interface + customizers = scanImpl(method, currentClassInfo, methodContext); + } + return customizers; + } else { + return scanImpl(method, actualEndpointClass, methodContext); + } + } + + private List scanImpl(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); } 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..2ddb0ee8b81a50 --- /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,82 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; + +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..aff5808b4773f3 --- /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,82 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; + +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..9ed3310ca2d5d1 --- /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,82 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +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..0ffef27beced1e --- /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,82 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +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..77597dfd22117b --- /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 io.restassured.RestAssured; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import org.jboss.resteasy.reactive.Cache; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +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..1b1b1d5121e22d --- /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,82 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; + +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..6abc52773e4a29 --- /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,82 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; + +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..f9c196dc1b54e5 --- /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,101 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +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..1f93642c64e4d2 --- /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,101 @@ +package org.jboss.resteasy.reactive.server.vertx.test.cache; + +import io.restassured.RestAssured; +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 java.util.function.Consumer; +import java.util.function.Supplier; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +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"; + } + } +}