Skip to content

Commit 675e94c

Browse files
authored
Merge pull request #304 from zonkyio/spring-7
#298 Get rid of deprecated ListenableFuture for Spring 7 compatibility
2 parents a40fb09 + 0bf5b95 commit 675e94c

File tree

4 files changed

+66
-51
lines changed

4 files changed

+66
-51
lines changed

build.gradle

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,20 @@ if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
105105
[name: '9.9.0', flyway: '9.9.0', 'flyway-test': '9.5.0', spring: '6.0.14', 'spring-boot': '3.0.13', 'zonky-postgres': 'default'],
106106
[name: '9.16.3', flyway: '9.16.3', 'flyway-test': '9.5.0', spring: '6.0.21', 'spring-boot': '3.1.12', 'zonky-postgres': 'default'],
107107
[name: '9.22.3', flyway: '9.22.3', 'flyway-test': '9.5.0', spring: '6.1.15', 'spring-boot': '3.2.12', 'zonky-postgres': 'default'],
108-
[name: '10.10.0', flyway: '10.10.0', 'flyway-test': '10.0.0', spring: '6.1.15', 'spring-boot': '3.3.6', 'zonky-postgres': 'default'],
109-
[name: '10.20.1', flyway: '10.20.1', 'flyway-test': '10.0.0', spring: '6.2.0', 'spring-boot': '3.4.0', 'zonky-postgres': 'default'],
110-
[name: '11.0.0', flyway: '11.0.0', 'flyway-test': '10.0.0', spring: '6.2.0', 'spring-boot': '3.4.0', 'zonky-postgres': 'default']
108+
[name: '10.10.0', flyway: '10.10.0', 'flyway-test': '10.0.0', spring: '6.1.15', 'spring-boot': '3.3.6', 'zonky-postgres': 'default'],
109+
[name: '10.20.1', flyway: '10.20.1', 'flyway-test': '10.0.0', spring: '6.2.12', 'spring-boot': '3.4.11', 'zonky-postgres': 'default'],
110+
[name: '11.7.2', flyway: '11.7.2', 'flyway-test': '10.0.0', spring: '6.2.12', 'spring-boot': '3.5.7', 'zonky-postgres': 'default'],
111+
[name: '11.14.1', flyway: '11.14.1', 'flyway-test': '10.0.0', spring: '7.0.0-RC3', 'spring-boot': '4.0.0-RC2', 'zonky-postgres': 'default']
111112
]
112113

113114
testSuites.find { it.name == 'liquibase' }.versions += [
114115
[name: '4.17.2', liquibase: '4.17.2', spring: '6.0.14', 'spring-boot': '3.0.13'],
115116
[name: '4.20.0', liquibase: '4.20.0', spring: '6.0.21', 'spring-boot': '3.1.12'],
116117
[name: '4.24.0', liquibase: '4.24.0', spring: '6.1.15', 'spring-boot': '3.2.12'],
117118
[name: '4.27.0', liquibase: '4.27.0', spring: '6.1.15', 'spring-boot': '3.3.6'],
118-
[name: '4.29.2', liquibase: '4.29.2', spring: '6.2.0', 'spring-boot': '3.4.0']
119+
[name: '4.29.2', liquibase: '4.29.2', spring: '6.2.12', 'spring-boot': '3.4.11'],
120+
[name: '4.31.1', liquibase: '4.31.1', spring: '6.2.12', 'spring-boot': '3.5.7'],
121+
[name: '5.0.1', liquibase: '5.0.1', spring: '7.0.0-RC3', 'spring-boot': '4.0.0-RC2']
119122
]
120123
}
121124

embedded-database-spring-test/src/main/java/io/zonky/test/db/context/DefaultDatabaseContext.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@
4444
import org.springframework.core.task.TaskExecutor;
4545
import org.springframework.core.task.support.TaskExecutorAdapter;
4646
import org.springframework.test.context.transaction.TestTransaction;
47-
import org.springframework.util.concurrent.SettableListenableFuture;
4847

4948
import java.sql.SQLException;
5049
import java.util.LinkedList;
5150
import java.util.List;
51+
import java.util.concurrent.CompletableFuture;
5252
import java.util.concurrent.Executor;
5353
import java.util.concurrent.Future;
5454

@@ -283,8 +283,8 @@ private EmbeddedDatabase awaitDatabase() {
283283
}
284284

285285
private Future<EmbeddedDatabase> databaseFuture(Object database) {
286-
SettableListenableFuture<EmbeddedDatabase> future = new SettableListenableFuture<>();
287-
future.set((EmbeddedDatabase) database);
286+
CompletableFuture<EmbeddedDatabase> future = new CompletableFuture<>();
287+
future.complete((EmbeddedDatabase) database);
288288
return future;
289289
}
290290

embedded-database-spring-test/src/main/java/io/zonky/test/db/flyway/preparer/FlywayDatabasePreparer.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,16 @@
2323
import io.zonky.test.db.preparer.DatabasePreparer;
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
26-
import org.springframework.util.concurrent.ListenableFuture;
27-
import org.springframework.util.concurrent.SettableListenableFuture;
2826

