1616
1717package org .springframework .boot .jdbc .autoconfigure ;
1818
19+ import java .util .Collection ;
20+ import java .util .LinkedList ;
21+ import java .util .function .Function ;
22+
1923import javax .sql .DataSource ;
2024
25+ import com .zaxxer .hikari .HikariConfigMXBean ;
2126import com .zaxxer .hikari .HikariDataSource ;
27+ import oracle .jdbc .OracleConnection ;
28+ import oracle .ucp .jdbc .PoolDataSource ;
29+ import oracle .ucp .jdbc .PoolDataSourceImpl ;
2230
23- import org .springframework .boot .autoconfigure .condition .ConditionalOnBean ;
31+ import org .springframework .beans .factory .ObjectProvider ;
32+ import org .springframework .beans .factory .SmartInitializingSingleton ;
33+ import org .springframework .boot .autoconfigure .condition .AnyNestedCondition ;
2434import org .springframework .boot .autoconfigure .condition .ConditionalOnCheckpointRestore ;
2535import org .springframework .boot .autoconfigure .condition .ConditionalOnClass ;
2636import org .springframework .boot .autoconfigure .condition .ConditionalOnMissingBean ;
37+ import org .springframework .boot .jdbc .DataSourceUnwrapper ;
2738import org .springframework .boot .jdbc .HikariCheckpointRestoreLifecycle ;
39+ import org .springframework .boot .jdbc .OracleUcpCheckpointRestoreLifecycle ;
40+ import org .springframework .boot .jdbc .autoconfigure .DataSourceCheckpointRestoreConfiguration .CheckpointRestorePoolsAvailableCondition ;
2841import org .springframework .context .ConfigurableApplicationContext ;
42+ import org .springframework .context .Lifecycle ;
2943import org .springframework .context .annotation .Bean ;
44+ import org .springframework .context .annotation .Conditional ;
3045import org .springframework .context .annotation .Configuration ;
3146
3247/**
3348 * Checkpoint-restore specific configuration.
3449 *
3550 * @author Olga Maciaszek-Sharma
51+ * @author Fabio Grassi
3652 */
3753@ Configuration (proxyBeanMethods = false )
3854@ ConditionalOnCheckpointRestore
39- @ ConditionalOnBean ( DataSource .class )
55+ @ Conditional ( CheckpointRestorePoolsAvailableCondition .class )
4056class DataSourceCheckpointRestoreConfiguration {
4157
4258 @ Configuration (proxyBeanMethods = false )
@@ -45,9 +61,125 @@ static class Hikari {
4561
4662 @ Bean
4763 @ ConditionalOnMissingBean
48- HikariCheckpointRestoreLifecycle hikariCheckpointRestoreLifecycle (DataSource dataSource ,
49- ConfigurableApplicationContext applicationContext ) {
50- return new HikariCheckpointRestoreLifecycle (dataSource , applicationContext );
64+ HikariCheckpointRestoreLifecycleRegistry hikariCheckpointRestoreLifecycle (
65+ final ObjectProvider <DataSource > dataSources , final ConfigurableApplicationContext applicationContext ) {
66+ return new HikariCheckpointRestoreLifecycleRegistry (dataSources , applicationContext );
67+ }
68+
69+ static final class HikariCheckpointRestoreLifecycleRegistry
70+ extends DataSourceCheckpointRestoreLifecycleRegistry <HikariConfigMXBean , HikariDataSource > {
71+
72+ HikariCheckpointRestoreLifecycleRegistry (final ObjectProvider <DataSource > dataSources ,
73+ final ConfigurableApplicationContext applicationContext ) {
74+ super (dataSources , HikariConfigMXBean .class , HikariDataSource .class ,
75+ hds -> new HikariCheckpointRestoreLifecycle (hds , applicationContext ));
76+ }
77+
78+ }
79+
80+ }
81+
82+ @ Configuration (proxyBeanMethods = false )
83+ @ ConditionalOnClass ({ PoolDataSourceImpl .class , OracleConnection .class })
84+ static class OracleUcp {
85+
86+ @ Bean
87+ @ ConditionalOnMissingBean
88+ OracleUcpCheckpointRestoreLifecycleRegistry oracleUcpCheckpointRestoreLifecycle (
89+ final ObjectProvider <DataSource > dataSources ) {
90+ return new OracleUcpCheckpointRestoreLifecycleRegistry (dataSources );
91+ }
92+
93+ static final class OracleUcpCheckpointRestoreLifecycleRegistry
94+ extends DataSourceCheckpointRestoreLifecycleRegistry <PoolDataSource , PoolDataSourceImpl > {
95+
96+ OracleUcpCheckpointRestoreLifecycleRegistry (final ObjectProvider <DataSource > dataSources ) {
97+ super (dataSources , PoolDataSource .class , PoolDataSourceImpl .class ,
98+ OracleUcpCheckpointRestoreLifecycle ::new );
99+ }
100+
101+ }
102+
103+ }
104+
105+ static class CheckpointRestorePoolsAvailableCondition extends AnyNestedCondition {
106+
107+ CheckpointRestorePoolsAvailableCondition () {
108+ super (ConfigurationPhase .PARSE_CONFIGURATION );
109+ }
110+
111+ @ ConditionalOnClass (HikariDataSource .class )
112+ static class HickariAvailable {
113+
114+ }
115+
116+ @ ConditionalOnClass ({ PoolDataSourceImpl .class , OracleConnection .class })
117+ static class OracleUcpAvailable {
118+
119+ }
120+
121+ }
122+
123+ /**
124+ * A {@link Lifecycle} container that propagates {@code start()} and {@code stop()}
125+ * signals to all its elements and {@code isRunning()} if and only if all its elements
126+ * are running or there are no elements.
127+ * <p>
128+ * This class implements also {@link SmartInitializingSingleton} to hook into the bean
129+ * factory lifecyle after all singleton beans registration and iterate over all
130+ * {@code DataSource}s, including the ones that are neither default nor autowire
131+ * candidates, unwrap each of them to reach the underlying data source, supply it to
132+ * the given factory to create a {@code Lifecycle} and add it its elements.
133+ *
134+ * @author Fabio Grassi
135+ * @since 4.1.0
136+ */
137+ static sealed class DataSourceCheckpointRestoreLifecycleRegistry <I , T extends I >
138+ implements SmartInitializingSingleton , Lifecycle {
139+
140+ private final ObjectProvider <DataSource > dataSources ;
141+
142+ private final Class <I > wrappingInterface ;
143+
144+ private final Class <T > targetClass ;
145+
146+ private final Function <T , Lifecycle > lifecycleFactory ;
147+
148+ private final Collection <Lifecycle > lifecycles ;
149+
150+ DataSourceCheckpointRestoreLifecycleRegistry (final ObjectProvider <DataSource > dataSources ,
151+ final Class <I > wrappingInterface , final Class <T > targetClass ,
152+ final Function <T , Lifecycle > lifecycleFactory ) {
153+ this .dataSources = dataSources ;
154+ this .wrappingInterface = wrappingInterface ;
155+ this .targetClass = targetClass ;
156+ this .lifecycleFactory = lifecycleFactory ;
157+ this .lifecycles = new LinkedList <>();
158+ }
159+
160+ @ Override
161+ public void afterSingletonsInstantiated () {
162+ this .dataSources .stream (ObjectProvider .UNFILTERED , false ).forEach (ds -> {
163+ final T unwrapped = DataSourceUnwrapper .unwrap (ds , this .wrappingInterface , this .targetClass );
164+ if (unwrapped != null ) {
165+ this .lifecycles .add (this .lifecycleFactory .apply (unwrapped ));
166+ }
167+ });
168+ }
169+
170+ @ Override
171+ public void start () {
172+ this .lifecycles .forEach (Lifecycle ::start );
173+ }
174+
175+ @ Override
176+ public void stop () {
177+ this .lifecycles .forEach (Lifecycle ::stop );
178+ }
179+
180+ @ Override
181+ public boolean isRunning () {
182+ return this .lifecycles .stream ().allMatch (Lifecycle ::isRunning );
51183 }
52184
53185 }
0 commit comments