diff --git a/discovery-service/src/main/java/org/zowe/apiml/discovery/ApimlInstanceRegistry.java b/discovery-service/src/main/java/org/zowe/apiml/discovery/ApimlInstanceRegistry.java index 365c01f977..45cb94a940 100644 --- a/discovery-service/src/main/java/org/zowe/apiml/discovery/ApimlInstanceRegistry.java +++ b/discovery-service/src/main/java/org/zowe/apiml/discovery/ApimlInstanceRegistry.java @@ -31,11 +31,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.invoke.WrongMethodTypeException; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -55,13 +51,6 @@ public class ApimlInstanceRegistry extends InstanceRegistry { private static final String EXCEPTION_MESSAGE = "Implementation of InstanceRegistry changed, please verify fix of order sending events"; - private MethodHandle handleRegistrationMethod; - private MethodHandle handlerResolveInstanceLeaseDurationMethod; - private MethodHandle handleCancellationMethod; - - private MethodHandle register2ArgsMethodHandle; - private MethodHandle register3ArgsMethodHandle; - private MethodHandle cancelMethodHandle; private MethodHandle replicateToPeersMethodHandle; private final ApplicationContext appCntx; @@ -70,6 +59,8 @@ public class ApimlInstanceRegistry extends InstanceRegistry { private ConcurrentHashMap>> registry; private Set staticRegistrationIds = Collections.synchronizedSet(new HashSet<>()); + private static final ThreadLocal RENEW_CORRECTION = new ThreadLocal<>(); + public ApimlInstanceRegistry( EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, @@ -96,79 +87,29 @@ public ApimlInstanceRegistry( */ private void init() { try { - Method registrationMethod = - InstanceRegistry.class.getDeclaredMethod("handleRegistration", - InstanceInfo.class, int.class, boolean.class - ); - registrationMethod.setAccessible(true); - handleRegistrationMethod = MethodHandles.lookup().unreflect(registrationMethod); - - Method cancelationMethod = - InstanceRegistry.class.getDeclaredMethod("handleCancelation", - String.class, String.class, boolean.class - ); - cancelationMethod.setAccessible(true); - handleCancellationMethod = MethodHandles.lookup().unreflect(cancelationMethod); - - Method resolveInstanceLeaseDurationMethod = - InstanceRegistry.class.getDeclaredMethod("resolveInstanceLeaseDuration", - InstanceInfo.class - ); - resolveInstanceLeaseDurationMethod.setAccessible(true); - handlerResolveInstanceLeaseDurationMethod = MethodHandles.lookup().unreflect(resolveInstanceLeaseDurationMethod); - - Constructor lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); - lookupConstructor.setAccessible(true); - MethodHandles.Lookup lookup = lookupConstructor.newInstance(PeerAwareInstanceRegistryImpl.class); - - register2ArgsMethodHandle = - lookup.findSpecial( - PeerAwareInstanceRegistryImpl.class, - "register", - MethodType.methodType(void.class, InstanceInfo.class, boolean.class), - PeerAwareInstanceRegistryImpl.class - ); - - cancelMethodHandle = - lookup.findSpecial( - PeerAwareInstanceRegistryImpl.class, - "cancel", - MethodType.methodType(boolean.class, String.class, String.class, boolean.class), - PeerAwareInstanceRegistryImpl.class - ); - - lookup = lookupConstructor.newInstance(AbstractInstanceRegistry.class); - - register3ArgsMethodHandle = - lookup.findSpecial( - AbstractInstanceRegistry.class, - "register", - MethodType.methodType(void.class, InstanceInfo.class, int.class, boolean.class), - AbstractInstanceRegistry.class - ); Field registryField = AbstractInstanceRegistry.class.getDeclaredField("registry"); registryField.setAccessible(true); this.registry = (ConcurrentHashMap>>) registryField.get(this); Method replicateToPeers = PeerAwareInstanceRegistryImpl.class.getDeclaredMethod("replicateToPeers", Action.class, String.class, String.class, InstanceInfo.class, InstanceStatus.class, boolean.class); replicateToPeers.setAccessible(true); - replicateToPeersMethodHandle = MethodHandles.lookup().unreflect(replicateToPeers); - } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) { throw new IllegalArgumentException(EXCEPTION_MESSAGE, e); } } - protected int resolveInstanceLeaseDurationRewritten(final InstanceInfo info) { - try { - return (int) handlerResolveInstanceLeaseDurationMethod.invokeWithArguments(this, info); - } catch (ClassCastException | WrongMethodTypeException e) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, e); - } catch (RuntimeException re) { - throw re; - } catch (Throwable t) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, t); + @Override + protected void updateRenewsPerMinThreshold() { + Integer correction = RENEW_CORRECTION.get(); + if (correction != null) { + synchronized (lock) { + this.expectedNumberOfClientsSendingRenews += correction; + } + RENEW_CORRECTION.remove(); } + + super.updateRenewsPerMinThreshold(); } public void peerAwareHeartbeat(InstanceInfo instanceInfo) { @@ -191,19 +132,17 @@ public void registerStatically(InstanceInfo instanceInfo, boolean isReplication, // the maximum lease duration time (Eureka bug: overflow of int during conversion to ms) int leaseDuration = Integer.MAX_VALUE / 1000; - // temporary register (do not increase count of service to avoid threshold) - synchronized (lock) { - int backup = expectedNumberOfClientsSendingRenews; - try { - register(instanceInfo, leaseDuration, isReplication); - if (peerReplicate) { - replicateToPeersMethodHandle.invokeWithArguments(this, Action.Register, instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null, isReplication); - } - } catch (Throwable e) { - throw new IllegalStateException(EXCEPTION_MESSAGE, e); - } finally { - expectedNumberOfClientsSendingRenews = backup; + try { + // temporary register (do not increase count of service to avoid threshold) + RENEW_CORRECTION.set(-1); + register(instanceInfo, leaseDuration, isReplication); + if (peerReplicate) { + replicateToPeersMethodHandle.invokeWithArguments(this, Action.Register, instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null, isReplication); } + } catch (Throwable e) { + throw new IllegalStateException(EXCEPTION_MESSAGE, e); + } finally { + RENEW_CORRECTION.remove(); } // register lease plan to never expired @@ -229,34 +168,18 @@ public boolean isExpired(long additionalLeaseMs) { */ @Override public void register(InstanceInfo info, int leaseDuration, boolean isReplication) { - validateInstanceInfo(info); - info = changeServiceId(info); - try { - register3ArgsMethodHandle.invokeWithArguments(this, info, leaseDuration, isReplication); - handleRegistrationMethod.invokeWithArguments(this, info, leaseDuration, isReplication); - } catch (ClassCastException | WrongMethodTypeException e) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, e); - } catch (RuntimeException re) { - throw re; - } catch (Throwable t) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, t); - } + validateInstanceInfo(info); + info = changeServiceId(info); + + super.register(info, leaseDuration, isReplication); } @Override public void register(InstanceInfo info, final boolean isReplication) { - validateInstanceInfo(info); - info = changeServiceId(info); - try { - register2ArgsMethodHandle.invokeWithArguments(this, info, isReplication); - handleRegistrationMethod.invokeWithArguments(this, info, resolveInstanceLeaseDurationRewritten(info), isReplication); - } catch (ClassCastException | WrongMethodTypeException e) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, e); - } catch (RuntimeException re) { - throw re; - } catch (Throwable t) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, t); - } + validateInstanceInfo(info); + info = changeServiceId(info); + + super.register(info, isReplication); } /** @@ -309,25 +232,16 @@ public boolean isRegisterable(InstanceInfo instanceInfo) { @Override public boolean cancel(String appName, String serverId, boolean isReplication) { - synchronized (lock) { - int backup = expectedNumberOfClientsSendingRenews; - try { - String[] updatedValues = replaceValues(appName, serverId); - final boolean out = (boolean) cancelMethodHandle.invokeWithArguments(this, updatedValues[0], updatedValues[1], isReplication); - handleCancellationMethod.invokeWithArguments(this, updatedValues[0], updatedValues[1], isReplication); - return out; - } catch (ClassCastException | WrongMethodTypeException e) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, e); - } catch (RuntimeException re) { - throw re; - } catch (Throwable t) { - throw new IllegalArgumentException(EXCEPTION_MESSAGE, t); - } finally { - if (staticRegistrationIds.removeAll(Optional.ofNullable(registry.get(appName)).orElse(Collections.emptyMap()).keySet())) { - // do not change count of instances if it was registered statically - expectedNumberOfClientsSendingRenews = backup; - } + try { + String[] updatedValues = replaceValues(appName, serverId); + + if (staticRegistrationIds.removeAll(Optional.ofNullable(registry.get(appName)).orElse(Collections.emptyMap()).keySet())) { + // do not change count of instances if it was registered statically + RENEW_CORRECTION.set(1); } + return super.cancel(updatedValues[0], updatedValues[1], isReplication); + } finally { + RENEW_CORRECTION.remove(); } } diff --git a/discovery-service/src/test/java/org/zowe/apiml/discovery/ApimlInstanceRegistryTest.java b/discovery-service/src/test/java/org/zowe/apiml/discovery/ApimlInstanceRegistryTest.java index 8da07bd686..2d06135f0b 100644 --- a/discovery-service/src/test/java/org/zowe/apiml/discovery/ApimlInstanceRegistryTest.java +++ b/discovery-service/src/test/java/org/zowe/apiml/discovery/ApimlInstanceRegistryTest.java @@ -10,15 +10,18 @@ package org.zowe.apiml.discovery; -import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.*; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; import com.netflix.eureka.DefaultEurekaServerConfig; import com.netflix.eureka.EurekaServerConfig; +import com.netflix.eureka.cluster.PeerEurekaNodes; import com.netflix.eureka.lease.Lease; import com.netflix.eureka.resources.ServerCodecs; import com.netflix.eureka.transport.EurekaServerHttpClientFactory; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -31,7 +34,6 @@ import org.zowe.apiml.discovery.config.EurekaConfig; import java.lang.invoke.MethodHandle; -import java.lang.invoke.WrongMethodTypeException; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -54,16 +56,19 @@ class ApimlInstanceRegistryTest { @Mock private EurekaServerHttpClientFactory eurekaServerHttpClientFactory; @Mock private InstanceRegistryProperties instanceRegistryProperties; @Mock private ApplicationContext appCntx; + @Mock private EurekaInstanceConfig eurekaInstanceConfig; + @Mock private PeerEurekaNodes peerEurekaNodes; private InstanceInfo standardInstance; private EurekaServerConfig serverConfig; @BeforeEach - void setUp() { + @SuppressWarnings("squid:S1874") + void setUp() throws Exception { standardInstance = getStandardInstance("hostname:serviceclient:10010", "serviceclient"); serverConfig = new DefaultEurekaServerConfig(); - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( + apimlInstanceRegistry = spy(init(new ApimlInstanceRegistry( serverConfig, clientConfig, serverCodecs, @@ -71,14 +76,19 @@ void setUp() { eurekaServerHttpClientFactory, instanceRegistryProperties, appCntx, - new EurekaConfig.Tuple("service*,hello"))); + new EurekaConfig.Tuple("service*,hello")))); - MethodHandle methodHandle = mock(MethodHandle.class); + doReturn("zowe").when(eurekaInstanceConfig).getNamespace(); + doReturn("discovery").when(eurekaInstanceConfig).getAppname(); + doReturn(new MyDataCenterInfo(DataCenterInfo.Name.MyOwn)).when(eurekaInstanceConfig).getDataCenterInfo(); + ApplicationInfoManager.getInstance().initComponent(eurekaInstanceConfig); + } - ReflectionTestUtils.setField(apimlInstanceRegistry, "handleRegistrationMethod", methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry, "register2ArgsMethodHandle", methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry, "register3ArgsMethodHandle", methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry, "handleCancellationMethod", methodHandle); + private ApimlInstanceRegistry init(ApimlInstanceRegistry apimlInstanceRegistry) throws Exception { + apimlInstanceRegistry.initializedResponseCache(); + apimlInstanceRegistry.init(peerEurekaNodes); + apimlInstanceRegistry.setApplicationContext(appCntx); + return apimlInstanceRegistry; } @Nested @@ -104,10 +114,10 @@ private static Stream instanceIds() { //we cannot fail for non-conformant services for backwards compatibility @ParameterizedTest @MethodSource("instanceIds") - void thenOnboard(String instanceId, String appName) { + void thenOnboard(String instanceId, String appName) throws Exception { InstanceInfo wrongInstance = getStandardInstance(instanceId, appName); - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( + apimlInstanceRegistry = spy(init(new ApimlInstanceRegistry( serverConfig, clientConfig, serverCodecs, @@ -115,10 +125,7 @@ void thenOnboard(String instanceId, String appName) { eurekaServerHttpClientFactory, instanceRegistryProperties, appCntx, - new EurekaConfig.Tuple(""))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry,"register3ArgsMethodHandle",methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry,"handleRegistrationMethod",methodHandle); + new EurekaConfig.Tuple("")))); assertDoesNotThrow( () -> apimlInstanceRegistry.register(wrongInstance, 1, false) ); @@ -130,9 +137,9 @@ void thenOnboard(String instanceId, String appName) { class GivenValidServiceIdWithDash { @Test - void thenShouldRegister() { + void thenShouldRegister() throws Exception { standardInstance = getStandardInstance("hostname:service-client:10010", "service-client"); - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( + apimlInstanceRegistry = spy(init(new ApimlInstanceRegistry( serverConfig, clientConfig, serverCodecs, @@ -140,18 +147,18 @@ void thenShouldRegister() { eurekaServerHttpClientFactory, instanceRegistryProperties, appCntx, - new EurekaConfig.Tuple(null))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry,"register2ArgsMethodHandle", methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry,"handleRegistrationMethod", methodHandle); + new EurekaConfig.Tuple(null)))); assertDoesNotThrow(() -> apimlInstanceRegistry.register(standardInstance, false)); } + } @Nested class GivenReplacerTuple { + @Nested class WhenChangeServiceId { + @Test void thenChangeServicePrefix() { InstanceInfo info = apimlInstanceRegistry.changeServiceId(standardInstance); @@ -164,8 +171,11 @@ void thenChangeServicePrefix() { assertEquals(9090, info.getSecurePort()); assertEquals("localhost", info.getSecureVipAddress()); } + } + } + private static Stream tuples() { return Stream.of( Arguments.of("service*,hello", "hostname:helloclient:10010"), @@ -182,8 +192,8 @@ private static Stream tuples() { @ParameterizedTest @MethodSource("tuples") - void thenShouldRegister(String tuple, String expectedServiceIdInResult) { - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( + void thenShouldRegister(String tuple, String expectedServiceIdInResult) throws Exception { + apimlInstanceRegistry = spy(init(new ApimlInstanceRegistry( serverConfig, clientConfig, serverCodecs, @@ -191,18 +201,15 @@ void thenShouldRegister(String tuple, String expectedServiceIdInResult) { eurekaServerHttpClientFactory, instanceRegistryProperties, appCntx, - new EurekaConfig.Tuple(tuple))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry,"register2ArgsMethodHandle", methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry,"handleRegistrationMethod", methodHandle); + new EurekaConfig.Tuple(tuple)))); apimlInstanceRegistry.register(standardInstance, false); assertEquals(expectedServiceIdInResult, standardInstance.getInstanceId()); } @ParameterizedTest @MethodSource("tuples") - void thenShouldRegisterWithSecondMethod(String tuple, String expectedServiceIdInResult) { - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( + void thenShouldRegisterWithSecondMethod(String tuple, String expectedServiceIdInResult) throws Exception { + apimlInstanceRegistry = spy(init(new ApimlInstanceRegistry( serverConfig, clientConfig, serverCodecs, @@ -210,138 +217,22 @@ void thenShouldRegisterWithSecondMethod(String tuple, String expectedServiceIdIn eurekaServerHttpClientFactory, instanceRegistryProperties, appCntx, - new EurekaConfig.Tuple(tuple))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry,"register3ArgsMethodHandle",methodHandle); - ReflectionTestUtils.setField(apimlInstanceRegistry,"handleRegistrationMethod",methodHandle); + new EurekaConfig.Tuple(tuple)))); apimlInstanceRegistry.register(standardInstance, 1, false); assertEquals(expectedServiceIdInResult, standardInstance.getInstanceId()); } @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - class WhenRegistrationMethodsFails { - - @ParameterizedTest - @MethodSource("exceptions") - void thenFirstMethodThrowsIllegalException(String tuple, Exception exception) throws Throwable { - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( - serverConfig, - clientConfig, - serverCodecs, - eurekaClient, - eurekaServerHttpClientFactory, - instanceRegistryProperties, - appCntx, - new EurekaConfig.Tuple(tuple))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "register2ArgsMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any())).thenThrow(exception); - assertThrows(IllegalArgumentException.class, () -> { - apimlInstanceRegistry.register(standardInstance, false); - }); - } - - @Test - void thenFirstMethodThrowRuntimeException() throws Throwable { - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( - serverConfig, - clientConfig, - serverCodecs, - eurekaClient, - eurekaServerHttpClientFactory, - instanceRegistryProperties, - appCntx, - new EurekaConfig.Tuple("service*,hello"))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "register2ArgsMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any())).thenThrow(new RuntimeException()); - assertThrows(RuntimeException.class, () -> { - apimlInstanceRegistry.register(standardInstance, false); - }); - } - - @ParameterizedTest - @MethodSource("exceptions") - void thenSecondMethodThrowsIllegalException(String tuple, Exception exception) throws Throwable { - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( - serverConfig, - clientConfig, - serverCodecs, - eurekaClient, - eurekaServerHttpClientFactory, - instanceRegistryProperties, - appCntx, - new EurekaConfig.Tuple(tuple))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "register3ArgsMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any(), any())).thenThrow(exception); - assertThrows(IllegalArgumentException.class, () -> { - apimlInstanceRegistry.register(standardInstance, 1, false); - }); - } - - @Test - void thenSecondMethodThrowRuntimeException() throws Throwable { - apimlInstanceRegistry = spy(new ApimlInstanceRegistry( - serverConfig, - clientConfig, - serverCodecs, - eurekaClient, - eurekaServerHttpClientFactory, - instanceRegistryProperties, - appCntx, - new EurekaConfig.Tuple("service*,hello"))); - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "register3ArgsMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any())).thenThrow(new RuntimeException()); - assertThrows(RuntimeException.class, () -> { - apimlInstanceRegistry.register(standardInstance, 1, false); - }); - } - - private Stream exceptions() { - return Stream.of( - Arguments.of("service*,hello", new WrongMethodTypeException()), - Arguments.of("service*,hello", new Exception(new Throwable())) - ); - } - } - - @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - class WhenResolveInstanceRewrittenFails { - @ParameterizedTest - @MethodSource("exceptions") - void thenThrowIllegalArgumentException(Exception exception) throws Throwable { - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "handlerResolveInstanceLeaseDurationMethod", methodHandle); - when(methodHandle.invokeWithArguments(any(), any())).thenThrow(exception); - assertThrows(IllegalArgumentException.class, () -> { - apimlInstanceRegistry.resolveInstanceLeaseDurationRewritten(standardInstance); - }); - } + class WhenStaticallyRegistration { - @Test - void thenThrowRuntimeException() throws Throwable { - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "handlerResolveInstanceLeaseDurationMethod", methodHandle); - when(methodHandle.invokeWithArguments(any(), any())).thenThrow(new RuntimeException()); - assertThrows(RuntimeException.class, () -> { - apimlInstanceRegistry.resolveInstanceLeaseDurationRewritten(standardInstance); - }); - } + private ThreadLocal renewCorrection; - private Stream exceptions() { - return Stream.of( - Arguments.of(new WrongMethodTypeException()), - Arguments.of(new Exception(new Throwable())) - ); + @BeforeEach + void setUp() { + renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); + renewCorrection.set(123); + ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); } - } - - @Nested - class WhenStaticallyRegistration { @Test @SuppressWarnings("unchecked") @@ -364,6 +255,44 @@ void givenStaticRegistration_thenSuccessful() throws Throwable { assertFalse(leaseMap.isEmpty()); } + @Test + void givenStaticDefinition_whenSuccessRegistration_thenExpectedNumberOfClientsSendingRenewsDidntChange() { + apimlInstanceRegistry.registerStatically(standardInstance, false, false); + + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + @Test + void givenStaticDefinition_whenFailRegistration_thenCorrectionAndExpectedNumberOfClientsSendingRenewsDidntChange() { + doThrow(new RuntimeException("test")).when(apimlInstanceRegistry).register(any(), anyInt(), anyBoolean()); + assertThrows(IllegalStateException.class, () -> apimlInstanceRegistry.registerStatically(standardInstance, false, false)); + + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + @Test + void givenStaticDefinition_whenSuccessCancellation_thenExpectedNumberOfClientsSendingRenewsDidntChange() { + apimlInstanceRegistry.registerStatically(standardInstance, false, false); + apimlInstanceRegistry.cancel(standardInstance.getAppName(), standardInstance.getInstanceId(), false); + + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + @Test + void givenNonStaticDefinition_whenSuccessCancellation_thenExpectedNumberOfClientsSendingRenewsChanged() { + renewCorrection.remove(); + apimlInstanceRegistry.register(standardInstance, false); // it increases the number to 6 + ((Set) ReflectionTestUtils.getField(apimlInstanceRegistry, "staticRegistrationIds")).clear(); + + apimlInstanceRegistry.cancel(standardInstance.getAppName(), standardInstance.getInstanceId(), false); + + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + } @Nested @@ -382,51 +311,6 @@ void givenPeerReplicaHeartbeat_thenSuccess() throws Throwable { } - @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - class WhenCancelRegistration { - - @Test - void thenIsSuccessful() throws Throwable { - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "cancelMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any(), any())).thenReturn(true); - apimlInstanceRegistry.register(standardInstance, false); - verify(apimlInstanceRegistry, times(1)).changeServiceId(any()); - boolean isCancelled = apimlInstanceRegistry.cancel("HELLO", "hello", false); - assertTrue(isCancelled); - } - - @ParameterizedTest - @MethodSource("exceptions") - void thenThrowIllegalArgumentException(Exception exception) throws Throwable { - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "cancelMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any(), any())).thenThrow(exception); - assertThrows(IllegalArgumentException.class, () -> { - apimlInstanceRegistry.cancel("HELLO", "hello", false); - }); - } - - @Test - void thenThrowRuntimeException() throws Throwable { - MethodHandle methodHandle = mock(MethodHandle.class); - ReflectionTestUtils.setField(apimlInstanceRegistry, "cancelMethodHandle", methodHandle); - when(methodHandle.invokeWithArguments(any(), any(), any(), any())).thenThrow(new RuntimeException()); - assertThrows(RuntimeException.class, () -> { - apimlInstanceRegistry.cancel("HELLO", "hello", false); - }); - } - - private Stream exceptions() { - return Stream.of( - Arguments.of(new WrongMethodTypeException()), - Arguments.of(new Exception(new Throwable())) - ); - } - } - - private InstanceInfo getStandardInstance(String instanceId, String appName) { return InstanceInfo.Builder.newBuilder() @@ -442,4 +326,39 @@ private InstanceInfo getStandardInstance(String instanceId, String appName) { .setStatus(InstanceInfo.InstanceStatus.UP) .build(); } + + @Nested + class UpdateRenewsPerMinThreshold { + + private ApimlInstanceRegistry registry; + private ThreadLocal renewCorrection; + + @BeforeEach + void setUp() { + registry = new ApimlInstanceRegistry( + serverConfig, clientConfig, serverCodecs, eurekaClient, + eurekaServerHttpClientFactory, instanceRegistryProperties, appCntx, new EurekaConfig.Tuple("") + ); + renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(registry, "RENEW_CORRECTION"); + } + + @Test + void givenCorrection_whenUpdateRenewsPerMinThreshold_thenProcessOnce() { + ReflectionTestUtils.setField(registry, "expectedNumberOfClientsSendingRenews", 5); + renewCorrection.set(-2); + registry.updateRenewsPerMinThreshold(); + assertEquals(3, ReflectionTestUtils.getField(registry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + @Test + void givenNoCorrection_whenUpdateRenewsPerMinThreshold_thenDoNothing() { + ReflectionTestUtils.setField(registry, "expectedNumberOfClientsSendingRenews", 5); + registry.updateRenewsPerMinThreshold(); + assertEquals(5, ReflectionTestUtils.getField(registry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + } + }