Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enables support of application.yaml use case in data source injection extensions #847

Merged
merged 2 commits into from
Jul 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions integrations/cdi/datasource-hikaricp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
</properties>

<dependencies>

<!-- Compile-scoped dependencies. -->
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
Expand Down Expand Up @@ -122,9 +123,28 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<groupId>io.helidon.microprofile.server</groupId>
<artifactId>helidon-microprofile-server</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<classpathDependencyExcludes>
<classpathDependencyExclude>io.helidon.serviceconfiguration:helidon-serviceconfiguration-hikaricp-accs</classpathDependencyExclude>
<classpathDependencyExclude>io.helidon.serviceconfiguration:helidon-serviceconfiguration-hikaricp-localhost</classpathDependencyExclude>
</classpathDependencyExcludes>
<systemPropertyVariables>
<java.util.logging.config.file>${project.basedir}/src/test/logging.properties</java.util.logging.config.file>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved.
*
* 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.hikaricp.cdi;

import java.sql.Connection;
import java.sql.SQLException;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.inject.Inject;
import javax.inject.Named;
import javax.sql.DataSource;

import io.helidon.microprofile.server.Server;

import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertNotNull;

@ApplicationScoped
class TestConfiguration {

@Inject
@Named("test")
private DataSource test;

private Server server;

TestConfiguration() {
super();
}

@BeforeEach
void startServer() {
this.stopServer();
final Server.Builder builder = Server.builder();
assertNotNull(builder);
// The Helidon MicroProfile server implementation uses
// ConfigProviderResolver#getConfig(ClassLoader) directly
// instead of ConfigProvider#getConfig() so we follow suit
// here for fidelity.
builder.config(ConfigProviderResolver.instance().getConfig(Thread.currentThread().getContextClassLoader()));
final SeContainerInitializer initializer = SeContainerInitializer.newInstance()
.addBeanClasses(TestConfiguration.class);
assertNotNull(initializer);
builder.cdiContainer(initializer.initialize());
this.server = builder.build();
assertNotNull(this.server);
this.server.start();
}

@AfterEach
void stopServer() {
if (this.server != null) {
this.server.stop();
this.server = null;
}
}

private void onStartup(@Observes @Initialized(ApplicationScoped.class) final Object event) throws SQLException {
assertNotNull(this.test);
assertNotNull(this.test.toString());
Connection connection = null;
try {
connection = this.test.getConnection();
} finally {
connection.close();
}
}

@Test
void testConfiguration() {

}

}
17 changes: 17 additions & 0 deletions integrations/cdi/datasource-hikaricp/src/test/logging.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
#
# 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.
.level=INFO
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level=FINEST
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
#
# 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.
javax:
sql:
DataSource:
test:
dataSourceClassName: org.h2.jdbcx.JdbcDataSource
dataSource:
url: jdbc:h2:mem:test
user: sa
password: ""

Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import oracle.ucp.jdbc.PoolDataSourceImpl;

