Skip to content

Comments

Add Post processing action support#1543

Merged
niveathika merged 10 commits intomasterfrom
post-processing
Feb 12, 2026
Merged

Add Post processing action support#1543
niveathika merged 10 commits intomasterfrom
post-processing

Conversation

@niveathika
Copy link
Contributor

@niveathika niveathika commented Feb 6, 2026

Purpose

Part of ballerina-platform/ballerina-library#8604

This pull request introduces support for post-processing actions in the FTP listener, allowing users to automatically move or delete files after they are processed, either on success or error. The changes span the public API, implementation, and documentation, making it easier to configure automated file handling patterns such as archiving or cleanup.

Key changes:

Public API and Annotations

  • Added DELETE constant and new Move record type to annotations.bal, and updated the FtpFunctionConfig annotation to support afterProcess and afterError fields for specifying post-processing actions. These fields accept either DELETE or a Move record, enabling flexible configuration for file handling after processing.

Implementation (Java backend)

  • Updated FormatMethodsHolder to parse and store post-processing actions from annotations, with new methods to retrieve these actions for each content handler. Added logic to handle both delete and move actions, including subdirectory preservation. [1] [2] [3] [4]
  • Modified FtpContentCallbackHandler to retrieve post-processing actions and invoke content methods asynchronously with support for executing these actions after processing completes, handling both success and error cases. [1] [2] [3] [4]

Documentation

  • Added a new section "4.3.2. Post-Processing Actions" to the specification, with explanations and code examples for configuring afterProcess and afterError behaviors using the new API. [1] [2]

Changelog

  • Added an entry to the changelog documenting the new post-processing action support

Examples

Checklist

  • Linked to an issue
  • Updated the changelog
  • Added tests
  • Updated the spec
  • Checked native-image compatibility

Summary

This pull request adds post-processing action support to the FTP listener, enabling automatic file handling after processing completes. Users can now configure actions to delete or move files after successful processing or when errors occur.

Key Changes

API Updates:

  • Added new DELETE constant and Move record type with moveTo (destination path) and preserveSubDirs (boolean flag, default true) fields
  • Extended FtpFunctionConfig with optional afterProcess and afterError fields accepting either MOVE or DELETE actions
  • Made fileNamePattern in FtpFunctionConfig optional

Java Implementation:

  • Introduced new PostProcessAction class to model and manage delete/move operations with metadata
  • Updated FormatMethodsHolder to parse, validate, and store per-handler post-processing actions with new accessor methods (getAfterProcessAction, getAfterErrorAction, hasPostProcessingActions)
  • Extended FtpContentCallbackHandler with post-processing engine: new methods to execute delete actions (removing files via client), move actions (relocating files with optional subdirectory preservation), and calculate move destinations with path normalization
  • Refactored FtpListener to use ServiceContext structures, replacing per-service maps to better organize service state including service object, configuration, format methods holder, and caller
  • Updated FtpListenerHelper to initialize service contexts and validate post-processing requirements for proper caller setup

Testing & Documentation:

  • Added comprehensive test suite (post_process_action_test.bal) covering DELETE and MOVE actions, subdirectory preservation modes, error-triggered actions, and combined scenarios
  • Added documentation section explaining post-processing configuration, behavior, and usage examples
  • Updated test fixtures with new post-processing test directories
  • Added changelog entry

Version & Dependency Updates:

  • Bumped module version from 2.16.1 to 2.17.0
  • Updated native dependency references accordingly

Implementation Details

Post-processing actions execute conditionally after content method invocation: afterProcess actions on successful processing, afterError actions when errors occur. The Move action respects the preserveSubDirs setting to optionally maintain relative subdirectory structures at the destination.

@niveathika niveathika requested a review from Copilot February 6, 2026 15:02
@niveathika niveathika added the Skip GraalVM Check This will skip the GraalVM compatibility check label Feb 6, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds configurable post-processing actions to the FTP listener so files can be automatically deleted or moved after a content handler completes (on success or error), including documentation and new tests.

