Skip to content

Conversation

@chanikag
Copy link
Contributor

@chanikag chanikag commented Dec 10, 2025

Add schedule task enble/disable/trigger
This is to fix wso2/product-micro-integrator#3567

Purpose

Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc.

Goals

Describe the solutions that this feature/fix will introduce to resolve the problems described above

Approach

Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email [email protected] to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here.

User stories

Summary of user stories addressed by this change>

Release note

Brief description of the new feature or bug fix as it will appear in the release notes

Documentation

Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact

Training

Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable

Certification

Type “Sent” when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to [email protected] and NOT pasted in this PR. If there is no impact on certification exams, type “N/A” and explain why.

Marketing

Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable

Automation tests

  • Unit tests

    Code coverage information

  • Integration tests

    Details about the test cases and coverage

Security checks

Samples

Provide high-level details about the samples related to this feature

Related PRs

List any other related PRs

Migrations (if applicable)

Describe migration steps and platforms on which migration has been tested

Test environment

List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested

Learning

Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem.

Summary by CodeRabbit

  • New Features

    • Startup tasks can now be activated, deactivated, and triggered at runtime, with persistent state tracking and explicit success/error results.
  • Refactor

    • Reorganized a utility component to a shared utilities area for clearer structure and reuse.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds synchronized control APIs (activate, deactivate, trigger) to StartUpController that manage startup task state and persist it to the registry; relocates DynamicControlOperationResult from org.apache.synapse.inbound to org.apache.synapse.util and updates imports accordingly.

Changes

Cohort / File(s) Change Summary
Control API Addition
modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java
Added public synchronized methods activate(), deactivate(), and trigger() returning DynamicControlOperationResult; introduced StartUpControllerState enum, registry field initialization in init(), setStartupControllerStateInRegistry(...) helper, and registry-based persistence of controller state; minor destroy() brace fix.
Package Relocation & Import
modules/core/src/main/java/org/apache/synapse/util/DynamicControlOperationResult.java, modules/core/src/main/java/org/apache/synapse/inbound/InboundEndpoint.java
Moved DynamicControlOperationResult class package from org.apache.synapse.inbound to org.apache.synapse.util; updated import in InboundEndpoint.java to reference new package.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as StartUpController
    participant Scheduler as StartupTask/Scheduler
    participant Registry

    Client->>Controller: activate()
    alt controller initial/inactive
        Controller->>Scheduler: start/activate task
        Scheduler-->>Controller: started / throws error
        Controller->>Registry: set state = ACTIVE
        Registry-->>Controller: ack
        Controller-->>Client: DynamicControlOperationResult(success=true)
    else already active or error
        Controller-->>Client: DynamicControlOperationResult(success=false, errMsg)
    end

    Client->>Controller: deactivate()
    alt controller active
        Controller->>Scheduler: stop/deactivate task
        Scheduler-->>Controller: stopped / throws error
        Controller->>Registry: set state = INACTIVE
        Registry-->>Controller: ack
        Controller-->>Client: DynamicControlOperationResult(success=true)
    else already inactive or error
        Controller-->>Client: DynamicControlOperationResult(success=false, errMsg)
    end

    Client->>Controller: trigger()
    alt controller active
        Controller->>Scheduler: trigger task execution
        Scheduler-->>Controller: triggered / throws error
        Controller-->>Client: DynamicControlOperationResult(success=true)
    else inactive or error
        Controller-->>Client: DynamicControlOperationResult(success=false, errMsg)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review synchronized methods in StartUpController.java for thread-safety, proper error handling, and correct registry interactions.
  • Verify setStartupControllerStateInRegistry(...) resilience to registry failures and correct path/key usage.
  • Confirm all imports updated to the new org.apache.synapse.util.DynamicControlOperationResult and check for any remaining references to the old package.

Poem

🐰 I hop to set tasks live or still,
Activate, pause, or give a thrill.
Registry notes each tiny feat,
From inbound roots to util seat—
My schedule garden grows complete. 🎋

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning Description is largely incomplete with most template sections left as placeholders and no actual content filled in for critical sections like Purpose, Goals, Approach, tests, or security checks. Complete all required sections: fill Purpose/Goals/Approach details, confirm security checks (yes/no), provide unit/integration test coverage info, and document other relevant sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately describes the main change: adding enable/disable/trigger functionalities for schedule tasks.
Linked Issues check ✅ Passed The PR implements enable/disable/trigger APIs for scheduled tasks as required by #3567, with new methods added to StartUpController class.
Out of Scope Changes check ✅ Passed All code changes are scoped to implementing scheduled task control: adding lifecycle methods to StartUpController, moving DynamicControlOperationResult to appropriate package, and adding necessary imports.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f6db35 and cf1cbfd.