/**
* An {@link Extension} that arranges for named {@link DataSource}
Expand Down Expand Up @@ -97,7 +96,6 @@ protected final void addBean(final BeanConfigurator<DataSource> beanConfigurator
beanConfigurator
.addQualifier(dataSourceName)
.addTransitiveTypeClosure(PoolDataSource.class)
.beanClass(PoolDataSourceImpl.class)
.scope(ApplicationScoped.class)
.createWith(cc -> {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import javax.enterprise.event.Observes;
import javax.enterprise.inject.literal.NamedLiteral;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.configurator.BeanConfigurator;
import javax.inject.Named;
Expand All @@ -41,11 +40,18 @@
/**
* An abstract {@link Extension} whose subclasses arrange for {@link
* DataSource} instances to be added as CDI beans.
*
* <h2>Thread Safety</h2>
*
* <p>As with all CDI portable extensions, this class' instances are
* not safe for concurrent use by multiple threads.</p>
*/
public abstract class AbstractDataSourceExtension implements Extension {

private final Map<String, Properties> masterProperties;

private final Map<String, Properties> explicitlySetProperties;

private final Config config;

/**
Expand All @@ -54,6 +60,7 @@ public abstract class AbstractDataSourceExtension implements Extension {
protected AbstractDataSourceExtension() {
super();
this.masterProperties = new HashMap<>();
this.explicitlySetProperties = new HashMap<>();
this.config = ConfigProvider.getConfig();
}

Expand Down Expand Up @@ -167,7 +174,17 @@ protected final Config getConfig() {
* of known data source names
*/
protected final Set<String> getDataSourceNames() {
return Collections.unmodifiableSet(this.masterProperties.keySet());
final Set<String> 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;
}

/**
Expand All @@ -188,10 +205,28 @@ protected final Set<String> getDataSourceNames() {
* {@code dataSourceName}, or {@code null}
*/
protected final Properties putDataSourceProperties(final String dataSourceName, final Properties properties) {
return this.masterProperties.put(dataSourceName, properties);
return this.explicitlySetProperties.put(dataSourceName, properties);
}

private void initializeMasterProperties(@Observes final BeforeBeanDiscovery event) {
/**
* <strong>{@linkplain Map#clear() Clears}</strong> 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.
*
* <p>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.</p>
*
* <p>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.</p>
*
* @see #addBean(BeanConfigurator, Named, Properties)
*/
protected final void initializeMasterProperties() {
this.masterProperties.clear();
final Set<? extends String> allPropertyNames = this.getPropertyNames();
if (allPropertyNames != null && !allPropertyNames.isEmpty()) {
for (final String propertyName : allPropertyNames) {
Expand All @@ -211,10 +246,12 @@ private void initializeMasterProperties(@Observes final BeforeBeanDiscovery even
}
}
}
this.masterProperties.putAll(this.explicitlySetProperties);
}

/**
* Returns a {@link Set} of all known configuration property names.
* Returns a {@link Set} of all known configuration property
* names.
*
* <p>This method never returns {@code null}.</p>
*
Expand Down Expand Up @@ -249,6 +286,16 @@ protected final Set<String> getPropertyNames() {
// make sure to get all property names from all ConfigSources
// "by hand".
//
// Additionally, the Helidon MicroProfile Config
// implementation may add on some Helidon SE Config sources
// that are not represented as MicroProfile Config sources.
// Consequently we have to source property names from both the
// MicroProfile Config ConfigSources and from Helidon itself.
// We do this by first iterating over the MicroProfile Config
// ConfigSources and then augmenting where necessary with any
// other (cached) property names reported by the Helidon
// MicroProfile Config implementation.
//
// (The MicroProfile Config specification also does not say
// whether a ConfigSource is thread-safe
// (https://github.com/eclipse/microprofile-config/issues/369),
Expand All @@ -259,8 +306,23 @@ protected final Set<String> getPropertyNames() {
// implementation caches all property names up front, which
// may not be correct, but is also not forbidden.
final Set<String> returnValue;
final Set<String> propertyNames = getPropertyNames(this.config.getConfigSources());
if (propertyNames == null || propertyNames.isEmpty()) {

// Start by getting all the property names directly from our
// MicroProfile Config ConfigSources. They take precedence.
Set<String> propertyNames = getPropertyNames(this.config.getConfigSources());
assert propertyNames != null;

// Add any property names that the Config itself might report
// that aren't reflected, for whatever reason, in the
// ConfigSources' property names.
final Iterable<String> configPropertyNames = this.config.getPropertyNames();
if (configPropertyNames != null) {
for (final String configPropertyName : configPropertyNames) {
propertyNames.add(configPropertyName);
}
}

if (propertyNames.isEmpty()) {
returnValue = Collections.emptySet();
} else {
returnValue = Collections.unmodifiableSet(propertyNames);
Expand All @@ -280,11 +342,14 @@ private static Set<String> getPropertyNames(final Iterable<? extends ConfigSourc
}
}
}
return Collections.unmodifiableSet(returnValue);
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()) {
Expand All @@ -296,6 +361,7 @@ private void afterBeanDiscovery(@Observes final AfterBeanDiscovery event) {
}
}
this.masterProperties.clear();
this.explicitlySetProperties.clear();
}

}