55 */
66package org .hibernate .reactive .pool .impl ;
77
8+ import java .lang .invoke .MethodHandles ;
9+ import java .lang .invoke .VarHandle ;
810import java .sql .ResultSet ;
911import java .sql .SQLException ;
1012import java .util .List ;
1820import org .hibernate .engine .jdbc .spi .SqlExceptionHelper ;
1921import org .hibernate .engine .jdbc .spi .SqlStatementLogger ;
2022import org .hibernate .reactive .adaptor .impl .ResultSetAdaptor ;
23+ import org .hibernate .reactive .logging .impl .Log ;
24+ import org .hibernate .reactive .logging .impl .LoggerFactory ;
2125import org .hibernate .reactive .pool .ReactiveConnection ;
2226import org .hibernate .reactive .pool .ReactiveConnectionPool ;
2327
2428import io .vertx .core .Future ;
29+ import io .vertx .core .internal .ContextInternal ;
2530import io .vertx .sqlclient .DatabaseException ;
2631import io .vertx .sqlclient .Pool ;
2732import io .vertx .sqlclient .Row ;
3035import io .vertx .sqlclient .Tuple ;
3136import io .vertx .sqlclient .spi .DatabaseMetadata ;
3237
33- import static org .hibernate .reactive .util .impl .CompletionStages .completedFuture ;
38+ import static java .lang .invoke .MethodHandles .lookup ;
39+ import static org .hibernate .reactive .util .impl .CompletionStages .failedFuture ;
3440import static org .hibernate .reactive .util .impl .CompletionStages .rethrow ;
3541import static org .hibernate .reactive .util .impl .CompletionStages .voidFuture ;
3642
@@ -123,12 +129,16 @@ public CompletionStage<ReactiveConnection> getConnection(String tenantId, SqlExc
123129 }
124130
125131 private CompletionStage <ReactiveConnection > getConnectionFromPool (Pool pool ) {
126- return completionStage ( pool .getConnection ().map ( this ::newConnection ), ReactiveConnection ::close );
132+ return completeFuture (
133+ pool .getConnection ().map ( this ::newConnection ),
134+ ReactiveConnection ::close
135+ );
127136 }
128137
129138 private CompletionStage <ReactiveConnection > getConnectionFromPool (Pool pool , SqlExceptionHelper sqlExceptionHelper ) {
130- return completionStage (
131- pool .getConnection ().map ( sqlConnection -> newConnection ( sqlConnection , sqlExceptionHelper ) ),
139+ return completeFuture (
140+ pool .getConnection ()
141+ .map ( sqlConnection -> newConnection ( sqlConnection , sqlExceptionHelper ) ),
132142 ReactiveConnection ::close
133143 );
134144 }
@@ -189,8 +199,8 @@ private void feedback(String sql) {
189199 /**
190200 * @param onCancellation invoke when converted {@link java.util.concurrent.CompletionStage} cancellation.
191201 */
192- private <T > CompletionStage <T > completionStage (Future <T > future , Consumer <T > onCancellation ) {
193- CompletableFuture <T > completableFuture = new CompletableFuture <>();
202+ private <T > CompletionStage <T > completeFuture (Future <T > future , Consumer <T > onCancellation ) {
203+ final CompletableFuture <T > completableFuture = new CompletableFuture <>();
194204 future .onComplete ( ar -> {
195205 if ( ar .succeeded () ) {
196206 if ( completableFuture .isCancelled () ) {
@@ -210,13 +220,35 @@ private SqlClientConnection newConnection(SqlConnection connection) {
210220 }
211221
212222 private SqlClientConnection newConnection (SqlConnection connection , SqlExceptionHelper sqlExceptionHelper ) {
213- return new SqlClientConnection ( connection , getPool (), getSqlStatementLogger (), sqlExceptionHelper );
223+ return new SqlClientConnection (
224+ connection ,
225+ getPool (),
226+ getSqlStatementLogger (),
227+ sqlExceptionHelper ,
228+ ContextInternal .current ()
229+ );
214230 }
215231
216232 private static class ProxyConnection implements ReactiveConnection {
233+
234+ private static final Log LOG = LoggerFactory .make ( Log .class , lookup () );
235+
236+ private static final VarHandle OPENED_HANDLE ;
237+
238+ static {
239+ try {
240+ MethodHandles .Lookup lookup = lookup ();
241+ OPENED_HANDLE = lookup .findVarHandle ( ProxyConnection .class , "opened" , boolean .class );
242+ }
243+ catch (ReflectiveOperationException e ) {
244+ throw new ExceptionInInitializerError ( e );
245+ }
246+ }
247+
217248 private final Supplier <CompletionStage <ReactiveConnection >> connectionSupplier ;
218- private Integer batchSize ;
219- private ReactiveConnection connection ;
249+ private final CompletableFuture <ReactiveConnection > connectionFuture = new CompletableFuture <>();
250+ private volatile boolean opened = false ;
251+ private volatile boolean closed = false ;
220252
221253 public ProxyConnection (Supplier <CompletionStage <ReactiveConnection >> connectionSupplier ) {
222254 this .connectionSupplier = connectionSupplier ;
@@ -225,29 +257,41 @@ public ProxyConnection(Supplier<CompletionStage<ReactiveConnection>> connectionS
225257 /**
226258 * @return the existing {@link ReactiveConnection}, or open a new one
227259 */
228- CompletionStage <ReactiveConnection > connection () {
229- if ( connection == null ) {
230- return connectionSupplier .get ()
231- .thenApply ( conn -> {
232- if ( batchSize != null ) {
233- conn .withBatchSize ( batchSize );
234- }
235- connection = conn ;
236- return connection ;
237- } );
260+ private CompletionStage <ReactiveConnection > connection () {
261+ if ( closed ) {
262+ return failedFuture ( LOG .connectionIsClosed () );
263+ }
264+ if ( opened ) {
265+ return connectionFuture ;
238266 }
239- return completedFuture ( connection );
267+ if ( OPENED_HANDLE .compareAndSet ( this , false , true ) ) {
268+ connectionSupplier .get ().whenComplete ( (connection , throwable ) -> {
269+ if ( throwable != null ) {
270+ connectionFuture .completeExceptionally ( throwable );
271+ }
272+ else {
273+ connectionFuture .complete ( connection );
274+ }
275+ } );
276+ }
277+ return connectionFuture ;
240278 }
241279
242280 @ Override
243281 public boolean isTransactionInProgress () {
244- return connection != null && connection .isTransactionInProgress ();
282+ ReactiveConnection reactiveConnection = connectionFuture .getNow ( null );
283+ return reactiveConnection != null && reactiveConnection .isTransactionInProgress ();
245284 }
246285
247286 @ Override
248287 public DatabaseMetadata getDatabaseMetadata () {
249- Objects .requireNonNull ( connection , "Database metadata not available until the connection is opened" );
250- return connection .getDatabaseMetadata ();
288+ if ( closed ) {
289+ throw LOG .connectionIsClosed ();
290+ }
291+
292+ return Objects
293+ .requireNonNull ( connectionFuture .getNow ( null ), "Database metadata not available until a connection has been created" )
294+ .getDatabaseMetadata ();
251295 }
252296
253297 @ Override
@@ -355,15 +399,22 @@ public CompletionStage<Void> rollbackTransaction() {
355399 return connection ().thenCompose ( ReactiveConnection ::rollbackTransaction );
356400 }
357401
358- @ Override
359402 public ReactiveConnection withBatchSize (int batchSize ) {
360- if ( connection == null ) {
361- this .batchSize = batchSize ;
403+ if ( closed ) {
404+ throw LOG .connectionIsClosed ();
405+ }
406+
407+ if ( connectionFuture .isDone () ) {
408+ // connection exists, we can let callers use the delegate and forget about the proxy.
409+ return connectionFuture .getNow ( null ).withBatchSize ( batchSize );
362410 }
363411 else {
364- connection = connection .withBatchSize ( batchSize );
412+ return new ProxyConnection ( () -> opened
413+ // Connection has been requested but not created yet
414+ ? connectionFuture .thenApply ( c -> c .withBatchSize ( batchSize ) )
415+ // Connection has not been requested
416+ : connectionSupplier .get ().thenApply ( c -> c .withBatchSize ( batchSize ) ) );
365417 }
366- return this ;
367418 }
368419
369420 @ Override
@@ -373,8 +424,9 @@ public CompletionStage<Void> executeBatch() {
373424
374425 @ Override
375426 public CompletionStage <Void > close () {
376- return connection != null
377- ? connection .close ().thenAccept ( v -> connection = null )
427+ closed = true ;
428+ return opened
429+ ? connectionFuture .thenCompose ( ReactiveConnection ::close )
378430 : voidFuture ();
379431 }
380432 }
0 commit comments