public static void executeAction( DatabaseMeta databaseMeta, Consumer<Database> dbAction ) {
    CompletableFuture.supplyAsync( () -> internalExec( databaseMeta, dbAction, new LoggingObject( databaseMeta ) ) )
      .acceptEither( timeout(), r -> {
        // no-op
      } );
    // ^ this trick is used to enforce a timeout on the async thread in Java8.
    //  With Java 9+ we can use the .orTimeout() method.

  }
@Test
  public void executeAction() throws InterruptedException, ExecutionException, TimeoutException {
    CompletableFuture<List<Object[]>> rowMetaCompletion = new CompletableFuture<>();
    AsyncDatabaseAction.executeAction( dbMeta, database -> {
      try {
        rowMetaCompletion.complete( database.getFirstRows( "BAR", 2 ) );
      } catch ( KettleDatabaseException e ) {
        throw new IllegalStateException( e );
      }
    } );
    List<Object[]> rows = rowMetaCompletion.get( COMPLETION_TIMEOUT, TimeUnit.MILLISECONDS );
    assertThat( rows.size(), equalTo( 1 ) );
    assertThat( rows.get( 0 )[ 0 ], equalTo( 123L ) );
    assertThat( rows.get( 0 )[ 1 ], equalTo( 321L ) );
  }