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:

+ * + *
    + * + *
  1. Type name: the class name of the kind of object this extension manages. Example: {@code + * javax.sql.DataSource}
  2. + * + *
  3. Name: a name designating one of potentially many such objects. Example: {@code prod}
  4. + * + *
  5. Property name: a name of a property of the kind of object this extension manages. Example: + * {@code user}
  6. + * + *
  7. 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}
  8. + * + *
+ * + *

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 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 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 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 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 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> masterPropertiesEntries = - this.masterProperties.entrySet(); - if (masterPropertiesEntries != null && !masterPropertiesEntries.isEmpty()) { - for (final Entry 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