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 HashMap<>(); + + private TlsManagerCache() { + } + + static TlsManager getOrCreate(T configBean, + Function creator) { + Objects.requireNonNull(configBean); + Objects.requireNonNull(creator); + LOCK.lock(); + try { + TlsManager manager = CACHE.get(configBean); + if (manager != null) { + return manager; + } + + manager = creator.apply(configBean); + Object existing = CACHE.put(configBean, manager); + assert (existing == null); + + return manager; + } finally { + LOCK.unlock(); + } + } + +} 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..e89173eba14 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,9 @@ package io.helidon.common.tls.spi; +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 +27,17 @@ */ public interface TlsManagerProvider extends ConfiguredProvider { + /** + * Provides the ability to have a unique {@link TlsManager} per unique {@link Config} instance provided. + * + * @param configBean the config bean instance + * @param creator the creator to apply if not already in cache, which takes the config bean instance + * @param the type of the config bean + * @return the tls manager instance from cache, defaulting to creation from the {@code creator} if not in cache + */ + static TlsManager getOrCreate(T configBean, + Function creator) { + 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..525848cc0b0 --- /dev/null +++ b/common/tls/src/test/java/io/helidon/common/tls/spi/TlsManagerProviderTest.java @@ -0,0 +1,63 @@ +/* + * 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 java.util.concurrent.atomic.AtomicInteger; + +import io.helidon.common.tls.TlsManager; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.hamcrest.MatcherAssert.assertThat; + +class TlsManagerProviderTest { + + @Test + void caching() { + TlsManager mock = Mockito.mock(TlsManager.class); + 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("1", (c) -> { + count.incrementAndGet(); + return mock; + }); + assertThat(manager1, sameInstance(mock)); + assertThat(count.get(), is(1)); + + TlsManager manager2 = TlsManagerProvider.getOrCreate("1", (c) -> { + count.incrementAndGet(); + return Mockito.mock(TlsManager.class); + }); + assertThat(manager2, sameInstance(mock)); + assertThat(count.get(), is(1)); + + TlsManager manager3 = TlsManagerProvider.getOrCreate("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..593824375da 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,8 @@ public String configKey() { public TlsManager create(Config config, String name) { OciCertificatesTlsManagerConfig cfg = OciCertificatesTlsManagerConfig.create(config); - return new DefaultOciCertificatesTlsManager(cfg, name, config); + return TlsManagerProvider.getOrCreate(cfg, + (c) -> new DefaultOciCertificatesTlsManager(cfg, name, config)); } }