@@ -214,6 +214,93 @@ that is specific to Oracle Database and the Oracle JDBC Driver. Extended options
214
214
are declared in the
215
215
[ OracleR2dbcOptions] ( src/main/java/oracle/r2dbc/OracleR2dbcOptions.java ) class.
216
216
217
+ #### Support for Supplier and Publisher as Option Values
218
+ Most options can have a value provided by a ` Supplier ` or ` Publisher ` .
219
+
220
+ Oracle R2DBC requests the value of an ` Option ` from a ` Supplier ` or ` Publisher `
221
+ each time the ` Publisher ` returned by ` ConnectionFactory.create() ` creates a new
222
+ ` Connection ` . Each ` Connection ` can then be configured with values that change
223
+ over time, such as a password which is periodically rotated.
224
+
225
+ If a ` Supplier ` provides the value of an ` Option ` , then Oracle R2DBC requests
226
+ the value by invoking ` Supplier.get() ` . If ` get() ` returns ` null ` ,
227
+ then no value is configured for the ` Option ` . If ` get() ` throws a
228
+ ` RuntimeException ` , then it is set as the initial cause of an
229
+ ` R2dbcException ` emitted by the ` Publisher ` returned by
230
+ ` ConnectionFactory.create() ` . The ` Supplier ` must have a thread safe ` get() `
231
+ method, as multiple subscribers may request connections concurrently.
232
+
233
+ If a ` Publisher ` provides the value of an ` Option ` , then Oracle R2DBC requests
234
+ the value by subscribing to the ` Publisher ` and signalling demand.
235
+ The first value emitted to ` onNext ` will be used as the value of the ` Option ` .
236
+ If the ` Publisher ` emits ` onComplete ` before ` onNext ` , then no value is
237
+ configured for the ` Option ` . If the ` Publisher ` emits ` onError ` before ` onNext ` ,
238
+ then the ` Throwable ` is set as the initial cause of an
239
+ ` R2dbcException ` emitted by the ` Publisher ` returned by
240
+ ` ConnectionFactory.create() ` .
241
+
242
+ The following example configures the ` PASSWORD ` option with a ` Supplier ` :
243
+ ``` java
244
+ void configurePassword(ConnectionFactoryOptions . Builder optionsBuilder) {
245
+
246
+ // Cast the PASSWORD option
247
+ Option<Supplier<CharSequence > > suppliedOption = OracleR2dbcOptions . supplied(PASSWORD );
248
+
249
+ // Supply a password
250
+ Supplier<CharSequence > supplier = () - > getPassword();
251
+
252
+ // Configure the builder
253
+ optionsBuilder. option(suppliedOption, supplier);
254
+ }
255
+ ```
256
+ A more concise example configures ` TLS_WALLET_PASSWORD ` as a ` Publisher `
257
+ ``` java
258
+ void configurePassword(ConnectionFactoryOptions . Builder optionsBuilder) {
259
+ optionsBuilder. option(
260
+ OracleR2dbcOptions . published(TLS_WALLET_PASSWORD ),
261
+ Mono . fromSupplier(() - > getWalletPassword()));
262
+ }
263
+ ```
264
+ These examples use the ` supplied(Option) ` and ` published(Option) ` methods
265
+ declared by ` oracle.r2dbc.OracleR2dbcOptions ` . These methods cast an ` Option<T> `
266
+ to ` Option<Supplier<T>> ` and ` Option<Publisher<T>> ` , respectively. It is
267
+ necessary to cast the generic type of the ` Option ` when calling
268
+ ` ConnectionFactoryOptions.Builder.option(Option<T>, T) ` in order for the call to
269
+ compile and not throw a ` ClassCastException ` at runtime. It is not strictly
270
+ required that ` supplied(Option) ` or ` published(Option) ` be used to cast the
271
+ ` Option ` . These methods are only meant to offer code readability and
272
+ convenience.
273
+
274
+ Note that the following code would compile, but fails at runtime with a
275
+ ` ClassCastException ` :
276
+ ``` java
277
+ void configurePassword(ConnectionFactoryOptions . Builder optionsBuilder) {
278
+ Publisher<CharSequence > publisher = Mono . fromSupplier(() - > getPassword());
279
+ // Doesn't work. Throws ClassCastException at runtime:
280
+ optionsBuilder. option(PASSWORD , PASSWORD . cast(publisher));
281
+ }
282
+ ```
283
+ To avoid a ` ClassCastException ` , the generic type of an ` Option ` must match the
284
+ actual type of the value passed to
285
+ ` ConnectionFactoryOptions.Builder.option(Option<T>, T) ` .
286
+
287
+ For a small set of options, providing values with a ` Supplier ` or ` Publisher `
288
+ is not supported:
289
+ - ` DRIVER `
290
+ - ` PROTOCOL `
291
+
292
+ Providing values for these options would not be interoperable with
293
+ ` io.r2dbc.spi.ConnectionFactories ` and ` r2dbc-pool ` .
294
+
295
+ Normally, Oracle R2DBC will not retain references to ` Option ` values after
296
+ ` ConnectionFactories.create(ConnectionFactoryOptions) ` returns. However, if
297
+ the value of at least one ` Option ` is provided by a ` Supplier ` or ` Publisher ` ,
298
+ then Oracle R2DBC will retain a reference to all ` Option ` values until the
299
+ ` ConnectionFactory.create() ` ` Publisher ` emits a ` Connection ` or error. This is
300
+ important to keep in mind when ` Option ` values may be mutated. In particular,
301
+ a password may only be cleared from memory after the ` create() ` ` Publisher `
302
+ emits a ` Connection ` or error.
303
+
217
304
#### Configuring an Oracle Net Descriptor
218
305
The ` oracle.r2dbc.OracleR2dbcOptions.DESCRIPTOR ` option may be used to configure
219
306
an Oracle Net Descriptor of the form ``` (DESCRIPTION=...) ``` . If this option is
0 commit comments