Thread Handler and Ac Listener
Describe the functionality and primary purpose of this group of files.
Create an analysis top down from the methods of the class ThreadHandler
The files you provided belong to the Participant Intermediary component of the ONAP CLAMP Automation Composition Management (ACM) runtime.
Functionality and Primary Purpose
The primary purpose of this group of files is to serve as a thread-safe, asynchronous communication layer for a Participant (a microservice that executes a part of the automation logic).
It achieves this by:
Providing a standard API: Defining the
AutomationCompositionElementListenerinterface, which is the contract that actual Participant logic must implement to receive state change commands (e.g.,deploy,undeploy,migrate).Handling Asynchronous Execution: The
ThreadHandlerclass manages a thread pool to execute the potentially long-running operations (likedeployormigrate) requested by the ACM runtime off the main message receiving thread. This ensures the Participant remains responsive and can process new messages immediately.State Reporting: The
ParticipantIntermediaryApidefines methods for the Participant to report its progress and final status (success, failure, or timeout) back to the central ACM runtime.Simulating Behavior (in this specific package): The
handler/directory, which includesSimulatorService.javaandAutomationCompositionElementHandler.java, suggests this is an implementation within a simulator participant. These classes demonstrate how a real Participant would use theThreadHandlerandParticipantIntermediaryApito process requests and update the central ACM with its element's state, but with simulated, delayed, or configurable behavior.
Top-Down Analysis from the Methods of ThreadHandler
The ThreadHandler is responsible for receiving synchronous requests from the ACM runtime (via message bus listeners) and immediately delegating the actual, blocking work to a separate execution thread. This prevents message processing backlogs.
The ThreadHandler utilizes a thread pool (ExecutorService) and an execution map (Map<UUID, Future<?>>) to manage pending tasks, keyed by the Automation Composition Element ID.
1. Request Handling and Threading
The key public methods of ThreadHandler implement the AutomationCompositionElementListener operations by wrapping them in an asynchronous task:
Method in | Primary Action | Result of Delegation |
| Submits a | Executes |
| Submits an | Executes |
| Submits a | Executes |
| Submits a | Executes |
| Submits a | Executes |
| Submits corresponding Runnables to the executor. | Executes the respective operation on a worker thread. |
2. The Asynchronous Process Methods (e.g., deployProcess)
All delegated worker methods follow a standard pattern:
Call the Listener: They call the corresponding method on the concrete implementation of the
AutomationCompositionElementListener(e.g.,listener.deploy(...)). This is where the actual Participant logic (like theSimulatorServicein this package) resides.Error Handling: They include a
try-catchblock forPfModelException(from the Policy framework).Report Failure: If an exception is caught, the method uses the injected
ParticipantIntermediaryApito report aFAILEDstate back to the ACM runtime:JavaintermediaryApi.updateAutomationCompositionElementState( instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Automation composition deployment failed");Cleanup: Once the process is complete (or fails), the
Futureresult is removed from theexecutionMap.
3. Management and Safety Methods
cleanExecution(UUID elementId, UUID messageId): This method cancels any currently running task associated with the givenelementIdif the incoming request (messageId) indicates a new, overriding operation. This is crucial for handling concurrent state change requests safely.close(): ImplementsCloseable. It shuts down the internalExecutorServiceand attempts to cancel all pending tasks, ensuring a graceful shutdown of the Participant.
In summary, the ThreadHandler is the concurrency control mechanism for an ACM Participant, taking complex, potentially long-running state transitions and executing them safely in the background while reporting progress and failure status via the ParticipantIntermediaryApi.
This scenario describes a common pattern used in Java for backward compatibility when evolving an interface, specifically using interface evolution through inheritance and method overloading/hiding.
The goal is to update the core API (AutomationCompositionElementListener) while ensuring that existing implementations (AutomationCompositionElementHandler) compiled against the older version of the interface do not break when the new version of the API is deployed.
Backward Compatibility Analysis
From the previos code, create adescription of backward compatibility in the follow scenario:
developer changes "rollbackMigration" method in AutomationCompositionElementListener (add new parameter)
developer creates a new method in AcElementListenerV4 that is equals to the old "rollbackMigration" and wrap the "rollbackMigration" changed
AutomationCompositionElementHandler will still be able to compile
1. The Change and Breakage
Component | Old Signature | New Signature (The Change) | Impact |
|
|
| Binary/Source Break: Any class, like |
2. The Solution: Interface Versioning and Wrapping
The solution uses the existing versioning hierarchy (AcElementListenerV3 extends AcElementListenerV4 which presumably implements a base version of the listener) to maintain compatibility.
Component | Role in Backward Compatibility |
| New Interface: Holds the new, 6-parameter |
| Compatibility Wrapper (Crux of the Solution): This class is essential for compatibility. It is the first concrete class or abstract class in the hierarchy to handle both old and new signatures. |
| Old Implementation: The class that needs to remain compilable. It only implements the old, 5-parameter version. |
3. How AutomationCompositionElementHandler Compiles
The ability of AutomationCompositionElementHandler to compile is preserved by the following mechanism:
Old Implementation Remains: The
AutomationCompositionElementHandler(the old code) implements theAutomationCompositionElementListenerand contains the old, 5-parameter version ofrollbackMigration.Java// AutomationCompositionElementHandler (Old Code) @Override public void rollbackMigration(..., int stage) { ... } // (5 parameters)The New Bridge Method is Added: The developer adds a new method to the
AcElementListenerV4class that matches the old signature, effectively hiding the new 6-parameter method from the interface for compatibility purposes:Java// AcElementListenerV4 (New Code) // Matches the OLD signature, hiding the interface's NEW method @Override public void rollbackMigration(..., int stage) { // 1. Log a warning that this is an old call // 2. Call the NEW, 6-parameter method with a default value for 'newParam' this.rollbackMigration(..., stage, defaultValueForNewParam); }Compilation Success:
When the compiler checks
AutomationCompositionElementHandler, it sees the implementation of the 5-parameterrollbackMigration.Since a superclass (
AcElementListenerV4) now provides a concrete, public implementation of the 5-parameter method, the compiler considers the contract fulfilled. Even if the handler also had to implement the new interface, the inherited 5-parameter method acts as the required implementation for the old clients. IfAcElementListenerV4directly implements the newAutomationCompositionElementListener, the handler would fail unless it extends a compatibility layer.
By having AcElementListenerV4 (or a similar compatibility layer) provide a default delegating implementation for the old 5-parameter method, any existing concrete class like AutomationCompositionElementHandler that implements the old method remains valid and compilable under the new interface structure. The new ACM runtime will communicate using the new 6-parameter method, which calls the actual logic, while older systems (or the existing handler logic) can still be safely compiled against the old signature.
The goal of creating an AcElementListenerV5 is to establish a clean, up-to-date abstract implementation of the AutomationCompositionElementListener interface. This new abstract class will enforce the use of the latest method signatures (like the 6-parameter rollbackMigration) while providing default, no-operation (NOOP) or simple logging implementations for every method.
This pattern benefits developers by:
Mandating Current Signatures: Any new Participant handler must extend
AcElementListenerV5, forcing them to implement the most recent API methods, which prevents future compatibility issues.Simplifying Development: A developer only needs to override the methods they actually need (e.g.,
deployandundeploy), as all other methods have a safe, inherited default implementation.
Implementation of AcElementListenerV5
Describe the implementation of AcElementListenerV5 to create a clean abstract class of AutomationCompositionElementListener after the changes
The AcElementListenerV5 class would be implemented as an abstract class that directly implements the latest version of the core interface, AutomationCompositionElementListener.
1. Class Definition and Dependencies
Javapackage org.onap.policy.clamp.acm.participant.intermediary.api.impl;
import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi;
import org.onap.policy.models.base.PfModelException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Clean abstract base class for new Automation Composition Element Listeners (V5).
* Provides NOOP implementations for all methods to simplify participant development.
*/
public abstract class AcElementListenerV5 implements AutomationCompositionElementListener {
private static final Logger LOGGER = LoggerFactory.getLogger(AcElementListenerV5.class);
protected final ParticipantIntermediaryApi intermediaryApi;
protected AcElementListenerV5(ParticipantIntermediaryApi intermediaryApi) {
this.intermediaryApi = intermediaryApi;
}
// ... method implementations below
}
2. Method Implementation Strategy (NOOP/Default)
The implementation for every method on the AutomationCompositionElementListener interface would provide a default, non-functional (NOOP) implementation. For state-changing operations, the default implementation should report success immediately via the ParticipantIntermediaryApi to signal completion and prevent the ACM runtime from timing out.
Listener Method | V5 Default Implementation | Purpose |
| Logs, then calls | Allows a developer to ignore elements that require no action on deploy. |
| Logs, then calls | Allows a developer to ignore elements that require no action on undeploy. |
| Logs, then calls | Provides a default for elements that do not require explicit locking. |
| Logs, then calls | Provides a default for elements that do not require explicit unlocking. |
| Logs, then calls | Provides a default for elements that are agnostic to migration. |
| Logs, then calls | Provides a default for elements that do not participate in migration rollback. |
| Similar to the above, logs and reports immediate success or NOOP, enforcing the latest signature for each. | Simplifies new implementations by providing a safe baseline. |
Example of a Default Implementation (for the latest rollbackMigration):
Java@Override
public void rollbackMigration(CompositionElementDto compositionElement,
CompositionElementDto compositionElementRollback, InstanceElementDto instanceElement,
InstanceElementDto instanceElementRollback, int stage, String newParam) throws PfModelException {
LOGGER.info("Default NOOP: rollbackMigration called for element {} at stage {} with newParam {}. Reporting NO_ERROR.",
instanceElement.elementId(), stage, newParam);
// Default action: immediately report success
intermediaryApi.updateAutomationCompositionElementState(
instanceElementRollback.instanceId(),
instanceElementRollback.elementId(),
DeployState.DEPLOYED, // Assuming the rollback target state is DEPLOYED
null,
StateChangeResult.NO_ERROR,
"Default NOOP Rollback complete");
}