diff --git a/core/common/util/src/test/java/org/eclipse/edc/util/collection/LruCacheTest.java b/core/common/util/src/test/java/org/eclipse/edc/util/collection/ConcurrentLruCacheTest.java similarity index 73% rename from core/common/util/src/test/java/org/eclipse/edc/util/collection/LruCacheTest.java rename to core/common/util/src/test/java/org/eclipse/edc/util/collection/ConcurrentLruCacheTest.java index 4a75303c5ae..b37bc52e71a 100644 --- a/core/common/util/src/test/java/org/eclipse/edc/util/collection/LruCacheTest.java +++ b/core/common/util/src/test/java/org/eclipse/edc/util/collection/ConcurrentLruCacheTest.java @@ -19,20 +19,20 @@ import static org.assertj.core.api.Assertions.assertThat; -class LruCacheTest { +class ConcurrentLruCacheTest { private ConcurrentLruCache cache; @Test void verifyEviction() { cache.put("foo", "foo"); cache.put("bar", "bar"); - assertThat(cache.containsKey("foo")).isTrue(); - assertThat(cache.containsKey("bar")).isTrue(); + assertThat(cache).containsKey("foo") + .containsKey("bar"); cache.put("baz", "baz"); - assertThat(cache.containsKey("baz")).isTrue(); - assertThat(cache.containsKey("bar")).isTrue(); - assertThat(cache.containsKey("foo")).isFalse(); + assertThat(cache).containsKey("baz") + .containsKey("bar") + .doesNotContainKey("foo"); } @BeforeEach diff --git a/extensions/common/iam/decentralized-identity/identity-did-core/build.gradle.kts b/extensions/common/iam/decentralized-identity/identity-did-core/build.gradle.kts index 4c93b1512a4..bc9976cb504 100644 --- a/extensions/common/iam/decentralized-identity/identity-did-core/build.gradle.kts +++ b/extensions/common/iam/decentralized-identity/identity-did-core/build.gradle.kts @@ -5,6 +5,7 @@ plugins { dependencies { api(project(":spi:common:identity-did-spi")) + implementation(project(":core:common:util")) implementation(project(":extensions:common:iam:decentralized-identity:identity-did-crypto")) implementation(libs.jakarta.rsApi) diff --git a/extensions/common/iam/decentralized-identity/identity-did-core/src/main/java/org/eclipse/edc/iam/did/resolution/DidResolverRegistryImpl.java b/extensions/common/iam/decentralized-identity/identity-did-core/src/main/java/org/eclipse/edc/iam/did/resolution/DidResolverRegistryImpl.java index ce688836732..cceab88eb51 100644 --- a/extensions/common/iam/decentralized-identity/identity-did-core/src/main/java/org/eclipse/edc/iam/did/resolution/DidResolverRegistryImpl.java +++ b/extensions/common/iam/decentralized-identity/identity-did-core/src/main/java/org/eclipse/edc/iam/did/resolution/DidResolverRegistryImpl.java @@ -18,21 +18,36 @@ import org.eclipse.edc.iam.did.spi.resolution.DidResolver; import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry; import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.util.collection.ConcurrentLruCache; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** - * Default implementation. + * Default implementation, that delegates to several {@link DidResolver} objects, caching the results in a {@link ConcurrentLruCache} */ public class DidResolverRegistryImpl implements DidResolverRegistry { private static final String DID = "did"; private static final int DID_PREFIX = 0; private static final int DID_METHOD_NAME = 1; - + private final ConcurrentLruCache didCache; private final Map resolvers = new HashMap<>(); + public DidResolverRegistryImpl() { + didCache = new ConcurrentLruCache<>(50); + } + + /** + * Constructs a DidResolverRegistryImpl object with the specified cache size. + * + * @param cacheSize the maximum number of entries that the cache can hold. Pass 0 to effectively deactivate the cache. + */ + public DidResolverRegistryImpl(int cacheSize) { + didCache = new ConcurrentLruCache<>(cacheSize); + } + @Override public void register(DidResolver resolver) { resolvers.put(resolver.getMethod(), resolver); @@ -41,6 +56,7 @@ public void register(DidResolver resolver) { @Override public Result resolve(String didKey) { Objects.requireNonNull(didKey); + // for the definition of DID syntax, .cf https://www.w3.org/TR/did-core/#did-syntax var tokens = didKey.split(":"); if (tokens.length < 3) { @@ -50,10 +66,26 @@ public Result resolve(String didKey) { return Result.failure("Invalid DID prefix"); } var methodName = tokens[DID_METHOD_NAME]; - var resolver = resolvers.get(methodName); - if (resolver == null) { - return Result.failure("No resolver registered for DID Method: " + methodName); + + return resolveCachedDocument(didKey, methodName); + } + + @NotNull + private Result resolveCachedDocument(String didKey, String methodName) { + var didDocument = didCache.get(didKey); + if (didDocument == null) { + var resolver = resolvers.get(methodName); + if (resolver == null) { + return Result.failure("No resolver registered for DID Method: " + methodName); + } + var resolveResult = resolver.resolve(didKey); + if (resolveResult.failed()) { + return resolveResult; + } + didDocument = resolveResult.getContent(); + didCache.put(didKey, didDocument); } - return resolver.resolve(didKey); + + return Result.success(didDocument); } }