Changes:

  • Extended Ballerina annotations/spec to support afterProcess / afterError actions (DELETE or Move).
  • Implemented annotation parsing and asynchronous execution of post-processing actions in the Java listener runtime.
  • Added test coverage and mock-server filesystem setup for post-processing scenarios.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test-utils/src/main/java/io/ballerina/stdlib/ftp/testutils/mockServerUtils/MockFtpServer.java Pre-creates post-process test directories in the fake FTP filesystem.
native/src/main/java/io/ballerina/stdlib/ftp/util/FtpConstants.java Adds annotation field name constants for post-processing actions.
native/src/main/java/io/ballerina/stdlib/ftp/transport/server/connector/contractimpl/RemoteFileSystemServerConnectorImpl.java Exposes listener path from the server connector implementation.
native/src/main/java/io/ballerina/stdlib/ftp/transport/server/RemoteFileSystemConsumer.java Includes listener path in emitted filesystem events.
native/src/main/java/io/ballerina/stdlib/ftp/transport/message/RemoteFileSystemEvent.java Carries listener path for subdirectory-preserving moves.
native/src/main/java/io/ballerina/stdlib/ftp/server/PostProcessAction.java New model to represent DELETE/MOVE actions with config.
native/src/main/java/io/ballerina/stdlib/ftp/server/FtpContentCallbackHandler.java Executes after-success/after-error file actions after handler invocation.
native/src/main/java/io/ballerina/stdlib/ftp/server/FormatMethodsHolder.java Parses afterProcess/afterError from annotations per handler method.
docs/spec/spec.md Documents the new post-processing feature and configuration.
changelog.md Adds changelog entry for post-processing action support.
ballerina/tests/post_process_action_test.bal Adds integration tests for delete/move post-processing behavior.
ballerina/annotations.bal Extends public annotation API with DELETE, Move, and new fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@niveathika
Copy link
Contributor Author

@copilot open a new pull request to apply changes based on the comments in this thread.
Remove yourself from authors list.

Copy link
Contributor

Copilot AI commented Feb 6, 2026

@niveathika I've opened a new pull request, #1544, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 59.17722% with 129 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.53%. Comparing base (238b5fd) to head (4a0c824).
⚠️ Report is 11 commits behind head on master.

Files with missing lines Patch % Lines
...a/stdlib/ftp/server/FtpContentCallbackHandler.java 53.84% 29 Missing and 19 partials ⚠️
...va/io/ballerina/stdlib/ftp/server/FtpListener.java 50.00% 29 Missing and 6 partials ⚠️
...ballerina/stdlib/ftp/server/FtpListenerHelper.java 64.78% 16 Missing and 9 partials ⚠️
...llerina/stdlib/ftp/server/FormatMethodsHolder.java 62.50% 12 Missing and 3 partials ⚠️
...ballerina/stdlib/ftp/server/PostProcessAction.java 72.22% 4 Missing and 1 partial ⚠️
...io/ballerina/stdlib/ftp/server/ServiceContext.java 92.30% 1 Missing ⚠️

