From 1877dfc341a311cb55e231af45683cd028d4b3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Fri, 29 May 2026 15:24:05 +0200 Subject: [PATCH 1/7] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../discovery/ApimlInstanceRegistry.java | 163 ++++-------------- 1 file changed, 37 insertions(+), 126 deletions(-) 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..428d2e421a 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 final ThreadLocal RENEW_CORRECTION = new ThreadLocal<>(); + public ApimlInstanceRegistry( EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, @@ -96,79 +87,26 @@ 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); + protected void updateRenewsPerMinThreshold() { + Integer correction = RENEW_CORRECTION.get(); + if (correction != null) { + this.numberOfRenewsPerMinThreshold += correction; + RENEW_CORRECTION.remove(); } + + super.updateRenewsPerMinThreshold(); } public void peerAwareHeartbeat(InstanceInfo instanceInfo) { @@ -191,19 +129,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 +165,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 +229,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(); } } From 17de7c4261801f5e43c9b36754dbd5cf62b92083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 10:22:54 +0200 Subject: [PATCH 2/7] test cleanup (removed methodhandles) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .run/ApimlApplication.run.xml | 15 +- .../discovery/ApimlInstanceRegistryTest.java | 230 ++++-------------- 2 files changed, 43 insertions(+), 202 deletions(-) diff --git a/.run/ApimlApplication.run.xml b/.run/ApimlApplication.run.xml index a7209e7323..1e9b5d85fc 100644 --- a/.run/ApimlApplication.run.xml +++ b/.run/ApimlApplication.run.xml @@ -1,17 +1,6 @@ - - + - + \ No newline at end of file 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..0aa041b28d 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,19 @@ 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.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -54,16 +58,18 @@ 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() { + 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 +77,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 +115,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 +126,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 +138,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 +148,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 +172,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 +193,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 +202,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,127 +218,14 @@ 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); - }); - } - - @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 Stream exceptions() { return Stream.of( @@ -338,6 +233,7 @@ private Stream exceptions() { Arguments.of(new Exception(new Throwable())) ); } + } @Nested @@ -382,51 +278,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 +293,5 @@ private InstanceInfo getStandardInstance(String instanceId, String appName) { .setStatus(InstanceInfo.InstanceStatus.UP) .build(); } + } From 100ff9b273199e1370972681d36012c48ae695b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 10:35:37 +0200 Subject: [PATCH 3/7] UpdateRenewsPerMinThreshold test + fix property name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../discovery/ApimlInstanceRegistry.java | 2 +- .../discovery/ApimlInstanceRegistryTest.java | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) 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 428d2e421a..05528a4876 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 @@ -102,7 +102,7 @@ private void init() { protected void updateRenewsPerMinThreshold() { Integer correction = RENEW_CORRECTION.get(); if (correction != null) { - this.numberOfRenewsPerMinThreshold += correction; + this.expectedNumberOfClientsSendingRenews += correction; 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 0aa041b28d..d699c2963e 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 @@ -294,4 +294,38 @@ private InstanceInfo getStandardInstance(String instanceId, String appName) { .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()); + } + + } + } From d40fa18f4a8510f4f1447b60b4e9c2c2152bbdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 11:01:20 +0200 Subject: [PATCH 4/7] tests regarding corrections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../discovery/ApimlInstanceRegistryTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) 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 d699c2963e..8a28e27e29 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 @@ -260,6 +260,61 @@ void givenStaticRegistration_thenSuccessful() throws Throwable { assertFalse(leaseMap.isEmpty()); } + @Test + void givenStaticDefinition_whenSuccessRegistration_thenExpectedNumberOfClientsSendingRenewsDidntChange() { + var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); + renewCorrection.set(123); + ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); + + apimlInstanceRegistry.registerStatically(standardInstance, false, false); + + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + @Test + void givenStaticDefinition_whenFailRegistration_thenCorrectionAndExpectedNumberOfClientsSendingRenewsDidntChange() { + var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); + renewCorrection.set(123); + ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); + + 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() { + var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); + apimlInstanceRegistry.registerStatically(standardInstance, false, false); + + renewCorrection.set(123); + ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); + + apimlInstanceRegistry.cancel(standardInstance.getAppName(), standardInstance.getInstanceId(), false); + + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + + @Test + void givenNonStaticDefinition_whenSuccessCancellation_thenExpectedNumberOfClientsSendingRenewsChanged() { + var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); + var staticRegistrationIds = (Set) ReflectionTestUtils.getField(apimlInstanceRegistry, "staticRegistrationIds"); + apimlInstanceRegistry.register(standardInstance, false); + + renewCorrection.remove(); + ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); + staticRegistrationIds.clear(); + + apimlInstanceRegistry.cancel(standardInstance.getAppName(), standardInstance.getInstanceId(), false); + + assertEquals(4, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertNull(renewCorrection.get()); + } + } @Nested From 6e2013e1f7adfd0d6312d39826974860dcd9fbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 12:27:15 +0200 Subject: [PATCH 5/7] fix sonar issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../apiml/discovery/ApimlInstanceRegistry.java | 7 +++++-- .../discovery/ApimlInstanceRegistryTest.java | 16 +--------------- 2 files changed, 6 insertions(+), 17 deletions(-) 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 05528a4876..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 @@ -59,7 +59,7 @@ public class ApimlInstanceRegistry extends InstanceRegistry { private ConcurrentHashMap>> registry; private Set staticRegistrationIds = Collections.synchronizedSet(new HashSet<>()); - private final ThreadLocal RENEW_CORRECTION = new ThreadLocal<>(); + private static final ThreadLocal RENEW_CORRECTION = new ThreadLocal<>(); public ApimlInstanceRegistry( EurekaServerConfig serverConfig, @@ -99,10 +99,13 @@ private void init() { } } + @Override protected void updateRenewsPerMinThreshold() { Integer correction = RENEW_CORRECTION.get(); if (correction != null) { - this.expectedNumberOfClientsSendingRenews += correction; + synchronized (lock) { + this.expectedNumberOfClientsSendingRenews += correction; + } 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 8a28e27e29..18ed5e8a64 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 @@ -22,7 +22,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -35,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; @@ -65,6 +63,7 @@ class ApimlInstanceRegistryTest { private EurekaServerConfig serverConfig; @BeforeEach + @SuppressWarnings("squid:S1874") void setUp() throws Exception { standardInstance = getStandardInstance("hostname:serviceclient:10010", "serviceclient"); serverConfig = new DefaultEurekaServerConfig(); @@ -223,19 +222,6 @@ void thenShouldRegisterWithSecondMethod(String tuple, String expectedServiceIdIn assertEquals(expectedServiceIdInResult, standardInstance.getInstanceId()); } - @Nested - @TestInstance(TestInstance.Lifecycle.PER_CLASS) - class WhenResolveInstanceRewrittenFails { - - private Stream exceptions() { - return Stream.of( - Arguments.of(new WrongMethodTypeException()), - Arguments.of(new Exception(new Throwable())) - ); - } - - } - @Nested class WhenStaticallyRegistration { From 3665966dbc72bf55071f9d08de6648dc13a29a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 13:49:30 +0200 Subject: [PATCH 6/7] rollback of ApimlApplication.run.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .run/ApimlApplication.run.xml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.run/ApimlApplication.run.xml b/.run/ApimlApplication.run.xml index 1e9b5d85fc..a7209e7323 100644 --- a/.run/ApimlApplication.run.xml +++ b/.run/ApimlApplication.run.xml @@ -1,6 +1,17 @@ + + - - \ No newline at end of file + From d5b1f709ae2bc48f3dfee051eaa3947e37317155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jare=C5=A1?= Date: Mon, 1 Jun 2026 16:20:13 +0200 Subject: [PATCH 7/7] cleaned up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Jareš --- .../discovery/ApimlInstanceRegistryTest.java | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) 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 18ed5e8a64..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 @@ -225,6 +225,15 @@ void thenShouldRegisterWithSecondMethod(String tuple, String expectedServiceIdIn @Nested class WhenStaticallyRegistration { + private ThreadLocal renewCorrection; + + @BeforeEach + void setUp() { + renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); + renewCorrection.set(123); + ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); + } + @Test @SuppressWarnings("unchecked") void givenStaticRegistration_thenSuccessful() throws Throwable { @@ -248,10 +257,6 @@ void givenStaticRegistration_thenSuccessful() throws Throwable { @Test void givenStaticDefinition_whenSuccessRegistration_thenExpectedNumberOfClientsSendingRenewsDidntChange() { - var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); - renewCorrection.set(123); - ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); - apimlInstanceRegistry.registerStatically(standardInstance, false, false); assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); @@ -260,10 +265,6 @@ void givenStaticDefinition_whenSuccessRegistration_thenExpectedNumberOfClientsSe @Test void givenStaticDefinition_whenFailRegistration_thenCorrectionAndExpectedNumberOfClientsSendingRenewsDidntChange() { - var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); - renewCorrection.set(123); - ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); - doThrow(new RuntimeException("test")).when(apimlInstanceRegistry).register(any(), anyInt(), anyBoolean()); assertThrows(IllegalStateException.class, () -> apimlInstanceRegistry.registerStatically(standardInstance, false, false)); @@ -273,12 +274,7 @@ void givenStaticDefinition_whenFailRegistration_thenCorrectionAndExpectedNumberO @Test void givenStaticDefinition_whenSuccessCancellation_thenExpectedNumberOfClientsSendingRenewsDidntChange() { - var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); apimlInstanceRegistry.registerStatically(standardInstance, false, false); - - renewCorrection.set(123); - ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); - apimlInstanceRegistry.cancel(standardInstance.getAppName(), standardInstance.getInstanceId(), false); assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); @@ -287,17 +283,13 @@ void givenStaticDefinition_whenSuccessCancellation_thenExpectedNumberOfClientsSe @Test void givenNonStaticDefinition_whenSuccessCancellation_thenExpectedNumberOfClientsSendingRenewsChanged() { - var renewCorrection = (ThreadLocal) ReflectionTestUtils.getField(apimlInstanceRegistry, "RENEW_CORRECTION"); - var staticRegistrationIds = (Set) ReflectionTestUtils.getField(apimlInstanceRegistry, "staticRegistrationIds"); - apimlInstanceRegistry.register(standardInstance, false); - renewCorrection.remove(); - ReflectionTestUtils.setField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews", 5); - staticRegistrationIds.clear(); + apimlInstanceRegistry.register(standardInstance, false); // it increases the number to 6 + ((Set) ReflectionTestUtils.getField(apimlInstanceRegistry, "staticRegistrationIds")).clear(); apimlInstanceRegistry.cancel(standardInstance.getAppName(), standardInstance.getInstanceId(), false); - assertEquals(4, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); + assertEquals(5, ReflectionTestUtils.getField(apimlInstanceRegistry, "expectedNumberOfClientsSendingRenews")); assertNull(renewCorrection.get()); }