29
29
import org .springframework .boot .autoconfigure .condition .ConditionalOnMissingBean ;
30
30
import org .springframework .boot .autoconfigure .condition .ConditionalOnProperty ;
31
31
import org .springframework .boot .autoconfigure .condition .SpringBootCondition ;
32
+ import org .springframework .boot .autoconfigure .r2dbc .R2dbcProperties .Pool ;
32
33
import org .springframework .boot .context .properties .PropertyMapper ;
34
+ import org .springframework .boot .context .properties .bind .BindResult ;
35
+ import org .springframework .boot .context .properties .bind .Bindable ;
36
+ import org .springframework .boot .context .properties .bind .Binder ;
33
37
import org .springframework .boot .r2dbc .EmbeddedDatabaseConnection ;
34
38
import org .springframework .context .annotation .Bean ;
35
39
import org .springframework .context .annotation .Condition ;
36
40
import org .springframework .context .annotation .ConditionContext ;
37
41
import org .springframework .context .annotation .Conditional ;
38
42
import org .springframework .context .annotation .Configuration ;
43
+ import org .springframework .core .env .Environment ;
39
44
import org .springframework .core .io .ResourceLoader ;
40
45
import org .springframework .core .type .AnnotatedTypeMetadata ;
46
+ import org .springframework .util .ClassUtils ;
41
47
import org .springframework .util .StringUtils ;
42
48
43
49
/**
@@ -51,39 +57,54 @@ abstract class ConnectionFactoryConfigurations {
51
57
52
58
protected static ConnectionFactory createConnectionFactory (R2dbcProperties properties , ClassLoader classLoader ,
53
59
List <ConnectionFactoryOptionsBuilderCustomizer > optionsCustomizers ) {
54
- return org .springframework .boot .r2dbc .ConnectionFactoryBuilder
55
- .withOptions (new ConnectionFactoryOptionsInitializer ().initialize (properties ,
56
- () -> EmbeddedDatabaseConnection .get (classLoader )))
57
- .configure ((options ) -> {
58
- for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers ) {
59
- optionsCustomizer .customize (options );
60
- }
61
- }).build ();
60
+ try {
61
+ return org .springframework .boot .r2dbc .ConnectionFactoryBuilder
62
+ .withOptions (new ConnectionFactoryOptionsInitializer ().initialize (properties ,
63
+ () -> EmbeddedDatabaseConnection .get (classLoader )))
64
+ .configure ((options ) -> {
65
+ for (ConnectionFactoryOptionsBuilderCustomizer optionsCustomizer : optionsCustomizers ) {
66
+ optionsCustomizer .customize (options );
67
+ }
68
+ }).build ();
69
+ }
70
+ catch (IllegalStateException ex ) {
71
+ String message = ex .getMessage ();
72
+ if (message != null && message .contains ("driver=pool" )
73
+ && !ClassUtils .isPresent ("io.r2dbc.pool.ConnectionPool" , classLoader )) {
74
+ throw new MissingR2dbcPoolDependencyException ();
75
+ }
76
+ throw ex ;
77
+ }
62
78
}
63
79
64
80
@ Configuration (proxyBeanMethods = false )
65
- @ ConditionalOnClass (ConnectionPool .class )
66
81
@ Conditional (PooledConnectionFactoryCondition .class )
67
82
@ ConditionalOnMissingBean (ConnectionFactory .class )
68
- static class Pool {
83
+ static class PoolConfiguration {
84
+
85
+ @ Configuration (proxyBeanMethods = false )
86
+ @ ConditionalOnClass (ConnectionPool .class )
87
+ static class PooledConnectionFactoryConfiguration {
88
+
89
+ @ Bean (destroyMethod = "dispose" )
90
+ ConnectionPool connectionFactory (R2dbcProperties properties , ResourceLoader resourceLoader ,
91
+ ObjectProvider <ConnectionFactoryOptionsBuilderCustomizer > customizers ) {
92
+ ConnectionFactory connectionFactory = createConnectionFactory (properties ,
93
+ resourceLoader .getClassLoader (), customizers .orderedStream ().collect (Collectors .toList ()));
94
+ R2dbcProperties .Pool pool = properties .getPool ();
95
+ PropertyMapper map = PropertyMapper .get ().alwaysApplyingWhenNonNull ();
96
+ ConnectionPoolConfiguration .Builder builder = ConnectionPoolConfiguration .builder (connectionFactory );
97
+ map .from (pool .getMaxIdleTime ()).to (builder ::maxIdleTime );
98
+ map .from (pool .getMaxLifeTime ()).to (builder ::maxLifeTime );
99
+ map .from (pool .getMaxAcquireTime ()).to (builder ::maxAcquireTime );
100
+ map .from (pool .getMaxCreateConnectionTime ()).to (builder ::maxCreateConnectionTime );
101
+ map .from (pool .getInitialSize ()).to (builder ::initialSize );
102
+ map .from (pool .getMaxSize ()).to (builder ::maxSize );
103
+ map .from (pool .getValidationQuery ()).whenHasText ().to (builder ::validationQuery );
104
+ map .from (pool .getValidationDepth ()).to (builder ::validationDepth );
105
+ return new ConnectionPool (builder .build ());
106
+ }
69
107
70
- @ Bean (destroyMethod = "dispose" )
71
- ConnectionPool connectionFactory (R2dbcProperties properties , ResourceLoader resourceLoader ,
72
- ObjectProvider <ConnectionFactoryOptionsBuilderCustomizer > customizers ) {
73
- ConnectionFactory connectionFactory = createConnectionFactory (properties , resourceLoader .getClassLoader (),
74
- customizers .orderedStream ().collect (Collectors .toList ()));
75
- R2dbcProperties .Pool pool = properties .getPool ();
76
- PropertyMapper map = PropertyMapper .get ().alwaysApplyingWhenNonNull ();
77
- ConnectionPoolConfiguration .Builder builder = ConnectionPoolConfiguration .builder (connectionFactory );
78
- map .from (pool .getMaxIdleTime ()).to (builder ::maxIdleTime );
79
- map .from (pool .getMaxLifeTime ()).to (builder ::maxLifeTime );
80
- map .from (pool .getMaxAcquireTime ()).to (builder ::maxAcquireTime );
81
- map .from (pool .getMaxCreateConnectionTime ()).to (builder ::maxCreateConnectionTime );
82
- map .from (pool .getInitialSize ()).to (builder ::initialSize );
83
- map .from (pool .getMaxSize ()).to (builder ::maxSize );
84
- map .from (pool .getValidationQuery ()).whenHasText ().to (builder ::validationQuery );
85
- map .from (pool .getValidationDepth ()).to (builder ::validationDepth );
86
- return new ConnectionPool (builder .build ());
87
108
}
88
109
89
110
}
@@ -92,7 +113,7 @@ ConnectionPool connectionFactory(R2dbcProperties properties, ResourceLoader reso
92
113
@ ConditionalOnProperty (prefix = "spring.r2dbc.pool" , value = "enabled" , havingValue = "false" ,
93
114
matchIfMissing = true )
94
115
@ ConditionalOnMissingBean (ConnectionFactory .class )
95
- static class Generic {
116
+ static class GenericConfiguration {
96
117
97
118
@ Bean
98
119
ConnectionFactory connectionFactory (R2dbcProperties properties , ResourceLoader resourceLoader ,
@@ -105,26 +126,35 @@ ConnectionFactory connectionFactory(R2dbcProperties properties, ResourceLoader r
105
126
106
127
/**
107
128
* {@link Condition} that checks that a {@link ConnectionPool} is requested. The
108
- * condition matches if pooling was opt-in via configuration and the r2dbc url does
109
- * not contain pooling-related options.
129
+ * condition matches if pooling was opt-in via configuration. If any of the
130
+ * spring.r2dbc.pool.* properties have been configured, an exception is thrown if the
131
+ * URL also contains pooling-related options or io.r2dbc.pool.ConnectionPool is not on
132
+ * the class path.
110
133
*/
111
134
static class PooledConnectionFactoryCondition extends SpringBootCondition {
112
135
113
136
@ Override
114
137
public ConditionOutcome getMatchOutcome (ConditionContext context , AnnotatedTypeMetadata metadata ) {
115
- boolean poolEnabled = context .getEnvironment ().getProperty ("spring.r2dbc.pool.enabled" , Boolean .class ,
116
- true );
117
- if (poolEnabled ) {
118
- // Make sure the URL does not have pool options
119
- String url = context .getEnvironment ().getProperty ("spring.r2dbc.url" );
120
- boolean pooledUrl = StringUtils .hasText (url ) && url .contains (":pool:" );
121
- if (pooledUrl ) {
122
- return ConditionOutcome .noMatch ("R2DBC Connection URL contains pooling-related options" );
138
+ BindResult <Pool > pool = Binder .get (context .getEnvironment ()).bind ("spring.r2dbc.pool" ,
139
+ Bindable .of (Pool .class ));
140
+ if (hasPoolUrl (context .getEnvironment ())) {
141
+ if (pool .isBound ()) {
142
+ throw new MultipleConnectionPoolConfigurationsException ();
123
143
}
124
- return ConditionOutcome
125
- .match ("Pooling is enabled and R2DBC Connection URL does not contain pooling-related options" );
144
+ return ConditionOutcome .noMatch ("URL-based pooling has been configured" );
145
+ }
146
+ if (pool .isBound () && !ClassUtils .isPresent ("io.r2dbc.pool.ConnectionPool" , context .getClassLoader ())) {
147
+ throw new MissingR2dbcPoolDependencyException ();
148
+ }
149
+ if (pool .orElseGet (Pool ::new ).isEnabled ()) {
150
+ return ConditionOutcome .match ("Property-based pooling is enabled" );
126
151
}
127
- return ConditionOutcome .noMatch ("Pooling is disabled" );
152
+ return ConditionOutcome .noMatch ("Property-based pooling is disabled" );
153
+ }
154
+
155
+ private boolean hasPoolUrl (Environment environment ) {
156
+ String url = environment .getProperty ("spring.r2dbc.url" );
157
+ return StringUtils .hasText (url ) && url .contains (":pool:" );
128
158
}
129
159
130
160
}
0 commit comments