This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new a5385bd3648 CAMEL-22440 - Camel-AWS2-S3 Streaming upload: Add a 
timestamp naming strategy (#19309)
a5385bd3648 is described below

commit a5385bd364868cd162add8144d4619024ef274cb
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Sep 24 11:57:27 2025 +0200

    CAMEL-22440 - Camel-AWS2-S3 Streaming upload: Add a timestamp naming 
strategy (#19309)
    
    * CAMEL-22440 - Camel-AWS2-S3 Streaming upload: Add a timestamp naming 
strategy
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    * CAMEL-22440 - Camel-AWS2-S3 Streaming upload: Add a timestamp naming 
strategy
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    * Regen
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    ---------
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../apache/camel/catalog/components/aws2-s3.json   |   4 +-
 .../apache/camel/component/aws2/s3/aws2-s3.json    |   4 +-
 .../aws2/s3/stream/AWS2S3StreamUploadProducer.java |  23 +++-
 .../aws2/s3/stream/AWSS3NamingStrategyEnum.java    |   3 +-
 .../S3StreamUploadTimestampNamingStrategyIT.java   | 117 +++++++++++++++++++
 .../S3StreamUploadTimestampTimeoutIT.java          | 117 +++++++++++++++++++
 .../aws2/s3/stream/AWSS3NamingStrategyTest.java    | 129 +++++++++++++++++++++
 7 files changed, 390 insertions(+), 7 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-s3.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-s3.json
index 169e659c57e..e2945ec0b23 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-s3.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-s3.json
@@ -57,7 +57,7 @@
     "keyName": { "index": 30, "kind": "property", "displayName": "Key Name", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Setting the key name for an element in the 
bucket through endpoint parameter" },
     "lazyStartProducer": { "index": 31, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fai [...]
     "multiPartUpload": { "index": 32, "kind": "property", "displayName": 
"Multi Part Upload", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.aws2.s3.AWS2S3Configuration", 
"configurationField": "configuration", "description": "If it is true, camel 
will upload the file with multipart format. The part size  [...]
-    "namingStrategy": { "index": 33, "kind": "property", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration" [...]
+    "namingStrategy": { "index": 33, "kind": "property", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random", "timestamp" ], "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"c [...]
     "operation": { "index": 34, "kind": "property", "displayName": 
"Operation", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.AWS2S3Operations", "enum": [ "copyObject", 
"listObjects", "deleteObject", "deleteBucket", "listBuckets", "getObject", 
"getObjectRange", "createDownloadLink", "headBucket", "headObject" ], 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.c [...]
     "partSize": { "index": 35, "kind": "property", "displayName": "Part Size", 
"group": "producer", "label": "producer", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 26214400, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Set up the partSize which is used in multipart 
upload, the default size is 25 MB. The minimum  [...]
     "restartingPolicy": { "index": 36, "kind": "property", "displayName": 
"Restarting Policy", "group": "producer", "label": "producer", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3RestartingPolicyEnum", "enum": 
[ "override", "lastPart" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "override", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuratio [...]
@@ -166,7 +166,7 @@
     "deleteAfterWrite": { "index": 36, "kind": "parameter", "displayName": 
"Delete After Write", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.aws2.s3.AWS2S3Configuration", 
"configurationField": "configuration", "description": "Delete file object after 
the S3 file has been uploaded" },
     "keyName": { "index": 37, "kind": "parameter", "displayName": "Key Name", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Setting the key name for an element in the 
bucket through endpoint parameter" },
     "multiPartUpload": { "index": 38, "kind": "parameter", "displayName": 
"Multi Part Upload", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.aws2.s3.AWS2S3Configuration", 
"configurationField": "configuration", "description": "If it is true, camel 
will upload the file with multipart format. The part size [...]
-    "namingStrategy": { "index": 39, "kind": "parameter", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration [...]
+    "namingStrategy": { "index": 39, "kind": "parameter", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random", "timestamp" ], "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
" [...]
     "operation": { "index": 40, "kind": "parameter", "displayName": 
"Operation", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.AWS2S3Operations", "enum": [ "copyObject", 
"listObjects", "deleteObject", "deleteBucket", "listBuckets", "getObject", 
"getObjectRange", "createDownloadLink", "headBucket", "headObject" ], 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel. [...]
     "partSize": { "index": 41, "kind": "parameter", "displayName": "Part 
Size", "group": "producer", "label": "producer", "required": false, "type": 
"integer", "javaType": "long", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 26214400, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Set up the partSize which is used in multipart 
upload, the default size is 25 MB. The minimum [...]
     "restartingPolicy": { "index": 42, "kind": "parameter", "displayName": 
"Restarting Policy", "group": "producer", "label": "producer", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3RestartingPolicyEnum", "enum": 
[ "override", "lastPart" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "override", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configurati [...]
diff --git 
a/components/camel-aws/camel-aws2-s3/src/generated/resources/META-INF/org/apache/camel/component/aws2/s3/aws2-s3.json
 
b/components/camel-aws/camel-aws2-s3/src/generated/resources/META-INF/org/apache/camel/component/aws2/s3/aws2-s3.json
index 169e659c57e..e2945ec0b23 100644
--- 
a/components/camel-aws/camel-aws2-s3/src/generated/resources/META-INF/org/apache/camel/component/aws2/s3/aws2-s3.json
+++ 
b/components/camel-aws/camel-aws2-s3/src/generated/resources/META-INF/org/apache/camel/component/aws2/s3/aws2-s3.json
@@ -57,7 +57,7 @@
     "keyName": { "index": 30, "kind": "property", "displayName": "Key Name", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Setting the key name for an element in the 
bucket through endpoint parameter" },
     "lazyStartProducer": { "index": 31, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fai [...]
     "multiPartUpload": { "index": 32, "kind": "property", "displayName": 
"Multi Part Upload", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.aws2.s3.AWS2S3Configuration", 
"configurationField": "configuration", "description": "If it is true, camel 
will upload the file with multipart format. The part size  [...]
-    "namingStrategy": { "index": 33, "kind": "property", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration" [...]
+    "namingStrategy": { "index": 33, "kind": "property", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random", "timestamp" ], "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"c [...]
     "operation": { "index": 34, "kind": "property", "displayName": 
"Operation", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.AWS2S3Operations", "enum": [ "copyObject", 
"listObjects", "deleteObject", "deleteBucket", "listBuckets", "getObject", 
"getObjectRange", "createDownloadLink", "headBucket", "headObject" ], 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.c [...]
     "partSize": { "index": 35, "kind": "property", "displayName": "Part Size", 
"group": "producer", "label": "producer", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 26214400, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Set up the partSize which is used in multipart 
upload, the default size is 25 MB. The minimum  [...]
     "restartingPolicy": { "index": 36, "kind": "property", "displayName": 
"Restarting Policy", "group": "producer", "label": "producer", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3RestartingPolicyEnum", "enum": 
[ "override", "lastPart" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "override", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuratio [...]
@@ -166,7 +166,7 @@
     "deleteAfterWrite": { "index": 36, "kind": "parameter", "displayName": 
"Delete After Write", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.aws2.s3.AWS2S3Configuration", 
"configurationField": "configuration", "description": "Delete file object after 
the S3 file has been uploaded" },
     "keyName": { "index": 37, "kind": "parameter", "displayName": "Key Name", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Setting the key name for an element in the 
bucket through endpoint parameter" },
     "multiPartUpload": { "index": 38, "kind": "parameter", "displayName": 
"Multi Part Upload", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.aws2.s3.AWS2S3Configuration", 
"configurationField": "configuration", "description": "If it is true, camel 
will upload the file with multipart format. The part size [...]
-    "namingStrategy": { "index": 39, "kind": "parameter", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration [...]
+    "namingStrategy": { "index": 39, "kind": "parameter", "displayName": 
"Naming Strategy", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3NamingStrategyEnum", "enum": [ 
"progressive", "random", "timestamp" ], "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "progressive", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
" [...]
     "operation": { "index": 40, "kind": "parameter", "displayName": 
"Operation", "group": "producer", "label": "producer", "required": false, 
"type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.AWS2S3Operations", "enum": [ "copyObject", 
"listObjects", "deleteObject", "deleteBucket", "listBuckets", "getObject", 
"getObjectRange", "createDownloadLink", "headBucket", "headObject" ], 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel. [...]
     "partSize": { "index": 41, "kind": "parameter", "displayName": "Part 
Size", "group": "producer", "label": "producer", "required": false, "type": 
"integer", "javaType": "long", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 26214400, "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configuration", "description": "Set up the partSize which is used in multipart 
upload, the default size is 25 MB. The minimum [...]
     "restartingPolicy": { "index": 42, "kind": "parameter", "displayName": 
"Restarting Policy", "group": "producer", "label": "producer", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.aws2.s3.stream.AWSS3RestartingPolicyEnum", "enum": 
[ "override", "lastPart" ], "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": "override", "configurationClass": 
"org.apache.camel.component.aws2.s3.AWS2S3Configuration", "configurationField": 
"configurati [...]
diff --git 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWS2S3StreamUploadProducer.java
 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWS2S3StreamUploadProducer.java
index 0834341cf4a..93baccd8248 100644
--- 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWS2S3StreamUploadProducer.java
+++ 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWS2S3StreamUploadProducer.java
@@ -193,8 +193,11 @@ public class AWS2S3StreamUploadProducer extends 
DefaultProducer {
             if (state.index == 1 && 
getConfiguration().getNamingStrategy().equals(AWSS3NamingStrategyEnum.random)) {
                 state.id = UUID.randomUUID();
             }
+            if (state.index == 1 && 
getConfiguration().getNamingStrategy().equals(AWSS3NamingStrategyEnum.timestamp))
 {
+                state.timestamp = System.currentTimeMillis();
+            }
             state.dynamicKeyName = fileNameToUpload(fileName, 
getConfiguration().getNamingStrategy(), extension,
-                    state.part, state.id);
+                    state.part, state.id, state.timestamp);
             CreateMultipartUploadRequest.Builder createMultipartUploadRequest
                     = 
CreateMultipartUploadRequest.builder().bucket(getConfiguration().getBucketName())
                             
.key(state.dynamicKeyName).checksumAlgorithm(algorithm);
@@ -318,7 +321,7 @@ public class AWS2S3StreamUploadProducer extends 
DefaultProducer {
     }
 
     private String fileNameToUpload(
-            String fileName, AWSS3NamingStrategyEnum strategy, String ext, int 
part, UUID id) {
+            String fileName, AWSS3NamingStrategyEnum strategy, String ext, int 
part, UUID id, long timestamp) {
         String dynamicKeyName;
         switch (strategy) {
             case progressive:
@@ -351,6 +354,21 @@ public class AWS2S3StreamUploadProducer extends 
DefaultProducer {
                     }
                 }
                 break;
+            case timestamp:
+                if (part > 0) {
+                    if (ObjectHelper.isNotEmpty(ext)) {
+                        dynamicKeyName = fileName + "-" + timestamp + ext;
+                    } else {
+                        dynamicKeyName = fileName + "-" + timestamp;
+                    }
+                } else {
+                    if (ObjectHelper.isNotEmpty(ext)) {
+                        dynamicKeyName = fileName + ext;
+                    } else {
+                        dynamicKeyName = fileName;
+                    }
+                }
+                break;
             default:
                 throw new IllegalArgumentException("Unsupported operation");
         }
@@ -414,6 +432,7 @@ public class AWS2S3StreamUploadProducer extends 
DefaultProducer {
         ByteArrayOutputStream buffer;
         String dynamicKeyName;
         UUID id;
+        long timestamp;
         CreateMultipartUploadResponse initResponse;
 
         UploadState() {
diff --git 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyEnum.java
 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyEnum.java
index bd8b94beaf5..3b9c3f6b431 100644
--- 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyEnum.java
+++ 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyEnum.java
@@ -18,5 +18,6 @@ package org.apache.camel.component.aws2.s3.stream;
 
 public enum AWSS3NamingStrategyEnum {
     progressive,
-    random
+    random,
+    timestamp
 }
diff --git 
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3StreamUploadTimestampNamingStrategyIT.java
 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3StreamUploadTimestampNamingStrategyIT.java
new file mode 100644
index 00000000000..914fb20d058
--- /dev/null
+++ 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3StreamUploadTimestampNamingStrategyIT.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.aws2.s3.integration;
+
+import java.util.List;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.aws2.s3.AWS2S3Constants;
+import org.apache.camel.component.aws2.s3.AWS2S3Operations;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.services.s3.model.S3Object;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class S3StreamUploadTimestampNamingStrategyIT extends Aws2S3Base {
+
+    @EndpointInject
+    private ProducerTemplate template;
+
+    @EndpointInject("mock:result")
+    private MockEndpoint result;
+
+    @Test
+    public void sendInWithTimestampNamingStrategy() throws Exception {
+        result.expectedMessageCount(1000);
+
+        long beforeUpload = System.currentTimeMillis();
+
+        for (int i = 0; i < 1000; i++) {
+            template.sendBody("direct:stream1", "TestData\n");
+        }
+
+        long afterUpload = System.currentTimeMillis();
+
+        MockEndpoint.assertIsSatisfied(context);
+
+        Exchange ex = template.request("direct:listObjects", new Processor() {
+
+            @Override
+            public void process(Exchange exchange) {
+                exchange.getIn().setHeader(AWS2S3Constants.S3_OPERATION, 
AWS2S3Operations.listObjects);
+            }
+        });
+
+        List<S3Object> resp = ex.getMessage().getBody(List.class);
+        assertEquals(40, resp.size());
+
+        // Verify that uploaded files use timestamp naming strategy
+        // Files should have names like: fileTest.txt, fileTest-<timestamp>.txt
+        boolean foundBaseFile = false;
+        boolean foundTimestampFile = false;
+
+        for (S3Object s3Object : resp) {
+            String key = s3Object.key();
+
+            if ("fileTest.txt".equals(key)) {
+                foundBaseFile = true;
+            } else if (key.startsWith("fileTest-") && key.endsWith(".txt")) {
+                foundTimestampFile = true;
+
+                // Extract timestamp from filename and verify it's within 
expected range
+                String timestampStr = key.substring("fileTest-".length(), 
key.length() - ".txt".length());
+                try {
+                    long timestamp = Long.parseLong(timestampStr);
+                    assertTrue(timestamp >= beforeUpload && timestamp <= 
afterUpload,
+                            "Timestamp " + timestamp + " should be between " + 
beforeUpload + " and " + afterUpload);
+                } catch (NumberFormatException e) {
+                    // This shouldn't happen with timestamp naming strategy
+                    throw new AssertionError("Expected numeric timestamp in 
filename: " + key, e);
+                }
+            }
+        }
+
+        assertTrue(foundBaseFile, "Should find base file (fileTest.txt)");
+        assertTrue(foundTimestampFile, "Should find timestamp-named files 
(fileTest-<timestamp>.txt)");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                String awsEndpoint1
+                        = String.format(
+                                
"aws2-s3://%s?autoCreateBucket=true&streamingUploadMode=true&keyName=fileTest.txt&batchMessageNumber=25&namingStrategy=timestamp",
+                                name.get());
+
+                from("direct:stream1").to(awsEndpoint1).to("mock:result");
+
+                String awsEndpoint = 
String.format("aws2-s3://%s?autoCreateBucket=true",
+                        name.get());
+
+                from("direct:listObjects").to(awsEndpoint);
+            }
+        };
+    }
+}
diff --git 
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3StreamUploadTimestampTimeoutIT.java
 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3StreamUploadTimestampTimeoutIT.java
new file mode 100644
index 00000000000..4776391db20
--- /dev/null
+++ 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3StreamUploadTimestampTimeoutIT.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.aws2.s3.integration;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.aws2.s3.AWS2S3Constants;
+import org.apache.camel.component.aws2.s3.AWS2S3Operations;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.services.s3.model.S3Object;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class S3StreamUploadTimestampTimeoutIT extends Aws2S3Base {
+
+    @EndpointInject
+    private ProducerTemplate template;
+
+    @EndpointInject("mock:result")
+    private MockEndpoint result;
+
+    @Test
+    public void sendInWithTimestampAndTimeout() throws Exception {
+
+        for (int i = 1; i <= 2; i++) {
+            int count = i * 23;
+
+            result.expectedMessageCount(count);
+
+            long beforeUpload = System.currentTimeMillis();
+
+            for (int j = 0; j < 23; j++) {
+                template.sendBody("direct:stream1", "TestData\n");
+            }
+
+            long afterUpload = System.currentTimeMillis();
+
+            Awaitility.await().atMost(11, TimeUnit.SECONDS)
+                    .untilAsserted(() -> 
MockEndpoint.assertIsSatisfied(context));
+
+            Awaitility.await().atMost(11, TimeUnit.SECONDS)
+                    .untilAsserted(() -> {
+                        Exchange ex = template.request("direct:listObjects", 
this::process);
+
+                        List<S3Object> resp = 
ex.getMessage().getBody(List.class);
+                        assertEquals(1, resp.size());
+
+                        // Verify the uploaded file uses timestamp naming 
strategy
+                        S3Object s3Object = resp.get(0);
+                        String key = s3Object.key();
+
+                        // The file should either be the base name or have a 
timestamp suffix
+                        if ("fileTest.txt".equals(key)) {
+                            // This is fine - it's the base file
+                        } else if (key.startsWith("fileTest-") && 
key.endsWith(".txt")) {
+                            // Extract and validate timestamp
+                            String timestampStr = 
key.substring("fileTest-".length(), key.length() - ".txt".length());
+                            try {
+                                long timestamp = Long.parseLong(timestampStr);
+                                assertTrue(timestamp >= beforeUpload && 
timestamp <= afterUpload + 11000, // Allow extra time for timeout
+                                        "Timestamp " + timestamp + " should be 
within expected range");
+                            } catch (NumberFormatException e) {
+                                throw new AssertionError("Expected numeric 
timestamp in filename: " + key, e);
+                            }
+                        } else {
+                            throw new AssertionError("Unexpected filename 
format: " + key);
+                        }
+                    });
+        }
+    }
+
+    private void process(Exchange exchange) {
+        exchange.getIn().setHeader(AWS2S3Constants.S3_OPERATION, 
AWS2S3Operations.listObjects);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                String awsEndpoint1
+                        = String.format(
+                                
"aws2-s3://%s?autoCreateBucket=true&streamingUploadMode=true&keyName=fileTest.txt&batchMessageNumber=25&namingStrategy=timestamp&streamingUploadTimeout=10000",
+                                name.get());
+
+                from("direct:stream1").to(awsEndpoint1).to("mock:result");
+
+                String awsEndpoint = 
String.format("aws2-s3://%s?autoCreateBucket=true",
+                        name.get());
+
+                from("direct:listObjects").to(awsEndpoint);
+            }
+        };
+    }
+}
diff --git 
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyTest.java
 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyTest.java
new file mode 100644
index 00000000000..4c8912a90df
--- /dev/null
+++ 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/stream/AWSS3NamingStrategyTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.aws2.s3.stream;
+
+import java.lang.reflect.Method;
+import java.util.UUID;
+
+import org.apache.camel.component.aws2.s3.AWS2S3Configuration;
+import org.apache.camel.component.aws2.s3.AWS2S3Endpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+
+public class AWSS3NamingStrategyTest extends CamelTestSupport {
+
+    @Test
+    public void testProgressiveNamingStrategy() throws Exception {
+        AWS2S3StreamUploadProducer producer = createProducer();
+
+        Method method = AWS2S3StreamUploadProducer.class.getDeclaredMethod(
+                "fileNameToUpload", String.class, 
AWSS3NamingStrategyEnum.class, String.class, int.class, UUID.class,
+                long.class);
+        method.setAccessible(true);
+
+        String result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.progressive, ".txt", 0, null, 0L);
+        assertEquals("testFile.txt", result);
+
+        result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.progressive, ".txt", 1, null, 0L);
+        assertEquals("testFile-1.txt", result);
+
+        result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.progressive, null, 2, null, 0L);
+        assertEquals("testFile-2", result);
+    }
+
+    @Test
+    public void testRandomNamingStrategy() throws Exception {
+        AWS2S3StreamUploadProducer producer = createProducer();
+
+        Method method = AWS2S3StreamUploadProducer.class.getDeclaredMethod(
+                "fileNameToUpload", String.class, 
AWSS3NamingStrategyEnum.class, String.class, int.class, UUID.class,
+                long.class);
+        method.setAccessible(true);
+
+        UUID testUuid = 
UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
+
+        String result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.random, ".txt", 0, testUuid, 0L);
+        assertEquals("testFile.txt", result);
+
+        result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.random, ".txt", 1, testUuid, 0L);
+        assertEquals("testFile-123e4567-e89b-12d3-a456-426614174000.txt", 
result);
+
+        result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.random, null, 1, testUuid, 0L);
+        assertEquals("testFile-123e4567-e89b-12d3-a456-426614174000", result);
+    }
+
+    @Test
+    public void testTimestampNamingStrategy() throws Exception {
+        AWS2S3StreamUploadProducer producer = createProducer();
+
+        Method method = AWS2S3StreamUploadProducer.class.getDeclaredMethod(
+                "fileNameToUpload", String.class, 
AWSS3NamingStrategyEnum.class, String.class, int.class, UUID.class,
+                long.class);
+        method.setAccessible(true);
+
+        long testTimestamp = 1632468273000L; // September 24, 2021 1:04:33 PM 
GMT
+
+        String result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.timestamp, ".txt", 0, null,
+                testTimestamp);
+        assertEquals("testFile.txt", result);
+
+        result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.timestamp, ".txt", 1, null,
+                testTimestamp);
+        assertEquals("testFile-1632468273000.txt", result);
+
+        result = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.timestamp, null, 1, null, testTimestamp);
+        assertEquals("testFile-1632468273000", result);
+    }
+
+    @Test
+    public void testTimestampNamingStrategyWithCurrentTime() throws Exception {
+        AWS2S3StreamUploadProducer producer = createProducer();
+
+        Method method = AWS2S3StreamUploadProducer.class.getDeclaredMethod(
+                "fileNameToUpload", String.class, 
AWSS3NamingStrategyEnum.class, String.class, int.class, UUID.class,
+                long.class);
+        method.setAccessible(true);
+
+        long currentTime = System.currentTimeMillis();
+
+        String result
+                = (String) method.invoke(producer, "testFile", 
AWSS3NamingStrategyEnum.timestamp, ".txt", 1, null, currentTime);
+
+        assertTrue(result.startsWith("testFile-"));
+        assertTrue(result.endsWith(".txt"));
+
+        String timestampPart = result.substring("testFile-".length(), 
result.length() - ".txt".length());
+        long parsedTimestamp = Long.parseLong(timestampPart);
+
+        // Allow for small time difference during test execution
+        assertTrue(Math.abs(parsedTimestamp - currentTime) < 1000,
+                "Timestamp should be within 1 second of current time");
+    }
+
+    private AWS2S3StreamUploadProducer createProducer() {
+        AWS2S3Endpoint endpoint = Mockito.mock(AWS2S3Endpoint.class);
+        AWS2S3Configuration configuration = new AWS2S3Configuration();
+        when(endpoint.getConfiguration()).thenReturn(configuration);
+
+        return new AWS2S3StreamUploadProducer(endpoint);
+    }
+}


Reply via email to