diff --git a/common/tls/pom.xml b/common/tls/pom.xml index 478f8804096..932bbca54e7 100644 --- a/common/tls/pom.xml +++ b/common/tls/pom.xml @@ -71,6 +71,11 @@ hamcrest-all test + + org.mockito + mockito-core + test + diff --git a/common/tls/src/main/java/io/helidon/common/tls/ConfiguredTlsManager.java b/common/tls/src/main/java/io/helidon/common/tls/ConfiguredTlsManager.java index 16ac897a094..cbe75e8444e 100644 --- a/common/tls/src/main/java/io/helidon/common/tls/ConfiguredTlsManager.java +++ b/common/tls/src/main/java/io/helidon/common/tls/ConfiguredTlsManager.java @@ -124,6 +124,14 @@ protected void reload(Optional keyManager, Optional CACHE = new ConcurrentHashMap<>(); + + private TlsManagerCache() { + } + + static TlsManager getOrCreate(Object configBean, + Function creator) { + Objects.requireNonNull(configBean); + Objects.requireNonNull(creator); + return CACHE.computeIfAbsent(configBean, creator); + } + +} diff --git a/common/tls/src/main/java/io/helidon/common/tls/spi/TlsManagerProvider.java b/common/tls/src/main/java/io/helidon/common/tls/spi/TlsManagerProvider.java index fdc8eb22552..aac9be531bc 100644 --- a/common/tls/src/main/java/io/helidon/common/tls/spi/TlsManagerProvider.java +++ b/common/tls/src/main/java/io/helidon/common/tls/spi/TlsManagerProvider.java @@ -16,6 +16,10 @@ package io.helidon.common.tls.spi; +import java.util.Objects; +import java.util.function.Function; + +import io.helidon.common.config.Config; import io.helidon.common.config.ConfiguredProvider; import io.helidon.common.tls.TlsManager; @@ -24,4 +28,22 @@ */ public interface TlsManagerProvider extends ConfiguredProvider { + /** + * Provides the ability to have a unique {@link TlsManager} per unique {@link Config} instance provided. + * + * @param config the raw config instance + * @param configBean the config bean instance + * @param name the config bean instance name + * @param creator the creator to apply if not already in cache, which takes the config bean instance + * @return the tls manager instance from cache, defaulting to creation from the {@code creator} if not in cache + */ + static TlsManager getOrCreate(Config config, + String name, + Object configBean, + Function creator) { + Objects.requireNonNull(config); + Objects.requireNonNull(name); + return TlsManagerCache.getOrCreate(configBean, creator); + } + } diff --git a/common/tls/src/test/java/io/helidon/common/tls/spi/TlsManagerProviderTest.java b/common/tls/src/test/java/io/helidon/common/tls/spi/TlsManagerProviderTest.java new file mode 100644 index 00000000000..4dcf07bf4ef --- /dev/null +++ b/common/tls/src/test/java/io/helidon/common/tls/spi/TlsManagerProviderTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * 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 io.helidon.common.tls.spi; + +import io.helidon.common.tls.ConfiguredTlsManager; +import io.helidon.common.tls.TlsManager; +import io.helidon.config.Config; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +class TlsManagerProviderTest { + + @Test + void caching() { + TlsManager mock = Mockito.mock(TlsManager.class); + Config config = Config.create(); + AtomicInteger count = new AtomicInteger(); + + // we are using "1" and "2" here abstractly to stand in for Config beans, which would hash properly + TlsManager manager1 = TlsManagerProvider.getOrCreate(config, "test", "1", (c) -> { + count.incrementAndGet(); + return mock; + }); + assertThat(manager1, sameInstance(mock)); + assertThat(count.get(), is(1)); + + TlsManager manager2 = TlsManagerProvider.getOrCreate(config, "test", "1", (c) -> { + count.incrementAndGet(); + return Mockito.mock(TlsManager.class); + }); + assertThat(manager2, sameInstance(mock)); + assertThat(count.get(), is(1)); + + TlsManager manager3 = TlsManagerProvider.getOrCreate(config, "test", "2", (c) -> { + count.incrementAndGet(); + return Mockito.mock(TlsManager.class); + }); + assertThat(manager3, notNullValue()); + assertThat(manager3, not(sameInstance(mock))); + assertThat(count.get(), is(2)); + } + +} diff --git a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java index 1ac5dff8d89..88a81312832 100644 --- a/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java +++ b/integrations/oci/tls-certificates/src/main/java/io/helidon/integrations/oci/tls/certificates/DefaultOciCertificatesTlsManagerProvider.java @@ -43,7 +43,10 @@ public String configKey() { public TlsManager create(Config config, String name) { OciCertificatesTlsManagerConfig cfg = OciCertificatesTlsManagerConfig.create(config); - return new DefaultOciCertificatesTlsManager(cfg, name, config); + return TlsManagerProvider.getOrCreate(config, + name, + cfg, + (c) -> new DefaultOciCertificatesTlsManager(cfg, name, config)); } }