This is an automated email from the ASF dual-hosted git repository.
davsclaus 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 d77f4acb888b CAMEL-25898: camel-ibm-cos - the producer stores an
object in the bucket of its endpoint, not in the bucket the consumer took it
from (#28426)
d77f4acb888b is described below
commit d77f4acb888b3e1f1a32311e59759d4993d6edd7
Author: allthingssecurity <[email protected]>
AuthorDate: Sun Oct 25 13:00:36 2026 +0530
CAMEL-25898: camel-ibm-cos - the producer stores an object in the bucket of
its endpoint, not in the bucket the consumer took it from (#28426)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../apache/camel/catalog/components/ibm-cos.json | 5 +-
.../camel/catalog/docs/ibm-cos-component.adoc | 6 +-
.../apache/camel/component/ibm/cos/ibm-cos.json | 5 +-
.../src/main/docs/ibm-cos-component.adoc | 6 +-
.../camel/component/ibm/cos/IBMCOSConstants.java | 6 +-
.../camel/component/ibm/cos/IBMCOSProducer.java | 4 +-
.../ibm/cos/IBMCOSProducerBucketHeaderTest.java | 125 +++++++++++++++++++++
.../IBMCOSProducerAdditionalOperationsIT.java | 4 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 11 ++
.../endpoint/dsl/IBMCOSEndpointBuilderFactory.java | 19 +++-
10 files changed, 174 insertions(+), 17 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-cos.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-cos.json
index d9a7f9333df9..8754bb8f56db 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-cos.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-cos.json
@@ -55,7 +55,7 @@
"serviceInstanceId": { "index": 28, "kind": "property", "displayName":
"Service Instance Id", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.ibm.cos.IBMCOSConfiguration", "configurationField":
"configuration", "description": "IBM COS Service Instance ID (CRN)" }
},
"headers": {
- "CamelIBMCOSBucketName": { "index": 0, "kind": "header", "displayName":
"", "group": "common", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The bucket Name which this object will be stored or
which will be used for the current operation", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_NAME" },
+ "CamelIBMCOSBucketName": { "index": 0, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The name of the bucket the consumed object is
contained in", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_NAME" },
"CamelIBMCOSBucketDestinationName": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "producer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The bucket Destination Name which will
be used for the current operation", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_DESTINATION_NAME" },
"CamelIBMCOSCacheControl": { "index": 2, "kind": "header", "displayName":
"", "group": "common", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The optional Cache-Control HTTP header which allows the
user to specify caching behavior", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#CACHE_CONTROL" },
"CamelIBMCOSContentDisposition": { "index": 3, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The optional Content-Disposition HTTP
header, which specifies presentational information such as the recommended
filename", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#CONTENT_DISPOSITION" },
@@ -75,7 +75,8 @@
"CamelIBMCOSMetadata": { "index": 17, "kind": "header", "displayName": "",
"group": "common", "label": "", "required": false, "javaType":
"java.util.Map<String, String>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "A map of metadata to be
stored with the object in IBM COS", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#METADATA" },
"CamelIBMCOSRangeStart": { "index": 18, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"Long", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The range start position for partial object
retrieval", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#RANGE_START" },
"CamelIBMCOSRangeEnd": { "index": 19, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"Long", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The range end position for partial object
retrieval", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#RANGE_END" },
- "CamelIBMCOSBucketExists": { "index": 20, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"Boolean", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "Whether the bucket exists or not",
"constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_EXISTS" }
+ "CamelIBMCOSBucketExists": { "index": 20, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"Boolean", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "Whether the bucket exists or not",
"constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_EXISTS" },
+ "CamelIBMCOSOverrideBucketName": { "index": 21, "kind": "header",
"displayName": "", "group": "producer", "label": "producer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The bucket name to use instead of the
bucket of the endpoint, in which this object will be stored or which will be
used for the current operation", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#OVERRID [...]
},
"bodyTypes": {
"consumerOut": { "carriers": [ "stream" ], "byOption": [ { "option":
"includeBody", "value": "false", "carriers": [ "null" ] } ], "description":
"The content of the object as a stream, one message per object, with its key
and metadata in headers. No body with includeBody=false" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-cos-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-cos-component.adoc
index 0d35dcd40f40..2907001b6900 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-cos-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ibm-cos-component.adoc
@@ -509,7 +509,7 @@ Java::
[source,java]
----
from("direct:start")
- .setHeader("CamelIBMCOSBucketName", constant("newBucket"))
+ .setHeader("CamelIBMCOSOverrideBucketName", constant("newBucket"))
.to("ibm-cos://mycamelbucket?cosClient=#cosClient&operation=createBucket")
.to("mock:result");
----
@@ -520,7 +520,7 @@ XML::
----
<route>
<from uri="direct:start"/>
- <setHeader name="CamelIBMCOSBucketName">
+ <setHeader name="CamelIBMCOSOverrideBucketName">
<constant>newBucket</constant>
</setHeader>
<to
uri="ibm-cos://mycamelbucket?cosClient=#cosClient&operation=createBucket"/>
@@ -537,7 +537,7 @@ YAML::
uri: direct:start
steps:
- setHeader:
- name: CamelIBMCOSBucketName
+ name: CamelIBMCOSOverrideBucketName
constant: newBucket
- to:
uri: ibm-cos://mycamelbucket
diff --git
a/components/camel-ibm/camel-ibm-cos/src/generated/resources/META-INF/org/apache/camel/component/ibm/cos/ibm-cos.json
b/components/camel-ibm/camel-ibm-cos/src/generated/resources/META-INF/org/apache/camel/component/ibm/cos/ibm-cos.json
index d9a7f9333df9..8754bb8f56db 100644
---
a/components/camel-ibm/camel-ibm-cos/src/generated/resources/META-INF/org/apache/camel/component/ibm/cos/ibm-cos.json
+++
b/components/camel-ibm/camel-ibm-cos/src/generated/resources/META-INF/org/apache/camel/component/ibm/cos/ibm-cos.json
@@ -55,7 +55,7 @@
"serviceInstanceId": { "index": 28, "kind": "property", "displayName":
"Service Instance Id", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.ibm.cos.IBMCOSConfiguration", "configurationField":
"configuration", "description": "IBM COS Service Instance ID (CRN)" }
},
"headers": {
- "CamelIBMCOSBucketName": { "index": 0, "kind": "header", "displayName":
"", "group": "common", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The bucket Name which this object will be stored or
which will be used for the current operation", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_NAME" },
+ "CamelIBMCOSBucketName": { "index": 0, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The name of the bucket the consumed object is
contained in", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_NAME" },
"CamelIBMCOSBucketDestinationName": { "index": 1, "kind": "header",
"displayName": "", "group": "producer", "label": "producer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The bucket Destination Name which will
be used for the current operation", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_DESTINATION_NAME" },
"CamelIBMCOSCacheControl": { "index": 2, "kind": "header", "displayName":
"", "group": "common", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The optional Cache-Control HTTP header which allows the
user to specify caching behavior", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#CACHE_CONTROL" },
"CamelIBMCOSContentDisposition": { "index": 3, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The optional Content-Disposition HTTP
header, which specifies presentational information such as the recommended
filename", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#CONTENT_DISPOSITION" },
@@ -75,7 +75,8 @@
"CamelIBMCOSMetadata": { "index": 17, "kind": "header", "displayName": "",
"group": "common", "label": "", "required": false, "javaType":
"java.util.Map<String, String>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "A map of metadata to be
stored with the object in IBM COS", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#METADATA" },
"CamelIBMCOSRangeStart": { "index": 18, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"Long", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The range start position for partial object
retrieval", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#RANGE_START" },
"CamelIBMCOSRangeEnd": { "index": 19, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"Long", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The range end position for partial object
retrieval", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#RANGE_END" },
- "CamelIBMCOSBucketExists": { "index": 20, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"Boolean", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "Whether the bucket exists or not",
"constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_EXISTS" }
+ "CamelIBMCOSBucketExists": { "index": 20, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"Boolean", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "Whether the bucket exists or not",
"constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#BUCKET_EXISTS" },
+ "CamelIBMCOSOverrideBucketName": { "index": 21, "kind": "header",
"displayName": "", "group": "producer", "label": "producer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The bucket name to use instead of the
bucket of the endpoint, in which this object will be stored or which will be
used for the current operation", "constantName":
"org.apache.camel.component.ibm.cos.IBMCOSConstants#OVERRID [...]
},
"bodyTypes": {
"consumerOut": { "carriers": [ "stream" ], "byOption": [ { "option":
"includeBody", "value": "false", "carriers": [ "null" ] } ], "description":
"The content of the object as a stream, one message per object, with its key
and metadata in headers. No body with includeBody=false" },
diff --git
a/components/camel-ibm/camel-ibm-cos/src/main/docs/ibm-cos-component.adoc
b/components/camel-ibm/camel-ibm-cos/src/main/docs/ibm-cos-component.adoc
index 0d35dcd40f40..2907001b6900 100644
--- a/components/camel-ibm/camel-ibm-cos/src/main/docs/ibm-cos-component.adoc
+++ b/components/camel-ibm/camel-ibm-cos/src/main/docs/ibm-cos-component.adoc
@@ -509,7 +509,7 @@ Java::
[source,java]
----
from("direct:start")
- .setHeader("CamelIBMCOSBucketName", constant("newBucket"))
+ .setHeader("CamelIBMCOSOverrideBucketName", constant("newBucket"))
.to("ibm-cos://mycamelbucket?cosClient=#cosClient&operation=createBucket")
.to("mock:result");
----
@@ -520,7 +520,7 @@ XML::
----
<route>
<from uri="direct:start"/>
- <setHeader name="CamelIBMCOSBucketName">
+ <setHeader name="CamelIBMCOSOverrideBucketName">
<constant>newBucket</constant>
</setHeader>
<to
uri="ibm-cos://mycamelbucket?cosClient=#cosClient&operation=createBucket"/>
@@ -537,7 +537,7 @@ YAML::
uri: direct:start
steps:
- setHeader:
- name: CamelIBMCOSBucketName
+ name: CamelIBMCOSOverrideBucketName
constant: newBucket
- to:
uri: ibm-cos://mycamelbucket
diff --git
a/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSConstants.java
b/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSConstants.java
index b3b396745ef1..e8463c16daf2 100644
---
a/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSConstants.java
+++
b/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSConstants.java
@@ -23,7 +23,7 @@ import org.apache.camel.spi.Metadata;
*/
public interface IBMCOSConstants {
- @Metadata(description = "The bucket Name which this object will be stored
or which will be used for the current operation",
+ @Metadata(label = "consumer", description = "The name of the bucket the
consumed object is contained in",
javaType = "String")
String BUCKET_NAME = "CamelIBMCOSBucketName";
@Metadata(label = "producer",
@@ -85,4 +85,8 @@ public interface IBMCOSConstants {
@Metadata(label = "producer", description = "Whether the bucket exists or
not",
javaType = "Boolean")
String BUCKET_EXISTS = "CamelIBMCOSBucketExists";
+ @Metadata(label = "producer",
+ description = "The bucket name to use instead of the bucket of
the endpoint, in which this object will be stored or which will be used for the
current operation",
+ javaType = "String")
+ String OVERRIDE_BUCKET_NAME = "CamelIBMCOSOverrideBucketName";
}
diff --git
a/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSProducer.java
b/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSProducer.java
index fed2e46fdb0c..654037a2f0f5 100644
---
a/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSProducer.java
+++
b/components/camel-ibm/camel-ibm-cos/src/main/java/org/apache/camel/component/ibm/cos/IBMCOSProducer.java
@@ -355,7 +355,9 @@ public class IBMCOSProducer extends DefaultProducer {
}
private String determineBucketName(Exchange exchange) {
- String bucketName =
exchange.getIn().getHeader(IBMCOSConstants.BUCKET_NAME, String.class);
+ // not the bucket name header the consumer sets, so an object routed
from one bucket to another is not written
+ // back to the bucket it was consumed from
+ String bucketName =
exchange.getIn().getHeader(IBMCOSConstants.OVERRIDE_BUCKET_NAME, String.class);
if (bucketName == null) {
bucketName = getConfiguration().getBucketName();
}
diff --git
a/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/IBMCOSProducerBucketHeaderTest.java
b/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/IBMCOSProducerBucketHeaderTest.java
new file mode 100644
index 000000000000..d39b5cdd7f54
--- /dev/null
+++
b/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/IBMCOSProducerBucketHeaderTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.ibm.cos;
+
+import java.lang.reflect.Proxy;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.ibm.cloud.objectstorage.auth.AWSStaticCredentialsProvider;
+import com.ibm.cloud.objectstorage.auth.BasicAWSCredentials;
+import com.ibm.cloud.objectstorage.client.builder.AwsClientBuilder;
+import com.ibm.cloud.objectstorage.services.s3.AmazonS3;
+import com.ibm.cloud.objectstorage.services.s3.AmazonS3ClientBuilder;
+import com.ibm.cloud.objectstorage.services.s3.model.PutObjectRequest;
+import com.ibm.cloud.objectstorage.services.s3.model.PutObjectResult;
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The producer stores an object in the bucket of its endpoint, not in the
bucket named by the header the consumer sets
+ * on the objects it consumes, so an object routed from one bucket to another
is not written back to (and with
+ * deleteAfterRead then deleted from) the bucket it was consumed from. The
override header still selects the bucket.
+ */
+class IBMCOSProducerBucketHeaderTest extends CamelTestSupport {
+
+ private final List<String> puts = new CopyOnWriteArrayList<>();
+ private FakeS3Server s3;
+ private AmazonS3 sourceClient;
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ s3 = new FakeS3Server("source");
+ s3.start();
+ sourceClient = AmazonS3ClientBuilder.standard()
+ .withCredentials(new AWSStaticCredentialsProvider(new
BasicAWSCredentials("xxx", "yyy")))
+ .withEndpointConfiguration(new
AwsClientBuilder.EndpointConfiguration(s3.endpoint(), "us-south"))
+ .withPathStyleAccessEnabled(true)
+ .build();
+ CamelContext context = super.createCamelContext();
+ context.getRegistry().bind("sourceClient", sourceClient);
+ context.getRegistry().bind("targetClient", recordingClient());
+ return context;
+ }
+
+ // records the bucket and key of each object put, answers that every
bucket exists
+ private AmazonS3 recordingClient() {
+ return (AmazonS3)
Proxy.newProxyInstance(AmazonS3.class.getClassLoader(), new Class<?>[] {
AmazonS3.class },
+ (proxy, method, args) -> switch (method.getName()) {
+ case "putObject" -> {
+ PutObjectRequest request = (PutObjectRequest) args[0];
+ puts.add(request.getBucketName() + "/" +
request.getKey());
+ yield new PutObjectResult();
+ }
+ case "doesBucketExistV2" -> true;
+ case "toString" -> "recordingClient";
+ case "hashCode" -> System.identityHashCode(proxy);
+ case "equals" -> proxy == args[0];
+ default -> throw new
UnsupportedOperationException(method.getName());
+ });
+ }
+
+ @AfterEach
+ void stopServer() {
+ if (sourceClient != null) {
+ sourceClient.shutdown();
+ }
+ if (s3 != null) {
+ s3.stop();
+ }
+ }
+
+ @Test
+ void consumedObjectIsStoredInTheBucketOfTheProducerEndpoint() throws
Exception {
+ s3.put("a.txt", "hello");
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedBodiesReceived("hello");
+ mock.expectedHeaderReceived(IBMCOSConstants.BUCKET_NAME, "source");
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("ibm-cos://source?cosClient=#sourceClient&delay=10")
+ .convertBodyTo(String.class)
+ .to("ibm-cos://target?cosClient=#targetClient")
+ .to("mock:result");
+ }
+ });
+
+ mock.assertIsSatisfied();
+ assertEquals(List.of("target/a.txt"), puts);
+ }
+
+ @Test
+ void overrideBucketNameHeaderSelectsTheBucket() {
+ template.sendBodyAndHeader("ibm-cos://target?cosClient=#targetClient",
"hello",
+ IBMCOSConstants.KEY, "b.txt");
+ template.send("ibm-cos://target?cosClient=#targetClient", exchange -> {
+ exchange.getIn().setBody("hello");
+ exchange.getIn().setHeader(IBMCOSConstants.KEY, "c.txt");
+ exchange.getIn().setHeader(IBMCOSConstants.OVERRIDE_BUCKET_NAME,
"other");
+ });
+
+ assertEquals(List.of("target/b.txt", "other/c.txt"), puts);
+ }
+}
diff --git
a/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/integration/IBMCOSProducerAdditionalOperationsIT.java
b/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/integration/IBMCOSProducerAdditionalOperationsIT.java
index d211d92ddafb..33621c54bb41 100644
---
a/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/integration/IBMCOSProducerAdditionalOperationsIT.java
+++
b/components/camel-ibm/camel-ibm-cos/src/test/java/org/apache/camel/component/ibm/cos/integration/IBMCOSProducerAdditionalOperationsIT.java
@@ -163,7 +163,7 @@ public class IBMCOSProducerAdditionalOperationsIT extends
IBMCOSTestSupport {
Exchange createExchange = template.request("direct:createBucket",
new Processor() {
@Override
public void process(Exchange exchange) {
- exchange.getIn().setHeader(IBMCOSConstants.BUCKET_NAME,
testBucketName);
+
exchange.getIn().setHeader(IBMCOSConstants.OVERRIDE_BUCKET_NAME,
testBucketName);
}
});
@@ -176,7 +176,7 @@ public class IBMCOSProducerAdditionalOperationsIT extends
IBMCOSTestSupport {
template.send("direct:deleteBucket", new Processor() {
@Override
public void process(Exchange exchange) {
- exchange.getIn().setHeader(IBMCOSConstants.BUCKET_NAME,
testBucketName);
+
exchange.getIn().setHeader(IBMCOSConstants.OVERRIDE_BUCKET_NAME,
testBucketName);
}
});
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index c4395b581d04..2d3c1d96e163 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -1249,6 +1249,17 @@ that lists them. Set `camel.management.port` to another
port, so that the manage
and applies them. To keep a single port, remove them and configure the HTTP
server instead: for example,
authentication configured on the HTTP server for `/*` also applies to the
management endpoints.
+=== camel-ibm-cos - the producer takes the bucket from the
CamelIBMCOSOverrideBucketName header (Breaking change)
+
+The producer used the bucket named in the `CamelIBMCOSBucketName` header
instead of the bucket of its endpoint. The
+consumer sets that header to the bucket an object was consumed from, so a
route from one bucket to another
+(`from("ibm-cos:source...").to("ibm-cos:target...")`) stored each object back
in the source bucket, and with the
+default `deleteAfterRead=true` the consumer then deleted it there. The
producer now ignores `CamelIBMCOSBucketName`
+and uses the bucket of the endpoint, or the bucket named in the new
`CamelIBMCOSOverrideBucketName` header
+(`IBMCOSConstants.OVERRIDE_BUCKET_NAME`), as camel-aws2-s3, camel-minio and
camel-google-storage do since 4.10. A route
+that sets `CamelIBMCOSBucketName` to choose the bucket of a producer operation
(for example `createBucket` or
+`deleteBucket`) must set `CamelIBMCOSOverrideBucketName` instead.
+
=== camel-ical - each unmarshal parses with its own calendar builder
The `ical` data format used one ical4j `CalendarBuilder` for all exchanges.
Two exchanges that were unmarshalled at
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/IBMCOSEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/IBMCOSEndpointBuilderFactory.java
index 053e072564db..14a2771f2f17 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/IBMCOSEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/IBMCOSEndpointBuilderFactory.java
@@ -1721,12 +1721,11 @@ public interface IBMCOSEndpointBuilderFactory {
public static final IBMCOSHeaderNameBuilder INSTANCE = new
IBMCOSHeaderNameBuilder();
/**
- * The bucket Name which this object will be stored or which will be
- * used for the current operation.
+ * The name of the bucket the consumed object is contained in.
*
* The option is a: {@code String} type.
*
- * Group: common
+ * Group: consumer
*
* @return the name of the header {@code IBMCOSBucketName}.
*/
@@ -1981,6 +1980,20 @@ public interface IBMCOSEndpointBuilderFactory {
public String iBMCOSBucketExists() {
return "CamelIBMCOSBucketExists";
}
+ /**
+ * The bucket name to use instead of the bucket of the endpoint, in
+ * which this object will be stored or which will be used for the
+ * current operation.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code IBMCOSOverrideBucketName}.
+ */
+ public String iBMCOSOverrideBucketName() {
+ return "CamelIBMCOSOverrideBucketName";
+ }
}
static IBMCOSEndpointBuilder endpointBuilder(String componentName, String
path) {
class IBMCOSEndpointBuilderImpl extends AbstractEndpointBuilder
implements IBMCOSEndpointBuilder, AdvancedIBMCOSEndpointBuilder {