theshoeshiner opened a new pull request, #1156:
URL: https://github.com/apache/commons-lang/pull/1156

   The current `EventListenerSupport ` class by default fires events in a 
fail-fast manner. When a listener throws an exception it prevents any further 
listeners from being called and propagates an `InvocationTargetException` or 
`UndeclaredThrowableException`[^1] out to the caller. 
   
   While that logic is appropriate for some applications, there is sometimes a 
desire to prevent listeners from interfering with one another, particularly in 
a more decoupled project.
   
   This change allows the caller to use the alternative `fireQuietly()` method 
to retrieve the proxy. This proxy catches and hides all 
InvocationTargetExceptions from the `java.lang.reflect.Method.invoke(...)` 
method, which effectively silences exceptions from listener invocations, 
guaranteeing that all listeners receive the event.
   
   See test below for basic operation:
   
   ```
   @Test
       public void testQuietInvocationHandling() throws Throwable {
           final EventListenerSupport<ExceptionThrowingListener> 
listenerSupport = EventListenerSupport.create(ExceptionThrowingListener.class);
           listenerSupport.addListener(new ExceptionThrowingListener() {
               public void declaredError() throws Error {
                   throw new Error();
               }
               public void declaredRuntime() throws RuntimeException {
                   throw new RuntimeException();
               }
               public void declaredThrowable() throws Throwable {
                   throw new Throwable();
               }
               public void declaredIo() throws IOException {
                   throw new IOException();
               }
               public void declaredException() throws Exception {
                   throw new Exception();
               }
               public void undeclaredRuntime() {
                   throw new RuntimeException();
               }
               public void undeclaredNotImplemented() {
                   throw new NotImplementedException();
               }
           });
   
           listenerSupport.fireQuietly().declaredError();
           listenerSupport.fireQuietly().declaredRuntime();
           listenerSupport.fireQuietly().declaredThrowable();
           listenerSupport.fireQuietly().declaredIo();
           listenerSupport.fireQuietly().declaredException();
           listenerSupport.fireQuietly().undeclaredRuntime();
           listenerSupport.fireQuietly().undeclaredNotImplemented();
           
           assertThrows(UndeclaredThrowableException.class, () -> 
listenerSupport.fire().declaredError());
           assertThrows(UndeclaredThrowableException.class, () -> 
listenerSupport.fire().declaredRuntime());
           assertThrows(InvocationTargetException.class, () -> 
listenerSupport.fire().declaredThrowable());
           assertThrows(UndeclaredThrowableException.class, () -> 
listenerSupport.fire().declaredIo());
           assertThrows(InvocationTargetException.class, () -> 
listenerSupport.fire().declaredException());
           assertThrows(UndeclaredThrowableException.class, () -> 
listenerSupport.fire().undeclaredRuntime());
           assertThrows(UndeclaredThrowableException.class, () -> 
listenerSupport.fire().undeclaredNotImplemented());
   
       }
   ```
   
   Other considerations...
   
   Technically it would be possible to implement the exception handling logic 
in different ways:
   1) The above proposal, which hides all exceptions from the caller. 
   2) Collect _all_ exceptions from the listeners and return those somehow to 
the caller. 
   3) Throw the first exception, after all listeners have received the event
   
   All of those have downsides, but IMO the current code offers very little in 
the way of exception handling to begin with - the caller doesnt know which 
listener produced the exception, and must also be prepared to handle the 
unusual exception wrapping logic. So the tradeoff is pretty break even.
   
   
   [^1]: I'm not exactly sure why the Proxy wraps an 
UndeclaredThrowableException around the InvocationTargetException for some 
exceptions even when they are declared. The docs for `java.lang.reflect.Proxy` 
seem to indicate that it wraps only checked exceptions with 
UndeclaredThrowableException, however in the above code you can see that some 
checked exceptions are wrapped, even if they are declared, some are not, and 
unchecked exceptions appear to always be wrapped. This oddness is not specific 
to this change, and exists in the current implementation, so it's not something 
that should affect this change.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@commons.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to