You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2026/01/12 10:56:37 UTC
(camel) 02/03: CAMEL-22837 - Camel-AWS components: Avoid duplicated code and add pagination to producer operation where it makes sense - AWS ECS
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch 22805-2
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5ab4655bdaeb66e3dbf1787f2f5187320660af3b
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Mon Jan 12 11:21:50 2026 +0100
CAMEL-22837 - Camel-AWS components: Avoid duplicated code and add pagination to producer operation where it makes sense - AWS ECS
Signed-off-by: Andrea Cosentino <an...@gmail.com>
---
.../apache/camel/catalog/components/aws2-ecs.json | 5 +-
.../apache/camel/component/aws2/ecs/aws2-ecs.json | 5 +-
.../camel/component/aws2/ecs/ECS2Constants.java | 13 ++
.../camel/component/aws2/ecs/ECS2Producer.java | 255 +++++++++++----------
.../endpoint/dsl/ECS2EndpointBuilderFactory.java | 36 +++
5 files changed, 196 insertions(+), 118 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-ecs.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-ecs.json
index c5ccb02bd00f..0e9a053db958 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-ecs.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws2-ecs.json
@@ -50,7 +50,10 @@
"headers": {
"CamelAwsECSOperation": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation we want to perform", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#OPERATION" },
"CamelAwsECSMaxResults": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The limit number of results while listing clusters", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#MAX_RESULTS" },
- "CamelAwsECSClusterName": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The cluster name", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#CLUSTER_NAME" }
+ "CamelAwsECSClusterName": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The cluster name", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#CLUSTER_NAME" },
+ "CamelAwsECSNextToken": { "index": 3, "kind": "header", "displayName": "", "group": "listClusters", "label": "listClusters", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The token for the next set of results.", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#NEXT_TOKEN" },
+ "CamelAwsECSIsTruncated": { "index": 4, "kind": "header", "displayName": "", "group": "listClusters", "label": "listClusters", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the response has more results (is truncated).", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#IS_TRUNCATED" },
+ "CamelAwsECSClusterArn": { "index": 5, "kind": "header", "displayName": "", "group": "createCluster describeCluster deleteCluster", "label": "createCluster describeCluster deleteCluster", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The ARN of the cluster.", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#CLUSTER_ARN" }
},
"properties": {
"label": { "index": 0, "kind": "path", "displayName": "Label", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.ecs.ECS2Configuration", "configurationField": "configuration", "description": "Logical name" },
diff --git a/components/camel-aws/camel-aws2-ecs/src/generated/resources/META-INF/org/apache/camel/component/aws2/ecs/aws2-ecs.json b/components/camel-aws/camel-aws2-ecs/src/generated/resources/META-INF/org/apache/camel/component/aws2/ecs/aws2-ecs.json
index c5ccb02bd00f..0e9a053db958 100644
--- a/components/camel-aws/camel-aws2-ecs/src/generated/resources/META-INF/org/apache/camel/component/aws2/ecs/aws2-ecs.json
+++ b/components/camel-aws/camel-aws2-ecs/src/generated/resources/META-INF/org/apache/camel/component/aws2/ecs/aws2-ecs.json
@@ -50,7 +50,10 @@
"headers": {
"CamelAwsECSOperation": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation we want to perform", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#OPERATION" },
"CamelAwsECSMaxResults": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The limit number of results while listing clusters", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#MAX_RESULTS" },
- "CamelAwsECSClusterName": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The cluster name", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#CLUSTER_NAME" }
+ "CamelAwsECSClusterName": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The cluster name", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#CLUSTER_NAME" },
+ "CamelAwsECSNextToken": { "index": 3, "kind": "header", "displayName": "", "group": "listClusters", "label": "listClusters", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The token for the next set of results.", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#NEXT_TOKEN" },
+ "CamelAwsECSIsTruncated": { "index": 4, "kind": "header", "displayName": "", "group": "listClusters", "label": "listClusters", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the response has more results (is truncated).", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#IS_TRUNCATED" },
+ "CamelAwsECSClusterArn": { "index": 5, "kind": "header", "displayName": "", "group": "createCluster describeCluster deleteCluster", "label": "createCluster describeCluster deleteCluster", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The ARN of the cluster.", "constantName": "org.apache.camel.component.aws2.ecs.ECS2Constants#CLUSTER_ARN" }
},
"properties": {
"label": { "index": 0, "kind": "path", "displayName": "Label", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.aws2.ecs.ECS2Configuration", "configurationField": "configuration", "description": "Logical name" },
diff --git a/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Constants.java b/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Constants.java
index bf228eb1d5fd..b3ba78c89ca8 100644
--- a/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Constants.java
+++ b/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Constants.java
@@ -28,4 +28,17 @@ public interface ECS2Constants {
String MAX_RESULTS = "CamelAwsECSMaxResults";
@Metadata(description = "The cluster name", javaType = "String")
String CLUSTER_NAME = "CamelAwsECSClusterName";
+
+ // Pagination constants
+ @Metadata(label = "listClusters",
+ description = "The token for the next set of results.", javaType = "String")
+ String NEXT_TOKEN = "CamelAwsECSNextToken";
+ @Metadata(label = "listClusters",
+ description = "Whether the response has more results (is truncated).", javaType = "Boolean")
+ String IS_TRUNCATED = "CamelAwsECSIsTruncated";
+
+ // Response metadata
+ @Metadata(label = "createCluster describeCluster deleteCluster",
+ description = "The ARN of the cluster.", javaType = "String")
+ String CLUSTER_ARN = "CamelAwsECSClusterArn";
}
diff --git a/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Producer.java b/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Producer.java
index 4b32ae30c226..49a40337e00b 100644
--- a/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Producer.java
+++ b/components/camel-aws/camel-aws2-ecs/src/main/java/org/apache/camel/component/aws2/ecs/ECS2Producer.java
@@ -16,6 +16,10 @@
*/
package org.apache.camel.component.aws2.ecs;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
@@ -37,7 +41,6 @@ import software.amazon.awssdk.services.ecs.model.DeleteClusterResponse;
import software.amazon.awssdk.services.ecs.model.DescribeClustersRequest;
import software.amazon.awssdk.services.ecs.model.DescribeClustersResponse;
import software.amazon.awssdk.services.ecs.model.ListClustersRequest;
-import software.amazon.awssdk.services.ecs.model.ListClustersRequest.Builder;
import software.amazon.awssdk.services.ecs.model.ListClustersResponse;
/**
@@ -101,145 +104,165 @@ public class ECS2Producer extends DefaultProducer {
}
private void listClusters(EcsClient ecsClient, Exchange exchange) throws InvalidPayloadException {
- if (getConfiguration().isPojoRequest()) {
- Object payload = exchange.getIn().getMandatoryBody();
- if (payload instanceof ListClustersRequest) {
- ListClustersResponse result;
- try {
- ListClustersRequest request = (ListClustersRequest) payload;
- result = ecsClient.listClusters(request);
- } catch (AwsServiceException ase) {
- LOG.trace("List Clusters command returned the error code {}", ase.awsErrorDetails().errorCode());
- throw ase;
- }
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
- } else {
- Builder builder = ListClustersRequest.builder();
- if (ObjectHelper.isNotEmpty(exchange.getIn().getHeader(ECS2Constants.MAX_RESULTS))) {
- int maxRes = exchange.getIn().getHeader(ECS2Constants.MAX_RESULTS, Integer.class);
- builder.maxResults(maxRes);
- }
- ListClustersResponse result;
- try {
- ListClustersRequest request = builder.build();
- result = ecsClient.listClusters(request);
- } catch (AwsServiceException ase) {
- LOG.trace("List Clusters command returned the error code {}", ase.awsErrorDetails().errorCode());
- throw ase;
- }
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
+ executeOperation(
+ exchange,
+ ListClustersRequest.class,
+ ecsClient::listClusters,
+ () -> {
+ ListClustersRequest.Builder builder = ListClustersRequest.builder();
+ Integer maxResults = getOptionalHeader(exchange, ECS2Constants.MAX_RESULTS, Integer.class);
+ if (maxResults != null) {
+ builder.maxResults(maxResults);
+ }
+ String nextToken = getOptionalHeader(exchange, ECS2Constants.NEXT_TOKEN, String.class);
+ if (nextToken != null) {
+ builder.nextToken(nextToken);
+ }
+ return ecsClient.listClusters(builder.build());
+ },
+ "List Clusters",
+ (ListClustersResponse response, Message message) -> {
+ message.setHeader(ECS2Constants.NEXT_TOKEN, response.nextToken());
+ message.setHeader(ECS2Constants.IS_TRUNCATED, response.nextToken() != null);
+ });
}
private void createCluster(EcsClient ecsClient, Exchange exchange) throws InvalidPayloadException {
- if (getConfiguration().isPojoRequest()) {
- Object payload = exchange.getIn().getMandatoryBody();
- if (payload instanceof CreateClusterRequest) {
- CreateClusterResponse result;
- try {
- CreateClusterRequest request = (CreateClusterRequest) payload;
- result = ecsClient.createCluster(request);
- } catch (AwsServiceException ase) {
- LOG.trace("Create Cluster command returned the error code {}", ase.awsErrorDetails().errorCode());
- throw ase;
- }
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
- } else {
- CreateClusterRequest.Builder builder = CreateClusterRequest.builder();
- if (ObjectHelper.isNotEmpty(exchange.getIn().getHeader(ECS2Constants.CLUSTER_NAME))) {
- String name = exchange.getIn().getHeader(ECS2Constants.CLUSTER_NAME, String.class);
- builder.clusterName(name);
- }
- CreateClusterResponse result;
- try {
- CreateClusterRequest request = builder.build();
- result = ecsClient.createCluster(request);
- } catch (AwsServiceException ase) {
- LOG.trace("Create Cluster command returned the error code {}", ase.awsErrorDetails().errorCode());
- throw ase;
- }
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
+ executeOperation(
+ exchange,
+ CreateClusterRequest.class,
+ ecsClient::createCluster,
+ () -> {
+ CreateClusterRequest.Builder builder = CreateClusterRequest.builder();
+ String clusterName = getOptionalHeader(exchange, ECS2Constants.CLUSTER_NAME, String.class);
+ if (clusterName != null) {
+ builder.clusterName(clusterName);
+ }
+ return ecsClient.createCluster(builder.build());
+ },
+ "Create Cluster",
+ (CreateClusterResponse response, Message message) -> {
+ if (response.cluster() != null) {
+ message.setHeader(ECS2Constants.CLUSTER_ARN, response.cluster().clusterArn());
+ }
+ });
}
private void describeCluster(EcsClient ecsClient, Exchange exchange) throws InvalidPayloadException {
- if (getConfiguration().isPojoRequest()) {
- Object payload = exchange.getIn().getMandatoryBody();
- if (payload instanceof DescribeClustersRequest) {
- DescribeClustersResponse result;
- try {
- DescribeClustersRequest request = (DescribeClustersRequest) payload;
- result = ecsClient.describeClusters(request);
- } catch (AwsServiceException ase) {
- LOG.trace("Describe Clusters command returned the error code {}", ase.awsErrorDetails().errorCode());
- throw ase;
- }
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
- } else {
- DescribeClustersRequest.Builder builder = DescribeClustersRequest.builder();
- if (ObjectHelper.isNotEmpty(exchange.getIn().getHeader(ECS2Constants.CLUSTER_NAME))) {
- String clusterName = exchange.getIn().getHeader(ECS2Constants.CLUSTER_NAME, String.class);
- builder.clusters(clusterName);
- }
- DescribeClustersResponse result;
- try {
- DescribeClustersRequest request = builder.build();
- result = ecsClient.describeClusters(request);
- } catch (AwsServiceException ase) {
- LOG.trace("Describe Clusters command returned the error code {}", ase.awsErrorDetails().errorCode());
- throw ase;
- }
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
+ executeOperation(
+ exchange,
+ DescribeClustersRequest.class,
+ ecsClient::describeClusters,
+ () -> {
+ DescribeClustersRequest.Builder builder = DescribeClustersRequest.builder();
+ String clusterName = getOptionalHeader(exchange, ECS2Constants.CLUSTER_NAME, String.class);
+ if (clusterName != null) {
+ builder.clusters(clusterName);
+ }
+ return ecsClient.describeClusters(builder.build());
+ },
+ "Describe Clusters",
+ (DescribeClustersResponse response, Message message) -> {
+ if (response.hasClusters() && !response.clusters().isEmpty()) {
+ message.setHeader(ECS2Constants.CLUSTER_ARN, response.clusters().get(0).clusterArn());
+ }
+ });
}
private void deleteCluster(EcsClient ecsClient, Exchange exchange) throws InvalidPayloadException {
+ executeOperation(
+ exchange,
+ DeleteClusterRequest.class,
+ ecsClient::deleteCluster,
+ () -> {
+ String clusterName = getRequiredHeader(exchange, ECS2Constants.CLUSTER_NAME, String.class,
+ "Cluster name must be specified");
+ return ecsClient.deleteCluster(DeleteClusterRequest.builder().cluster(clusterName).build());
+ },
+ "Delete Cluster",
+ (DeleteClusterResponse response, Message message) -> {
+ if (response.cluster() != null) {
+ message.setHeader(ECS2Constants.CLUSTER_ARN, response.cluster().clusterArn());
+ }
+ });
+ }
+
+ public static Message getMessageForResponse(final Exchange exchange) {
+ return exchange.getMessage();
+ }
+
+ /**
+ * Executes an ECS operation with POJO request support.
+ */
+ private <REQ, RES> void executeOperation(
+ Exchange exchange,
+ Class<REQ> requestClass,
+ Function<REQ, RES> pojoExecutor,
+ Supplier<RES> headerExecutor,
+ String operationName)
+ throws InvalidPayloadException {
+ executeOperation(exchange, requestClass, pojoExecutor, headerExecutor, operationName, null);
+ }
+
+ /**
+ * Executes an ECS operation with POJO request support and optional response post-processing.
+ */
+ private <REQ, RES> void executeOperation(
+ Exchange exchange,
+ Class<REQ> requestClass,
+ Function<REQ, RES> pojoExecutor,
+ Supplier<RES> headerExecutor,
+ String operationName,
+ BiConsumer<RES, Message> responseProcessor)
+ throws InvalidPayloadException {
+
+ RES result;
if (getConfiguration().isPojoRequest()) {
Object payload = exchange.getIn().getMandatoryBody();
- if (payload instanceof DeleteClusterRequest) {
- DeleteClusterResponse result;
+ if (requestClass.isInstance(payload)) {
try {
- DeleteClusterRequest request = (DeleteClusterRequest) payload;
- result = ecsClient.deleteCluster(request);
+ result = pojoExecutor.apply(requestClass.cast(payload));
} catch (AwsServiceException ase) {
- LOG.trace("Delete Cluster command returned the error code {}", ase.awsErrorDetails().errorCode());
+ LOG.trace("{} command returned the error code {}", operationName, ase.awsErrorDetails().errorCode());
throw ase;
}
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
- }
- } else {
- DeleteClusterRequest.Builder builder = DeleteClusterRequest.builder();
- if (ObjectHelper.isNotEmpty(exchange.getIn().getHeader(ECS2Constants.CLUSTER_NAME))) {
- String name = exchange.getIn().getHeader(ECS2Constants.CLUSTER_NAME, String.class);
- builder.cluster(name);
} else {
- throw new IllegalArgumentException("Cluster name must be specified");
+ throw new IllegalArgumentException(
+ String.format("Expected body of type %s but was %s",
+ requestClass.getName(),
+ payload != null ? payload.getClass().getName() : "null"));
}
- DeleteClusterResponse result;
+ } else {
try {
- DeleteClusterRequest request = builder.build();
- result = ecsClient.deleteCluster(request);
+ result = headerExecutor.get();
} catch (AwsServiceException ase) {
- LOG.trace("Delete Cluster command returned the error code {}", ase.awsErrorDetails().errorCode());
+ LOG.trace("{} command returned the error code {}", operationName, ase.awsErrorDetails().errorCode());
throw ase;
}
- Message message = getMessageForResponse(exchange);
- message.setBody(result);
+ }
+ Message message = getMessageForResponse(exchange);
+ message.setBody(result);
+ if (responseProcessor != null) {
+ responseProcessor.accept(result, message);
}
}
- public static Message getMessageForResponse(final Exchange exchange) {
- return exchange.getMessage();
+ /**
+ * Gets a required header value or throws an IllegalArgumentException.
+ */
+ private <T> T getRequiredHeader(Exchange exchange, String headerName, Class<T> headerType, String errorMessage) {
+ T value = exchange.getIn().getHeader(headerName, headerType);
+ if (ObjectHelper.isEmpty(value)) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ return value;
+ }
+
+ /**
+ * Gets an optional header value.
+ */
+ private <T> T getOptionalHeader(Exchange exchange, String headerName, Class<T> headerType) {
+ return exchange.getIn().getHeader(headerName, headerType);
}
@Override
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ECS2EndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ECS2EndpointBuilderFactory.java
index 89b2befe7871..c964a3c5de08 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ECS2EndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ECS2EndpointBuilderFactory.java
@@ -627,6 +627,42 @@ public interface ECS2EndpointBuilderFactory {
public String awsECSClusterName() {
return "CamelAwsECSClusterName";
}
+ /**
+ * The token for the next set of results.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: listClusters
+ *
+ * @return the name of the header {@code AwsECSNextToken}.
+ */
+ public String awsECSNextToken() {
+ return "CamelAwsECSNextToken";
+ }
+ /**
+ * Whether the response has more results (is truncated).
+ *
+ * The option is a: {@code Boolean} type.
+ *
+ * Group: listClusters
+ *
+ * @return the name of the header {@code AwsECSIsTruncated}.
+ */
+ public String awsECSIsTruncated() {
+ return "CamelAwsECSIsTruncated";
+ }
+ /**
+ * The ARN of the cluster.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: createCluster describeCluster deleteCluster
+ *
+ * @return the name of the header {@code AwsECSClusterArn}.
+ */
+ public String awsECSClusterArn() {
+ return "CamelAwsECSClusterArn";
+ }
}
static ECS2EndpointBuilder endpointBuilder(String componentName, String path) {
class ECS2EndpointBuilderImpl extends AbstractEndpointBuilder implements ECS2EndpointBuilder, AdvancedECS2EndpointBuilder {