diff --git a/all/pom.xml b/all/pom.xml
index 938982eb245..f97cd9e2c7c 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -569,6 +569,10 @@
io.helidon.integrations.db
helidon-integrations-db-mysql
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-configurable
+
io.helidon.integrations.cdi
helidon-integrations-cdi-delegates
diff --git a/bom/pom.xml b/bom/pom.xml
index 88a10fcf973..2715a75f343 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -762,6 +762,11 @@
helidon-integrations-db-mysql
${helidon.version}
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-configurable
+ ${helidon.version}
+
io.helidon.integrations.cdi
helidon-integrations-cdi-delegates
@@ -1062,29 +1067,29 @@
- io.helidon.integrations.microstream
- helidon-integrations-microstream
- ${helidon.version}
+ io.helidon.integrations.microstream
+ helidon-integrations-microstream
+ ${helidon.version}
- io.helidon.integrations.microstream
- helidon-integrations-microstream-cdi
- ${helidon.version}
+ io.helidon.integrations.microstream
+ helidon-integrations-microstream-cdi
+ ${helidon.version}
- io.helidon.integrations.microstream
- helidon-integrations-microstream-health
- ${helidon.version}
+ io.helidon.integrations.microstream
+ helidon-integrations-microstream-health
+ ${helidon.version}
- io.helidon.integrations.microstream
- helidon-integrations-microstream-metrics
- ${helidon.version}
+ io.helidon.integrations.microstream
+ helidon-integrations-microstream-metrics
+ ${helidon.version}
- io.helidon.integrations.microstream
- helidon-integrations-microstream-cache
- ${helidon.version}
+ io.helidon.integrations.microstream
+ helidon-integrations-microstream-cache
+ ${helidon.version}
diff --git a/dependencies/pom.xml b/dependencies/pom.xml
index 020acad7820..e7af1a00b66 100644
--- a/dependencies/pom.xml
+++ b/dependencies/pom.xml
@@ -127,11 +127,12 @@
5.12.0
4.1.100.Final
3.34.0
+ 21
- 21.9.0.0
+ ${version.lib.ojdbc.family}.9.0.0
${version.lib.ojdbc}
3.4.0
diff --git a/etc/javadoc/ojdbc/element-list b/etc/javadoc/ojdbc/element-list
new file mode 100644
index 00000000000..6d507553195
--- /dev/null
+++ b/etc/javadoc/ojdbc/element-list
@@ -0,0 +1,12 @@
+oracle.jdbc
+oracle.jdbc.aq
+oracle.jdbc.babelfish
+oracle.jdbc.datasource
+oracle.jdbc.datasource.impl
+oracle.jdbc.dcn
+oracle.jdbc.pool
+oracle.jdbc.replay
+oracle.jdbc.xa
+oracle.jdbc.xa.client
+oracle.sql
+oracle.sql.json
diff --git a/etc/javadoc/ucp/element-list b/etc/javadoc/ucp/element-list
new file mode 100644
index 00000000000..b18dcf79658
--- /dev/null
+++ b/etc/javadoc/ucp/element-list
@@ -0,0 +1,5 @@
+oracle.ucp
+oracle.ucp.admin
+oracle.ucp.jdbc
+oracle.ucp.jdbc.oracle
+oracle.ucp.util.logging
diff --git a/integrations/cdi/common-cdi/configurable/pom.xml b/integrations/cdi/common-cdi/configurable/pom.xml
new file mode 100644
index 00000000000..117cc591369
--- /dev/null
+++ b/integrations/cdi/common-cdi/configurable/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+ 4.0.0
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-common-project
+ 4.0.0-SNAPSHOT
+
+ helidon-integrations-cdi-configurable
+ Helidon CDI Integrations Common Configurable
+
+
+
+
+
+ org.eclipse.microprofile.config
+ microprofile-config-api
+ compile
+
+
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+ provided
+
+
+
+
diff --git a/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java
new file mode 100644
index 00000000000..3c632ec0236
--- /dev/null
+++ b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/AbstractConfigurableExtension.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2024 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.integrations.cdi.configurable;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.inject.literal.NamedLiteral;
+import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
+import jakarta.enterprise.inject.spi.Extension;
+import jakarta.enterprise.inject.spi.configurator.BeanConfigurator;
+import jakarta.inject.Named;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+
+/**
+ * A convenient, abstract {@link Extension} whose subclasses arrange for instances of a particular type to be configured
+ * and added as CDI beans.
+ *
+ * There are four kinds of names defined by this extension:
+ *
+ *
+ *
+ * - Type name: the class name of the kind of object this extension manages. Example: {@code
+ * javax.sql.DataSource}
+ *
+ * - Name: a name designating one of potentially many such objects. Example: {@code prod}
+ *
+ * - Property name: a name of a property of the kind of object this extension manages. Example:
+ * {@code user}
+ *
+ * - Configuration property name: a name of a configuration property that logically contains at least
+ * the other three kinds of names. Example: {@code javax.sql.DataSource.prod.user}
+ *
+ *
+ *
+ * Thread Safety
+ *
+ * As with all CDI portable extensions, this class' instances are not safe for concurrent use by multiple
+ * threads.
+ *
+ * @param the type for which this {@link AbstractConfigurableExtension} implementation installs instances
+ */
+public abstract class AbstractConfigurableExtension implements Extension {
+
+
+ /*
+ * Instance fields.
+ */
+
+
+ // clear()ed after bean discovery
+ private final Map namedProperties;
+
+ // clear()ed after bean discovery
+ private final Map explicitlySetProperties;
+
+ // Implementation detail.
+ private final Config config;
+
+
+ /*
+ * Constructors.
+ */
+
+
+ /**
+ * Creates a new {@link AbstractConfigurableExtension}.
+ */
+ protected AbstractConfigurableExtension() {
+ super();
+ this.namedProperties = new HashMap<>();
+ this.explicitlySetProperties = new HashMap<>();
+ this.config = ConfigProvider.getConfig();
+ }
+
+
+ /*
+ * Instance methods.
+ */
+
+
+ /**
+ * Returns a {@link Matcher} given a configuration property name that can logically identify and provide
+ * access to at least its three component names.
+ *
+ * Implementations of this method must not return {@code null}.
+ *
+ * Given a {@link String} that is a configuration property name, like
+ * com.foo.Bar.name.propertyName
, any implementation of this method must return a
+ * non-{@code null} {@link Matcher} that is capable of being supplied to the {@link #name(Matcher)} and {@link
+ * #propertyName(Matcher)} methods.
+ *
+ * @param configPropertyName a configuration property name that logically contains a type name, a
+ * name and a property name; must not be {@code null}
+ *
+ * @return a non-{@code null} {@link Matcher}
+ *
+ * @exception NullPointerException if {@code configPropertyName} is {@code null}
+ *
+ * @see #name(Matcher)
+ *
+ * @see #propertyName(Matcher)
+ */
+ protected abstract Matcher matcher(String configPropertyName);
+
+ /**
+ * Given a {@link Matcher} that has been produced by the {@link #matcher(String)} method, returns the relevant name.
+ *
+ * Implementations of this method may return {@code null}.
+ *
+ * @param configPropertyNameMatcher a {@link Matcher} produced by the {@link #matcher(String)} method; must not be
+ * {@code null}
+ *
+ * @return a name, or {@code null}
+ *
+ * @exception NullPointerException if {@code configPropertyNameMatcher} is {@code null}
+ *
+ * @see #matcher(String)
+ */
+ protected abstract String name(Matcher configPropertyNameMatcher);
+
+ /**
+ * Given a {@link Matcher} that has been produced by the {@link #matcher(String)} method, returns the relevant
+ * property name, or {@code null} if there is no such property name.
+ *
+ * Most implementations of this method will use the {@link Matcher#group(int)} method to produce the required
+ * property name.
+ *
+ * Implementations of this method may return {@code null}.
+ *
+ * @param configPropertyNameMatcher a {@link Matcher} produced by the {@link #matcher(String)} method; must not be
+ * {@code null}
+ *
+ * @return a property name, or {@code null}
+ *
+ * @exception NullPointerException if {@code configPropertyNameMatcher} is {@code null}
+ *
+ * @see #matcher(String)
+ */
+ protected abstract String propertyName(Matcher configPropertyNameMatcher);
+
+ /**
+ * Called internally to permit subclasses to add a {@code T}-typed bean, qualified with at least the supplied {@link
+ * Named}, using the supplied {@link BeanConfigurator}.
+ *
+ * Implementations of this method will be called from an observer method that is observing the {@link
+ * AfterBeanDiscovery} container lifecycle event.
+ *
+ * @param beanConfigurator the {@link BeanConfigurator} to use to actually add a new bean; must not be {@code null}
+ *
+ * @param name a {@link Named} instance qualifying the {@code T}-typed bean to be added; may be {@code
+ * null}
+ *
+ * @param properties a {@link Properties} instance containing properties relevant to the object; must not be {@code
+ * null}
+ *
+ * @exception NullPointerException if {@code beanConfigurator} or {@code properties} is {@code null}
+ *
+ * @see AfterBeanDiscovery
+ */
+ protected abstract void addBean(BeanConfigurator beanConfigurator, Named name, Properties properties);
+
+ /**
+ * Given a configuration property name, returns an {@link Optional Optional<String>} containing its
+ * value, or an {@linkplain Optional#empty() empty Optional
} if no such configuration property value
+ * exists.
+ *
+ * This method never returns {@code null}.
+ *
+ * Overrides of this method must not return {@code null}.
+ *
+ * @param configPropertyName a configuration property name; must not be {@code null}
+ *
+ * @return a non-{@code null} {@link Optional}
+ *
+ * @exception NullPointerException if {@code configPropertyName} is {@code null}
+ */
+ protected Optional configPropertyValue(String configPropertyName) {
+ return this.config.getOptionalValue(configPropertyName, String.class);
+ }
+
+ /**
+ * Returns a {@link Set} of names known to this {@link AbstractConfigurableExtension} implementation.
+ *
+ * This method never returns {@code null}.
+ *
+ * The {@link Set} returned by this method is {@linkplain Collections#unmodifiableSet(Set) unmodifiable}.
+ *
+ * @return a non-{@code null}, {@linkplain Collections#unmodifiableSet(Set) unmodifiable Set
} of known
+ * names
+ */
+ protected final Set names() {
+ if (this.namedProperties.isEmpty()) {
+ if (this.explicitlySetProperties.isEmpty()) {
+ return Set.of();
+ }
+ return Collections.unmodifiableSet(this.explicitlySetProperties.keySet());
+ }
+ return Collections.unmodifiableSet(this.namedProperties.keySet());
+ }
+
+ /**
+ * Adds additional synthesized properties (property names and their values) to an internal map of such
+ * properties whose contents will be processed eventually by the {@link #addBean(BeanConfigurator, Named,
+ * Properties)} method.
+ *
+ * This method may return {@code null}.
+ *
+ * @param name the name of the object under which the supplied {@link Properties} will be indexed; may be
+ * {@code null}
+ *
+ * @param properties the {@link Properties} to put consisting of property names and their values; must not be {@code
+ * null}
+ *
+ * @return the prior {@link Properties} indexed under the supplied {@code name}, or {@code null}
+ *
+ * @exception NullPointerException if {@code properties} is {@code null}
+ */
+ protected final Properties put(String name, Properties properties) {
+ return this.explicitlySetProperties.put(name, Objects.requireNonNull(properties, "properties"));
+ }
+
+ /**
+ * {@linkplain Map#clear() Clears}, and then builds or rebuilds, an internal set of properties
+ * whose contents will be processed eventually by the {@link #addBean(BeanConfigurator, Named, Properties)} method.
+ *
+ * If no subclass explicitly calls this method, as is common, it will be called by the {@link
+ * #addBean(BeanConfigurator, Named, Properties)} method just prior to its other activities.
+ *
+ * Once the {@link #addBean(BeanConfigurator, Named, Properties)} method has run to completion, while this method
+ * may be called freely, its use is discouraged and its effects will no longer be used.
+ *
+ * @see #addBean(BeanConfigurator, Named, Properties)
+ */
+ protected final void initializeNamedProperties() {
+ this.namedProperties.clear();
+ Set extends String> allConfigPropertyNames = this.configPropertyNames();
+ if (!allConfigPropertyNames.isEmpty()) {
+ for (String configPropertyName : allConfigPropertyNames) {
+ Optional propertyValue = this.configPropertyValue(configPropertyName);
+ if (propertyValue.isPresent()) {
+ Matcher matcher = this.matcher(configPropertyName);
+ if (matcher != null && matcher.matches()) {
+ this.namedProperties.computeIfAbsent(this.name(matcher), n -> new Properties())
+ .setProperty(this.propertyName(matcher), propertyValue.get());
+ }
+ }
+ }
+ }
+ this.namedProperties.putAll(this.explicitlySetProperties);
+ }
+
+ /**
+ * Returns a {@link Set} of all known configuration property names.
+ *
+ * This method never returns {@code null}.
+ *
+ * Overrides of this method must not return {@code null}.
+ *
+ * The {@link Set} returned by this method is {@linkplain Collections#unmodifiableSet(Set) unmodifiable}.
+ *
+ * Overrides of this method must ensure that the returned {@link Set} is {@linkplain
+ * Collections#unmodifiableSet(Set) unmodifiable}.
+ *
+ * The {@link Set} returned by this method is not safe for concurrent use by multiple threads.
+ *
+ * Overrides of this method may return {@link Set} instances that are not safe for concurrent use by multiple
+ * threads.
+ *
+ * The {@link Set} returned by this method and its overrides may be {@linkplain Set#isEmpty() empty}.
+ *
+ * The ordering of the elements contained by the {@link Set} returned by this method and its overrides is
+ * indeterminate.
+ *
+ * @return a non-{@code null}, {@linkplain Collections#unmodifiableSet(Set) unmodifiable Set
} of all
+ * known configuration property names
+ */
+ protected Set configPropertyNames() {
+ Iterable propertyNames = this.config.getPropertyNames();
+ if (propertyNames == null) {
+ return Set.of();
+ }
+ Set set = new HashSet<>();
+ propertyNames.iterator().forEachRemaining(pn -> set.add(pn));
+ return Set.copyOf(set);
+ }
+
+ private void afterBeanDiscovery(@Observes AfterBeanDiscovery event) {
+ if (event != null) {
+ if (this.namedProperties.isEmpty()) {
+ this.initializeNamedProperties();
+ }
+ for (Entry extends String, ? extends Properties> entry : this.namedProperties.entrySet()) {
+ this.addBean(event.addBean(), NamedLiteral.of(entry.getKey()), entry.getValue());
+ }
+ }
+ this.namedProperties.clear();
+ this.explicitlySetProperties.clear();
+ }
+
+}
diff --git a/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/package-info.java b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/package-info.java
new file mode 100644
index 00000000000..125090dd65a
--- /dev/null
+++ b/integrations/cdi/common-cdi/configurable/src/main/java/io/helidon/integrations/cdi/configurable/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+/**
+ * Provides classes and interfaces that help to produce things from configuration.
+ */
+package io.helidon.integrations.cdi.configurable;
diff --git a/integrations/cdi/common-cdi/configurable/src/main/java/module-info.java b/integrations/cdi/common-cdi/configurable/src/main/java/module-info.java
new file mode 100644
index 00000000000..f3472f275ea
--- /dev/null
+++ b/integrations/cdi/common-cdi/configurable/src/main/java/module-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+/**
+ * Provides classes and interfaces that assist in producing things from configuration.
+ */
+@SuppressWarnings({ "requires-automatic", "requires-transitive-automatic" })
+module io.helidon.integrations.cdi.configurable {
+
+ requires transitive jakarta.cdi;
+ requires microprofile.config.api;
+
+ exports io.helidon.integrations.cdi.configurable;
+
+}
diff --git a/integrations/cdi/common-cdi/pom.xml b/integrations/cdi/common-cdi/pom.xml
index b3d86924167..0ecd914eb87 100644
--- a/integrations/cdi/common-cdi/pom.xml
+++ b/integrations/cdi/common-cdi/pom.xml
@@ -30,6 +30,7 @@
Helidon CDI Integrations Common
+ configurable
delegates
reference-counted-context
diff --git a/integrations/cdi/datasource-hikaricp/pom.xml b/integrations/cdi/datasource-hikaricp/pom.xml
index f400f493a82..63a213b3a2c 100644
--- a/integrations/cdi/datasource-hikaricp/pom.xml
+++ b/integrations/cdi/datasource-hikaricp/pom.xml
@@ -46,11 +46,6 @@
HikariCP
compile
-
- org.eclipse.microprofile.config
- microprofile-config-api
- compile
-
diff --git a/integrations/cdi/datasource-hikaricp/src/main/java/io/helidon/integrations/datasource/hikaricp/cdi/HikariCPBackedDataSourceExtension.java b/integrations/cdi/datasource-hikaricp/src/main/java/io/helidon/integrations/datasource/hikaricp/cdi/HikariCPBackedDataSourceExtension.java
index 703f22845fd..1ce37bf81dc 100644
--- a/integrations/cdi/datasource-hikaricp/src/main/java/io/helidon/integrations/datasource/hikaricp/cdi/HikariCPBackedDataSourceExtension.java
+++ b/integrations/cdi/datasource-hikaricp/src/main/java/io/helidon/integrations/datasource/hikaricp/cdi/HikariCPBackedDataSourceExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2021 Oracle and/or its affiliates.
+ * Copyright (c) 2018, 2024 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.
@@ -49,7 +49,6 @@
import jakarta.enterprise.inject.spi.configurator.BeanConfigurator;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Named;
-import org.eclipse.microprofile.config.Config;
/**
* An {@link Extension} that arranges for named {@link DataSource}
@@ -160,13 +159,13 @@ private void processAnnotatedType(@Observes
if (dataSourceDefinitions != null && !dataSourceDefinitions.isEmpty()) {
for (final DataSourceDefinition dsd : dataSourceDefinitions) {
assert dsd != null;
- final Set knownDataSourceNames = this.getDataSourceNames();
+ final Set knownDataSourceNames = this.names();
assert knownDataSourceNames != null;
final String dataSourceName = dsd.name();
if (!knownDataSourceNames.contains(dataSourceName)) {
final Entry extends String, ? extends Properties> entry = toProperties(dsd);
if (entry != null) {
- this.putDataSourceProperties(dataSourceName, entry.getValue());
+ this.put(dataSourceName, entry.getValue());
}
}
}
@@ -183,8 +182,6 @@ private void processInjectionPoint(@Observes final Proces
if (type instanceof Class && DataSource.class.isAssignableFrom((Class>) type)) {
final Set extends Annotation> qualifiers = injectionPoint.getQualifiers();
if (qualifiers != null && !qualifiers.isEmpty()) {
- final Config config = this.getConfig();
- assert config != null;
for (final Annotation qualifier : qualifiers) {
if (qualifier instanceof Named) {
final String dataSourceName = ((Named) qualifier).value();
@@ -202,10 +199,9 @@ private void processInjectionPoint(@Observes final Proces
// bootstrap here by issuing a
// request for a commonly present
// data source property value.
- config.getOptionalValue("javax.sql.DataSource."
- + dataSourceName
- + ".dataSourceClassName",
- String.class);
+ configPropertyValue("javax.sql.DataSource."
+ + dataSourceName
+ + ".dataSourceClassName");
}
}
}
diff --git a/integrations/cdi/datasource-hikaricp/src/main/java/module-info.java b/integrations/cdi/datasource-hikaricp/src/main/java/module-info.java
index 8ff20d2ed32..6ce41f5bf9f 100644
--- a/integrations/cdi/datasource-hikaricp/src/main/java/module-info.java
+++ b/integrations/cdi/datasource-hikaricp/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -16,7 +16,7 @@
/**
* CDI integration for the Hikari connection pool.
*
* @see
@@ -27,9 +27,8 @@
requires com.zaxxer.hikari;
requires jakarta.annotation;
- requires microprofile.config.api;
- requires static microprofile.metrics.api;
+ requires microprofile.metrics.api;
requires transitive io.helidon.integrations.datasource.cdi;
requires transitive jakarta.cdi;
diff --git a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java
index 9367deae66e..03d40623d11 100644
--- a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java
+++ b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java
@@ -37,7 +37,6 @@
import jakarta.enterprise.event.Event;
import jakarta.enterprise.inject.CreationException;
import jakarta.enterprise.inject.Instance;
-import jakarta.enterprise.inject.spi.Extension;
import jakarta.enterprise.inject.spi.configurator.BeanConfigurator;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Named;
@@ -48,25 +47,60 @@
import oracle.ucp.jdbc.PoolXADataSourceImpl;
/**
- * An {@link Extension} that arranges for named {@link DataSource} injection points to be satisfied by the Oracle Universal Connection
+ * An {@link AbstractDataSourceExtension} that arranges for {@linkplain Named named} {@link DataSource} injection points
+ * to be satisfied by the Oracle Universal Connection
* Pool.
*
+ * As with all portable extensions, to begin to make use of the features enabled by this class, ensure its containing
+ * artifact (normally a jar file) is on the runtime classpath of your CDI-enabled application.
+ *
* In accordance with the CDI specification, instances of this class are not necessarily safe for concurrent use by
* multiple threads.
+ *
+ * To support injection of a {@linkplain PoolDataSource Universal Connection Pool-backed PoolDataSource
}
+ * named {@code test}, first ensure that enough MicroProfile Config configuration is present to create a valid {@link
+ * PoolDataSource}. For example, the following sample system properties are sufficient for a {@link PoolDataSource}
+ * named {@code test} to be created:
+ *
+ * {@snippet lang="properties" :
+ * # (Note that "oracle.ucp.jdbc.PoolDataSource" below could be "javax.sql.DataSource" instead if you prefer
+ * # that your configuration not refer directly to Oracle-specific classnames in its keys.)
+ * # @link substring="oracle.ucp.jdbc.PoolDataSource" target="PoolDataSource" @link substring="oracle.jdbc.pool.OracleDataSource" target="oracle.jdbc.pool.OracleDataSource" :
+ * oracle.ucp.jdbc.PoolDataSource.test.connectionFactoryClassName=oracle.jdbc.pool.OracleDataSource
+ * oracle.ucp.jdbc.PoolDataSource.test.URL=jdbc:oracle:thin://@localhost:1521/XE
+ * oracle.ucp.jdbc.PoolDataSource.test.user=scott
+ * oracle.ucp.jdbc.PoolDataSource.test.password=tiger
+ * }
+ *
+ * With configuration such as the above, you can now inject the implicit {@link PoolDataSource} it defines:
+ *
+ * {@snippet :
+ * // Inject a PoolDataSource qualified with the name test into a private javax.sql.DataSource-typed field named ds:
+ * @jakarta.inject.Inject // @link substring="jakarta.inject.Inject" target="jakarta.inject.Inject"
+ * @jakarta.inject.Named("test") // @link substring="jakarta.inject.Named" target="jakarta.inject.Named"
+ * private javax.sql.DataSource ds; // @link substring="javax.sql.DataSource" target="DataSource"
+ * }
*/
public class UCPBackedDataSourceExtension extends AbstractDataSourceExtension {
- private static final Pattern DATASOURCE_NAME_PATTERN =
+ /**
+ * A {@link Pattern} capable of producing {@link Matcher} instances that identify certain portions of a
+ * configuration property name.
+ */
+ static final Pattern DATASOURCE_NAME_PATTERN =
Pattern.compile("^(?:javax\\.sql\\.|oracle\\.ucp\\.jdbc\\.Pool)(XA)?DataSource\\.([^.]+)\\.(.*)$");
// Capturing groups: (1 ) (2 ) (3 )
- // Are we XA? DS name DS Property
+ // Are we XA? DS name DS property name
private final Map xa;
/**
* Creates a new {@link UCPBackedDataSourceExtension}.
+ *
+ * @deprecated For use by CDI only.
*/
+ @Deprecated // For use by CDI only
public UCPBackedDataSourceExtension() {
super();
this.xa = new HashMap<>();
diff --git a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UniversalConnectionPoolExtension.java b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UniversalConnectionPoolExtension.java
new file mode 100644
index 00000000000..5882cbe81b3
--- /dev/null
+++ b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UniversalConnectionPoolExtension.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2024 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.integrations.datasource.ucp.cdi;
+
+import java.util.Properties;
+import java.util.regex.Matcher;
+
+import io.helidon.integrations.cdi.configurable.AbstractConfigurableExtension;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Event;
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.inject.CreationException;
+import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
+import jakarta.enterprise.inject.spi.configurator.BeanConfigurator;
+import jakarta.enterprise.util.TypeLiteral;
+import jakarta.inject.Named;
+import oracle.ucp.UniversalConnectionPool;
+import oracle.ucp.UniversalConnectionPoolAdapter;
+import oracle.ucp.UniversalConnectionPoolException;
+import oracle.ucp.admin.UniversalConnectionPoolManager;
+import oracle.ucp.admin.UniversalConnectionPoolManagerImpl;
+import oracle.ucp.jdbc.PoolDataSource;
+
+import static io.helidon.integrations.datasource.ucp.cdi.UCPBackedDataSourceExtension.DATASOURCE_NAME_PATTERN;
+
+/**
+ * An {@link AbstractConfigurableExtension} that provides injection support for {@link UniversalConnectionPoolManager}
+ * and {@linkplain Named named} {@link UniversalConnectionPool} instances.
+ *
+ * As with all portable extensions, to begin to make use of the features enabled by this class, ensure its containing
+ * artifact (normally a jar file) is on the runtime classpath of your CDI-enabled application.
+ *
+ * To support injection of the {@link UniversalConnectionPoolManager}, use normal CDI injection idioms:
+ *
+ * {@snippet :
+ * // Inject the UniversalConnectionPoolManager into a private field named ucpManager:
+ * @jakarta.inject.Inject // @link substring="jakarta.inject.Inject" target="jakarta.inject.Inject"
+ * private oracle.ucp.admin.UniversalConnectionPoolManager ucpManager;
+ * }
+ *
+ * To support injection of a {@link UniversalConnectionPool} named {@code test}, first ensure that enough MicroProfile
+ * Config configuration is present to create a valid {@link PoolDataSource}. For example, the following sample system
+ * properties are sufficient for a {@link PoolDataSource} named {@code test} to be created:
+ *
+ * {@snippet lang="properties" :
+ * # @link substring="oracle.ucp.jdbc.PoolDataSource" target="PoolDataSource" @link substring="oracle.jdbc.pool.OracleDataSource" target="oracle.jdbc.pool.OracleDataSource" :
+ * oracle.ucp.jdbc.PoolDataSource.test.connectionFactoryClassName=oracle.jdbc.pool.OracleDataSource
+ * oracle.ucp.jdbc.PoolDataSource.test.URL=jdbc:oracle:thin://@localhost:1521/XE
+ * oracle.ucp.jdbc.PoolDataSource.test.user=scott
+ * oracle.ucp.jdbc.PoolDataSource.test.password=tiger
+ * }
+ *
+ * See the {@link UCPBackedDataSourceExtension} documentation for more information about how {@link PoolDataSource}
+ * instances are made eligible for injection from configuration such as this.
+ *
+ * With configuration such as the above, you can now inject the implicit {@link UniversalConnectionPool} it also
+ * defines:
+ *
+ * {@snippet :
+ * // Inject a UniversalConnectionPool whose getName() method returns test into a private field named ucp:
+ * @jakarta.inject.Inject // @link substring="jakarta.inject.Inject" target="jakarta.inject.Inject"
+ * @jakarta.inject.Named("test") // @link substring="jakarta.inject.Named" target="jakarta.inject.Named"
+ * private oracle.ucp.UniversalConnectionPool ucp; // assert "test".equals(ucp.getName());
+ * }
+ *
+ * Note: Working directly with a {@link UniversalConnectionPool} is for advanced use cases
+ * only. Injecting and working with {@link PoolDataSource} instances is much more common, and {@link PoolDataSource} is
+ * the interface recommended by Oracle's documentation for users to interact with. See {@link
+ * UCPBackedDataSourceExtension}'s documentation for more details.
+ *
+ * @see UniversalConnectionPool
+ *
+ * @see UCPBackedDataSourceExtension
+ */
+public class UniversalConnectionPoolExtension extends AbstractConfigurableExtension {
+
+ /**
+ * Creates a new {@link UniversalConnectionPoolExtension}.
+ *
+ * @deprecated For use by CDI only.
+ */
+ @Deprecated // For use by CDI only
+ public UniversalConnectionPoolExtension() {
+ super();
+ }
+
+ @Override
+ protected final Matcher matcher(String configPropertyName) {
+ return configPropertyName == null ? null : DATASOURCE_NAME_PATTERN.matcher(configPropertyName);
+ }
+
+ @Override
+ protected final String name(Matcher configPropertyNameMatcher) {
+ return configPropertyNameMatcher == null ? null : configPropertyNameMatcher.group(2);
+ }
+
+ @Override
+ protected final String propertyName(Matcher configPropertyNameMatcher) {
+ return configPropertyNameMatcher == null ? null : configPropertyNameMatcher.group(3);
+ }
+
+ private void installManager(@Observes AfterBeanDiscovery event) {
+ event.addBean()
+ .addTransitiveTypeClosure(UniversalConnectionPoolManager.class)
+ .scope(ApplicationScoped.class)
+ .produceWith(instance -> {
+ UniversalConnectionPoolManager ucpm;
+ try {
+ ucpm = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager();
+ } catch (final UniversalConnectionPoolException e) {
+ throw new CreationException(e.getMessage(), e);
+ }
+ instance.select(new TypeLiteral>() {})
+ .get()
+ .fire(ucpm);
+ return ucpm;
+ });
+ }
+
+ @Override
+ protected void addBean(BeanConfigurator beanConfigurator,
+ Named name,
+ Properties properties) {
+ beanConfigurator
+ .addQualifier(name)
+ .addTransitiveTypeClosure(UniversalConnectionPool.class)
+ .scope(ApplicationScoped.class)
+ .produceWith(instance -> {
+ PoolDataSource pds = instance.select(PoolDataSource.class, name).get();
+ UniversalConnectionPoolManager ucpm = instance.select(UniversalConnectionPoolManager.class).get();
+ UniversalConnectionPool ucp;
+ try {
+ ucpm.createConnectionPool((UniversalConnectionPoolAdapter) pds);
+ ucp = ucpm.getConnectionPool(pds.getConnectionPoolName());
+ } catch (UniversalConnectionPoolException e) {
+ throw new CreationException(e.getMessage(), e);
+ }
+ instance.select(new TypeLiteral>() {})
+ .get()
+ .fire(ucp);
+ return ucp;
+ })
+ .disposeWith((ucp, instance) -> {
+ try {
+ ucp.stop();
+ UniversalConnectionPoolManager ucpm = instance.select(UniversalConnectionPoolManager.class).get();
+ ucpm.destroyConnectionPool(ucp.getName());
+ } catch (UniversalConnectionPoolException e) {
+ throw new CreationException(e.getMessage(), e);
+ }
+ });
+ }
+
+}
diff --git a/integrations/cdi/datasource-ucp/src/main/java/module-info.java b/integrations/cdi/datasource-ucp/src/main/java/module-info.java
index 98fe46cd31d..9c527873989 100644
--- a/integrations/cdi/datasource-ucp/src/main/java/module-info.java
+++ b/integrations/cdi/datasource-ucp/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -26,6 +26,7 @@
@SuppressWarnings({ "requires-automatic"})
module io.helidon.integrations.datasource.ucp.cdi {
+ requires com.oracle.database.jdbc; // com.oracle.database.ucp needs this (!)
requires com.oracle.database.ucp;
requires java.desktop; // For java.beans
requires java.naming; // PoolDataSourceImpl implements javax.naming.Referenceable
@@ -39,6 +40,7 @@
exports io.helidon.integrations.datasource.ucp.cdi;
provides jakarta.enterprise.inject.spi.Extension
- with io.helidon.integrations.datasource.ucp.cdi.UCPBackedDataSourceExtension;
+ with io.helidon.integrations.datasource.ucp.cdi.UCPBackedDataSourceExtension,
+ io.helidon.integrations.datasource.ucp.cdi.UniversalConnectionPoolExtension;
}
diff --git a/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestDataSourceAcquisition.java b/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestDataSourceAcquisition.java
index 4d0560499bd..1ddd7056287 100644
--- a/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestDataSourceAcquisition.java
+++ b/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestDataSourceAcquisition.java
@@ -27,6 +27,7 @@
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.inject.Named;
+import oracle.ucp.UniversalConnectionPool;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceImpl;
import oracle.ucp.jdbc.PoolXADataSource;
@@ -48,6 +49,10 @@ class TestDataSourceAcquisition {
@Named("test")
private DataSource test;
+ @Inject
+ @Named("test")
+ private UniversalConnectionPool testUcp;
+
private Server server;
TestDataSourceAcquisition() {
@@ -81,6 +86,8 @@ void stopServer() {
private void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event) throws SQLException {
assertThat(this.test, notNullValue());
assertThat(this.test.toString(), notNullValue());
+ assertThat(this.testUcp, notNullValue());
+ assertThat(this.testUcp.getName(), is("test"));
final PoolDataSourceImpl contextualInstance =
(PoolDataSourceImpl) ((WeldClientProxy) this.test).getMetadata().getContextualInstance();
assertThat(contextualInstance.getDescription(), is("A test datasource"));
diff --git a/integrations/cdi/datasource/pom.xml b/integrations/cdi/datasource/pom.xml
index cd5bbd0c73c..ac11ec88115 100644
--- a/integrations/cdi/datasource/pom.xml
+++ b/integrations/cdi/datasource/pom.xml
@@ -35,6 +35,10 @@
+
+ io.helidon.integrations.cdi
+ helidon-integrations-cdi-configurable
+
org.eclipse.microprofile.config
microprofile-config-api
diff --git a/integrations/cdi/datasource/src/main/java/io/helidon/integrations/datasource/cdi/AbstractDataSourceExtension.java b/integrations/cdi/datasource/src/main/java/io/helidon/integrations/datasource/cdi/AbstractDataSourceExtension.java
index 41107b45af9..93d616a7646 100644
--- a/integrations/cdi/datasource/src/main/java/io/helidon/integrations/datasource/cdi/AbstractDataSourceExtension.java
+++ b/integrations/cdi/datasource/src/main/java/io/helidon/integrations/datasource/cdi/AbstractDataSourceExtension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021 Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2024 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.
@@ -16,41 +16,29 @@
package io.helidon.integrations.datasource.cdi;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import javax.sql.DataSource;
-import jakarta.enterprise.event.Observes;
-import jakarta.enterprise.inject.literal.NamedLiteral;
-import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
-import jakarta.enterprise.inject.spi.Extension;
+import io.helidon.integrations.cdi.configurable.AbstractConfigurableExtension;
+
import jakarta.enterprise.inject.spi.configurator.BeanConfigurator;
import jakarta.inject.Named;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
-import org.eclipse.microprofile.config.spi.ConfigSource;
/**
- * An abstract {@link Extension} whose subclasses arrange for {@link
- * DataSource} instances to be added as CDI beans.
+ * An {@link AbstractConfigurableExtension} whose subclasses arrange for {@link DataSource} instances to be added as CDI
+ * beans.
*
* Thread Safety
*
- * As with all CDI portable extensions, this class' instances are
- * not safe for concurrent use by multiple threads.
+ * As with all CDI portable extensions, this class' instances are not safe for concurrent use by multiple
+ * threads.
*/
-public abstract class AbstractDataSourceExtension implements Extension {
-
- private final Map masterProperties;
-
- private final Map explicitlySetProperties;
+public abstract class AbstractDataSourceExtension extends AbstractConfigurableExtension {
private final Config config;
@@ -59,26 +47,65 @@ public abstract class AbstractDataSourceExtension implements Extension {
*/
protected AbstractDataSourceExtension() {
super();
- this.masterProperties = new HashMap<>();
- this.explicitlySetProperties = new HashMap<>();
this.config = ConfigProvider.getConfig();
}
+ /**
+ * Returns the {@link Config} instance used to {@linkplain #configPropertyValue(String) acquire configuration
+ * property values}.
+ *
+ * This method never returns {@code null}.
+ *
+ * This method exists for backwards compatibility only and its usage is discouraged.
+ *
+ * @return a non-{@code null} {@link Config} instance
+ *
+ * @deprecated This method exists for backwards compatibility only and has no replacement.
+ */
+ @Deprecated // For backwards compatibility only.
+ protected final Config getConfig() {
+ return this.config;
+ }
+
+ /**
+ * Calls the {@link #configPropertyNames()} method and returns its return value.
+ *
+ * This method never returns {@code null}.
+ *
+ * This method exists for backwards compatibility only and the {@link #configPropertyNames()} method is
+ * preferred.
+ *
+ * @return the return value of an invocation of the {@link #configPropertyNames()} method
+ *
+ * @see #configPropertyNames()
+ *
+ * @deprecated This method exists for backwards compatibility only. Please use the {@link #configPropertyNames()} method instead.
+ */
+ @Deprecated
+ protected final Set getPropertyNames() {
+ return this.configPropertyNames();
+ }
+
+ @Override
+ protected final Matcher matcher(String configPropertyName) {
+ return this.getDataSourcePropertyPatternMatcher(configPropertyName);
+ }
+
/**
* Returns a {@link Matcher} for a property name.
*
* Implementations of this method must not return {@code null}.
*
+ * Implementations of this method must not invoke the {@link #matcher(String)} method or an infinite loop may
+ * result.
+ *
* Given a {@link String} like
- * javax.sql.DataSource.dataSourceName.dataSourcePropertyName
,
- * any implementation of this method should return a non-{@code
- * null} {@link Matcher} that is capable of being supplied to the
- * {@link #getDataSourceName(Matcher)} and {@link
- * #getDataSourcePropertyName(Matcher)} methods.
+ * javax.sql.DataSource.dataSourceName.dataSourcePropertyName
, any implementation of
+ * this method should return a non-{@code null} {@link Matcher} that is capable of being supplied to the {@link
+ * #getDataSourceName(Matcher)} and {@link #getDataSourcePropertyName(Matcher)} methods.
*
- * @param configPropertyName the name of a configuration property
- * that logically contains a data source name and a
- * data source property name; must not be {@code null}
+ * @param configPropertyName the name of a configuration property that logically contains a data source
+ * name and a data source property name; must not be {@code null}
*
* @return a non-{@code null} {@link Matcher}
*
@@ -88,17 +115,22 @@ protected AbstractDataSourceExtension() {
*/
protected abstract Matcher getDataSourcePropertyPatternMatcher(String configPropertyName);
+ @Override
+ protected final String name(Matcher propertyPatternMatcher) {
+ return this.getDataSourceName(propertyPatternMatcher);
+ }
+
/**
- * Given a {@link Matcher} that has been produced by the {@link
- * #getDataSourcePropertyPatternMatcher(String)} method, returns
- * the relevant data source name.
+ * Given a {@link Matcher} that has been produced by the {@link #getDataSourcePropertyPatternMatcher(String)}
+ * method, returns the relevant data source name.
*
* Implementations of this method may return {@code null}.
*
- * @param dataSourcePropertyPatternMatcher a {@link Matcher}
- * produced by the {@link
- * #getDataSourcePropertyPatternMatcher(String)} method; must not
- * be {@code null}
+ * Implementations of this method must not invoke the {@link #name(Matcher)} method or an infinite loop may
+ * result.
+ *
+ * @param dataSourcePropertyPatternMatcher a {@link Matcher} produced by the {@link
+ * #getDataSourcePropertyPatternMatcher(String)} method; must not be {@code null}
*
* @return a data source name, or {@code null}
*
@@ -106,17 +138,22 @@ protected AbstractDataSourceExtension() {
*/
protected abstract String getDataSourceName(Matcher dataSourcePropertyPatternMatcher);
+ @Override
+ protected final String propertyName(Matcher propertyPatternMatcher) {
+ return this.getDataSourcePropertyName(propertyPatternMatcher);
+ }
+
/**
- * Given a {@link Matcher} that has been produced by the {@link
- * #getDataSourcePropertyPatternMatcher(String)} method, returns
- * the relevant data source property name.
+ * Given a {@link Matcher} that has been produced by the {@link #getDataSourcePropertyPatternMatcher(String)}
+ * method, returns the relevant data source property name.
*
* Implementations of this method may return {@code null}.
*
- * @param dataSourcePropertyPatternMatcher a {@link Matcher}
- * produced by the {@link
- * #getDataSourcePropertyPatternMatcher(String)} method; must not
- * be {@code null}
+ * Implementations of this method must not invoke the {@link #propertyName(Matcher)} method or an infinite loop
+ * may result.
+ *
+ * @param dataSourcePropertyPatternMatcher a {@link Matcher} produced by the {@link
+ * #getDataSourcePropertyPatternMatcher(String)} method; must not be {@code null}
*
* @return a data source property name, or {@code null}
*
@@ -125,185 +162,55 @@ protected AbstractDataSourceExtension() {
protected abstract String getDataSourcePropertyName(Matcher dataSourcePropertyPatternMatcher);
/**
- * Called to permit subclasses to add a {@link DataSource}-typed
- * bean using the supplied {@link BeanConfigurator}.
- *
- * Implementations of this method will be called from an
- * observer method that is observing the {@link
- * AfterBeanDiscovery} container lifecycle event.
- *
- * @param beanConfigurator the {@link BeanConfigurator} to use to
- * actually add a new bean; must not be {@code null}
- *
- * @param name a {@link Named} instance qualifying the {@link
- * DataSource}-typed bean to be added; may be {@code null}
- *
- * @param properties a {@link Properties} instance containing
- * properties relevant to the data source; must not be {@code
- * null}
- */
- protected abstract void addBean(BeanConfigurator beanConfigurator,
- Named name,
- Properties properties);
-
- /**
- * Returns the {@link Config} instance used to acquire
- * configuration property values.
+ * Returns a {@link Set} of data source names known to this {@link AbstractDataSourceExtension} implementation.
*
* This method never returns {@code null}.
*
- * @return a non-{@code null} {@link Config} instance
- *
- * @see Config
- */
- protected final Config getConfig() {
- return this.config;
- }
-
- /**
- * Returns a {@link Set} of data source names known to this {@link
- * AbstractDataSourceExtension} implementation.
- *
- * This method never returns {@code null}.
+ * The {@link Set} returned by this method is {@linkplain Collections#unmodifiableSet(Set) unmodifiable}.
*
- * The {@link Set} returned by this method is {@linkplain
- * Collections#unmodifiableSet(Set) unmodifiable}.
+ * @return a non-{@code null}, {@linkplain Collections#unmodifiableSet(Set) unmodifiable Set
} of known
+ * data source names
*
- * @return a non-{@code null}, {@linkplain
- * Collections#unmodifiableSet(Set) unmodifiable Set
}
- * of known data source names
+ * @deprecated This method exists for backwards compatibility only. Please use the {@link #names()} method
+ * instead.
*/
+ @Deprecated // for backwards compatibility only
protected final Set getDataSourceNames() {
- final Set returnValue;
- if (this.masterProperties.isEmpty()) {
- if (this.explicitlySetProperties.isEmpty()) {
- returnValue = Collections.emptySet();
- } else {
- returnValue = Collections.unmodifiableSet(this.explicitlySetProperties.keySet());
- }
- } else {
- returnValue = Collections.unmodifiableSet(this.masterProperties.keySet());
- }
- return returnValue;
+ return this.names();
}
/**
- * Adds additional synthesized properties to an internal map of
- * data source properties whose contents will be processed
- * eventually by the {@link #addBean(BeanConfigurator, Named,
- * Properties)} method.
+ * Adds additional synthesized properties to an internal map of data source properties whose contents will be
+ * processed eventually by the {@link #addBean(BeanConfigurator, Named, Properties)} method.
*
* This method may return {@code null}.
*
- * @param dataSourceName the name of the data source under which
- * the supplied {@link Properties} will be indexed; may be {@code
- * null}
+ * @param dataSourceName the name of the data source under which the supplied {@link Properties} will be indexed;
+ * may be {@code null}
*
- * @param properties the {@link Properties} to put; may be {@code null}
+ * @param properties the {@link Properties} to put; must not be {@code null}
*
- * @return the prior {@link Properties} indexed under the supplied
- * {@code dataSourceName}, or {@code null}
- */
- protected final Properties putDataSourceProperties(final String dataSourceName, final Properties properties) {
- return this.explicitlySetProperties.put(dataSourceName, properties);
- }
-
- /**
- * {@linkplain Map#clear() Clears} and then
- * builds or rebuilds an internal map of data source properties
- * whose contents will be processed eventually by the {@link
- * #addBean(BeanConfigurator, Named, Properties)} method.
+ * @return the prior {@link Properties} indexed under the supplied {@code dataSourceName}, or {@code null}
*
- * If no subclass explicitly calls this method, it will be
- * called by the {@link #addBean(BeanConfigurator, Named,
- * Properties)} method just prior to its other activities.
+ * @exception NullPointerException if {@code properties} is {@code null}
*
- * Once the {@link #addBean(BeanConfigurator, Named,
- * Properties)} method has run to completion, while this method
- * may be called freely its use is discouraged and its effects
- * will no longer be used.
- *
- * @see #addBean(BeanConfigurator, Named, Properties)
+ * @deprecated This method exists for backwards compatibility only. Please use the {@link #put(String, Properties)}
+ * method instead.
*/
- protected final void initializeMasterProperties() {
- this.masterProperties.clear();
- final Set extends String> allPropertyNames = this.getPropertyNames();
- if (allPropertyNames != null && !allPropertyNames.isEmpty()) {
- for (final String propertyName : allPropertyNames) {
- final Optional propertyValue = this.config.getOptionalValue(propertyName, String.class);
- if (propertyValue != null && propertyValue.isPresent()) {
- final Matcher matcher = this.getDataSourcePropertyPatternMatcher(propertyName);
- if (matcher != null && matcher.matches()) {
- final String dataSourceName = this.getDataSourceName(matcher);
- Properties properties = this.masterProperties.get(dataSourceName);
- if (properties == null) {
- properties = new Properties();
- this.masterProperties.put(dataSourceName, properties);
- }
- final String dataSourcePropertyName = this.getDataSourcePropertyName(matcher);
- properties.setProperty(dataSourcePropertyName, propertyValue.get());
- }
- }
- }
- }
- this.masterProperties.putAll(this.explicitlySetProperties);
+ @Deprecated // for backwards compatibility only
+ protected final Properties putDataSourceProperties(final String dataSourceName, final Properties properties) {
+ return this.put(dataSourceName, properties);
}
/**
- * Returns a {@link Set} of all known configuration property
- * names.
- *
- * This method never returns {@code null}.
- *
- * The {@link Set} returned by this method is {@linkplain
- * Collections#unmodifiableSet(Set) unmodifiable}.
+ * Calls the {@link #initializeNamedProperties()} method.
*
- * The {@link Set} returned by this method is not safe for
- * concurrent use by multiple threads.
- *
- * Any other semantics of the {@link Set} returned by this
- * method are governed by the MicroProfile Config specification.
- *
- * @return a non-{@code null}, {@linkplain
- * Collections#unmodifiableSet(Set) unmodifiable Set
}
- * of all known configuration property names
- *
- * @see Config#getConfigSources()
- *
- * @see ConfigSource#getPropertyNames()
+ * @deprecated This method exists for backwards compatibility only. Please use the {@link
+ * #initializeNamedProperties()} method instead.
*/
- protected final Set getPropertyNames() {
- final Set returnValue;
- final Iterable propertyNames = this.config.getPropertyNames();
- if (propertyNames == null) {
- returnValue = Collections.emptySet();
- } else {
- final Set set = new HashSet<>();
- propertyNames.iterator().forEachRemaining(n -> set.add(n));
- returnValue = Set.copyOf(set);
- }
- return returnValue;
- }
-
- private void afterBeanDiscovery(@Observes final AfterBeanDiscovery event) {
- if (event != null) {
- if (this.masterProperties.isEmpty()) {
- this.initializeMasterProperties();
- }
- final Set extends Entry extends String, ? extends Properties>> masterPropertiesEntries =
- this.masterProperties.entrySet();
- if (masterPropertiesEntries != null && !masterPropertiesEntries.isEmpty()) {
- for (final Entry extends String, ? extends Properties> entry : masterPropertiesEntries) {
- if (entry != null) {
- this.addBean(event.addBean(), NamedLiteral.of(entry.getKey()), entry.getValue());
- }
- }
- }
- }
- this.masterProperties.clear();
- this.explicitlySetProperties.clear();
+ @Deprecated // for backwards compatibility only
+ protected final void initializeMasterProperties() {
+ this.initializeNamedProperties();
}
}
diff --git a/integrations/cdi/datasource/src/main/java/module-info.java b/integrations/cdi/datasource/src/main/java/module-info.java
index 23141f7b0c5..5eb0fa40056 100644
--- a/integrations/cdi/datasource/src/main/java/module-info.java
+++ b/integrations/cdi/datasource/src/main/java/module-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 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.
@@ -27,8 +27,8 @@
requires transitive jakarta.cdi;
requires transitive jakarta.inject;
requires transitive java.sql;
+ requires transitive io.helidon.integrations.cdi.configurable;
requires transitive microprofile.config.api;
-
exports io.helidon.integrations.datasource.cdi;
}
diff --git a/pom.xml b/pom.xml
index 29e1d775b56..7eae5c0673c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -165,6 +165,8 @@
https://narayana.io/docs/api/
https://www.eclipse.org/eclipselink/api/4.0/
https://docs.jboss.org/hibernate/orm/${version.lib.hibernate.family}/javadocs/
+ https://docs.oracle.com/en/database/oracle/oracle-database/${version.lib.ojdbc.family}/jajdb/
+ https://docs.oracle.com/en/database/oracle/oracle-database/${version.lib.ojdbc.family}/jjuar/
2.14
https://fasterxml.github.io/jackson-annotations/javadoc/${javadoc.jackson.version}/
@@ -399,6 +401,14 @@
${javadoc.link.hibernate}
${javadoc.pkg.dir}/hibernate
+
+ ${javadoc.link.ojdbc}
+ ${javadoc.pkg.dir}/ojdbc
+
+
+ ${javadoc.link.ucp}
+ ${javadoc.pkg.dir}/ucp
+
-J-Dhttp.agent=maven-javadoc-plugin