efs v. 0.7.2
API Specification
Introduction
efs - Event File System is envisioned as a reliable, robust, and low-latency event persistence and delivery API from publishing agent to subscribing agent.
"Envision" means that efs is not yet implemented to this point - but enough is implemented to warrant an initial release. efs has three levels:
- Events, Agents, and Dispatchers.
- Simple event bus based on type+topic event routing.
- Persistent event store and forward.
Events, Agents, and Dispatchers
These three types underly all of efs. Events are posted to an agent's event queue and the agent is posted to a dispatcher run queue. A dispatcher thread takes the agent off the run queue and has that agent process its event queue. An agent is acquired by only one thread at a time making all agent event queue processing effectively single-threaded. This makes using efs very simple because there is no multi-threaded programming concerns to deal with.
Events
An efs event is a POJO implementing the
IEfsEvent marker interface. There
are no further restrictions place on how you define an
event class. It is strongly recommended that an event
class be immutable. It is also recommended that the
Builder pattern be used to create correctly configured
event instances.
Events should also be passive objects - meaning an
event class do not initiate actions but is only acted
upon. An example of a passive object is a
java.util.ArrayList instance. Once
instantiated, a list will do nothing until an external
object adds, removes, or retrieves items. Events should
behave in the same way. Again, an event does not have
to be passive and there might be interesting use of
active events.
Agents
An efs agent is a POJO implementing the
IEfsAgent interface. This
interface has a single method: String name().
The name() implementation is expected to return
a unique agent name within the JVM's scope. If this
name is used by more than one agent, this results in
only the first agent successfully registering with a
dispatcher (see next section).
Agents are active objects which means they initiate
actions. These actions generally include posting events
to other agents or calling passive object methods.
An agent must be
registered
with a Dispatcher after
instantiation and before it may send or receive events.
There are no further limitations placed on agent implementation. It is strongly recommended that agents communicate with each other via events. If an agent directly calls another agent's method, then this means the called agent is being accessed from multiple threads, losing the benefit of single-threaded access, and bringing thread control issues back into play. So if agents are allowed to call each other's methods directly, then care must be taken to do this correctly.
Dispatchers
efs dispatchers consists of an agent run queue and a fixed size thread pool. An agent with one or more events on its event queue is placed on to its dispatcher's run queue. A dispatcher thread takes this agent from the run queue and has the agent process its event queue.
An agent is registered with a single dispatcher and processes its events on that dispatcher's threads only. This is why agents must have unique names within a JVM. If not, then the first agent to successfully register with a dispatcher will prevent the second agent with the same name from successfully registering with any dispatcher.
Dispatchers have a number of configurable properties:
thread type, thread pool size, and thread priority.
Thread type
is either blocking, spinning, sping+park, or
spin+yield. If a spinning (busy spin) thread type is
used, then thread affinity should also be configured
so spinning dispatcher threads can be associated with
CPU cores isolated from the operating system.
Getting Events to Agents
There are three ways to get an event from the agent producing the event to the consuming agent:
- producing agent directly dispatching event to consuming agent,
-
producing agent publishes event to
EfsEventBuson a specific topic. Event bus forwards event to all consuming agents subscribed to that topic. - producing agent publishes event to an event file and event file forwards event to consuming agents reading it.
Direct Event Forwarding
The simplest and fastest way to foward an event between agents is the producing agent to directly dispatch the event to the consuming agent. This requires the producing agent to know the consuming agent's unique name so it can dispatch the event to the consuming agent. The following code snippet demonstrates how to do this:
// Consumer is an application class implementing IEfsAgent.
// Note: returned agent may be null. Example assumes not.
final Consumer consumer = (Consumer) EfsDispatcher.agent("consumer-agent");
final AppEvent event = new AppEvent();
EfsDispatcher.dispatch(consumer::onAppEvent, event, consumer);
It is obvious there is a short-coming with direct forwarding: producing agent needs to know to which consuming agent method to forward this event and have access to that method. That said, it is no different than one object calling another object's method except there is a thread hand-off between producing agent and consuming agent.
As an aside, if producing agent expects a reply event
from consuming agent, efs provides
EfsDispatchTarget class. The
producing agent creates a EfsDispatchTarget instance
containing the producing agent's method lambda and
this reference and placing that dispatch target
instance into the event. Extending the above example to
demonstrate how consuming agent replies to an event:
// Note: method onReply does not need to be public.
final EfsDispatchTarget<ReplyEvent> replyTo = new EfsDispatchTarget<>(this::onReply, this);
final AppEvent event = new AppEvent(replyTo);
EfsDispatcher.dispatch(consumer::onAppEvent, event, consumer);
// Consumer agent posts a reply as follows:
public void onAppEvent(final AppEvent event) {
final ReplyEvent reply = new ReplyEvent("done");
// In this example, AppEvent.reply method passes ReplyEvent instance to
// EfsDispatchTarget.dispatch method.
event.reply(reply);
}
Unlike the initial dispatch, the consuming agent does not need to have access to the producing agent's target method since the method lambda is set by the producing agent.
Event Bus Forwarding
An event bus loosens up the
producer, consumer coupling by acting as a go-between agents.
A producing agent first advertises the topics on which it
publishes events. A consuming agent subscribes to the topics
on which it wishes to receive events.
Note: all agents must be registered
with a dispatcher prior to interacting with an event bus.
Failure to do so results in the interaction throwing an
IllegalStateException.
Each efs event bus is initialized with a name unique within
the JVM. This allows the event space to be partioned within a
JVM where each event bus handles a given set of topic keys.
The benefit of such partioning is to separate high importance
events from low importance events. That said, the same
topic key may be assigned to
more than one event bus and publishing and subscribing agents
may work with more than one event bus.
See Event bus package info for a detailed
explanation on using EfsEventBus.
Event File System
This is a work-in-progress and explanation will be provided later.
Agent Activation
An agent is more than a Java object. An agent is active, it
is registered with an efs dispatcher, advertises or
subscribes with an event bus, initiating asynchronous
communication with other agents. Java object initiation is
done within the class constructor. The constructor is meant
for object data member initialization but not for connecting
an agent with an event bus. org.efs.activator solves
that problem.
When an efs agent implements
activate agents
(an extension to
IEfsAgent), that agent
can be referenced in an
activator
workflow. A workflow takes
one or more agents from a stopped state to stand-by, then to
active and backwards to stopped again. As an agent
transitions between states, the agent can connect itself up
to other agents, either directly and indirectly using an
event bus.
The idea here is that in the stopped state, an agent is disconnected from other agents and is doing nothing. In the stand-by state, the agent is monitoring its environment but not actively participating in application work. In the active state, the agent is fully participating.
See org.efs.activator for a detailed example on how
workflows transitions activate agents between states.
efs package javadocs explain how to use the package together with example code. You are encouraged to explore the javadoc packages in the following order:
-
dispatcher: explains dispatcher architecture and how to use. Compares efs dispatcher with LMAX Disruptor and SEDA (staged event driven architecture), and Actor model/AKKA. -
dispatcher configuration: explains how to configure dispatchers using typesafe configuration bean classes stored in a JSON file format.Please note that efs uses typesafe for all its configuration.
-
timer: contains theEfsScheduledExecutorwhich somewhat follows theScheduledExecutorServiceinterface but does not implement that interface because it does not deliver expired timers using aScheduledFuturebut instead usesEfsDispatcherfor delivery.An efs scheduled executor may be created either programmatically or by typesafe configuration file.
-
activator: this service steps efs agents through stopped, stand by, active states in a thread-safe manner using efs dispatcher's virtual single-thread environment. User definedworkflowsprovide the order in which efs agents are stepped through their states. -
activator configuration: explains how to define one or more activation workflows using a typesafe configuration file. -
logging: implements slf4jLoggerandLoggerFactorywithAsyncLoggerandAsyncLoggerFactory. This logging uses efs dispatcher to perform the actual logging on a dispatcher thread rather than inline with application code.
efs Background
efs is a direct descendent of the 25 year old eBus project. The goal here is to tease out the best of that work, forming a better API using Java 21 features. This work is not a re-implementation of eBus but a step beyond. efs is based on the observation that applications require:
- historic events,
- live events, and
- combination of historic and live events.
eBus started with live event distribution only. A later attempt to add historic events to eBus designed to fit into the eBus framework was less than satisfactory. Hence, a need for a new framework supporting both historic and live event distribution from the start. Designing a new efs event persistence and distribution API is on-going.
Comparing efs with Existing Concurrency Frameworks
org.efs.dispatcher package information
provides a detailed comparison between efs and:
- LMAX Disruptor
- Staged Event-Driven Architecture
- Actor Model/Akka
About efs
Copyright © 2026, Charles W. Rapp. All Rights Reserved.
efs GitHub project was created on October, 2025 and is being actively improved and extended since then. efs' goal is to bring the power of distributed, reactive events together with agentic programming in a simple API, so simple that you don't notice efs when you transmit events within your JVM or around the world.
Updated: July 11, 2026
Version: 0.7.2
- Author:
- Charles Rapp
EfsEventBus provides a
loosely-coupled interface between
efs agents.EfsEventLayout class which provides a
reflective definition of an IEfsEvent
class.EfsScheduledExecutor
which encapsulates a
ScheduledExecutorService.