❌ Your project status has failed because the head coverage (76.53%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1543      +/-   ##
============================================
- Coverage     76.78%   76.53%   -0.25%     
- Complexity      769     1043     +274     
============================================
  Files            58       76      +18     
  Lines          3928     4833     +905     
  Branches        653      838     +185     
============================================
+ Hits           3016     3699     +683     
- Misses          634      760     +126     
- Partials        278      374      +96     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements post-processing action support for the Ballerina FTP module. It introduces DELETE and MOVE actions that execute after file processing completes or on errors. The feature includes new type definitions (Move record, DELETE constant), updated FtpFunctionConfig to accept optional afterProcess/afterError actions, service-level configuration support via ServiceConfiguration type and ServiceConfig annotation, corresponding Java implementation for action coordination, and comprehensive test coverage with version bump to 2.17.0.

Changes

Cohort / File(s) Summary
Type & Annotation Definitions
ballerina/annotations.bal
Adds public constants DELETE, types Move and MOVE, updates FtpFunctionConfig to support optional afterProcess/afterError fields, introduces ServiceConfiguration record and ServiceConfig annotation for service-level FTP monitoring configuration.
Documentation
docs/spec/spec.md, changelog.md
Adds comprehensive Post-Processing Actions subsection documenting DELETE and MOVE types, behavior, prerequisites, and usage examples; updates Contents and adds changelog entry for feature release.
Post-Processing Engine
native/src/main/java/io/ballerina/stdlib/ftp/server/PostProcessAction.java, FormatMethodsHolder.java, FtpContentCallbackHandler.java
Introduces PostProcessAction model with DELETE/MOVE actions and subdirectory preservation; extends FormatMethodsHolder to parse and store per-method post-actions; refactors FtpContentCallbackHandler to coordinate post-processing execution (delete/move) after content method completion or error handling with destination path calculation and logging.
Service Context & Listener Architecture
native/src/main/java/io/ballerina/stdlib/ftp/server/ServiceContext.java, FtpListener.java, FtpListenerHelper.java
Introduces ServiceContext to encapsulate per-service state; refactors FtpListener to use service-level configuration contexts instead of flat maps; extends FtpListenerHelper to initialize ServiceContext with FormatMethodsHolder and path-aware caller configuration; replaces addServiceConfiguration with ServiceContext-based API.
Constants & Test Infrastructure
native/src/main/java/io/ballerina/stdlib/ftp/util/FtpConstants.java, test-utils/src/main/java/io/ballerina/stdlib/ftp/testutils/.../MockFtpServer.java
Adds public constants for afterProcess, afterError, moveTo, and preserveSubDirs annotations; extends mock FTP server with test directories for post-processing scenarios.
Tests
ballerina/tests/post_process_action_test.bal, ballerina/tests/client_endpoint_test.bal
Adds 608-line comprehensive test module covering afterProcess DELETE/MOVE (with preserveSubDirs variants), afterError DELETE/MOVE, combined scenarios, and no-action cases with file upload, processing, and cleanup verification; extends client endpoint test with three new post-processing test directories.
Version & Configuration
ballerina/Ballerina.toml, ballerina/Dependencies.toml, ballerina/CompilerPlugin.toml, gradle.properties
Bumps module version and all native dependency versions from 2.16.1 to 2.17.0 across all configuration files.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant FtpListener
    participant FtpContentCallbackHandler
    participant FormatMethodsHolder
    participant PostProcessEngine
    participant FtpClient

    Client->>FtpListener: File arrives at monitored path
    FtpListener->>FtpContentCallbackHandler: processContentBasedCallbacks(fileInfo)
    FtpContentCallbackHandler->>FormatMethodsHolder: getAfterProcessAction(methodName)
    FormatMethodsHolder-->>FtpContentCallbackHandler: Optional<PostProcessAction>
    
    alt Content Method Success
        FtpContentCallbackHandler->>FtpContentCallbackHandler: invokeContentMethodAsync()
        FtpContentCallbackHandler->>PostProcessEngine: executePostProcessAction(afterProcess)
        PostProcessEngine->>FtpClient: delete(filePath) OR move(filePath, destination)
        FtpClient-->>PostProcessEngine: success/error
        PostProcessEngine-->>FtpContentCallbackHandler: logged outcome
    else Content Method Failure
        FtpContentCallbackHandler->>FtpContentCallbackHandler: routeToOnError()
        FtpContentCallbackHandler->>FormatMethodsHolder: getAfterErrorAction(methodName)
        FormatMethodsHolder-->>FtpContentCallbackHandler: Optional<PostProcessAction>
        FtpContentCallbackHandler->>PostProcessEngine: executePostProcessAction(afterError)
        PostProcessEngine->>FtpClient: delete(filePath) OR move(filePath, destination)
        FtpClient-->>PostProcessEngine: success/error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Add service level monitoring support #1539: Directly related code-level modifications involving ServiceConfiguration type and ServiceConfig annotation surface in both ballerina/annotations.bal and documentation, indicating potential interdependency in service-level configuration design.

Suggested reviewers

  • ThisaruGuruge
  • MohamedSabthar
  • DimuthuMadushan
  • shafreenAnfar

Poem

🐰 Files now delete with a hop and a bound,
or migrate to archives without any sound,
The FTP module hops faster with grace,
post-processing actions in every new place! 📦✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Post processing action support' accurately summarizes the main feature introduced in this changeset: support for post-processing actions (delete/move) in FTP file handling.
Description check ✅ Passed The description covers all required template sections with comprehensive details: Purpose (with linked issue), Key changes across API/implementation/documentation, Examples section (present but empty), and Checklist mostly completed (4 of 5 items checked).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch post-processing

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: 3

🤖 Fix all issues with AI agents
In `@ballerina/tests/client_endpoint_test.bal`:
- Around line 1221-1226: resourceNames and fileSizes arrays are out of sync:
resourceNames contains 21 entries while fileSizes only has 20; update the
fileSizes array to include corresponding size entries for the added resource
names ("post-process", "post-process-archive", "post-process-error") so both
arrays align. Locate the resourceNames and fileSizes declarations and append
three size values (use the expected sizes for "post-process" and
"post-process-archive" and set "post-process-error" to 0 if unknown) to the
fileSizes array so its length matches resourceNames.

In `@ballerina/tests/post_process_action_test.bal`:
- Around line 228-294: The test testAfterProcessMoveWithPreserveSubDirsTrue
fails because the destination subdirectory under POST_PROCESS_ARCHIVE_DIR is
never created; before uploading the test file (before check
(<Client>clientEp)->putJson(...)), create the archive subdirectory (e.g., check
(<Client>clientEp)->exists(POST_PROCESS_ARCHIVE_DIR + "/preserve-subdir") and if
missing call check (<Client>clientEp)->mkdir(POST_PROCESS_ARCHIVE_DIR +
"/preserve-subdir")) so the MOVE with preserveSubDirs can succeed; keep cleanup
calls for deleting the archived file and removing the created directories as
already present.

In `@changelog.md`:
- Line 10: Update the changelog entry text "[Add post processing action
support]" to hyphenate the compound modifier by changing "post processing" to
"post-processing" so the line reads "[Add post-processing action support]";
ensure only that phrase is modified and punctuation/links remain unchanged.
🧹 Nitpick comments (4)
native/src/main/java/io/ballerina/stdlib/ftp/transport/message/RemoteFileSystemEvent.java (1)

