diff --git a/server/src/main/java/org/elasticsearch/action/ActionListener.java b/server/src/main/java/org/elasticsearch/action/ActionListener.java index 0e464682219..47b6109f199 100644 --- a/server/src/main/java/org/elasticsearch/action/ActionListener.java +++ b/server/src/main/java/org/elasticsearch/action/ActionListener.java @@ -315,12 +315,28 @@ public interface ActionListener { /** * Completes the given listener with the result from the provided supplier accordingly. * This method is mainly used to complete a listener with a block of synchronous code. + * + * If the supplier fails, the listener's onFailure handler will be called. + * It is the responsibility of {@code delegate} to handle its own exceptions inside `onResponse` and `onFailure`. */ static void completeWith(ActionListener listener, CheckedSupplier supplier) { + Response response; try { - listener.onResponse(supplier.get()); + response = supplier.get(); } catch (Exception e) { - listener.onFailure(e); + try { + listener.onFailure(e); + } catch (RuntimeException ex) { + assert false : ex; + throw ex; + } + return; + } + try { + listener.onResponse(response); + } catch (RuntimeException ex) { + assert false : ex; + throw ex; } } } diff --git a/server/src/test/java/org/elasticsearch/action/ActionListenerTests.java b/server/src/test/java/org/elasticsearch/action/ActionListenerTests.java index e72fde85871..44cee8f81f2 100644 --- a/server/src/test/java/org/elasticsearch/action/ActionListenerTests.java +++ b/server/src/test/java/org/elasticsearch/action/ActionListenerTests.java @@ -233,6 +233,33 @@ public class ActionListenerTests extends ESTestCase { ActionListener.completeWith(onFailureListener, () -> { throw new IOException("not found"); }); assertThat(onFailureListener.isDone(), equalTo(true)); assertThat(expectThrows(ExecutionException.class, onFailureListener::get).getCause(), instanceOf(IOException.class)); + + AtomicReference exReference = new AtomicReference<>(); + ActionListener listener = new ActionListener() { + @Override + public void onResponse(String s) { + if (s == null) { + throw new IllegalArgumentException("simulate onResponse exception"); + } + } + + @Override + public void onFailure(Exception e) { + exReference.set(e); + if (e instanceof IllegalArgumentException) { + throw (IllegalArgumentException) e; + } + } + }; + + AssertionError assertionError = expectThrows(AssertionError.class, () -> ActionListener.completeWith(listener, () -> null)); + assertThat(assertionError.getCause(), instanceOf(IllegalArgumentException.class)); + assertNull(exReference.get()); + + assertionError = expectThrows(AssertionError.class, () -> ActionListener.completeWith(listener, + () -> { throw new IllegalArgumentException(); })); + assertThat(assertionError.getCause(), instanceOf(IllegalArgumentException.class)); + assertThat(exReference.get(), instanceOf(IllegalArgumentException.class)); } /**