📒 Files selected for processing (3)
  • modules/core/src/main/java/org/apache/synapse/inbound/InboundEndpoint.java (1 hunks)
  • modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java (6 hunks)
  • modules/core/src/main/java/org/apache/synapse/util/DynamicControlOperationResult.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
modules/core/src/main/java/org/apache/synapse/inbound/InboundEndpoint.java (1)
modules/core/src/main/java/org/apache/synapse/util/DynamicControlOperationResult.java (1)
  • DynamicControlOperationResult (21-37)
🔇 Additional comments (6)
modules/core/src/main/java/org/apache/synapse/inbound/InboundEndpoint.java (1)

34-34: LGTM! Import updated correctly.

The import statement has been updated to reflect the new package location of DynamicControlOperationResult.

modules/core/src/main/java/org/apache/synapse/util/DynamicControlOperationResult.java (1)

19-37: LGTM! Package relocation is appropriate.

Moving DynamicControlOperationResult to the util package is a good refactoring decision, as this class is now shared across multiple components (InboundEndpoint and StartUpController).

modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java (4)

68-76: LGTM! Registry integration infrastructure is well-structured.

The registry field, state enum, and constants are appropriately defined and follow the same pattern as InboundEndpoint.


193-215: LGTM! Deactivation logic is correct.

The deactivate() method properly synchronizes access, delegates to deactivateTask(), and persists the state change to the registry on success.


258-265: LGTM! Registry state persistence is correctly implemented.

The helper method properly handles null registry and persists the task state using appropriate registry APIs.


287-330: Verify if state restoration on initialization is needed.

The registry is initialized at line 289, but unlike InboundEndpoint (which restores state from registry in startInPausedMode() method at lines 529-535), StartUpController doesn't appear to restore the persisted task state (ACTIVE/INACTIVE) when the controller is initialized after a server restart.

This means:

  • If a task is deactivated and the server restarts, the task may start in its default state rather than remaining deactivated
  • The persisted state in the registry may not be honored on initialization

Please confirm whether state restoration is intentionally omitted or if it should be added to maintain task state across restarts.

If state restoration is needed, consider adding a method similar to InboundEndpoint.startInPausedMode() that reads the state from the registry and conditionally calls deactivateTask() during initialization.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java (2)

264-271: Consider adding error handling for registry operations.

The method lacks exception handling for registry.newNonEmptyResource(). If the registry operation fails (e.g., due to connectivity issues, permissions, or resource conflicts), the exception would propagate to the caller, potentially causing the activate/deactivate operations to fail unexpectedly even though the task state change itself succeeded.

Apply this diff to add defensive error handling:

 private void setStartupControllerStateInRegistry(StartUpController.StartUpControllerState state) {
     if (Objects.isNull(registry)) {
         logger.warn("Registry not available! The state of the Task will not be saved.");
         return;
     }
-    registry.newNonEmptyResource(REG_STARTUP_CONTROLLER_BASE_PATH + getName(), false, "text/plain",
-            state.toString(), STARTUP_CONTROLLER_STATE);
+    try {
+        registry.newNonEmptyResource(REG_STARTUP_CONTROLLER_BASE_PATH + getName(), false, "text/plain",
+                state.toString(), STARTUP_CONTROLLER_STATE);
+    } catch (Exception e) {
+        logger.warn("Failed to persist task state to registry for Task: " + getName(), e);
+    }
 }

109-111: Consider thread safety for destroy() method.

While the new activate(), deactivate(), and trigger() methods are properly synchronized, the destroy() method is not. If destroy() is called concurrently with any of the synchronized state-change methods, it could lead to race conditions where:

  1. A task is being activated/deactivated while being destroyed
  2. The task state becomes inconsistent

If concurrent destroy operations are possible in your deployment scenario (e.g., concurrent undeployment and state changes via API), consider adding synchronization:

