Class BaseBehavior
- All Implemented Interfaces:
Behavior
- Direct Known Subclasses:
CyclicBehavior,HumanCheckpointBehavior,OneShotBehavior
-
Field Summary
Fields -
Constructor Summary
ConstructorsModifierConstructorDescriptionprotectedBaseBehavior(BehaviorType type) protectedBaseBehavior(BehaviorType type, Duration interval) protectedBaseBehavior(String behaviorId, BehaviorType type, Duration interval) -
Method Summary
Modifier and TypeMethodDescriptionprotected abstract voidaction()The main action to be performed by this behavior.booleanactivate()Reactivates a stopped behavior, allowing it to be scheduled again.execute()Executes this behavior once, asynchronously.getAgent()Returns the agent that owns this behavior.Returns the unique identifier for this behavior.Returns the execution interval for cyclic behaviors.getType()Returns the type of this behavior.booleanisActive()Checks whether this behavior should continue running.protected voidCalled when an error occurs during execution.protected voidonStop()Called when the behavior is stopped.voidSet the owning agent for this behaviorvoidstop()Stops this behavior, preventing further execution.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface dev.agenor.core.Behavior
getInitialDelay
-
Field Details
-
log
protected final org.slf4j.Logger log
-
-
Constructor Details
-
BaseBehavior
-
BaseBehavior
-
BaseBehavior
-
-
Method Details
-
getBehaviorId
Description copied from interface:BehaviorReturns the unique identifier for this behavior.The behavior ID must be unique within the owning agent's scope. It is used for behavior management operations such as removal and cancellation.
Uniqueness: Two behaviors in the same agent cannot have the same ID. Attempting to add a behavior with a duplicate ID may either replace the existing behavior or fail, depending on implementation.
Naming Convention: Use descriptive IDs that indicate the behavior's purpose:
- Good: "health-monitor", "order-processor", "data-collector"
- Avoid: "behavior1", "temp", "test"
- Specified by:
getBehaviorIdin interfaceBehavior- Returns:
- the behavior's unique identifier, never null or empty
- See Also:
-
getAgent
Description copied from interface:BehaviorReturns the agent that owns this behavior.Every behavior belongs to exactly one agent. This relationship is established when the behavior is added to the agent via
Agent.addBehavior(Behavior)and provides the behavior with:- Access to the agent's
MessageDispatcher - Agent identity for logging and monitoring
- Context for behavior execution
Lifecycle: The agent reference is typically set when the behavior is added to the agent and remains constant for the behavior's lifetime.
Null Safety: May return
nullif the behavior has not yet been added to an agent. Implementations should handle this case gracefully. - Access to the agent's
-
getType
Description copied from interface:BehaviorReturns the type of this behavior.The behavior type determines how the
BehaviorSchedulermanages its execution. Different types have different scheduling strategies and lifecycle patterns.Type Impact on Scheduling:
Behavior scheduling characteristics by type Type Scheduling Interval Required ONE_SHOT Executed once immediately No CYCLIC Repeated at fixed intervals Yes - via Behavior.getInterval()EVENT_DRIVEN Not scheduled, responds to events No WAKER Custom wake-up logic No Type Immutability: The behavior type is typically set at construction time and should not change during the behavior's lifetime. This allows the scheduler to make optimization decisions based on the type.
Behaviors that do not fit a standard pattern: answer with the type whose scheduling semantics you want. There is no constant meaning "none of these" — a behavior that runs on an interval is
BehaviorType.CYCLICand one that runs once isBehaviorType.ONE_SHOT, however it is implemented internally. A composite expresses what the type alone cannot throughCompositeBehavior.getSchedulingHint(), which is what the scheduler inspects for it. -
getInterval
Description copied from interface:BehaviorReturns the execution interval for cyclic behaviors.The interval specifies how much time should elapse between consecutive executions of a
CYCLICbehavior. This value is used by the scheduler to determine the fixed rate or fixed delay for repeated execution.Applicability: This method is primarily relevant for:
CYCLIC- Required, must not be null- Other types - Typically returns null (interval not applicable)
Scheduling Semantics: The interval represents the time between the start of consecutive executions (fixed rate), not between the end of one and start of the next:
Time: 0s 10s 20s 30s |---------|---------|---------| Execute Execute Execute Execute Interval = 10 seconds (fixed rate)Choosing Intervals:
- Short intervals (< 1s) - Polling, real-time monitoring. Use cautiously to avoid scheduler overhead.
- Medium intervals (1s-1m) - Regular checks, health monitoring, periodic data collection.
- Long intervals (> 1m) - Cleanup tasks, batch processing, low-priority maintenance.
Performance Considerations:
- Ensure execution time is less than the interval to prevent overlap
- Very short intervals can saturate the scheduler thread pool
- Consider using event-driven behaviors for immediate responses
Example:
// Health check every 30 seconds Behavior healthCheck = new BaseBehavior( BehaviorType.CYCLIC, Duration.ofSeconds(30) ) { @Override public CompletableFuture<Void> execute() { return CompletableFuture.runAsync(() -> { checkSystemHealth(); }); } }; // Data collection every 5 minutes Behavior dataCollector = new BaseBehavior( BehaviorType.CYCLIC, Duration.ofMinutes(5) ) { @Override public CompletableFuture<Void> execute() { return CompletableFuture.runAsync(() -> { collectAndStoreData(); }); } };Validation: Schedulers should validate that:
- CYCLIC behaviors have a non-null interval
- Interval is positive and reasonable (e.g., > 1ms)
- Specified by:
getIntervalin interfaceBehavior- Returns:
- the execution interval for cyclic behaviors, or null for non-cyclic behaviors or when interval is not applicable
- See Also:
-
isActive
public boolean isActive()Description copied from interface:BehaviorChecks whether this behavior should continue running.A behavior is considered active if it has not been explicitly stopped and is eligible for execution. The scheduler uses this flag to determine whether to continue scheduling the behavior.
Active State Semantics:
true- Behavior can execute and should be scheduledfalse- Behavior should not execute and may be unscheduled
Transition to Inactive: A behavior becomes inactive when:
Behavior.stop()is called explicitly- One-shot behaviors complete their execution
- The owning agent is stopped
- Internal logic determines the behavior should end
Usage in Execute: Always check this flag at the beginning of
Behavior.execute():public CompletableFuture<Void> execute() { if (!isActive()) { return CompletableFuture.completedFuture(null); } // Behavior logic... }For Long-Running Operations: Check the active flag periodically during execution to enable responsive cancellation:
while (isActive() && hasMoreWork()) { processNextItem(); }Thread Safety: This method must be thread-safe as it may be called concurrently with
Behavior.stop()from different threads. -
stop
public void stop()Description copied from interface:BehaviorStops this behavior, preventing further execution.This method transitions the behavior to an inactive state. After calling
stop(),Behavior.isActive()will returnfalseand the behavior will no longer be executed by the scheduler.Stopping Semantics:
- Immediate Effect - The behavior is marked inactive immediately
- Graceful - Current execution completes naturally
- Idempotent - Multiple calls to stop() are safe
- Permanent - Stopped behaviors cannot be restarted (create a new instance instead)
Scheduler Integration: When a behavior is stopped:
- The active flag is set to
false - The scheduler is notified to cancel future executions
- Current execution (if any) continues to completion
- No new executions are scheduled
Resource Cleanup: Implementations should use this method to clean up resources:
@Override public void stop() { super.stop(); // Mark inactive // Clean up resources if (connection != null) { connection.close(); } if (executor != null) { executor.shutdown(); } }Composite Behaviors: For composite behaviors (SEQUENTIAL, PARALLEL, FSM), stopping the parent should propagate to all child behaviors:
@Override public void stop() { super.stop(); childBehaviors.forEach(Behavior::stop); }Thread Safety: This method must be thread-safe and may be called:
- While the behavior is executing
- From a different thread than execution
- Multiple times concurrently
Agent Lifecycle: When an agent stops, it calls
stop()on all its behaviors. There is no need to explicitly stop behaviors during agent shutdown. -
activate
public boolean activate()Reactivates a stopped behavior, allowing it to be scheduled again.This method is called by the agent when restarting after a stop. It resets the active flag to true, enabling the behavior to execute.
- Returns:
- true if the behavior was reactivated, false if already active
- Since:
- 0.4.0
-
execute
Description copied from interface:BehaviorExecutes this behavior once, asynchronously.This method performs a single execution cycle of the behavior's logic. The actual execution pattern (one-shot, cyclic, etc.) is determined by the
BehaviorTypeand managed by theBehaviorScheduler.Asynchronous Execution: This method is non-blocking and returns immediately with a
CompletableFuturethat completes when the behavior's work is done. This enables:- Parallel execution of multiple behaviors
- Non-blocking agent operations
- Compositional behavior chains
- Centralized error handling
Active State Check: Implementations should check
Behavior.isActive()before performing work and return early if the behavior has been stopped:public CompletableFuture<Void> execute() { if (!isActive()) { return CompletableFuture.completedFuture(null); } // Perform behavior logic... }Error Handling: Exceptions thrown during execution should be handled within the behavior or propagated via the returned future. Uncaught exceptions may:
- Stop cyclic behavior execution
- Mark the behavior as failed
- Trigger agent error handlers
Thread Safety: This method may be called concurrently from different threads, especially for event-driven behaviors. Implementations must be thread-safe.
Performance: Keep execution time reasonable, especially for cyclic behaviors. Long-running operations should be delegated to separate threads or use async APIs.
Example:
@Override public CompletableFuture<Void> execute() { return CompletableFuture.supplyAsync(() -> { if (!isActive()) return null; try { // Perform behavior work Data data = collectData(); processData(data); // Send results Message result = Message.builder() .topic("data.processed") .content(data) .build(); agent.getMessageDispatcher().publish("data.processed", result); return null; } catch (Exception e) { log.error("Behavior execution failed", e); throw new CompletionException(e); } }); } -
setAgent
Set the owning agent for this behavior -
action
protected abstract void action()The main action to be performed by this behavior. Must be implemented by subclasses. -
onStop
protected void onStop()Called when the behavior is stopped. Override for cleanup logic. -
onError
Called when an error occurs during execution. Override for custom error handling.
-