Class BlockchainEventBus

java.lang.Object
com.privatechain.core.event.BlockchainEventBus

public final class BlockchainEventBus extends Object
Thread-safe, asynchronous publish/subscribe event bus for blockchain lifecycle events.

The event bus decouples blockchain internals from observer code. Any component may publish an event; any number of BlockchainEventListener implementations can react without the publisher knowing about them (FR-EVENT-01, FR-EVENT-02).

Delivery guarantees

  • Events are dispatched to all registered listeners on a dedicated single-thread executor so that the calling thread (e.g., the miner that just sealed a block) is never blocked by slow listeners (FR-EVENT-03).
  • Listener exceptions are caught and logged at WARNING level; they do not propagate back to the publisher or affect other listeners.
  • Listener registration uses CopyOnWriteArrayList, which means registration/unregistration never blocks concurrent event publications.
  • Events submitted before shutdown are delivered before the executor terminates.

Wiring (Milestone 8 — T-066)

The event bus is wired into three core subsystems:

  1. Blockchain.addBlock() — publishes BlockchainEvent.BlockAddedEvent
  2. TransactionMempool.submit() — publishes BlockchainEvent.TransactionSubmittedEvent
  3. PeerManager.connect() / disconnect() — publishes BlockchainEvent.PeerConnectedEvent / BlockchainEvent.PeerDisconnectedEvent

Lifecycle

Call shutdown() when the owning BlockchainNode stops to drain pending events and terminate the delivery thread cleanly. The bus supports awaitQuiescence(long, TimeUnit) for test synchronization.

Usage


 BlockchainEventBus bus = new BlockchainEventBus();
 bus.register(event -> {
     if (event instanceof BlockchainEvent.BlockAddedEvent e) {
         log.info("Block #{} added", e.getBlock().getIndex());
     }
 });
 bus.publish(new BlockchainEvent.BlockAddedEvent(block));
 bus.shutdown();
 
Since:
1.0.0
See Also:
  • Constructor Details

    • BlockchainEventBus

      public BlockchainEventBus()
  • Method Details

    • register

      public void register(BlockchainEventListener listener)
      Registers a listener to receive future events.

      A listener may be registered multiple times; each registration results in one additional delivery per event. To avoid duplicate delivery, callers should check isRegistered(BlockchainEventListener) first or use a single shared reference.

      Parameters:
      listener - the listener to register (non-null)
      Throws:
      NullPointerException - if listener is null
    • unregister

      public void unregister(BlockchainEventListener listener)
      Unregisters a previously registered listener.

      If the listener was registered multiple times, only the first occurrence is removed per call. If the listener is not registered, this method is a no-op.

      Parameters:
      listener - the listener to unregister (non-null)
      Throws:
      NullPointerException - if listener is null
    • isRegistered

      public boolean isRegistered(BlockchainEventListener listener)
      Returns true if the given listener is currently registered.
      Parameters:
      listener - the listener to check (non-null)
      Returns:
      true if registered at least once
      Throws:
      NullPointerException - if listener is null
    • listenerCount

      public int listenerCount()
      Returns the number of currently registered listeners.
      Returns:
      listener count (≥ 0)
    • publish

      public void publish(BlockchainEvent event)
      Publishes an event to all currently registered listeners asynchronously.

      This method returns immediately after submitting the delivery task to the internal executor. Listeners are notified on the event-bus delivery thread, not on the caller's thread. Delivery order among listeners for a single event matches registration order (FR-EVENT-03).

      If the delivery executor has been shut down (after shutdown()), the event is silently dropped and a warning is logged.

      Parameters:
      event - the event to publish (non-null)
      Throws:
      NullPointerException - if event is null
    • awaitQuiescence

      public boolean awaitQuiescence(long timeout, TimeUnit unit) throws InterruptedException
      Blocks the caller until all events published before this call have been delivered, or until the timeout elapses.

      This method is intended for test synchronization only. Production code should not need it — the event bus is explicitly asynchronous by design.

      Implementation: submits a sentinel no-op task and waits for its completion. Because the executor is single-threaded, completing the sentinel guarantees all previously submitted tasks have also completed.

      Parameters:
      timeout - maximum time to wait
      unit - time unit of the timeout
      Returns:
      true if all pending deliveries completed within the timeout; false if the timeout elapsed first
      Throws:
      InterruptedException - if the calling thread is interrupted while waiting
    • awaitQuiescence

      public boolean awaitQuiescence() throws InterruptedException
      Blocks the caller until all pending events are delivered, using a 2000L ms default timeout.
      Returns:
      true if quiescent within the default timeout
      Throws:
      InterruptedException - if interrupted while waiting
    • shutdown

      public void shutdown()
      Shuts down the event delivery executor gracefully.

      Events already submitted to the executor are delivered before shutdown completes (orderly drain). No new events are accepted after this call. This method does not block; the drain happens asynchronously on the delivery thread itself. Call awaitQuiescence(long, java.util.concurrent.TimeUnit) first if synchronous completion is required.

      Typically called from BlockchainNode.stop().

    • isShutdown

      public boolean isShutdown()
      Returns true if the event bus has been shut down.
      Returns:
      true after shutdown() has been called