Skip to content

GH-9988: Add FileExistsMode expression support #10019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

anthologia
Copy link
Contributor

Fixes #9988

In AbstractRemoteFileOutboundGateway:

  • Add fileExistsModeExpression field and setter methods
  • Use resolveFileExistsMode in put and get operations
  • Remove logic that disabled temporary filenames when setting APPEND mode

In RemoteFileTemplate:

  • Remove exception validation when APPEND mode is used with temporary filenames
  • Modify logic to skip applying temporaryFileSuffix in APPEND mode

Fixes: spring-projects#9988
Issue link: spring-projects#9988

This change allows dynamic determination of FileExistsMode using SpEL expressions,
making the component more flexible when handling file existence conflicts.

* Add fileExistsModeExpression field and setter methods
* Use resolveFileExistsMode in put and get operations
* Add changes to the docs

Signed-off-by: Jooyoung Pyoung <[email protected]>
Improve runtime behavior by ignoring temporary filename settings when file exists mode is APPEND. Now, in FileExistsMode.APPEND mode, content is always appended directly to the original file regardless of useTemporaryFileName setting.

In RemoteFileTemplate:
- Remove exception validation when APPEND mode is used with temporary filenames
- Modify logic to skip applying temporaryFileSuffix in APPEND mode

In AbstractRemoteFileOutboundGateway:
- Remove logic that disabled temporary filenames when setting APPEND mode

Signed-off-by: Jooyoung Pyoung <[email protected]>
Signed-off-by: Jooyoung Pyoung <[email protected]>
Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

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

Please, consider to describe this new feature in the: https://docs.spring.io/spring-integration/reference/ftp/rft.html#page-title. (src/reference/antora/modules/ROOT/pages/sftp/rft.adoc file)

And also mention new feature in the whats-new.adoc.

And as a bonus we also can consider to expose this new option on the RemoteFileOutboundGatewaySpec for Java DSL.

Thanks

String tempFilePath = tempRemoteFilePath + (this.useTemporaryFileName ? this.temporaryFileSuffix : "");
String tempFilePath = tempRemoteFilePath;
if (!FileExistsMode.APPEND.equals(mode)) {
tempFilePath += this.useTemporaryFileName ? this.temporaryFileSuffix : "";
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to append empty string to the end if not this.useTemporaryFileName?
How about this instead:

if (!FileExistsMode.APPEND.equals(mode) &&  this.useTemporaryFileName) {
			tempFilePath += this.temporaryFileSuffix;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, there's no need to append an empty string. Thanks!

private FileExistsMode resolveFileExistsMode(Message<?> message) {
if (this.fileExistsModeExpression != null) {
EvaluationContext evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
evaluationContext.setVariable("fileExistsMode", this.fileExistsMode);
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this variable in the expression?
In most cases no one is going to care about whatever is there by default in the gateway setting.
And with that we could use a global EvaluationContext created only once in the doInit().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed the unnecessary variable and implemented a global EvaluationContext that's created once in doInit().

Additionally, I was thinking that in the long term, it might be beneficial to move the Expression evaluation logic to the IntegrationObjectSupport class. This could standardize expression evaluation across Spring Integration and reduce duplicate code in each component. Would this be something worth considering?

if (this.fileExistsModeExpression != null) {
EvaluationContext evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
evaluationContext.setVariable("fileExistsMode", this.fileExistsMode);
return this.fileExistsModeExpression.getValue(evaluationContext, message, FileExistsMode.class);
Copy link
Member

Choose a reason for hiding this comment

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

I also think we should be able to allow to evaluate expression to just string and then try to resolve enum manually.
This way our contract for this expression would be much friendly for end-users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I should have considered user-friendliness more! The expression can now return either a FileExistsMode enum value or a String representation (case-insensitive). Would it be better not to support case-insensitive matching?

@@ -303,8 +304,6 @@ public String send(Message<?> message, String subDirectory, FileExistsMode... mo

private String send(Message<?> message, String subDirectory, FileExistsMode mode) {
Assert.notNull(this.directoryExpressionProcessor, "'remoteDirectoryExpression' is required");
Assert.isTrue(!FileExistsMode.APPEND.equals(mode) || !this.useTemporaryFileName,
"Cannot append when using a temporary file name");
Copy link
Member

Choose a reason for hiding this comment

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

I suggest to make a warning message in the doInit() when this condition is true.
We still gong to ignore, but users would know that it does not make sense to use both useTemporaryFileName and FileExistsMode.APPEND

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a warning message in doInit() when APPEND mode is configured with useTemporaryFileName is true. Thanks for the good suggestion!

- Optimize EvaluationContext usage by creating it once in doInit()
- Enhance expression evaluation to support String representation of FileExistsMode
- Optimize temporary filename handling logic in RemoteFileTemplate
- Add warning message for incompatible APPEND mode with temporary filenames
- Rename method to setFileExistsModeExpressionString for consistency
- Update Java DSL support in RemoteFileOutboundGatewaySpec
- Update reference documentation and release notes

Signed-off-by: Jooyoung Pyoung <[email protected]>
@anthologia
Copy link
Contributor Author

I've implemented all these suggestions:

  • Added documentation to the rft.adoc and whats-new.adoc
  • Implemented Java DSL support in RemoteFileOutboundGatewaySpec with both Expression and String parameter variants

Thank you for the helpful guidance! Please let me know if any further adjustments are needed.

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.

Support fileExistModeExpression() in Ftp.outboundGateway
2 participants