This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26270-openstack-thread-session in repository https://gitbox.apache.org/repos/asf/camel.git
commit afc523fcb00ddb881dad2fef0f193e07bd2787d3 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Nov 3 15:44:35 2026 +0100 CAMEL-26270: camel-openstack - producers work from any thread, and the swift and glance payload converts the body The openstack4j session is thread local, and every producer authenticated its client on the thread that created it, so a call from another thread (a file, seda or timer consumer) failed with "Unable to retrieve current session". The producer now creates the session of the calling thread from the token of its client on each call (OSFactory.clientFromToken / clientFromAccess), and keeps the token that openstack4j renews when it expires. The producers implement doProcess instead of process. The payload of swift object create and glance create/upload now converts a body that is not a Payload, URL, File or InputStream to an InputStream, so text, bytes and the file of the file consumer work. 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 +}
