diff --git a/component-server-parent/component-server/src/main/java/org/talend/sdk/component/server/lang/MapCache.java b/component-server-parent/component-server/src/main/java/org/talend/sdk/component/server/lang/MapCache.java
index 94b63d5d6d3cc..1f3d04842e889 100644
--- a/component-server-parent/component-server/src/main/java/org/talend/sdk/component/server/lang/MapCache.java
+++ b/component-server-parent/component-server/src/main/java/org/talend/sdk/component/server/lang/MapCache.java
@@ -24,18 +24,18 @@
@ApplicationScoped
public class MapCache {
- // simple - but enough - protection against too much entries in the cache
- // this mainly targets ?query king of parameters
+ // simple - but enough - protection against too many entries in the cache
+ // this mainly targets ?query kind of parameters
public void evictIfNeeded(final ConcurrentMap cache, final int maxSize) {
- if (maxSize < 0) {
+ if (maxSize <= 0) {
cache.clear();
return;
}
- while (cache.size() > maxSize) {
- final Iterator> iterator = cache.entrySet().iterator();
- if (iterator.hasNext()) {
- iterator.remove();
- }
+ final Iterator it = cache.keySet().iterator();
+ int excess = cache.size() - maxSize;
+ while (excess-- > 0 && it.hasNext()) {
+ it.next();
+ it.remove();
}
}
}
diff --git a/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/lang/MapCacheTest.java b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/lang/MapCacheTest.java
new file mode 100644
index 0000000000000..cd6e604636d80
--- /dev/null
+++ b/component-server-parent/component-server/src/test/java/org/talend/sdk/component/server/lang/MapCacheTest.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2006-2026 Talend Inc. - www.talend.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.talend.sdk.component.server.lang;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class MapCacheTest {
+
+ @ParameterizedTest
+ @MethodSource("emptyCacheTestCases")
+ void shouldEmptyCache(final ConcurrentMap cache, final int maxSize) {
+ new MapCache().evictIfNeeded(cache, maxSize);
+ assertTrue(cache.isEmpty());
+ }
+
+ public static Stream emptyCacheTestCases() {
+ return Stream.of(
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2")), 0),
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2")), -1),
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2", "3", "4")), 0)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("updatedCacheTestCases")
+ void shouldKeepTheExpectedNumberOfItems(final ConcurrentMap cache, final int maxSize) {
+ new MapCache().evictIfNeeded(cache, maxSize);
+ assertEquals(maxSize, cache.size());
+ }
+
+ public static Stream updatedCacheTestCases() {
+ return Stream.of(
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2", "3", "4", "5", "6")), 1),
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2", "3", "4", "5", "6")), 2)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("unmodifiedCacheTestCases")
+ void shouldNotUpdateTheCache(final ConcurrentMap cache, final int maxSize) {
+ int initialSize = cache.size();
+ new MapCache().evictIfNeeded(cache, maxSize);
+ assertEquals(initialSize, cache.size());
+ }
+
+ public static Stream unmodifiedCacheTestCases() {
+ return Stream.of(
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2", "3", "4", "5", "6")), 3),
+ Arguments.of(new ConcurrentHashMap<>(Map.of("1", "2", "3", "4", "5", "6")), 4)
+ );
+ }
+}