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 6e6449e8ccdf CAMEL-26270: camel-openstack - producers work from any
thread, and the swift and glance payload converts the body (#29109)
6e6449e8ccdf is described below
commit 6e6449e8ccdf57e6b28c487f16b9a7f9ac89e700
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Nov 5 07:16:47 2026 +0100
CAMEL-26270: camel-openstack - producers work from any thread, and the
swift and glance payload converts the body (#29109)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../camel/catalog/components/openstack-glance.json | 2 +-
.../camel/catalog/components/openstack-swift.json | 2 +-
.../catalog/docs/openstack-swift-component.adoc | 4 +-
.../openstack/glance/openstack-glance.json | 2 +-
.../component/openstack/swift/openstack-swift.json | 2 +-
.../src/main/docs/openstack-swift-component.adoc | 4 +-
.../cinder/producer/SnapshotProducer.java | 2 +-
.../openstack/cinder/producer/VolumeProducer.java | 2 +-
.../common/AbstractOpenstackProducer.java | 55 +++++++++--
.../component/openstack/glance/GlanceEndpoint.java | 10 +-
.../component/openstack/glance/GlanceProducer.java | 2 +-
.../keystone/producer/DomainProducer.java | 2 +-
.../openstack/keystone/producer/GroupProducer.java | 2 +-
.../keystone/producer/ProjectProducer.java | 2 +-
.../keystone/producer/RegionProducer.java | 2 +-
.../openstack/keystone/producer/UserProducer.java | 2 +-
.../neutron/producer/NetworkProducer.java | 2 +-
.../openstack/neutron/producer/PortProducer.java | 2 +-
.../openstack/neutron/producer/RouterProducer.java | 2 +-
.../openstack/neutron/producer/SubnetProducer.java | 2 +-
.../openstack/nova/producer/FlavorsProducer.java | 2 +-
.../openstack/nova/producer/KeypairProducer.java | 2 +-
.../openstack/nova/producer/ServerProducer.java | 2 +-
.../component/openstack/swift/SwiftEndpoint.java | 6 +-
.../swift/producer/ContainerProducer.java | 2 +-
.../openstack/swift/producer/ObjectProducer.java | 2 +-
.../openstack/it/OpenstackGlanceTest.java | 30 ++++++
.../openstack/it/OpenstackSessionTest.java | 109 +++++++++++++++++++++
.../openstack/it/OpenstackSwiftObjectTest.java | 86 ++++++++++++++++
.../openstack/it/OpenstackWiremockTestSupport.java | 4 +
.../mappings/swift/objects/object_put.json | 20 ++++
31 files changed, 330 insertions(+), 40 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-glance.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-glance.json
index e3ec1c39a4d0..0415971ad6e4 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-glance.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-glance.json
@@ -42,7 +42,7 @@
"CamelOpenstackProperties": { "index": 11, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Map<String, String>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The image properties.",
"constantName":
"org.apache.camel.component.openstack.common.OpenstackConstants#PROPERTIES" }
},
"bodyTypes": {
- "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "create", "carriers": [ "stream", "file", "pojo" ] }, {
"option": "operation", "value": "upload", "carriers": [ "stream", "file",
"pojo" ] } ], "description": "For create and upload the image data: an
InputStream, a File, a URL or a Payload of openstack4j; text, bytes or the file
of the file consumer fail: convert it to a File or an InputStream first.
reserve and update take an Image of openstack4j [...]
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "create", "carriers": [ "stream", "file", "text",
"bytes", "pojo" ] }, { "option": "operation", "value": "upload", "carriers": [
"stream", "file", "text", "bytes", "pojo" ] } ], "description": "For create and
upload the image data: an InputStream, a File, a URL or a Payload of
openstack4j, or else a body that converts to an InputStream such as text, bytes
or the file of the file consumer. reserve a [...]
"producerOut": { "carriers": [ "pojo", "list", "null", "unchanged" ],
"byOption": [ { "option": "operation", "value": "reserve", "carriers": [ "pojo"
] }, { "option": "operation", "value": "create", "carriers": [ "pojo" ] }, {
"option": "operation", "value": "upload", "carriers": [ "pojo" ] }, { "option":
"operation", "value": "update", "carriers": [ "pojo" ] }, { "option":
"operation", "value": "get", "carriers": [ "pojo", "null" ] }, { "option":
"operation", "value": "getAll", "car [...]
},
"properties": {
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-swift.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-swift.json
index f830823a94c3..8ac355c86ae4 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-swift.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openstack-swift.json
@@ -44,7 +44,7 @@
"CamelOpenstackName": { "index": 13, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The name.", "constantName":
"org.apache.camel.component.openstack.common.OpenstackConstants#NAME" }
},
"bodyTypes": {
- "producerIn": { "carriers": [ "any" ], "description": "For create of an
object the content: an InputStream, a File, a URL or a Payload of openstack4j;
text, bytes or the file of the file consumer fail: convert it to a File or an
InputStream first. createUpdateMetadata and deleteMetadata take the metadata as
a Map. The containers take a CreateUpdateContainerOptions or
ContainerListOptions of openstack4j, or else headers" },
+ "producerIn": { "carriers": [ "any" ], "description": "For create of an
object the content: an InputStream, a File, a URL or a Payload of openstack4j,
or else a body that converts to an InputStream such as text, bytes or the file
of the file consumer. createUpdateMetadata and deleteMetadata take the metadata
as a Map. The containers take a CreateUpdateContainerOptions or
ContainerListOptions of openstack4j, or else headers" },
"producerOut": { "carriers": [ "text", "pojo", "list", "map", "null",
"unchanged" ], "byOption": [ { "option": "operation", "value": "getAll",
"carriers": [ "list" ] }, { "option": "operation", "value": "getMetadata",
"carriers": [ "map" ] }, { "option": "operation", "value": "delete",
"carriers": [ "unchanged" ] }, { "option": "operation", "value":
"createUpdateMetadata", "carriers": [ "unchanged" ] }, { "option": "operation",
"value": "deleteMetadata", "carriers": [ "unchanged" ] } [...]
},
"properties": {
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openstack-swift-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openstack-swift-component.adoc
index 9ec139f27562..451d479f8d85 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openstack-swift-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openstack-swift-component.adoc
@@ -103,7 +103,7 @@ or
`org.openstack4j.model.storage.object.options.ContainerListOptions` for listi
=== Example
The following route stores each file of the `outbox` folder as an object of
the `reports` container.
-The body must be an `InputStream`, a `File`, a `URL` or a `Payload` of
openstack4j, so the route converts the file of the file consumer to a
`java.io.File` first.
+The body is the content of the object: an `InputStream`, a `File`, a `URL` or
a `Payload` of openstack4j, or else a body that converts to an `InputStream`,
such as text, bytes or the file of the file consumer.
The endpoint is the address of the identity service (Keystone), and the
credentials are property placeholders:
[source,yaml]
@@ -122,8 +122,6 @@ The endpoint is the address of the identity service
(Keystone), and the credenti
expression:
simple:
expression: "${header.CamelFileName}"
- - convertBodyTo:
- type: java.io.File
- to:
uri: openstack-swift:http://controller:5000/v3
parameters:
diff --git
a/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/glance/openstack-glance.json
b/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/glance/openstack-glance.json
index e3ec1c39a4d0..0415971ad6e4 100644
---
a/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/glance/openstack-glance.json
+++
b/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/glance/openstack-glance.json
@@ -42,7 +42,7 @@
"CamelOpenstackProperties": { "index": 11, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Map<String, String>", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The image properties.",
"constantName":
"org.apache.camel.component.openstack.common.OpenstackConstants#PROPERTIES" }
},
"bodyTypes": {
- "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "create", "carriers": [ "stream", "file", "pojo" ] }, {
"option": "operation", "value": "upload", "carriers": [ "stream", "file",
"pojo" ] } ], "description": "For create and upload the image data: an
InputStream, a File, a URL or a Payload of openstack4j; text, bytes or the file
of the file consumer fail: convert it to a File or an InputStream first.
reserve and update take an Image of openstack4j [...]
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "create", "carriers": [ "stream", "file", "text",
"bytes", "pojo" ] }, { "option": "operation", "value": "upload", "carriers": [
"stream", "file", "text", "bytes", "pojo" ] } ], "description": "For create and
upload the image data: an InputStream, a File, a URL or a Payload of
openstack4j, or else a body that converts to an InputStream such as text, bytes
or the file of the file consumer. reserve a [...]
"producerOut": { "carriers": [ "pojo", "list", "null", "unchanged" ],
"byOption": [ { "option": "operation", "value": "reserve", "carriers": [ "pojo"
] }, { "option": "operation", "value": "create", "carriers": [ "pojo" ] }, {
"option": "operation", "value": "upload", "carriers": [ "pojo" ] }, { "option":
"operation", "value": "update", "carriers": [ "pojo" ] }, { "option":
"operation", "value": "get", "carriers": [ "pojo", "null" ] }, { "option":
"operation", "value": "getAll", "car [...]
},
"properties": {
diff --git
a/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/swift/openstack-swift.json
b/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/swift/openstack-swift.json
index f830823a94c3..8ac355c86ae4 100644
---
a/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/swift/openstack-swift.json
+++
b/components/camel-openstack/src/generated/resources/META-INF/org/apache/camel/component/openstack/swift/openstack-swift.json
@@ -44,7 +44,7 @@
"CamelOpenstackName": { "index": 13, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The name.", "constantName":
"org.apache.camel.component.openstack.common.OpenstackConstants#NAME" }
},
"bodyTypes": {
- "producerIn": { "carriers": [ "any" ], "description": "For create of an
object the content: an InputStream, a File, a URL or a Payload of openstack4j;
text, bytes or the file of the file consumer fail: convert it to a File or an
InputStream first. createUpdateMetadata and deleteMetadata take the metadata as
a Map. The containers take a CreateUpdateContainerOptions or
ContainerListOptions of openstack4j, or else headers" },
+ "producerIn": { "carriers": [ "any" ], "description": "For create of an
object the content: an InputStream, a File, a URL or a Payload of openstack4j,
or else a body that converts to an InputStream such as text, bytes or the file
of the file consumer. createUpdateMetadata and deleteMetadata take the metadata
as a Map. The containers take a CreateUpdateContainerOptions or
ContainerListOptions of openstack4j, or else headers" },
"producerOut": { "carriers": [ "text", "pojo", "list", "map", "null",
"unchanged" ], "byOption": [ { "option": "operation", "value": "getAll",
"carriers": [ "list" ] }, { "option": "operation", "value": "getMetadata",
"carriers": [ "map" ] }, { "option": "operation", "value": "delete",
"carriers": [ "unchanged" ] }, { "option": "operation", "value":
"createUpdateMetadata", "carriers": [ "unchanged" ] }, { "option": "operation",
"value": "deleteMetadata", "carriers": [ "unchanged" ] } [...]
},
"properties": {
diff --git
a/components/camel-openstack/src/main/docs/openstack-swift-component.adoc
b/components/camel-openstack/src/main/docs/openstack-swift-component.adoc
index 9ec139f27562..451d479f8d85 100644
--- a/components/camel-openstack/src/main/docs/openstack-swift-component.adoc
+++ b/components/camel-openstack/src/main/docs/openstack-swift-component.adoc
@@ -103,7 +103,7 @@ or
`org.openstack4j.model.storage.object.options.ContainerListOptions` for listi
=== Example
The following route stores each file of the `outbox` folder as an object of
the `reports` container.
-The body must be an `InputStream`, a `File`, a `URL` or a `Payload` of
openstack4j, so the route converts the file of the file consumer to a
`java.io.File` first.
+The body is the content of the object: an `InputStream`, a `File`, a `URL` or
a `Payload` of openstack4j, or else a body that converts to an `InputStream`,
such as text, bytes or the file of the file consumer.
The endpoint is the address of the identity service (Keystone), and the
credentials are property placeholders:
[source,yaml]
@@ -122,8 +122,6 @@ The endpoint is the address of the identity service
(Keystone), and the credenti
expression:
simple:
expression: "${header.CamelFileName}"
- - convertBodyTo:
- type: java.io.File
- to:
uri: openstack-swift:http://controller:5000/v3
parameters:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/SnapshotProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/SnapshotProducer.java
index 59cc2f0158ce..181f23235b68 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/SnapshotProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/SnapshotProducer.java
@@ -39,7 +39,7 @@ public class SnapshotProducer extends
AbstractOpenstackProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
String operation = getOperation(exchange);
switch (operation) {
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/VolumeProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/VolumeProducer.java
index 673cf3f03275..0126ea7ae6d7 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/VolumeProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/cinder/producer/VolumeProducer.java
@@ -40,7 +40,7 @@ public class VolumeProducer extends AbstractOpenstackProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
String operation = getOperation(exchange);
switch (operation) {
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/common/AbstractOpenstackProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/common/AbstractOpenstackProducer.java
index 9eea683d1aab..0f5177b449c9 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/common/AbstractOpenstackProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/common/AbstractOpenstackProducer.java
@@ -26,20 +26,58 @@ import org.apache.camel.support.DefaultProducer;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import org.openstack4j.api.OSClient;
+import org.openstack4j.api.OSClient.OSClientV2;
+import org.openstack4j.api.OSClient.OSClientV3;
import org.openstack4j.model.common.ActionResponse;
import org.openstack4j.model.common.Payload;
import org.openstack4j.model.common.Payloads;
+import org.openstack4j.openstack.OSFactory;
public abstract class AbstractOpenstackProducer extends DefaultProducer {
protected OSClient os;
- private AbstractOpenstackEndpoint endpoint;
+ private final AbstractOpenstackEndpoint endpoint;
+
+ // the session with the newest token, as openstack4j re-authenticates an
expired token into the session in use
+ private volatile OSClient session;
protected AbstractOpenstackProducer(AbstractOpenstackEndpoint endpoint,
OSClient client) {
super(endpoint);
this.os = client;
this.endpoint = endpoint;
+ this.session = client;
+ }
+
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ final OSClient current = useSession();
+ try {
+ doProcess(exchange);
+ } finally {
+ session = current;
+ }
+ }
+
+ /**
+ * Processes the exchange with the services of {@link #os}, which use the
openstack4j session of the calling thread.
+ */
+ protected abstract void doProcess(Exchange exchange) throws Exception;
+
+ /**
+ * The services of openstack4j use the session of the calling thread
(OSClientSession is thread local), and the
+ * client was authenticated on the thread that created the producer.
Creates the session of the calling thread from
+ * the token of the client, as openstack4j documents for using a client
from several threads.
+ */
+ private OSClient useSession() {
+ final OSClient client = session;
+ if (client instanceof OSClientV3 v3 && v3.getToken() != null) {
+ return OSFactory.clientFromToken(v3.getToken(),
endpoint.getConfig());
+ }
+ if (client instanceof OSClientV2 v2 && v2.getAccess() != null) {
+ return OSFactory.clientFromAccess(v2.getAccess(),
endpoint.getConfig());
+ }
+ return client;
}
protected Payload createPayload(Message msg) {
@@ -52,16 +90,21 @@ public abstract class AbstractOpenstackProducer extends
DefaultProducer {
Object messageBody = msg.getBody();
if (messageBody instanceof URL) {
payload = Payloads.create((URL) messageBody);
- }
- if (messageBody instanceof File) {
+ } else if (messageBody instanceof File) {
payload = Payloads.create((File) messageBody);
- }
- if (messageBody instanceof InputStream) {
+ } else if (messageBody instanceof InputStream) {
payload = Payloads.create((InputStream) messageBody);
+ } else if (messageBody != null) {
+ // such as text, bytes or the file of the file consumer
+ InputStream is = msg.getBody(InputStream.class);
+ if (is != null) {
+ payload = Payloads.create(is);
+ }
}
if (payload == null) {
- throw new IllegalArgumentException("You have to set payload. It
can be InputStream, File or URL class");
+ throw new IllegalArgumentException(
+ "You have to set payload. It can be InputStream, File or
URL class, or a body that converts to InputStream");
}
return payload;
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceEndpoint.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceEndpoint.java
index a0f0defb853d..82ddb285d238 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceEndpoint.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceEndpoint.java
@@ -36,11 +36,13 @@ import static
org.apache.camel.component.openstack.common.OpenstackConstants.SCH
@UriEndpoint(firstVersion = "2.19.0", scheme = SCHEME_GLANCE, title =
"OpenStack Glance", syntax = "openstack-glance:host",
category = { Category.CONTAINER }, producerOnly = true,
headersClass = GlanceConstants.class)
@BodyTypes(producerIn = @BodyType(value = Carrier.ANY, byOption = {
- @BodyType.When(option = "operation", value = "create", carriers = {
Carrier.STREAM, Carrier.FILE, Carrier.POJO }),
- @BodyType.When(option = "operation", value = "upload", carriers = {
Carrier.STREAM, Carrier.FILE, Carrier.POJO }) },
+ @BodyType.When(option = "operation", value = "create",
+ carriers = { Carrier.STREAM, Carrier.FILE,
Carrier.TEXT, Carrier.BYTES, Carrier.POJO }),
+ @BodyType.When(option = "operation", value = "upload",
+ carriers = { Carrier.STREAM, Carrier.FILE,
Carrier.TEXT, Carrier.BYTES, Carrier.POJO }) },
description = "For create and upload the
image data: an InputStream, a File, a URL or a Payload of "
- + "openstack4j; text, bytes or
the file of the file consumer fail: convert it to a File or "
- + "an InputStream first.
reserve and update take an Image of openstack4j, or else the fields from "
+ + "openstack4j, or else a body
that converts to an InputStream such as text, bytes or "
+ + "the file of the file
consumer. reserve and update take an Image of openstack4j, or else the fields
from "
+ "headers such as
CamelOpenstackName"),
producerOut = @BodyType(value = { Carrier.POJO, Carrier.LIST,
Carrier.NULL, Carrier.UNCHANGED }, byOption = {
@BodyType.When(option = "operation", value = "reserve",
carriers = Carrier.POJO),
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceProducer.java
index ec5ebb6db8a7..15237ba7dd2f 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/glance/GlanceProducer.java
@@ -41,7 +41,7 @@ public class GlanceProducer extends AbstractOpenstackProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
String operation = getOperation(exchange);
switch (operation) {
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/DomainProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/DomainProducer.java
index 1bca892728e8..54bc267dc2f2 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/DomainProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/DomainProducer.java
@@ -38,7 +38,7 @@ public class DomainProducer extends AbstractKeystoneProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/GroupProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/GroupProducer.java
index e1413b4dcbc1..26098fea32b5 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/GroupProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/GroupProducer.java
@@ -38,7 +38,7 @@ public class GroupProducer extends AbstractKeystoneProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/ProjectProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/ProjectProducer.java
index 262ca8f2ffe1..858a6f7cdf92 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/ProjectProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/ProjectProducer.java
@@ -38,7 +38,7 @@ public class ProjectProducer extends AbstractKeystoneProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/RegionProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/RegionProducer.java
index d43f9bdf5de8..316d5d51983b 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/RegionProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/RegionProducer.java
@@ -38,7 +38,7 @@ public class RegionProducer extends AbstractKeystoneProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/UserProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/UserProducer.java
index 86420cf8dd1b..a58c8a23bf31 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/UserProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/keystone/producer/UserProducer.java
@@ -38,7 +38,7 @@ public class UserProducer extends AbstractKeystoneProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/NetworkProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/NetworkProducer.java
index 2e8cb66abd6b..317de9908944 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/NetworkProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/NetworkProducer.java
@@ -40,7 +40,7 @@ public class NetworkProducer extends
AbstractOpenstackProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/PortProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/PortProducer.java
index f22b7f440d02..e6d1fb9fc796 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/PortProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/PortProducer.java
@@ -39,7 +39,7 @@ public class PortProducer extends AbstractOpenstackProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/RouterProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/RouterProducer.java
index 377ce99b0f73..77922ce219c0 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/RouterProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/RouterProducer.java
@@ -42,7 +42,7 @@ public class RouterProducer extends AbstractOpenstackProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/SubnetProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/SubnetProducer.java
index 7895218af641..8a4ddedd830b 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/SubnetProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/neutron/producer/SubnetProducer.java
@@ -42,7 +42,7 @@ public class SubnetProducer extends AbstractOpenstackProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/FlavorsProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/FlavorsProducer.java
index 7d60b31014c7..85fca63301ae 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/FlavorsProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/FlavorsProducer.java
@@ -39,7 +39,7 @@ public class FlavorsProducer extends
AbstractOpenstackProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/KeypairProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/KeypairProducer.java
index cccea3330296..ce5933981129 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/KeypairProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/KeypairProducer.java
@@ -35,7 +35,7 @@ public class KeypairProducer extends
AbstractOpenstackProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/ServerProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/ServerProducer.java
index ebe64ff5023f..f6fa642431c8 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/ServerProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/nova/producer/ServerProducer.java
@@ -42,7 +42,7 @@ public class ServerProducer extends AbstractOpenstackProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
final String operation = getOperation(exchange);
switch (operation) {
case OpenstackConstants.CREATE:
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/SwiftEndpoint.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/SwiftEndpoint.java
index c070f6d2a683..246c611061f4 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/SwiftEndpoint.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/SwiftEndpoint.java
@@ -38,9 +38,9 @@ import static
org.apache.camel.component.openstack.common.OpenstackConstants.SCH
@UriEndpoint(firstVersion = "2.19.0", scheme = SCHEME_SWIFT, title =
"OpenStack Swift", syntax = "openstack-swift:host",
category = { Category.CONTAINER }, producerOnly = true,
headersClass = SwiftConstants.class)
@BodyTypes(producerIn = @BodyType(value = Carrier.ANY,
- description = "For create of an object the
content: an InputStream, a File, a URL or a Payload of openstack4j; "
- + "text, bytes or the file of
the file consumer fail: convert it to a File or an InputStream first. "
- + "createUpdateMetadata and
deleteMetadata take the metadata as a Map. The containers take a "
+ description = "For create of an object the
content: an InputStream, a File, a URL or a Payload of openstack4j, "
+ + "or else a body that
converts to an InputStream such as text, bytes or the file of the file "
+ + "consumer.
createUpdateMetadata and deleteMetadata take the metadata as a Map. The
containers take a "
+
"CreateUpdateContainerOptions or ContainerListOptions of openstack4j, or else
headers"),
producerOut = @BodyType(value = {
Carrier.TEXT, Carrier.POJO, Carrier.LIST, Carrier.MAP,
Carrier.NULL, Carrier.UNCHANGED },
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ContainerProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ContainerProducer.java
index 74ecb6f4e3fa..7626293d8e49 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ContainerProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ContainerProducer.java
@@ -40,7 +40,7 @@ public class ContainerProducer extends
AbstractOpenstackProducer {
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
String operation = getOperation(exchange);
switch (operation) {
diff --git
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ObjectProducer.java
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ObjectProducer.java
index fcf8281e9c4c..b0b81f16e076 100644
---
a/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ObjectProducer.java
+++
b/components/camel-openstack/src/main/java/org/apache/camel/component/openstack/swift/producer/ObjectProducer.java
@@ -41,7 +41,7 @@ public class ObjectProducer extends AbstractOpenstackProducer
{
}
@Override
- public void process(Exchange exchange) throws Exception {
+ protected void doProcess(Exchange exchange) throws Exception {
String operation = getOperation(exchange);
switch (operation) {
diff --git
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackGlanceTest.java
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackGlanceTest.java
index 93da9987d532..02346b7d2d49 100644
---
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackGlanceTest.java
+++
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackGlanceTest.java
@@ -21,6 +21,8 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.openstack.common.OpenstackConstants;
import org.apache.camel.component.openstack.glance.GlanceConstants;
import org.junit.jupiter.api.Test;
@@ -38,6 +40,18 @@ public class OpenstackGlanceTest extends
OpenstackWiremockTestSupport {
private static final String URI_FORMAT =
"openstack-glance://%s?username=user&password=secret&project=project&operation=%s";
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // the producer is created when the route starts, and called
from the seda consumer thread
+ from("seda:upload")
+ .to(String.format(URI_FORMAT, url(),
GlanceConstants.UPLOAD));
+ }
+ };
+ }
+
@Test
void createShouldSucceed() {
Map<String, Object> headers = new HashMap<>();
@@ -60,6 +74,22 @@ public class OpenstackGlanceTest extends
OpenstackWiremockTestSupport {
assertEquals(0L, out.getMinRam());
}
+ @Test
+ void uploadTextFromAnotherThreadShouldSucceed() {
+ Map<String, Object> headers = new HashMap<>();
+ headers.put(OpenstackConstants.NAME, "amphora-x64-haproxy");
+ headers.put(OpenstackConstants.ID,
"4b434528-032b-4467-946c-b5880ce15c06");
+ headers.put(GlanceConstants.DISK_FORMAT, DiskFormat.QCOW2);
+ headers.put(GlanceConstants.CONTAINER_FORMAT, ContainerFormat.BARE);
+ headers.put(GlanceConstants.MIN_DISK, 0L);
+ headers.put(GlanceConstants.MIN_RAM, 0L);
+
+ Image out = template.requestBodyAndHeaders("seda:upload", "image
data", headers, Image.class);
+
+ assertNotNull(out);
+ assertEquals("4b434528-032b-4467-946c-b5880ce15c06", out.getId());
+ }
+
@Test
void uploadShouldSucceed() {
Map<String, Object> headers = new HashMap<>();
diff --git
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSessionTest.java
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSessionTest.java
new file mode 100644
index 000000000000..ac10f789b4a4
--- /dev/null
+++
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSessionTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.openstack.it;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.openstack.common.OpenstackConstants;
+import org.apache.camel.component.openstack.swift.SwiftConstants;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openstack4j.model.storage.object.SwiftObject;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.head;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The producer keeps using the token that openstack4j renewed when the first
token expired, from any thread.
+ */
+public class OpenstackSessionTest extends OpenstackWiremockTestSupport {
+
+ private static final String URI_FORMAT
+ =
"openstack-swift://%s?username=user&password=secret&project=project&operation=%s&subsystem="
+ + SwiftConstants.SWIFT_SUBSYSTEM_OBJECTS;
+
+ private static final String AUTH_URL = "/auth/tokens";
+ private static final String OBJECT_URL =
"/12345321ae80457abc3728fa1e6feb78/test-container/test-file";
+
+ @BeforeAll
+ public static void stubExpiredToken() throws Exception {
+ String body;
+ try (InputStream is =
OpenstackSessionTest.class.getResourceAsStream("/mappings/keystone/authv3_project.json"))
{
+ JsonNode mapping = new ObjectMapper().readTree(is);
+ body = mapping.get("response").get("body").asText();
+ }
+
+ // the first authentication gives a token that has expired, the next
one a valid token
+ server().stubFor(post(urlEqualTo(AUTH_URL)).atPriority(1)
+
.inScenario("token").whenScenarioStateIs(STARTED).willSetStateTo("renewed")
+ .willReturn(tokenResponse(body, "expired")));
+ server().stubFor(post(urlEqualTo(AUTH_URL)).atPriority(1)
+ .inScenario("token").whenScenarioStateIs("renewed")
+ .willReturn(tokenResponse(body, "renewed")));
+ server().stubFor(head(urlEqualTo(OBJECT_URL)).atPriority(1)
+ .withHeader("X-Auth-Token", equalTo("expired"))
+ .willReturn(aResponse().withStatus(401)));
+ }
+
+ private static ResponseDefinitionBuilder tokenResponse(String body, String
token) {
+ return aResponse().withStatus(201)
+ .withHeader("Content-Type", "application/json; charset=UTF-8")
+ .withHeader("X-Subject-Token", token)
+ .withBody(body)
+ .withTransformers("response-template");
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("seda:get?concurrentConsumers=2")
+ .to(String.format(URI_FORMAT, url(),
OpenstackConstants.GET));
+ }
+ };
+ }
+
+ @Test
+ void renewedTokenShouldBeKept() {
+ Map<String, Object> headers = new HashMap<>();
+ headers.put(SwiftConstants.CONTAINER_NAME, "test-container");
+ headers.put(SwiftConstants.OBJECT_NAME, "test-file");
+
+ for (int i = 0; i < 5; i++) {
+ SwiftObject swiftObject =
template.requestBodyAndHeaders("seda:get", null, headers, SwiftObject.class);
+ assertEquals("test-file", swiftObject.getName());
+ }
+
+ // the authentication of the producer, and the one renewal of the
expired token
+ server().verify(2, postRequestedFor(urlEqualTo(AUTH_URL)));
+ }
+
+}
diff --git
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSwiftObjectTest.java
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSwiftObjectTest.java
index 5a442711cc9b..159023822ec0 100644
---
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSwiftObjectTest.java
+++
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackSwiftObjectTest.java
@@ -16,15 +16,25 @@
*/
package org.apache.camel.component.openstack.it;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.component.openstack.common.OpenstackConstants;
import org.apache.camel.component.openstack.swift.SwiftConstants;
import org.junit.jupiter.api.Test;
import org.openstack4j.model.storage.object.SwiftObject;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class OpenstackSwiftObjectTest extends OpenstackWiremockTestSupport {
@@ -34,6 +44,27 @@ public class OpenstackSwiftObjectTest extends
OpenstackWiremockTestSupport {
private static final String OBJECT_CONTAINER_NAME = "test-container";
private static final String OBJECT_NAME = "test-file";
+ // the stub only answers a PUT of this content; WireMock adds a --gzip
suffix to the ETag of a gzip request
+ private static final String OBJECT_ETAG =
"d8b3a3e8f1b7c9a2e4f6a8b0c2d4e6f8";
+ private static final String OBJECT_CONTENT = "Hello Swift";
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // the producers are created when the routes start, and called
from the consumer threads
+ from("seda:get?concurrentConsumers=5")
+ .to(String.format(URI_FORMAT, url(),
OpenstackConstants.GET));
+
+
from("file:target/openstack-swift-outbox?initialDelay=0&delay=10&delete=true")
+ .setHeader(SwiftConstants.CONTAINER_NAME,
constant(OBJECT_CONTAINER_NAME))
+ .setHeader(SwiftConstants.OBJECT_NAME,
header(Exchange.FILE_NAME))
+ .to(String.format(URI_FORMAT, url(),
OpenstackConstants.CREATE))
+ .to("mock:stored");
+ }
+ };
+ }
@Test
void getShouldSucceed() {
@@ -51,4 +82,59 @@ public class OpenstackSwiftObjectTest extends
OpenstackWiremockTestSupport {
assertEquals("12345678901234567890", swiftObject.getETag());
}
+ @Test
+ void getFromAnotherThreadShouldSucceed() {
+ Map<String, Object> headers = new HashMap<>();
+ headers.put(SwiftConstants.CONTAINER_NAME, OBJECT_CONTAINER_NAME);
+ headers.put(SwiftConstants.OBJECT_NAME, OBJECT_NAME);
+
+ List<CompletableFuture<SwiftObject>> futures = new ArrayList<>();
+ for (int i = 0; i < 20; i++) {
+ futures.add(template.asyncRequestBodyAndHeaders("seda:get", null,
headers, SwiftObject.class));
+ }
+
+ for (CompletableFuture<SwiftObject> future : futures) {
+ SwiftObject swiftObject = future.join();
+ assertEquals(OBJECT_NAME, swiftObject.getName());
+ assertEquals("12345678901234567890", swiftObject.getETag());
+ }
+ }
+
+ @Test
+ void createFromTextShouldSucceed() {
+ String uri = String.format(URI_FORMAT, url(),
OpenstackConstants.CREATE);
+ String etag = template.requestBodyAndHeaders(uri, OBJECT_CONTENT,
objectHeaders(), String.class);
+
+ assertNotNull(etag);
+ assertTrue(etag.startsWith(OBJECT_ETAG), etag);
+ }
+
+ @Test
+ void createFromBytesShouldSucceed() {
+ String uri = String.format(URI_FORMAT, url(),
OpenstackConstants.CREATE);
+ String etag = template.requestBodyAndHeaders(uri,
OBJECT_CONTENT.getBytes(StandardCharsets.UTF_8), objectHeaders(),
+ String.class);
+
+ assertNotNull(etag);
+ assertTrue(etag.startsWith(OBJECT_ETAG), etag);
+ }
+
+ @Test
+ void createFromFileConsumerShouldSucceed() throws Exception {
+ MockEndpoint stored = getMockEndpoint("mock:stored");
+ stored.expectedMessageCount(1);
+ stored.message(0).body(String.class).startsWith(OBJECT_ETAG);
+
+ template.sendBodyAndHeader("file:target/openstack-swift-outbox",
OBJECT_CONTENT, Exchange.FILE_NAME, OBJECT_NAME);
+
+ stored.assertIsSatisfied();
+ }
+
+ private static Map<String, Object> objectHeaders() {
+ Map<String, Object> headers = new HashMap<>();
+ headers.put(SwiftConstants.CONTAINER_NAME, OBJECT_CONTAINER_NAME);
+ headers.put(SwiftConstants.OBJECT_NAME, OBJECT_NAME);
+ return headers;
+ }
+
}
diff --git
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackWiremockTestSupport.java
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackWiremockTestSupport.java
index eb347e0e8129..2af14beacdd0 100644
---
a/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackWiremockTestSupport.java
+++
b/components/camel-openstack/src/test/java/org/apache/camel/component/openstack/it/OpenstackWiremockTestSupport.java
@@ -47,4 +47,8 @@ public class OpenstackWiremockTestSupport extends
CamelTestSupport {
return server.baseUrl();
}
+ protected static WireMockServer server() {
+ return server;
+ }
+
}
diff --git
a/components/camel-openstack/src/test/resources/mappings/swift/objects/object_put.json
b/components/camel-openstack/src/test/resources/mappings/swift/objects/object_put.json
new file mode 100644
index 000000000000..bebe9a916573
--- /dev/null
+++
b/components/camel-openstack/src/test/resources/mappings/swift/objects/object_put.json
@@ -0,0 +1,20 @@
+{
+ "id" : "8d0c4c8e-5b8e-4a0f-9f3e-2f6d1c7a9b10",
+ "name" : "object_put.json",
+ "request" : {
+ "url" : "/12345321ae80457abc3728fa1e6feb78/test-container/test-file",
+ "method" : "PUT",
+ "bodyPatterns" : [ {
+ "equalTo" : "Hello Swift"
+ } ]
+ },
+ "response" : {
+ "headers" : {
+ "ETag" : "d8b3a3e8f1b7c9a2e4f6a8b0c2d4e6f8"
+ },
+ "status" : 201
+ },
+ "uuid" : "8d0c4c8e-5b8e-4a0f-9f3e-2f6d1c7a9b10",
+ "persistent" : true,
+ "insertionIndex" : 1
+}