From 06658c3c717dc28cfac68d901cd6312a3b318108 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 27 Sep 2023 13:02:46 +0200 Subject: [PATCH] Restore zero capacity support in ConcurrentLruCache Since the rewrite of ConcurrentLruCache in Spring Framework 6.0, an attempt to create a ConcurrentLruCache with zero capacity results in an IllegalArgumentException even though the documentation states that zero capacity indicates "no caching, always generating a new value". This commit restores the ability to configure a ConcurrentLruCache with zero capacity and introduces corresponding tests (which were first verified against the 5.3.x branch to ensure backward compatibility). See gh-26320 Closes gh-31317 --- .../util/ConcurrentLruCache.java | 7 +++-- .../util/ConcurrentLruCacheTests.java | 27 ++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java b/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java index 8e93e2449851..4d014b1a2b11 100644 --- a/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java +++ b/spring-core/src/main/java/org/springframework/util/ConcurrentLruCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,7 +81,7 @@ public ConcurrentLruCache(int capacity, Function generator) { } private ConcurrentLruCache(int capacity, Function generator, int concurrencyLevel) { - Assert.isTrue(capacity > 0, "Capacity must be > 0"); + Assert.isTrue(capacity >= 0, "Capacity must be >= 0"); this.capacity = capacity; this.cache = new ConcurrentHashMap<>(16, 0.75f, concurrencyLevel); this.generator = generator; @@ -95,6 +95,9 @@ private ConcurrentLruCache(int capacity, Function generator, int concurren * @return the cached or newly generated value */ public V get(K key) { + if (this.capacity == 0) { + return this.generator.apply(key); + } final Node node = this.cache.get(key); if (node == null) { V value = this.generator.apply(key); diff --git a/spring-core/src/test/java/org/springframework/util/ConcurrentLruCacheTests.java b/spring-core/src/test/java/org/springframework/util/ConcurrentLruCacheTests.java index 5c62362c112a..d605dcf6b278 100644 --- a/spring-core/src/test/java/org/springframework/util/ConcurrentLruCacheTests.java +++ b/spring-core/src/test/java/org/springframework/util/ConcurrentLruCacheTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,38 @@ /** * Tests for {@link ConcurrentLruCache}. + * * @author Juergen Hoeller + * @author Sam Brannen */ class ConcurrentLruCacheTests { private final ConcurrentLruCache cache = new ConcurrentLruCache<>(2, key -> key + "value"); + @Test + void zeroCapacity() { + ConcurrentLruCache cache = new ConcurrentLruCache<>(0, key -> key + "value"); + + assertThat(cache.capacity()).isZero(); + assertThat(cache.size()).isZero(); + + assertThat(cache.get("k1")).isEqualTo("k1value"); + assertThat(cache.size()).isZero(); + assertThat(cache.contains("k1")).isFalse(); + + assertThat(cache.get("k2")).isEqualTo("k2value"); + assertThat(cache.size()).isZero(); + assertThat(cache.contains("k1")).isFalse(); + assertThat(cache.contains("k2")).isFalse(); + + assertThat(cache.get("k3")).isEqualTo("k3value"); + assertThat(cache.size()).isZero(); + assertThat(cache.contains("k1")).isFalse(); + assertThat(cache.contains("k2")).isFalse(); + assertThat(cache.contains("k3")).isFalse(); + } + @Test void getAndSize() { assertThat(this.cache.capacity()).isEqualTo(2);