InitMgr implements Initializer { @Override public Initialization initialize(final Runnable task) { return new Initialization() { long id = 0; final Future<?> initProc = executor.submit(() -> { boolean success = false; id = Thread.currentThread().getId(); while (!success) { try { task.run(); success = true; } catch (final Exception e) { try { LOG.info("Caught exception while initializing. Trying again in {} ms", retryWait, e); Thread.sleep(retryWait); } catch (final InterruptedException i) { Thread.currentThread().interrupt(); throw new RuntimeException("Initialization interrupted", i); } } } }); @Override public void verify() { if (notInitThread()) { try { initProc.get(0, TimeUnit.SECONDS); } catch (final TimeoutException t) { throw new IllegalStateException("Not initialized"); } catch (final ExecutionException e) { throw new IllegalStateException("Not initialized", e); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Initialization terminated", e); } } } @Override public void await() { if (notInitThread()) { try { initProc.get(); } catch (final ExecutionException e) { throw new RuntimeException("Operation terminated", e); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Operation terminated", e); } } } @Override public void await(final long time, final TimeUnit unit) throws TimeoutException { if (notInitThread()) { try { initProc.get(time, unit); } catch (final ExecutionException e) { throw new RuntimeException("Operation terminated", e); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Operation terminated", e); } } } @Override public void cancel() { initProc.cancel(true); } boolean notInitThread() { return Thread.currentThread().getId() != id; } }; }  void setRetryWait(final long millis); @Override Initialization initialize(final Runnable task);  }
@Test public void successfulInitTest() throws Exception { final Initialization init = toTest.initialize(() -> { }); attempt(() -> { init.await(); }); init.verify(); init.cancel(); }
@Test public void unsuccessfulInitTest() throws Exception { final CountDownLatch working = new CountDownLatch(1); final Initialization init = toTest.initialize(() -> { working.countDown(); throw new RuntimeException(); }); working.await(1, TimeUnit.SECONDS); try { attempt(() -> { init.await(); }, 10, TimeUnit.MILLISECONDS); fail("Should have timed out!"); } catch (final TimeoutException e) { } try { init.verify(); fail("Should have failed!"); } catch (final Exception e) { } init.cancel(); }
@Test public void cancelTest() throws Exception { final CountDownLatch working = new CountDownLatch(1); final Initialization init = toTest.initialize(() -> { working.countDown(); throw new RuntimeException(); }); working.await(1, TimeUnit.SECONDS); init.cancel(); try { attempt(() -> { init.await(); }, 10, TimeUnit.MILLISECONDS); fail("Should not have succeeded!"); } catch (final TimeoutException e) { fail("Should not have failed with timeout exception"); } catch (final Exception e) { } try { init.verify(); fail("Should have failed!"); } catch (final Exception e) { } }
@Test(expected = TimeoutException.class) public void awaitTimeoutTest() throws Exception { final CountDownLatch working = new CountDownLatch(1); final Initialization init = toTest.initialize(() -> { working.countDown(); throw new RuntimeException(); }); working.await(1, TimeUnit.SECONDS); init.await(10, TimeUnit.MILLISECONDS); }