2927
import javax.sql.DataSource;
3028
import java.util.Objects;
29+
import java.util.concurrent.CompletableFuture;
3130

3231
public abstract class FlywayDatabasePreparer implements DatabasePreparer {
3332

3433
protected final Logger logger = LoggerFactory.getLogger(getClass());
3534

36-
protected final SettableListenableFuture<Object> result = new SettableListenableFuture<>();
35+
protected final CompletableFuture<Object> result = new CompletableFuture<>();
3736
protected final FlywayDescriptor descriptor;
3837

3938
public FlywayDatabasePreparer(FlywayDescriptor descriptor) {
@@ -44,7 +43,7 @@ public FlywayDescriptor getDescriptor() {
4443
return descriptor;
4544
}
4645

47-
public ListenableFuture<Object> getResult() {
46+
public CompletableFuture<Object> getResult() {
4847
return result;
4948
}
5049

@@ -59,10 +58,10 @@ public void prepare(DataSource dataSource) {
5958
wrapper.setDataSource(dataSource);
6059

6160
try {
62-
result.set(doOperation(wrapper));
61+
result.complete(doOperation(wrapper));
6362
logger.trace("Database has been successfully prepared in {}", stopwatch);
6463
} catch (RuntimeException e) {
65-
result.setException(e);
64+
result.completeExceptionally(e);
6665
throw e;
6766
}
6867
}

embedded-database-spring-test/src/main/java/io/zonky/test/db/provider/common/PrefetchingDatabaseProvider.java

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
import org.slf4j.Logger;
3030
import org.slf4j.LoggerFactory;
3131
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
32-
import org.springframework.util.concurrent.ListenableFutureCallback;
33-
import org.springframework.util.concurrent.ListenableFutureTask;
3432

3533
import java.util.Comparator;
3634
import java.util.List;
@@ -43,6 +41,8 @@
4341
import java.util.concurrent.CancellationException;
4442
import java.util.concurrent.ConcurrentHashMap;
4543
import java.util.concurrent.ConcurrentMap;
44+
import java.util.concurrent.ExecutionException;
45+
import java.util.concurrent.FutureTask;
4646
import java.util.concurrent.LinkedBlockingQueue;
4747
import java.util.concurrent.PriorityBlockingQueue;
4848
import java.util.concurrent.atomic.AtomicBoolean;
@@ -167,15 +167,15 @@ protected PrefetchingTask prepareNewDatabase(PipelineKey key, int priority) {
167167
databaseCount.decrementAndGet();
168168

169169
if (databaseToRemove.getKey().equals(key)) {
170-
return executeTask(key, PrefetchingTask.withDatabase(databaseToRemove.getValue(), priority));
170+
return executeTask(PrefetchingTask.withDatabase(key, databaseToRemove.getValue(), priority));
171171
} else {
172172
databaseToRemove.getValue().close();
173173
DatabasePipeline pipeline = pipelines.get(databaseToRemove.getKey());
174174
logger.trace("Prepared database has been cleaned: {}", pipeline.key);
175175
}
176176
}
177177

178-
return executeTask(key, PrefetchingTask.forPreparer(key.provider, key.preparer, priority));
178+
return executeTask(PrefetchingTask.forPreparer(key, key.provider, key.preparer, priority));
179179
}
180180

181181
protected Optional<PrefetchingTask> prepareExistingDatabase(PipelineKey key, int priority) {
@@ -197,7 +197,7 @@ protected Optional<PrefetchingTask> prepareExistingDatabase(PipelineKey key, int
197197
if (result != null) {
198198
CompositeDatabasePreparer complementaryPreparer = new CompositeDatabasePreparer(preparers.subList(i, preparers.size()));
199199
logger.trace("Preparing existing database from {} pipeline by using the complementary preparer {}", existingPipeline.key, complementaryPreparer);
200-
PrefetchingTask task = executeTask(key, PrefetchingTask.withDatabase(result.get(), complementaryPreparer, priority));
200+
PrefetchingTask task = executeTask(PrefetchingTask.withDatabase(key, result.get(), complementaryPreparer, priority));
201201

202202
prepareDatabase(pipelineKey, LOWEST_PRECEDENCE);
203203
reschedulePipeline(pipelineKey);
@@ -223,33 +223,13 @@ protected void reschedulePipeline(PipelineKey key) {
223223

224224
for (int i = 0; i < cancelledTasks.size(); i++) {
225225
int priority = -1 * (int) (invocationCount / cancelledTasks.size() * (i + 1));
226-
executeTask(key, PrefetchingTask.fromTask(cancelledTasks.get(i), priority));
226+
executeTask(PrefetchingTask.fromTask(key, cancelledTasks.get(i), priority));
227227
}
228228
}
229229
}
230230

231-
protected PrefetchingTask executeTask(PipelineKey key, PrefetchingTask task) {
232-
DatabasePipeline pipeline = pipelines.get(key);
233-
234-
task.addCallback(new ListenableFutureCallback<EmbeddedDatabase>() {
235-
@Override
236-
public void onSuccess(EmbeddedDatabase result) {
237-
if (task.type == NEW_DATABASE) {
238-
pipeline.state.set(INITIALIZED);
239-
}
240-
pipeline.tasks.remove(task);
241-
pipeline.results.offer(PreparedResult.success(result));
242-
}
243-
244-
@Override
245-
public void onFailure(Throwable error) {
246-
pipeline.tasks.remove(task);
247-
if (!(error instanceof CancellationException)) {
248-
pipeline.results.offer(PreparedResult.failure(error));
249-
}
250-
}
251-
});
252-
231+
protected PrefetchingTask executeTask(PrefetchingTask task) {
232+
DatabasePipeline pipeline = pipelines.get(task.key);
253233
pipeline.tasks.add(task);
254234
taskExecutor.execute(task);
255235
return task;
@@ -379,36 +359,38 @@ protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
379359
}
380360
}
381361

382-
protected static class PrefetchingTask extends ListenableFutureTask<EmbeddedDatabase> implements Comparable<PrefetchingTask> {
362+
protected static class PrefetchingTask extends FutureTask<EmbeddedDatabase> implements Comparable<PrefetchingTask> {
383363

384364
private final AtomicBoolean executed = new AtomicBoolean(false);
385365

366+
public final PipelineKey key;
386367
public final Callable<EmbeddedDatabase> action;
387368
public final TaskType type;
388369
public final int priority;
389370

390-
public static PrefetchingTask forPreparer(DatabaseProvider provider, DatabasePreparer preparer, int priority) {
391-
return new PrefetchingTask(priority, NEW_DATABASE, () -> provider.createDatabase(preparer));
371+
public static PrefetchingTask forPreparer(PipelineKey key, DatabaseProvider provider, DatabasePreparer preparer, int priority) {
372+
return new PrefetchingTask(key, priority, NEW_DATABASE, () -> provider.createDatabase(preparer));
392373
}
393374

394-
public static PrefetchingTask withDatabase(EmbeddedDatabase database, DatabasePreparer preparer, int priority) {
395-
return new PrefetchingTask(priority, EXISTING_DATABASE, () -> {
375+
public static PrefetchingTask withDatabase(PipelineKey key, EmbeddedDatabase database, DatabasePreparer preparer, int priority) {
376+
return new PrefetchingTask(key, priority, EXISTING_DATABASE, () -> {
396377
preparer.prepare(database);
397378
return database;
398379
});
399380
}
400381

401-
public static PrefetchingTask withDatabase(EmbeddedDatabase database, int priority) {
402-
return new PrefetchingTask(priority, EXISTING_DATABASE, () -> database);
382+
public static PrefetchingTask withDatabase(PipelineKey key, EmbeddedDatabase database, int priority) {
383+
return new PrefetchingTask(key, priority, EXISTING_DATABASE, () -> database);
403384
}
404385

405-
public static PrefetchingTask fromTask(PrefetchingTask task, int priority) {
406-
return new PrefetchingTask(priority, task.type, task.action);
386+
public static PrefetchingTask fromTask(PipelineKey key, PrefetchingTask task, int priority) {
387+
return new PrefetchingTask(key, priority, task.type, task.action);
407388
}
408389

409-
private PrefetchingTask(int priority, TaskType type, Callable<EmbeddedDatabase> action) {
390+
private PrefetchingTask(PipelineKey key, int priority, TaskType type, Callable<EmbeddedDatabase> action) {
410391
super(action);
411392

393+
this.key = key;
412394
this.action = action;
413395
this.type = type;
414396
this.priority = priority;
@@ -421,6 +403,37 @@ public void run() {
421403
}
422404
}
423405

406+
@Override
407+
protected void done() {
408+
DatabasePipeline pipeline = pipelines.get(key);
409+
Throwable cause;
410+
411+
try {
412+
EmbeddedDatabase result = get();
413+
414+
if (type == NEW_DATABASE) {
415+
pipeline.state.set(INITIALIZED);
416+
}
417+
pipeline.tasks.remove(this);
418+
pipeline.results.offer(PreparedResult.success(result));
419+
return;
420+
}
421+
catch (ExecutionException ex) {
422+
cause = ex.getCause();
423+
if (cause == null) {
424+
cause = ex;
425+
}
426+
}
427+
catch (Throwable ex) {
428+
cause = ex;
429+
}
430+
431+
pipeline.tasks.remove(this);
432+
if (!(cause instanceof CancellationException)) {
433+
pipeline.results.offer(PreparedResult.failure(cause));
434+
}
435+
}
436+
424437
@Override
425438
public boolean cancel(boolean mayInterruptIfRunning) {
426439
if (mayInterruptIfRunning || executed.compareAndSet(false, true)) {

0 commit comments

Comments
 (0)