32-41: Consider constructor chaining to reduce duplication.

The new constructor duplicates the field assignments from the original constructor. Using this(...) would reduce duplication and ensure consistency.

♻️ Suggested refactor
     public RemoteFileSystemEvent(List<FileInfo> addedFiles, List<String> deletedFiles) {
         this.addedFiles = addedFiles;
         this.deletedFiles = deletedFiles;
     }

     public RemoteFileSystemEvent(List<FileInfo> addedFiles, List<String> deletedFiles, String listenerPath) {
-        this.addedFiles = addedFiles;
-        this.deletedFiles = deletedFiles;
+        this(addedFiles, deletedFiles);
         this.listenerPath = listenerPath;
     }
native/src/main/java/io/ballerina/stdlib/ftp/server/FtpListenerHelper.java (1)

166-179: Minor: Duplicate FormatMethodsHolder instantiation and trailing whitespace.

  1. FormatMethodsHolder is instantiated here for validation and again in FtpListener.dispatchFileEventToService() for dispatch. Consider caching the validated holder in native data to avoid duplicate parsing.

  2. Line 179 has trailing whitespace before the if (!needsCaller) block.

♻️ Optional: Cache FormatMethodsHolder to avoid duplicate instantiation
         if (contentMethod.isPresent()) {
             try {
                 FormatMethodsHolder holder = new FormatMethodsHolder(service);
                 if (!needsCaller && holder.hasPostProcessingActions()) {
                     needsCaller = true;
                 }
+                // Cache for later use in dispatch
+                service.addNativeData("FORMAT_METHODS_HOLDER", holder);
             } catch (FtpInvalidConfigException e) {
                 return FtpUtil.createError(e.getMessage(), e, FtpUtil.ErrorType.InvalidConfigError.errorType());
             }
         }
