Compare commits
	
		
			4 Commits
		
	
	
		
			b758f4cef1
			...
			b915a5c490
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						b915a5c490
	
				 | 
					
					
						|||
| 
						
						
							
						
						205a183db7
	
				 | 
					
					
						|||
| 
						
						
							
						
						74447dea59
	
				 | 
					
					
						|||
| 
						
						
							
						
						6eebd3c121
	
				 | 
					
					
						
@@ -27,7 +27,21 @@ public final class EventBus {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static final class DispatchState {
 | 
						private static final class DispatchState {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		boolean isDispatching, isCancelled;
 | 
							/**
 | 
				
			||||||
 | 
							 * Indicates that the last event handler invoked has called {@link EventBus#cancel}. In that
 | 
				
			||||||
 | 
							 * case, the event is not dispatched further.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @since 0.1.0
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							boolean isCancelled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * Is incremented when {@link EventBus#dispatch(Object)} is invoked and decremented when it
 | 
				
			||||||
 | 
							 * finishes. This allows keeping track of nested dispatches.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * @since 1.2.0
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							int nestingCount;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -79,9 +93,11 @@ public final class EventBus {
 | 
				
			|||||||
		Objects.requireNonNull(event);
 | 
							Objects.requireNonNull(event);
 | 
				
			||||||
		logger.log(Level.INFO, "Dispatching event {0}", event);
 | 
							logger.log(Level.INFO, "Dispatching event {0}", event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Set dispatch state
 | 
							// Look up dispatch state
 | 
				
			||||||
		var state = dispatchState.get();
 | 
							var state = dispatchState.get();
 | 
				
			||||||
		state.isDispatching = true;
 | 
					
 | 
				
			||||||
 | 
							// Increment nesting count (becomes > 1 during nested dispatches)
 | 
				
			||||||
 | 
							++state.nestingCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Iterator<EventHandler> handlers = getHandlersFor(event.getClass());
 | 
							Iterator<EventHandler> handlers = getHandlersFor(event.getClass());
 | 
				
			||||||
		if (handlers.hasNext()) {
 | 
							if (handlers.hasNext()) {
 | 
				
			||||||
@@ -94,14 +110,14 @@ public final class EventBus {
 | 
				
			|||||||
					try {
 | 
										try {
 | 
				
			||||||
						handlers.next().execute(event);
 | 
											handlers.next().execute(event);
 | 
				
			||||||
					} catch (InvocationTargetException e) {
 | 
										} catch (InvocationTargetException e) {
 | 
				
			||||||
						if (event instanceof DeadEvent || event instanceof ExceptionEvent)
 | 
											if (e.getCause() instanceof Error)
 | 
				
			||||||
 | 
					 | 
				
			||||||
							// Warn about system event not being handled
 | 
					 | 
				
			||||||
							logger.log(Level.WARNING, event + " not handled due to exception", e);
 | 
					 | 
				
			||||||
						else if (e.getCause() instanceof Error)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
							// Transparently pass error to the caller
 | 
												// Transparently pass error to the caller
 | 
				
			||||||
							throw (Error) e.getCause();
 | 
												throw (Error) e.getCause();
 | 
				
			||||||
 | 
											else if (event instanceof DeadEvent || event instanceof ExceptionEvent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												// Warn about system event not being handled
 | 
				
			||||||
 | 
												logger.log(Level.WARNING, event + " not handled due to exception", e);
 | 
				
			||||||
						else
 | 
											else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							// Dispatch exception event
 | 
												// Dispatch exception event
 | 
				
			||||||
@@ -118,8 +134,8 @@ public final class EventBus {
 | 
				
			|||||||
			dispatch(new DeadEvent(this, event));
 | 
								dispatch(new DeadEvent(this, event));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Reset dispatch state
 | 
							// Decrement nesting count (becomes 0 when all dispatches on the thread are finished)
 | 
				
			||||||
		state.isDispatching = false;
 | 
							--state.nestingCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		logger.log(Level.DEBUG, "Finished dispatching event {0}", event);
 | 
							logger.log(Level.DEBUG, "Finished dispatching event {0}", event);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -155,7 +171,7 @@ public final class EventBus {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void cancel() {
 | 
						public void cancel() {
 | 
				
			||||||
		var state = dispatchState.get();
 | 
							var state = dispatchState.get();
 | 
				
			||||||
		if (state.isDispatching && !state.isCancelled)
 | 
							if (state.nestingCount > 0 && !state.isCancelled)
 | 
				
			||||||
			state.isCancelled = true;
 | 
								state.isCancelled = true;
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			throw new EventBusException("Calling thread not an active dispatching thread!");
 | 
								throw new EventBusException("Calling thread not an active dispatching thread!");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					package dev.kske.eventbus.core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.jupiter.api.Assertions.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.jupiter.api.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tests nested event dispatches.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Kai S. K. Engelbart
 | 
				
			||||||
 | 
					 * @since 1.2.0
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class NestedTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EventBus	bus;
 | 
				
			||||||
 | 
						boolean		nestedHit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs an event bus and registers this test instance as an event listener.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since 1.2.0
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@BeforeEach
 | 
				
			||||||
 | 
						void registerListener() {
 | 
				
			||||||
 | 
							bus = new EventBus();
 | 
				
			||||||
 | 
							bus.registerListener(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Dispatches a simple event, which should in turn cause a string to be dispatched as a nested
 | 
				
			||||||
 | 
						 * event. If the corresponding handler sets {@link #nestedHit} to {@code true}, the test is
 | 
				
			||||||
 | 
						 * successful.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since 1.2.0
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						void testNestedDispatch() {
 | 
				
			||||||
 | 
							bus.dispatch(new SimpleEvent());
 | 
				
			||||||
 | 
							assertTrue(nestedHit);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Dispatches a string as a nested event and cancels the current dispatch afterwards.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since 1.2.0
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Event(SimpleEvent.class)
 | 
				
			||||||
 | 
						void onSimpleEvent() {
 | 
				
			||||||
 | 
							bus.dispatch("Nested event");
 | 
				
			||||||
 | 
							bus.cancel();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Sets {@link #nestedHit} to {@code true} indicating that nested dispatches work.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since 1.2.0
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Event(String.class)
 | 
				
			||||||
 | 
						void onString() {
 | 
				
			||||||
 | 
							nestedHit = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Fails the test if an exception is caused during the dispatch.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param e the event containing the exception
 | 
				
			||||||
 | 
						 * @since 1.2.0
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Event
 | 
				
			||||||
 | 
						void onException(ExceptionEvent e) {
 | 
				
			||||||
 | 
							fail("Exception during dispatch", e.getCause());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user