-    public void destroy() {
+    public synchronized void destroy() {
         destroy(true);
     }

Additionally, consider making destroy(boolean removeTask) synchronized:

-    public void destroy(boolean removeTask) {
+    public synchronized void destroy(boolean removeTask) {
         if (!destroyTask()) {
             return;
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf1cbfd and f84dbe5.

📒 Files selected for processing (1)
  • modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java (1)
modules/core/src/main/java/org/apache/synapse/util/DynamicControlOperationResult.java (1)
  • DynamicControlOperationResult (21-37)
🔇 Additional comments (5)
modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java (5)

34-35: LGTM!

The imports are correct. The DynamicControlOperationResult package relocation from org.apache.synapse.inbound to org.apache.synapse.util aligns with the broader refactoring in this PR.


160-182: LGTM!

The activate() method is well-implemented with proper synchronization, error handling, and state persistence. The previous review feedback on variable naming consistency has been addressed.


193-215: LGTM!

The deactivate() method follows the same robust pattern as activate() with proper synchronization, error handling, and state persistence.


224-252: LGTM!

The trigger() method correctly implements manual task execution with proper type safety checks (addressing previous review feedback). The decision not to persist state for a one-time trigger operation is appropriate.


293-336: Verify if task state should be restored from registry on initialization.

The code persists task state (ACTIVE/INACTIVE) to the registry in activate() and deactivate() methods, but init() doesn't read this state back to restore the previous state. This means:

  1. When the server restarts, tasks will revert to their default scheduled state (as defined in configuration)
  2. The state persisted in the registry becomes stale and unused

If the intent is to preserve enable/disable state across server restarts, you should add state restoration logic in init(). If tasks should start fresh on each restart, this behavior is correct but should be documented.

Based on the PR objectives to "enable/disable scheduled tasks," please clarify:

  1. Should task state persist across server restarts?
  2. Is the registry state intended for cluster coordination or just runtime tracking?

If state restoration is needed, consider adding this logic after line 295:

private void restoreTaskStateFromRegistry() {
    if (Objects.isNull(registry)) {
        return;
    }
    try {
        String statePath = REG_STARTUP_CONTROLLER_BASE_PATH + getName();
        if (registry.resourceExists(statePath)) {
            String savedState = (String) registry.getResourceProperty(statePath, STARTUP_CONTROLLER_STATE);
            if (StartUpControllerState.INACTIVE.toString().equals(savedState)) {
                deactivateTask();
                logger.info("Restored task [" + getName() + "] to INACTIVE state from registry.");
            }
        }
    } catch (Exception e) {
        logger.warn("Failed to restore task state from registry for Task: " + getName(), e);
    }
}

Then call it in init() after task initialization (after line 311).

Comment on lines +70 to +76
private enum StartUpControllerState {
INITIAL, ACTIVE, INACTIVE
}

private static final String REG_STARTUP_CONTROLLER_BASE_PATH = "/repository/components/org.apache.synapse.startup/";
private static final String STARTUP_CONTROLLER_STATE = "TASK_STATE";

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cd modules/core/src/main/java/org/apache/synapse/startup/quartz && wc -l StartUpController.java

Repository: wso2/wso2-synapse

Length of output: 87


🏁 Script executed:

# Search for INITIAL state usage in StartUpController.java
rg -n "INITIAL" modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java

Repository: wso2/wso2-synapse

Length of output: 97


🏁 Script executed:

# Find setStartupControllerStateInRegistry method
rg -n -A 10 "setStartupControllerStateInRegistry" modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java

Repository: wso2/wso2-synapse

Length of output: 1873


🏁 Script executed:

# Search for task name assignments and validation
rg -n -C 3 "getName\(\)|setName|TaskDescription" modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java

Repository: wso2/wso2-synapse

Length of output: 10296


🏁 Script executed:

# Check TaskDescription class for name validation
fd TaskDescription.java | head -5 | xargs -I {} wc -l {}

Repository: wso2/wso2-synapse

Length of output: 137


🏁 Script executed:

# Search for where task names are used in registry paths
rg -n "REG_STARTUP_CONTROLLER_BASE_PATH" modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java

Repository: wso2/wso2-synapse

Length of output: 292


🏁 Script executed:

# Examine TaskDescription class for name validation
fd TaskDescription.java -type f | xargs cat | head -100

Repository: wso2/wso2-synapse

Length of output: 231


🏁 Script executed:

# Check if there's any name validation in TaskDescription setName method
rg -n -A 5 "setName\s*\(" modules/tasks/src/main/java/org/apache/synapse/task/TaskDescription.java

Repository: wso2/wso2-synapse

Length of output: 257


🏁 Script executed:

# Check the entire setName method and getName method
rg -n -B 2 -A 8 "public.*setName|public.*getName" modules/tasks/src/main/java/org/apache/synapse/task/TaskDescription.java

Repository: wso2/wso2-synapse

Length of output: 461


🏁 Script executed:

# Search for any task name validation or constraints in the codebase
rg -n "TaskDescription|task.*name.*valid|task.*name.*constraint" modules/tasks/src/main/java/org/apache/synapse/task/ --type java | grep -i "valid\|constraint\|sanitize\|name" | head -20

Repository: wso2/wso2-synapse

Length of output: 3102


🏁 Script executed:

# Check the validateTaskDescription method
rg -n -A 15 "validateTaskDescription" modules/tasks/src/main/java/org/apache/synapse/task/TaskDescriptionRepository.java

Repository: wso2/wso2-synapse

Length of output: 761


🏁 Script executed:

# Also check if task names come from configuration files or user input
rg -n "TaskDescription\(\)|setName|taskName.*=" modules/core/src/main/java/org/apache/synapse/startup/ --type-list | head -30

Repository: wso2/wso2-synapse

Length of output: 450


🏁 Script executed:

# Search for TaskDescription instantiation to see where names come from
rg -n -B 2 -A 2 "new TaskDescription" modules/ --type-list | head -50

Repository: wso2/wso2-synapse

Length of output: 450


🏁 Script executed:

# Check validateName and validateUniqueness methods
rg -n -A 10 "private void validateName|private void validateUniqueness" modules/tasks/src/main/java/org/apache/synapse/task/TaskDescriptionRepository.java

Repository: wso2/wso2-synapse

Length of output: 756


🏁 Script executed:

# Search for where TaskDescription names are assigned in configuration parsing
rg -n "setName\|getName" modules/core/src/main/java/org/apache/synapse/config/xml/ | grep -i task | head -20

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Check if task names are parsed from XML configuration
fd -e xml modules/core/src/main/resources | head -5 | xargs grep -l "task\|startup" 2>/dev/null | head -3

Repository: wso2/wso2-synapse

Length of output: 490


🏁 Script executed:

# Check for task configuration parsing to see if names come from user input
rg -n "task.*name\|Task.*name" modules/core/src/main/java/org/apache/synapse/config/xml/StartupFactoryDeserializer.java

Repository: wso2/wso2-synapse

Length of output: 189


🏁 Script executed:

# Look at how task startup elements are configured
fd StartupFactoryDeserializer.java | xargs cat | head -150

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Check Registry interface for constraints on resource paths
fd Registry.java | xargs cat | grep -A 5 "newNonEmptyResource\|interface\|class"

Repository: wso2/wso2-synapse

Length of output: 3178


🏁 Script executed:

# Check what characters are allowed/constrained in registry paths
rg -n "path.*valid\|path.*sanitize\|special.*char" modules/ -i | head -20

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Look at the actual registry implementation details for path handling
fd -e java | xargs rg -l "newNonEmptyResource" | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && grep -A 15 "newNonEmptyResource" {}'

Repository: wso2/wso2-synapse

Length of output: 3989


🏁 Script executed:

# Check if task names are used directly from user configuration (XML/YAML)
rg -n "taskName\|task.*name" modules/core/src/main/java/org/apache/synapse/config/ | grep -i "xml\|parse\|config" | head -15

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Check if there are any test cases or examples showing task names with special characters
rg -n "setName\|getName" modules/core/src/test modules/tasks/src/test -i | grep -i "task.*name\|special\|char\|path" | head -20

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Look for task configuration examples in test resources
fd -e xml modules/*/src/test/resources | xargs grep -l "task\|startup" | head -3 | xargs cat | grep -A 2 -B 2 "name" | head -40

Repository: wso2/wso2-synapse

Length of output: 502


🏁 Script executed:

# Search for any error handling or issues with registry paths containing special characters
rg -n "resource.*path\|registry.*path" modules/core/src/main/java/org/apache/synapse/registry/ -A 3 | head -40

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Check if there are any path sanitization utilities in the codebase
rg -n "sanitize\|escape.*path\|encode.*path" modules/ -i | head -15

Repository: wso2/wso2-synapse

Length of output: 43


🏁 Script executed:

# Look at where task names are parsed from configuration - likely XML parsers
fd StaxBuilder.java | xargs cat 2>/dev/null | grep -A 10 "task\|startup" | head -50

Repository: wso2/wso2-synapse

Length of output: 43


Remove the unused INITIAL state or document its purpose.

The INITIAL state in the enum is never used in the code. Only ACTIVE and INACTIVE states are persisted to the registry. Either remove INITIAL if it's not needed, or document its purpose if it represents a default/uninitialized state.

Regarding registry path construction at line 269 (REG_STARTUP_CONTROLLER_BASE_PATH + getName()): while task name validation only enforces non-null and non-empty constraints without character restrictions, the current approach mirrors patterns used elsewhere in the codebase (e.g., ScheduledMessageProcessor, InboundEndpoint). If task names are controlled through configuration schemas, this may be acceptable, but consider documenting this assumption.

🤖 Prompt for AI Agents
In
modules/core/src/main/java/org/apache/synapse/startup/quartz/StartUpController.java
around lines 70-76, the enum StartUpControllerState declares INITIAL which is
never referenced and should be removed to avoid dead code, or if intended as a
default/uninitialized marker add a short Javadoc on the enum explaining its
purpose and where it would be used; additionally, add a brief comment near the
REG_STARTUP_CONTROLLER_BASE_PATH + getName() usage (around line ~269)
documenting the assumption that task names are validated via configuration
schemas and may contain characters used directly in registry paths (or introduce
explicit sanitization if that assumption is false).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Task]: Add APIs for enable/disable scheduled tasks

1 participant