Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 12 additions & 21 deletions src/main/java/com/onionnetworks/util/ReflectiveEventDispatch.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
* <p>The dispatcher maintains a daemon thread that repeatedly removes queued {@code EventObject}
* instances, looks up listeners that registered interest in the originating event source and the
* supplied method name, and invokes that method with the event as its sole argument. Listener look-
* * ups and queue mutations are synchronized on the dispatcher instance, allowing multiple producer
* * ups and queue mutations are synchronized in the dispatcher instance, allowing multiple producer
* threads to submit events while the consumer thread drains the queue. Method lookups are cached to
* avoid repeated reflective discovery, but execution order remains FIFO for submitted events. When
* no listeners are registered for a given method name and source, the event is silently skipped.
* avoid repeated reflective discovery, but the execution order remains FIFO for submitted events.
* When no listeners are registered for a given method name and source, the event is silently
* skipped.
*
* <p>Use this class when you need a lightweight, per-source event bus without committing to a
* broader framework. Typical usage registers listeners tied to a particular publisher object and
Expand All @@ -46,11 +47,11 @@ public class ReflectiveEventDispatch implements Runnable {

private static final Logger LOGGER = Logger.getLogger(ReflectiveEventDispatch.class.getName());

private final Thread thread;
private final Map<Tuple, Method> methodCache = new HashMap<>();
private final Map<Object, Map<String, Set<EventListener>>> listeners = new HashMap<>();
// Holds either Tuple(event, methodName) or a sentinel (this) to signal shutdown
private final ArrayDeque<Object> eventQueue = new ArrayDeque<>();
private final Thread thread;
private ExceptionHandler handler;

/**
Expand All @@ -62,28 +63,18 @@ public class ReflectiveEventDispatch implements Runnable {
* required.
*/
public ReflectiveEventDispatch() {
thread = new Thread(this, "Reflective Dispatch#" + hashCode());
thread.setDaemon(true);
thread.start();
this.thread = new Thread(this, "Reflective Dispatch#" + hashCode());
this.thread.setDaemon(true);
this.thread.start();
}

/**
* Accepts a requested priority for API compatibility.
*
* <p>Thread priority adjustments are intentionally ignored; the dispatch thread runs at the
* JVM-default priority.
*
* @param priority requested thread priority (ignored)
*/
public void setPriority(int priority) {}

/**
* Registers an exception handler that receives failures raised during listener invocation.
*
* <p>When set, the handler is invoked with an {@link ExceptionEvent} every time reflective
* invocation of a listener method throws an exception. Passing {@code null} restores the default
* behavior, which logs the exception at {@link Level#SEVERE}. The handler runs on the dispatch
* thread, so heavy processing may delay subsequent event delivery.
* thread, so heavy processing may delay further event delivery.
*
* @param h handler invoked for dispatch-time exceptions; may be {@code null} to disable custom
* handling.
Expand Down Expand Up @@ -133,10 +124,10 @@ public synchronized void addListener(Object source, EventListener el, String met
* case-sensitive.
*/
public synchronized void addListener(Object source, EventListener el, String[] methodNames) {
Map<String, Set<EventListener>> hm = listeners.computeIfAbsent(source, k -> new HashMap<>());
Map<String, Set<EventListener>> hm = listeners.computeIfAbsent(source, _ -> new HashMap<>());

for (String methodName : methodNames) {
hm.computeIfAbsent(methodName, k -> new HashSet<>()).add(el);
hm.computeIfAbsent(methodName, _ -> new HashSet<>()).add(el);
}
}

Expand Down Expand Up @@ -217,7 +208,7 @@ public synchronized void fire(EventObject ev, String methodName) {
* The method is idempotent but does not interrupt a listener currently executing.
*/
public synchronized void close() {
// Place this on the queue to signify that we are done.
// Place this in the queue to signify that we are done.
eventQueue.add(this);
this.notifyAll();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,4 @@
* @param latestSuccess time of the most recent successful block, or {@code null} if none yet
* @param latestFailure time of the most recent failure, or {@code null} if none yet
*/
public record SplitfileProgressTimestamps(Instant latestSuccess, Instant latestFailure) {
/**
* Creates a snapshot of the latest success and failure timestamps.
*
* <p>This constructor stores the supplied {@link Instant} instances directly. It performs no
* validation beyond null handling. Passing {@code null} for either argument leaves that component
* unset in the snapshot, which callers can interpret as “no event yet.”
*
* @param latestSuccess time of the most recent successful block, or {@code null} if absent
* @param latestFailure time of the most recent failure, or {@code null} if absent
*/
public SplitfileProgressTimestamps {}
}
public record SplitfileProgressTimestamps(Instant latestSuccess, Instant latestFailure) {}
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ protected void writeHTMLReply(ToadletContext ctx, int code, String desc, String
}

@Override
protected void writeTemporaryRedirect(ToadletContext ctx, String msg, String location) {}
protected void writeTemporaryRedirect(ToadletContext ctx, String msg, String location) {
// Intentionally no-op in tests; we only assert that redirects were requested.
}
}
}
Loading