Thread Handler and Ac Listener

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:

  1. Providing a standard API: Defining the AutomationCompositionElementListener interface, which is the contract that actual Participant logic must implement to receive state change commands (e.g., deploy, undeploy, migrate).

  2. Handling Asynchronous Execution: The ThreadHandler class manages a thread pool to execute the potentially long-running operations (like deploy or migrate) requested by the ACM runtime off the main message receiving thread. This ensures the Participant remains responsive and can process new messages immediately.

  3. State Reporting: The ParticipantIntermediaryApi defines methods for the Participant to report its progress and final status (success, failure, or timeout) back to the central ACM runtime.

  4. Simulating Behavior (in this specific package): The handler/ directory, which includes SimulatorService.java and AutomationCompositionElementHandler.java, suggests this is an implementation within a simulator participant. These classes demonstrate how a real Participant would use the ThreadHandler and ParticipantIntermediaryApi to 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 ThreadHandler

Primary Action

Result of Delegation

deploy

Submits a deployProcess Runnable to the executor.

Executes listener.deploy(...) on a worker thread.

undeploy

Submits an undeployProcess Runnable to the executor.

Executes listener.undeploy(...) on a worker thread.

migrate

Submits a migrateProcess Runnable to the executor.

Executes listener.migrate(...) on a worker thread.

rollbackMigration

Submits a rollbackProcess Runnable to the executor.

Executes listener.rollbackMigration(...) on a worker thread.

lock, unlock

Submits a lockProcess/unlockProcess Runnable to the executor.

Executes listener.lock/unlock on a worker thread.

prepare, review, migratePrecheck

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:

  1. 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 the SimulatorService in this package) resides.

  2. Error Handling: They include a try-catch block for PfModelException (from the Policy framework).

  3. Report Failure: If an exception is caught, the method uses the injected ParticipantIntermediaryApi to report a FAILED state back to the ACM runtime:

    Java
    intermediaryApi.updateAutomationCompositionElementState( instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED, null, StateChangeResult.FAILED, "Automation composition deployment failed");
  4. Cleanup: Once the process is complete (or fails), the Future result is removed from the executionMap.

 

3. Management and Safety Methods

 

  • cleanExecution(UUID elementId, UUID messageId): This method cancels any currently running task associated with the given elementId if the incoming request (messageId) indicates a new, overriding operation. This is crucial for handling concurrent state change requests safely.

  • close(): Implements Closeable. It shuts down the internal ExecutorService and 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

AutomationCompositionElementListener

void rollbackMigration(..., int stage) (5 parameters)

void rollbackMigration(..., int stage, **newParam**) (6 parameters)

Binary/Source Break: Any class, like AutomationCompositionElementHandler, that implements the old 5-parameter signature now violates the contract of the new AutomationCompositionElementListener interface and will fail to compile.

 

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

AutomationCompositionElementListener

New Interface: Holds the new, 6-parameter rollbackMigration method.

AcElementListenerV4

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.

AutomationCompositionElementHandler

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:

  1. Old Implementation Remains: The AutomationCompositionElementHandler (the old code) implements the AutomationCompositionElementListener and contains the old, 5-parameter version of rollbackMigration.

    Java
    // AutomationCompositionElementHandler (Old Code) @Override public void rollbackMigration(..., int stage) { ... } // (5 parameters)
  2. The New Bridge Method is Added: The developer adds a new method to the AcElementListenerV4 class 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); }
  3. Compilation Success:

    • When the compiler checks AutomationCompositionElementHandler, it sees the implementation of the 5-parameter rollbackMigration.

    • 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. If AcElementListenerV4 directly implements the new AutomationCompositionElementListener, 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:

  1. Mandating Current Signatures: Any new Participant handler must extend AcElementListenerV5, forcing them to implement the most recent API methods, which prevents future compatibility issues.

  2. Simplifying Development: A developer only needs to override the methods they actually need (e.g., deploy and undeploy), 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

 

Java
package 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

deploy

Logs, then calls intermediaryApi.updateAutomationCompositionElementState(...) to report success (StateChangeResult.NO_ERROR) and transition to DEPLOYED.

Allows a developer to ignore elements that require no action on deploy.

undeploy

Logs, then calls intermediaryApi.updateAutomationCompositionElementState(...) to report success and transition to UNDEPLOYED.

Allows a developer to ignore elements that require no action on undeploy.

lock

Logs, then calls intermediaryApi.updateAutomationCompositionElementLockState(...) to report success and transition to LOCKED.

Provides a default for elements that do not require explicit locking.

unlock

Logs, then calls intermediaryApi.updateAutomationCompositionElementLockState(...) to report success and transition to UNLOCKED.

Provides a default for elements that do not require explicit unlocking.

migrate (Latest 6-param)

Logs, then calls intermediaryApi.updateAutomationCompositionElementState(...) to report success.

Provides a default for elements that are agnostic to migration.

rollbackMigration (Latest 6-param)

Logs, then calls intermediaryApi.updateAutomationCompositionElementState(...) to report success.

Provides a default for elements that do not participate in migration rollback.

prepare, review, etc.

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"); }