From 0785411be4f212c970c62c4073a0e91078c2ecff Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 21 Dec 2023 11:54:33 -0500 Subject: [PATCH] Add lazy Supplier based versions of callHooks (#5566) Add lazy Supplier based versions of callHooks pom warnings --- .../api/IBaseInterceptorBroadcaster.java | 27 ++++ .../executor/InterceptorServiceTest.java | 121 ++++++++++++++++-- hapi-tinder-plugin/pom.xml | 1 + pom.xml | 1 + 4 files changed, 136 insertions(+), 14 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorBroadcaster.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorBroadcaster.java index 7c37ca9aced..a7545a51ae8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorBroadcaster.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorBroadcaster.java @@ -19,6 +19,8 @@ */ package ca.uhn.fhir.interceptor.api; +import java.util.function.Supplier; + public interface IBaseInterceptorBroadcaster { /** @@ -29,6 +31,18 @@ public interface IBaseInterceptorBroadcaster { */ boolean callHooks(POINTCUT thePointcut, HookParams theParams); + /** + * A supplier-based callHooks() for lazy construction of the HookParameters. + * @return false if any hook methods return false, return true otherwise. + */ + default boolean ifHasCallHooks(POINTCUT thePointcut, Supplier theParamsSupplier) { + if (hasHooks(thePointcut)) { + HookParams params = theParamsSupplier.get(); + return callHooks(thePointcut, params); + } + return true; // callHooks returns true when none present; + } + /** * Invoke registered interceptor hook methods for the given Pointcut. This method * should only be called for pointcuts that return a type other than @@ -38,6 +52,19 @@ public interface IBaseInterceptorBroadcaster { */ Object callHooksAndReturnObject(POINTCUT thePointcut, HookParams theParams); + /** + * A supplier-based version of callHooksAndReturnObject for lazy construction of the params. + * + * @return Returns the object returned by the first hook method that did not return null or null + */ + default Object ifHasCallHooksAndReturnObject(POINTCUT thePointcut, Supplier theParams) { + if (hasHooks(thePointcut)) { + HookParams params = theParams.get(); + return callHooksAndReturnObject(thePointcut, params); + } + return null; + } + /** * Does this broadcaster have any hooks for the given pointcut? * diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java index d087b1d697f..9b8a5086835 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/interceptor/executor/InterceptorServiceTest.java @@ -11,7 +11,9 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -20,12 +22,7 @@ import java.util.List; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; public class InterceptorServiceTest { @@ -210,17 +207,17 @@ public class InterceptorServiceTest { svc.registerInterceptor(myInterceptorManual); List globalInterceptors = svc.getGlobalInterceptorsForUnitTest(); assertEquals(3, globalInterceptors.size()); - assertTrue(globalInterceptors.get(0) instanceof MyTestInterceptorOne, globalInterceptors.get(0).getClass().toString()); - assertTrue(globalInterceptors.get(1) instanceof MyTestInterceptorManual, globalInterceptors.get(1).getClass().toString()); - assertTrue(globalInterceptors.get(2) instanceof MyTestInterceptorTwo, globalInterceptors.get(2).getClass().toString()); + assertInstanceOf(MyTestInterceptorOne.class, globalInterceptors.get(0), globalInterceptors.get(0).getClass().toString()); + assertInstanceOf(MyTestInterceptorManual.class, globalInterceptors.get(1), globalInterceptors.get(1).getClass().toString()); + assertInstanceOf(MyTestInterceptorTwo.class, globalInterceptors.get(2), globalInterceptors.get(2).getClass().toString()); // Try to register again (should have no effect svc.registerInterceptor(myInterceptorManual); globalInterceptors = svc.getGlobalInterceptorsForUnitTest(); assertEquals(3, globalInterceptors.size()); - assertTrue(globalInterceptors.get(0) instanceof MyTestInterceptorOne, globalInterceptors.get(0).getClass().toString()); - assertTrue(globalInterceptors.get(1) instanceof MyTestInterceptorManual, globalInterceptors.get(1).getClass().toString()); - assertTrue(globalInterceptors.get(2) instanceof MyTestInterceptorTwo, globalInterceptors.get(2).getClass().toString()); + assertInstanceOf(MyTestInterceptorOne.class, globalInterceptors.get(0), globalInterceptors.get(0).getClass().toString()); + assertInstanceOf(MyTestInterceptorManual.class, globalInterceptors.get(1), globalInterceptors.get(1).getClass().toString()); + assertInstanceOf(MyTestInterceptorTwo.class, globalInterceptors.get(2), globalInterceptors.get(2).getClass().toString()); // Make sure we have the right invokers in the right order List invokers = svc.getInterceptorsWithInvokersForPointcut(Pointcut.TEST_RB); @@ -232,8 +229,8 @@ public class InterceptorServiceTest { svc.unregisterInterceptor(myInterceptorManual); globalInterceptors = svc.getGlobalInterceptorsForUnitTest(); assertEquals(2, globalInterceptors.size()); - assertTrue(globalInterceptors.get(0) instanceof MyTestInterceptorOne, globalInterceptors.get(0).getClass().toString()); - assertTrue(globalInterceptors.get(1) instanceof MyTestInterceptorTwo, globalInterceptors.get(1).getClass().toString()); + assertInstanceOf(MyTestInterceptorOne.class, globalInterceptors.get(0), globalInterceptors.get(0).getClass().toString()); + assertInstanceOf(MyTestInterceptorTwo.class, globalInterceptors.get(1), globalInterceptors.get(1).getClass().toString()); // Unregister the two others assertTrue(svc.hasHooks(Pointcut.TEST_RB)); @@ -512,6 +509,102 @@ public class InterceptorServiceTest { } } + /** + * Verify the ifPresent methods match the base behaviour. + */ + @Nested + class SupplierDefaultMethods { + HookParams params = new HookParams("1", "2"); + + @Test + void testBooleanWithNoHooks_returnsTrue() { + InterceptorService svc = new InterceptorService(); + + assertTrue(svc.callHooks(Pointcut.TEST_RB, params)); + assertTrue(svc.ifHasCallHooks(Pointcut.TEST_RB, ()->params)); + } + + @Test + void testBooleanWithAllHooksReturnTrue_returnsTrue() { + InterceptorService svc = new InterceptorService(); + svc.registerInterceptor(new BooleanHook(true)); + svc.registerInterceptor(new BooleanHook(true)); + + assertTrue(svc.callHooks(Pointcut.TEST_RB, params)); + assertTrue(svc.ifHasCallHooks(Pointcut.TEST_RB, ()->params)); + } + + @Test + void testBooleanWithAHookReturnFalse_returnsFalse() { + InterceptorService svc = new InterceptorService(); + svc.registerInterceptor(new BooleanHook(true)); + svc.registerInterceptor(new BooleanHook(false)); + svc.registerInterceptor(new BooleanHook(true)); + + assertFalse(svc.callHooks(Pointcut.TEST_RB, params)); + assertFalse(svc.ifHasCallHooks(Pointcut.TEST_RB, ()->params)); + } + + + @Test + void testObjectWithNoHooks_returnsNull() { + InterceptorService svc = new InterceptorService(); + + assertNull(svc.callHooksAndReturnObject(Pointcut.TEST_RO, params)); + assertNull(svc.ifHasCallHooksAndReturnObject(Pointcut.TEST_RO, ()->params)); + } + + @Test + void testObjectWithAllHooksReturnNull_returnsNull() { + InterceptorService svc = new InterceptorService(); + svc.registerInterceptor(new ObjectHook<>(null)); + svc.registerInterceptor(new ObjectHook<>(null)); + + assertNull(svc.callHooksAndReturnObject(Pointcut.TEST_RO, params)); + assertNull(svc.ifHasCallHooksAndReturnObject(Pointcut.TEST_RO, ()->params)); + } + + @Test + void testObjectWithAHookReturnValue_returnsFirstValue() { + InterceptorService svc = new InterceptorService(); + svc.registerInterceptor(new ObjectHook<>(null)); + svc.registerInterceptor(new ObjectHook<>(new ResourceNotFoundException("first"))); + svc.registerInterceptor(new ObjectHook<>(new ResourceNotFoundException("second"))); + + assertEquals("first", ((BaseServerResponseException) svc.callHooksAndReturnObject(Pointcut.TEST_RO, params)).getMessage()); + assertEquals("first", ((BaseServerResponseException) svc.ifHasCallHooksAndReturnObject(Pointcut.TEST_RO, () -> params)).getMessage()); + } + + static class BooleanHook { + + final boolean myResult; + + BooleanHook(boolean theResult) { + myResult = theResult; + } + + @Hook(Pointcut.TEST_RB) + boolean doIt() { + return myResult; + } + } + + static class ObjectHook { + final T myResult; + + ObjectHook(T theResult) { + myResult = theResult; + } + + @Hook(Pointcut.TEST_RO) + T doIt() { + return myResult; + } + + } + } + + @BeforeEach public void before() { myInvocations.clear(); diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 310dbbcd3b4..9168501c763 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -184,6 +184,7 @@ org.apache.maven maven-plugin-api + provided org.apache.maven.plugin-tools diff --git a/pom.xml b/pom.xml index 8c52e0c4f79..ed1ecd0457e 100644 --- a/pom.xml +++ b/pom.xml @@ -1582,6 +1582,7 @@ org.apache.maven maven-plugin-api 3.8.6 + provided org.apache.maven.plugin-tools