-        
+
         if (!needsCaller) {
native/src/main/java/io/ballerina/stdlib/ftp/server/FtpContentCallbackHandler.java (2)

500-521: Edge case: file path without directory separator.

Line 502 extracts the filename using filePath.lastIndexOf('/'). If filePath contains no / (unlikely for FTP paths but possible), lastIndexOf returns -1 and substring(0) returns the entire path, which is correct. However, this assumption should be documented.

Additionally, the method doesn't handle potential path normalization issues (e.g., double slashes, Windows-style backslashes in edge cases).

♻️ Optional: Add comment documenting the assumption
     private String calculateMoveDestination(String filePath, String listenerPath, PostProcessAction action) {
         String moveTo = action.getMoveTo();
+        // Extract filename from path. If no '/' exists, lastIndexOf returns -1, 
+        // and substring(0) returns the entire path which is treated as filename.
         String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);

333-364: Consider documenting the onError post-processing behavior.

The onError handler can have its own afterProcess/afterError actions configured. This means:

  • If onError completes successfully → afterProcess action runs
  • If onError throws an error → afterError action runs

This is semantically correct but may be confusing. Consider adding inline documentation to clarify this behavior for future maintainers.

📝 Optional: Add clarifying comment
+    /**
+     * Invokes the onError handler method asynchronously with post-processing support.
+     * Note: The onError handler can have its own post-processing actions configured.
+     * - If onError completes successfully (no error), afterProcess action is executed.
+     * - If onError returns/throws an error, afterError action is executed.
+     */
     private void invokeOnErrorMethodAsync(BObject service, String methodName, Object[] methodArguments,
                                           FileInfo fileInfo, BObject callerObject, String listenerPath,

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

🤖 Fix all issues with AI agents
In `@docs/spec/spec.md`:
- Around line 996-1027: Add a documented type alias for the Move record so
readers understand the MOVE identifier used in the FtpFunctionConfig union:
after the Move record definition, add a type alias declaration named MOVE that
aliases Move and include a short doc comment like "Type alias for Move record,
used in union types for post-processing actions" so FtpFunctionConfig's
MOVE|DELETE appearance is clear (referencing the Move record, the MOVE alias,
and the FtpFunctionConfig type).
🧹 Nitpick comments (1)
native/src/main/java/io/ballerina/stdlib/ftp/server/FtpContentCallbackHandler.java (1)

500-521: Consider edge case where file path contains no directory separator.

Line 502 uses lastIndexOf('/') to extract the filename. If filePath somehow doesn't contain a /, the result would be -1 + 1 = 0, making fileName equal to the entire filePath. While FTP paths typically include /, this edge case could lead to unexpected behavior.

♻️ Suggested defensive improvement
     private String calculateMoveDestination(String filePath, String listenerPath, PostProcessAction action) {
         String moveTo = action.getMoveTo();
-        String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
+        int lastSlashIndex = filePath.lastIndexOf('/');
+        String fileName = lastSlashIndex >= 0 ? filePath.substring(lastSlashIndex + 1) : filePath;

Nuvindu
Nuvindu previously approved these changes Feb 10, 2026
Copy link
Contributor

@Nuvindu Nuvindu left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

🤖 Fix all issues with AI agents
In
`@native/src/main/java/io/ballerina/stdlib/ftp/server/FormatMethodsHolder.java`:
- Around line 132-157: In parsePostProcessAction, tighten validation: only treat
actionObj as DELETE when it is a STRING whose value equals the DELETE constant
(don't treat any arbitrary string as delete), verify actionObj is a BMap before
casting to avoid NPEs, validate that moveTo (ANNOTATION_MOVE_TO) is non-null and
non-empty before using it (throw FtpInvalidConfigException with context if
invalid), and implement the preserveSubDirs default to true when
ANNOTATION_PRESERVE_SUB_DIRS is missing or not a boolean (otherwise use the
boolean value); update logic around TypeTags.STRING_TAG, the BMap cast,
ANNOTATION_MOVE_TO and ANNOTATION_PRESERVE_SUB_DIRS, and return
PostProcessAction.delete() or PostProcessAction.move(moveTo, preserveSubDirs)
accordingly.

In `@native/src/main/java/io/ballerina/stdlib/ftp/server/ServiceContext.java`:
- Line 31: The non-volatile field `caller` in ServiceContext can cause
visibility issues between the registration thread and async event handler
threads; make the field declaration for `caller` volatile in the ServiceContext
class so writes from setCaller() are visible to readers calling getCaller() in
dispatchFileEventToService() (the ServiceContext instance published by
FtpListener), ensuring thread-safe visibility across threads.

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

🤖 Fix all issues with AI agents
In
`@native/src/main/java/io/ballerina/stdlib/ftp/server/FtpContentCallbackHandler.java`:
- Around line 451-472: In calculateMoveDestination, handle cases where filePath
ends with '/' by normalizing filePath before extracting fileName and computing
relativePath: trim any trailing slashes from filePath (but preserve a single
leading slash if needed), then compute fileName =
trimmedFilePath.substring(trimmedFilePath.lastIndexOf('/') + 1) and use
trimmedFilePath for normalizedFilePath comparisons; if trimming produces an
empty name (e.g., root path), fall back to using a sensible default (the
original moveTo directory or a placeholder) to avoid returning an empty
filename. Ensure references to action.getMoveTo(), action.isPreserveSubDirs(),
listenerPath, ensureTrailingSlash(moveTo) and normalizedListenerPath remain
consistent.

In `@native/src/main/java/io/ballerina/stdlib/ftp/server/FtpListenerHelper.java`:
- Around line 286-318: The current flow creates a per-service caller (via
createCaller or createCallerWithPath) before calling
listener.addServiceContext(context), which can leak resources if
addServiceContext fails; change the flow so you register the ServiceContext
first (create ServiceContext with caller=null), call
listener.addServiceContext(context) and only on success create the caller (use
createCaller or createCallerWithPath as before), set it on the listener or
context (listener.setCaller(createdCaller) or set into the ServiceContext), and
if caller creation fails return that error; alternatively, if you must create
the caller earlier then ensure you clean it up when addServiceContext returns
false (close/release and do not retain it). Ensure you update references to
ServiceContext, addServiceContext, createCaller, createCallerWithPath and
listener.setCaller accordingly.
🧹 Nitpick comments (1)
native/src/main/java/io/ballerina/stdlib/ftp/server/FtpContentCallbackHandler.java (1)

289-300: Simplify redundant conditional logic.

The hasOnErrorActions check is unnecessary since empty Optionals will behave identically to explicitly passing Optional.empty(). The conditional ternary operators on lines 299-300 don't change the behavior.

♻️ Proposed simplification
-        Optional<PostProcessAction> onErrorAfterProcess = holder.getAfterProcessAction(onErrorMethod.getName());
-        Optional<PostProcessAction> onErrorAfterError = holder.getAfterErrorAction(onErrorMethod.getName());
-        boolean hasOnErrorActions = onErrorAfterProcess.isPresent() || onErrorAfterError.isPresent();
-
         // Prepare arguments for onError method
         Object[] methodArguments = prepareOnErrorMethodArguments(onErrorMethod, error, callerObject);

+        Optional<PostProcessAction> onErrorAfterProcess = holder.getAfterProcessAction(onErrorMethod.getName());
+        Optional<PostProcessAction> onErrorAfterError = holder.getAfterErrorAction(onErrorMethod.getName());
+
         // Invoke onError asynchronously and apply afterProcess/afterError actions
         invokeOnErrorMethodAsync(service, onErrorMethod.getName(), methodArguments, fileInfo, callerObject,
-                listenerPath,
-                hasOnErrorActions ? onErrorAfterProcess : Optional.empty(),
-                hasOnErrorActions ? onErrorAfterError : Optional.empty());
+                listenerPath, onErrorAfterProcess, onErrorAfterError);

@sonarqubecloud
Copy link

@niveathika niveathika requested a review from Nuvindu February 12, 2026 10:41
@niveathika niveathika merged commit 0f4d1b1 into master Feb 12, 2026
7 of 9 checks passed
@niveathika niveathika deleted the post-processing branch February 12, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Skip GraalVM Check This will skip the GraalVM compatibility check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants