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));
}
}