-
Notifications
You must be signed in to change notification settings - Fork 49
feat: Add Global Datasource configuration application for data-source-properties #1648
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
base: 6.2.x
Are you sure you want to change the base?
Changes from all commits
7e4e2c0
6c774eb
ccf4de9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2017-2020 original authors | ||
* | ||
* 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 | ||
* | ||
* https://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.micronaut.configuration.jdbc.dbcp; | ||
|
||
import io.micronaut.context.annotation.Requires; | ||
import io.micronaut.context.event.BeanCreatedEvent; | ||
import io.micronaut.context.event.BeanCreatedEventListener; | ||
import jakarta.inject.Inject; | ||
import jakarta.inject.Singleton; | ||
|
||
/** | ||
* A bean created event listener that applies global datasource properties to | ||
* {@link DatasourceConfiguration} beans when they are created. This modifier | ||
* ensures that global properties defined under the "global.datasources.data-source-properties" | ||
* configuration prefix are automatically applied to all datasource configurations, | ||
* while preserving individual datasource-specific settings that take precedence. | ||
* | ||
* <p>The modifier only adds global properties that are not already present in the | ||
* individual datasource configuration, ensuring that specific configurations always | ||
* override global defaults. Properties with null values are ignored.</p> | ||
* | ||
* <p>This bean is only created when the "global.datasources.data-source-properties" | ||
* configuration property is present.</p> | ||
* | ||
* @author James Forward | ||
*/ | ||
@Requires(property = "global.datasources.data-source-properties") | ||
@Singleton | ||
public class GlobalDatasourceConfigModifier implements BeanCreatedEventListener<DatasourceConfiguration> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pls make final, package private and annotate |
||
|
||
@Inject | ||
GlobalDatasourceProperties globalDatasourceProperties; | ||
|
||
@Override | ||
public DatasourceConfiguration onCreated(BeanCreatedEvent<DatasourceConfiguration> event) { | ||
|
||
DatasourceConfiguration configuration = event.getBean(); | ||
globalDatasourceProperties.getDataSourceProperties() | ||
.forEach((key, value) -> { | ||
if (value != null && !configuration.getIndividualDsProperties().containsKey(key)) { | ||
configuration.addConnectionProperty(key, value); | ||
} | ||
} | ||
); | ||
return configuration; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright 2017-2020 original authors | ||
* | ||
* 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 | ||
* | ||
* https://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.micronaut.configuration.jdbc.dbcp; | ||
|
||
import io.micronaut.context.annotation.ConfigurationProperties; | ||
import io.micronaut.context.annotation.Requires; | ||
import io.micronaut.core.convert.format.MapFormat; | ||
import io.micronaut.core.naming.conventions.StringConvention; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* Configuration properties for global datasource settings that can be applied | ||
* to all {@link DatasourceConfiguration} instances. This class binds configuration | ||
* properties under the "global.datasources" prefix and provides a way to define | ||
* common datasource properties that should be applied across all datasources | ||
* in the application. | ||
* | ||
* <p>Properties defined here serve as defaults that can be overridden by | ||
* individual datasource configurations. The primary use case is to avoid | ||
* repetition when multiple datasources share common settings such as | ||
* connection pool parameters, SSL settings, or application-specific properties.</p> | ||
* | ||
* <p>This bean is only created when the "global.datasources.data-source-properties" | ||
* configuration property is present, ensuring it doesn't interfere with applications | ||
* that don't use global datasource configuration.</p> | ||
* | ||
* @author James Forward | ||
*/ | ||
@Requires(property = "global.datasources.data-source-properties") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think better name would be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dstepanov The idea was to have |
||
@ConfigurationProperties("global.datasources") | ||
public class GlobalDatasourceProperties { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pls make final and add javadoc to methods |
||
private Map<String, String> dataSourceProperties = new HashMap<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this has to be mutatable? |
||
|
||
public Map<String, String> getDataSourceProperties() { | ||
return dataSourceProperties; | ||
} | ||
|
||
public void setDataSourceProperties(@MapFormat(transformation = MapFormat.MapTransformation.FLAT, keyFormat = StringConvention.RAW) Map<String, String> dataSourceProperties) { | ||
this.dataSourceProperties.putAll(dataSourceProperties); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* Copyright 2017-2020 original authors | ||
* | ||
* 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 | ||
* | ||
* https://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. | ||
*/ | ||
//file:noinspection GroovyAccessibility | ||
package io.micronaut.configuration.jdbc.dbcp | ||
|
||
import io.micronaut.context.ApplicationContext | ||
import io.micronaut.context.DefaultApplicationContext | ||
import io.micronaut.context.env.MapPropertySource | ||
import spock.lang.Specification | ||
|
||
class GlobalDatasourcePropertiesSpec extends Specification { | ||
|
||
void "test no global datasource configuration exists when no global properties are present"() { | ||
given: | ||
ApplicationContext applicationContext = new DefaultApplicationContext("test") | ||
applicationContext.environment.addPropertySource(MapPropertySource.of( | ||
'test', | ||
['datasources.default.url': 'jdbc:h2:mem:default'] | ||
)) | ||
applicationContext.start() | ||
|
||
when: | ||
Optional<GlobalDatasourceProperties> properties = applicationContext.findBean(GlobalDatasourceProperties) | ||
Optional<DatasourceConfiguration> datasourceConfig = applicationContext.findBean(DatasourceConfiguration) | ||
|
||
then: "No global beans are created when no global configuration is present" | ||
datasourceConfig.isPresent() | ||
datasourceConfig.get().connectionProperties.isEmpty() | ||
datasourceConfig.get().url == 'jdbc:h2:mem:default' | ||
properties.isEmpty() | ||
|
||
cleanup: | ||
applicationContext.close() | ||
} | ||
|
||
void "test global datasource properties configuration creates correct beans"() { | ||
given: | ||
ApplicationContext applicationContext = new DefaultApplicationContext("test") | ||
applicationContext.environment.addPropertySource(MapPropertySource.of( | ||
'test', | ||
['global.datasources.data-source-properties.ApplicationName': 'MyApp', | ||
'global.datasources.data-source-properties.assumeMinServerVersion': '9.0', | ||
'global.datasources.data-source-properties.reWriteBatchInserts': true] | ||
)) | ||
applicationContext.start() | ||
|
||
when: | ||
GlobalDatasourceProperties properties = applicationContext.getBean(GlobalDatasourceProperties) | ||
|
||
then: "GlobalDatasourceProperties bean is created with correct properties" | ||
properties != null | ||
properties.dataSourceProperties != null | ||
properties.dataSourceProperties.size() == 3 | ||
properties.dataSourceProperties['ApplicationName'] == 'MyApp' | ||
properties.dataSourceProperties['assumeMinServerVersion'] == '9.0' | ||
properties.dataSourceProperties['reWriteBatchInserts'] == "true" | ||
|
||
and: "GlobalDatasourceConfigModifier bean is also created" | ||
applicationContext.containsBean(GlobalDatasourceConfigModifier) | ||
|
||
cleanup: | ||
applicationContext.close() | ||
} | ||
|
||
void "test global properties are applied to all datasource configurations"() { | ||
given: | ||
ApplicationContext applicationContext = new DefaultApplicationContext("test") | ||
applicationContext.environment.addPropertySource(MapPropertySource.of( | ||
'test', | ||
['datasources.default.url': 'jdbc:h2:mem:default', | ||
'datasources.secondary.url': 'jdbc:h2:mem:secondary', | ||
'global.datasources.data-source-properties.ApplicationName': 'GlobalApp', | ||
'global.datasources.data-source-properties.assumeMinServerVersion': '9.0'] | ||
)) | ||
applicationContext.start() | ||
|
||
when: | ||
def datasourceConfigs = applicationContext.getBeansOfType(DatasourceConfiguration) | ||
|
||
then: "Global properties are applied to all DatasourceConfiguration beans" | ||
datasourceConfigs.size() == 2 | ||
datasourceConfigs[0].connectionProperties['ApplicationName'] == 'GlobalApp' | ||
datasourceConfigs[0].connectionProperties['assumeMinServerVersion'] == '9.0' | ||
datasourceConfigs[1].connectionProperties['ApplicationName'] == 'GlobalApp' | ||
datasourceConfigs[1].connectionProperties['assumeMinServerVersion'] == '9.0' | ||
|
||
cleanup: | ||
applicationContext.close() | ||
} | ||
|
||
void "test individual datasource properties override global properties"() { | ||
given: | ||
ApplicationContext applicationContext = new DefaultApplicationContext("test") | ||
applicationContext.environment.addPropertySource(MapPropertySource.of( | ||
'test', | ||
['datasources.default.url': 'jdbc:h2:mem:default', | ||
'datasources.default.data-source-properties.ApplicationName': 'SpecificApp', | ||
'datasources.default.data-source-properties.specificProperty': 'specificValue', | ||
'global.datasources.data-source-properties.ApplicationName': 'GlobalApp', | ||
'global.datasources.data-source-properties.assumeMinServerVersion': '9.0', | ||
'global.datasources.data-source-properties.globalProperty': 'globalValue'] | ||
)) | ||
applicationContext.start() | ||
|
||
when: | ||
DatasourceConfiguration datasourceConfig = applicationContext.getBean(DatasourceConfiguration) | ||
|
||
then: "Individual properties override globals, but global properties are still added" | ||
datasourceConfig != null | ||
datasourceConfig.connectionProperties['ApplicationName'] == 'SpecificApp' // Overridden | ||
datasourceConfig.connectionProperties['specificProperty'] == 'specificValue' // Individual property | ||
datasourceConfig.connectionProperties['assumeMinServerVersion'] == '9.0' // Added from global | ||
datasourceConfig.connectionProperties['globalProperty'] == 'globalValue' // Added from global | ||
|
||
cleanup: | ||
applicationContext.close() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2017-2020 original authors | ||
* | ||
* 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 | ||
* | ||
* https://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.micronaut.configuration.jdbc.hikari; | ||
|
||
import io.micronaut.context.annotation.Requires; | ||
import io.micronaut.context.event.BeanCreatedEvent; | ||
import io.micronaut.context.event.BeanCreatedEventListener; | ||
import jakarta.inject.Inject; | ||
import jakarta.inject.Singleton; | ||
|
||
/** | ||
* A bean created event listener that applies global datasource properties to | ||
* {@link DatasourceConfiguration} beans when they are created. This modifier | ||
* ensures that global properties defined under the "global.datasources.data-source-properties" | ||
* configuration prefix are automatically applied to all datasource configurations, | ||
* while preserving individual datasource-specific settings that take precedence. | ||
* | ||
* <p>The modifier only adds global properties that are not already present in the | ||
* individual datasource configuration, ensuring that specific configurations always | ||
* override global defaults. Properties with null values are ignored.</p> | ||
* | ||
* <p>This bean is only created when the "global.datasources.data-source-properties" | ||
* configuration property is present.</p> | ||
* | ||
* @author James Forward | ||
*/ | ||
@Requires(property = "global.datasources.data-source-properties") | ||
@Singleton | ||
public class GlobalDatasourceConfigModifier implements BeanCreatedEventListener<DatasourceConfiguration> { | ||
|
||
@Inject | ||
GlobalDatasourceProperties globalDatasourceProperties; | ||
|
||
@Override | ||
public DatasourceConfiguration onCreated(BeanCreatedEvent<DatasourceConfiguration> event) { | ||
|
||
DatasourceConfiguration configuration = event.getBean(); | ||
globalDatasourceProperties.getDataSourceProperties() | ||
.forEach((key, value) -> { | ||
if (value != null && !configuration.getDataSourceProperties().containsKey(key)) { | ||
configuration.addDataSourceProperty(key, value); | ||
} | ||
} | ||
); | ||
return configuration; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this exposed?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dstepanov The dbcp implementation needs to access the properties to work properly