You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/02/19 12:55:08 UTC

(camel) 01/01: CAMEL-20381: camel-core - EIP model should include which exchange properties they set when using the EIP.

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

davsclaus pushed a commit to branch model-metadata
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 396d66fc219b9916b90c9c0f98e0cc61f3740835
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 19 13:54:47 2024 +0100

    CAMEL-20381: camel-core - EIP model should include which exchange properties they set when using the EIP.
---
 .../src/main/java/org/apache/camel/Exchange.java   |  6 +++
 .../resources/org/apache/camel/model/loop.json     |  4 ++
 .../resources/org/apache/camel/model/split.json    |  5 +++
 .../org/apache/camel/tooling/model/EipModel.java   | 12 ++++++
 .../org/apache/camel/tooling/model/JsonMapper.java | 24 +++++++++---
 .../camel/maven/packaging/SchemaGeneratorMojo.java | 44 +++++++++++++++++++++-
 6 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/Exchange.java b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
index 3495a865391..4ad323018c1 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Exchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
@@ -19,6 +19,7 @@ package org.apache.camel;
 import java.util.Map;
 
 import org.apache.camel.clock.Clock;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UnitOfWork;
 import org.apache.camel.spi.annotations.ConstantProvider;
 
@@ -177,7 +178,9 @@ public interface Exchange extends VariableAware {
     String LOG_DEBUG_BODY_MAX_CHARS = "CamelLogDebugBodyMaxChars";
     String LOG_DEBUG_BODY_STREAMS = "CamelLogDebugStreams";
     String LOG_EIP_NAME = "CamelLogEipName";
+    @Metadata(label = "loop", description = "Index of the current iteration (0 based).", javaType = "int")
     String LOOP_INDEX = "CamelLoopIndex";
+    @Metadata(label = "loop", description = "Total number of loops. This is not available if running the loop in while loop mode.", javaType = "int")
     String LOOP_SIZE = "CamelLoopSize";
 
     // Long running action (saga): using "Long-Running-Action" as header value allows sagas
@@ -237,8 +240,11 @@ public interface Exchange extends VariableAware {
     String SKIP_WWW_FORM_URLENCODED = "CamelSkipWwwFormUrlEncoding";
     String SLIP_ENDPOINT = "CamelSlipEndpoint";
     String SLIP_PRODUCER = "CamelSlipProducer";
+    @Metadata(label = "split", description = "A split counter that increases for each Exchange being split. The counter starts from 0.", javaType = "int")
     String SPLIT_INDEX = "CamelSplitIndex";
+    @Metadata(label = "split", description = "Whether this Exchange is the last.", javaType = "boolean")
     String SPLIT_COMPLETE = "CamelSplitComplete";
+    @Metadata(label = "split", description = "The total number of Exchanges that was split. This property is not applied for stream based splitting, except for the very last message because then Camel knows the total size.", javaType = "int")
     String SPLIT_SIZE = "CamelSplitSize";
     String STEP_ID = "CamelStepId";
 
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json
index 47ecdaae33d..8985d2f5af7 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json
@@ -20,5 +20,9 @@
     "doWhile": { "index": 5, "kind": "attribute", "displayName": "Do While", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enables the while loop that loops until the predicate evaluates to false or null." },
     "breakOnShutdown": { "index": 6, "kind": "attribute", "displayName": "Break On Shutdown", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the breakOnShutdown attribute is true, then the loop will not iterate until it reaches the end when Camel is shut down." },
     "outputs": { "index": 7, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
+  },
+  "exchangeProperties": {
+    "CamelLoopIndex": { "index": 0, "kind": "exchangeProperty", "displayName": "Loop Index", "required": false, "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "Index of the current iteration (0 based)." },
+    "CamelLoopSize": { "index": 1, "kind": "exchangeProperty", "displayName": "Loop Size", "required": false, "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "Total number of loops. This is not available if running the loop in while loop mode." }
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json
index beddd3dbbe8..73db824f70f 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json
@@ -30,5 +30,10 @@
     "onPrepare": { "index": 15, "kind": "attribute", "displayName": "On Prepare", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.Processor", "deprecated": false, "autowired": false, "secret": false, "description": "Uses the Processor when preparing the org.apache.camel.Exchange to be sent. This can be used to deep-clone messages that should be sent, or any custom logic needed before the exchange is sent." },
     "shareUnitOfWork": { "index": 16, "kind": "attribute", "displayName": "Share Unit Of Work", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Shares the org.apache.camel.spi.UnitOfWork with the parent and each of the sub messages. Splitter will by default not share unit of work between the parent exchange and each split exchange. This means each s [...]
     "outputs": { "index": 17, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", " [...]
+  },
+  "exchangeProperties": {
+    "CamelSplitIndex": { "index": 0, "kind": "exchangeProperty", "displayName": "Split Index", "required": false, "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "A split counter that increases for each Exchange being split. The counter starts from 0." },
+    "CamelSplitComplete": { "index": 1, "kind": "exchangeProperty", "displayName": "Split Complete", "required": false, "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether this Exchange is the last." },
+    "CamelSplitSize": { "index": 2, "kind": "exchangeProperty", "displayName": "Split Size", "required": false, "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The total number of Exchanges that was split. This property is not applied for stream based splitting, except for the very last message because then Camel knows the total size." }
   }
 }
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java
index 256eb5e582b..88b8676ec08 100644
--- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java
@@ -16,11 +16,15 @@
  */
 package org.apache.camel.tooling.model;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class EipModel extends BaseModel<EipModel.EipOptionModel> {
 
     protected boolean abstractModel;  // used in models from camel-core-engine
     protected boolean input;          // used in models from camel-core-engine
     protected boolean output;         // used in models from camel-core-engine
+    protected final List<EipModel.EipOptionModel> exchangeProperties = new ArrayList<>();
 
     public EipModel() {
     }
@@ -54,6 +58,14 @@ public class EipModel extends BaseModel<EipModel.EipOptionModel> {
         this.output = output;
     }
 
+    public List<EipOptionModel> getExchangeProperties() {
+        return exchangeProperties;
+    }
+
+    public void addExchangeProperty(EipOptionModel property) {
+        exchangeProperties.add(property);
+    }
+
     public String getDocLink() {
         // lets store EIP docs in a sub-folder as we have many EIPs
         return "src/main/docs/eips/";
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
index 81f129e8d7c..69e3333359f 100644
--- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
@@ -294,11 +294,22 @@ public final class JsonMapper {
         model.setInput(mobj.getBooleanOrDefault("input", false));
         model.setOutput(mobj.getBooleanOrDefault("output", false));
         JsonObject mprp = (JsonObject) obj.get("properties");
-        for (Map.Entry<String, Object> entry : mprp.entrySet()) {
-            JsonObject mp = (JsonObject) entry.getValue();
-            EipOptionModel option = new EipOptionModel();
-            parseOption(mp, option, entry.getKey());
-            model.addOption(option);
+        if (mprp != null) {
+            for (Map.Entry<String, Object> entry : mprp.entrySet()) {
+                JsonObject mp = (JsonObject) entry.getValue();
+                EipOptionModel option = new EipOptionModel();
+                parseOption(mp, option, entry.getKey());
+                model.addOption(option);
+            }
+        }
+        mprp = (JsonObject) obj.get("exchangeProperties");
+        if (mprp != null) {
+            for (Map.Entry<String, Object> entry : mprp.entrySet()) {
+                JsonObject mp = (JsonObject) entry.getValue();
+                EipOptionModel option = new EipOptionModel();
+                parseOption(mp, option, entry.getKey());
+                model.addExchangeProperty(option);
+            }
         }
         return model;
     }
@@ -318,6 +329,9 @@ public final class JsonMapper {
         JsonObject wrapper = new JsonObject();
         wrapper.put("model", obj);
         wrapper.put("properties", asJsonObject(model.getOptions()));
+        if (!model.getExchangeProperties().isEmpty()) {
+            wrapper.put("exchangeProperties", asJsonObject(model.getExchangeProperties()));
+        }
         return wrapper;
     }
 
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SchemaGeneratorMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SchemaGeneratorMojo.java
index 4ac704452c5..cfb668e0a3e 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SchemaGeneratorMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SchemaGeneratorMojo.java
@@ -74,6 +74,8 @@ import org.jboss.jandex.ClassInfo.NestingType;
 import org.jboss.jandex.DotName;
 import org.jboss.jandex.IndexView;
 
+import static java.lang.reflect.Modifier.isStatic;
+
 @Mojo(name = "generate-schema", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
       defaultPhase = LifecyclePhase.PROCESS_CLASSES)
 public class SchemaGeneratorMojo extends AbstractGeneratorMojo {
@@ -247,8 +249,9 @@ public class SchemaGeneratorMojo extends AbstractGeneratorMojo {
         String javaTypeName = element.name().toString();
         Class<?> classElement = loadClass(javaTypeName);
 
-        // gather eip information
+        // gather eip information and what metadata the EIP can store as exchange properties
         EipModel eipModel = findEipModelProperties(classElement, name);
+        findEipModelExchangeProperties(classElement, name, eipModel);
 
         // get endpoint information which is divided into paths and options
         // (though there should really only be one path)
@@ -328,6 +331,45 @@ public class SchemaGeneratorMojo extends AbstractGeneratorMojo {
         return model;
     }
 
+    protected void findEipModelExchangeProperties(Class<?> classElement, String name, EipModel eipModel) {
+        // load Exchange and find options for this EIP
+        Class<?> clazz = loadClass("org.apache.camel.Exchange");
+        for (Field field : clazz.getFields()) {
+            if ((isStatic(field.getModifiers()) && field.getType() == String.class)
+                    && field.isAnnotationPresent(Metadata.class)) {
+                Metadata metadata = field.getAnnotation(Metadata.class);
+                String labels = metadata.label();
+                for (String lab : labels.split(",")) {
+                    if (lab.equals(name)) {
+                        EipOptionModel o = new EipOptionModel();
+                        o.setKind("exchangeProperty");
+                        // SPLIT_INDEX => CamelSplitIndex
+                        String n = field.getName().toLowerCase().replace('_', '-');
+                        String n2 = SchemaHelper.dashToCamelCase(n);
+                        String valueName = "Camel" + Character.toUpperCase(n2.charAt(0)) + n2.substring(1);
+                        o.setName(valueName);
+                        o.setDescription(metadata.description());
+                        if (!metadata.displayName().isEmpty()) {
+                            o.setDisplayName(metadata.displayName());
+                        } else {
+                            o.setDisplayName(Strings.asTitle(o.getName().substring(5)));
+                        }
+                        o.setRequired(metadata.required());
+                        o.setDefaultValue(metadata.defaultValue());
+                        o.setDeprecated(field.isAnnotationPresent(Deprecated.class));
+                        if (!metadata.deprecationNote().isEmpty()) {
+                            o.setDeprecationNote(metadata.deprecationNote());
+                        }
+                        o.setSecret(metadata.secret());
+                        o.setJavaType(metadata.javaType());
+                        eipModel.addExchangeProperty(o);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
     protected void findClassProperties(
             Set<EipOptionModel> eipOptions,
             Class<?> originalClassType, Class<?> classElement,