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 2022/04/06 17:35:53 UTC

[camel] branch main updated: CAMEL-17897: camel-exec - add exitValues parameter (#7376)

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 e657c37d515 CAMEL-17897: camel-exec - add exitValues parameter (#7376)
e657c37d515 is described below

commit e657c37d515d156397648edd8221b812a01236d7
Author: sbalmer <sb...@cis.ch>
AuthorDate: Wed Apr 6 19:35:46 2022 +0200

    CAMEL-17897: camel-exec - add exitValues parameter (#7376)
    
    Co-authored-by: Stephan Balmer <st...@meteotest.ch>
---
 .../org/apache/camel/catalog/components/exec.json  | 51 ----------------------
 .../component/exec/ExecEndpointConfigurer.java     |  6 +++
 .../component/exec/ExecEndpointUriFactory.java     |  3 +-
 .../org/apache/camel/component/exec/exec.json      |  2 +
 .../apache/camel/component/exec/ExecBinding.java   | 10 +++++
 .../apache/camel/component/exec/ExecCommand.java   | 19 +++++++-
 .../apache/camel/component/exec/ExecEndpoint.java  | 15 +++++++
 .../component/exec/impl/DefaultExecBinding.java    | 15 ++++++-
 .../exec/impl/DefaultExecCommandExecutor.java      |  6 +++
 .../camel/component/exec/impl/ExecParseUtils.java  |  9 ++++
 .../camel/component/exec/ExecEndpointTest.java     | 14 ++++++
 .../camel/component/exec/ExecProducerTest.java     | 28 ++++++++++++
 .../endpoint/dsl/ExecEndpointBuilderFactory.java   | 17 ++++++++
 13 files changed, 139 insertions(+), 56 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json
deleted file mode 100644
index f53b85b1274..00000000000
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-  "component": {
-    "kind": "component",
-    "name": "exec",
-    "title": "Exec",
-    "description": "Execute commands on the underlying operating system.",
-    "deprecated": false,
-    "firstVersion": "2.3.0",
-    "label": "system",
-    "javaType": "org.apache.camel.component.exec.ExecComponent",
-    "supportLevel": "Stable",
-    "groupId": "org.apache.camel",
-    "artifactId": "camel-exec",
-    "version": "3.17.0-SNAPSHOT",
-    "scheme": "exec",
-    "extendsScheme": "",
-    "syntax": "exec:executable",
-    "async": false,
-    "api": false,
-    "consumerOnly": false,
-    "producerOnly": true,
-    "lenientProperties": false
-  },
-  "componentProperties": {
-    "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during star [...]
-    "autowiredEnabled": { "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which t [...]
-  },
-  "headers": {
-    "CamelExecCommandExecutable": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the system command that will be executed. Overrides executable in the URI." },
-    "CamelExecCommandArgs": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "java.util.List<String> or String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Command-line argument(s) to pass to the executed process. The argument(s) is\/are used literally - no quoting is applied. Overrides any existing args in the URI." },
-    "CamelExecCommandOutFile": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. Overrides any existing outFile in the URI." },
-    "CamelExecCommandWorkingDir": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The directory in which the command should be executed. Overrides any existing workingDir in the URI." },
-    "CamelExecCommandTimeout": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The timeout, in milliseconds, after which the executable should be terminated. Overrides any existing timeout in the URI." },
-    "CamelExecStderr": { "kind": "header", "displayName": "", "group": "out", "label": "out", "required": false, "javaType": "java.io.InputStream", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The value of this header points to the standard error stream (stderr) of the executable. If no stderr is written, the value is null." },
-    "CamelExecExitValue": { "kind": "header", "displayName": "", "group": "out", "label": "out", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The value of this header is the _exit value_ of the executable. Non-zero exit values typically indicate abnormal termination. Note that the exit value is OS-dependent." },
-    "CamelExecUseStderrOnEmptyStdout": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Indicates that when stdout is empty, this component will populate the Camel Message Body with stderr. This behavior is disabled (false) by default." },
-    "CamelExecCommandLogLevel": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR or OFF (Values of LoggingLevel enum)" }
-  },
-  "properties": {
-    "executable": { "kind": "path", "displayName": "Executable", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Sets the executable to be executed. The executable must not be empty or null." },
-    "args": { "kind": "parameter", "displayName": "Args", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The arguments may be one or many whitespace-separated tokens." },
-    "binding": { "kind": "parameter", "displayName": "Binding", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecBinding in the Registry." },
-    "commandExecutor": { "kind": "parameter", "displayName": "Command Executor", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecCommandExecutor in the Registry that customizes the command execution. The default command executor utilizes the commons-exec library, which adds a shutdo [...]
-    "commandLogLevel": { "kind": "parameter", "displayName": "Command Log Level", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "DEBUG", "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERR [...]
-    "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during sta [...]
-    "outFile": { "kind": "parameter", "displayName": "Out File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. If no outFile is set, the standard output (stdout) of the executable will be used instead." },
-    "timeout": { "kind": "parameter", "displayName": "Timeout", "group": "producer", "label": "", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "description": "The timeout, in milliseconds, after which the executable should be terminated. If execution has not completed within the timeout, the component will send a termination request." },
-    "useStderrOnEmptyStdout": { "kind": "parameter", "displayName": "Use Stderr On Empty Stdout", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "A boolean indicating that when stdout is empty, this component will populate the Camel Message Body with stderr. This behavior is disabled (false) by default." },
-    "workingDir": { "kind": "parameter", "displayName": "Working Dir", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The directory in which the command should be executed. If null, the working directory of the current process will be used." }
-  }
-}
diff --git a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java
index dfabbb7f22e..fee0b475eeb 100644
--- a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java
+++ b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java
@@ -27,6 +27,8 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements
         case "commandExecutor": target.setCommandExecutor(property(camelContext, org.apache.camel.component.exec.ExecCommandExecutor.class, value)); return true;
         case "commandloglevel":
         case "commandLogLevel": target.setCommandLogLevel(property(camelContext, org.apache.camel.LoggingLevel.class, value)); return true;
+        case "exitvalues":
+        case "exitValues": target.setExitValues(property(camelContext, java.lang.String.class, value)); return true;
         case "lazystartproducer":
         case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
         case "outfile":
@@ -49,6 +51,8 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements
         case "commandExecutor": return org.apache.camel.component.exec.ExecCommandExecutor.class;
         case "commandloglevel":
         case "commandLogLevel": return org.apache.camel.LoggingLevel.class;
+        case "exitvalues":
+        case "exitValues": return java.lang.String.class;
         case "lazystartproducer":
         case "lazyStartProducer": return boolean.class;
         case "outfile":
@@ -72,6 +76,8 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements
         case "commandExecutor": return target.getCommandExecutor();
         case "commandloglevel":
         case "commandLogLevel": return target.getCommandLogLevel();
+        case "exitvalues":
+        case "exitValues": return target.getExitValues();
         case "lazystartproducer":
         case "lazyStartProducer": return target.isLazyStartProducer();
         case "outfile":
diff --git a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java
index b1c237c8ca6..e6d32573e4c 100644
--- a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java
+++ b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java
@@ -21,12 +21,13 @@ public class ExecEndpointUriFactory extends org.apache.camel.support.component.E
     private static final Set<String> SECRET_PROPERTY_NAMES;
     private static final Set<String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(10);
+        Set<String> props = new HashSet<>(11);
         props.add("args");
         props.add("binding");
         props.add("commandExecutor");
         props.add("commandLogLevel");
         props.add("executable");
+        props.add("exitValues");
         props.add("lazyStartProducer");
         props.add("outFile");
         props.add("timeout");
diff --git a/components/camel-exec/src/generated/resources/org/apache/camel/component/exec/exec.json b/components/camel-exec/src/generated/resources/org/apache/camel/component/exec/exec.json
index f53b85b1274..2e0dcc77377 100644
--- a/components/camel-exec/src/generated/resources/org/apache/camel/component/exec/exec.json
+++ b/components/camel-exec/src/generated/resources/org/apache/camel/component/exec/exec.json
@@ -31,6 +31,7 @@
     "CamelExecCommandOutFile": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. Overrides any existing outFile in the URI." },
     "CamelExecCommandWorkingDir": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The directory in which the command should be executed. Overrides any existing workingDir in the URI." },
     "CamelExecCommandTimeout": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The timeout, in milliseconds, after which the executable should be terminated. Overrides any existing timeout in the URI." },
+    "CamelExecExitValues": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The exit values for successful execution of the process. Overrides any existing exitValues in the URI." },
     "CamelExecStderr": { "kind": "header", "displayName": "", "group": "out", "label": "out", "required": false, "javaType": "java.io.InputStream", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The value of this header points to the standard error stream (stderr) of the executable. If no stderr is written, the value is null." },
     "CamelExecExitValue": { "kind": "header", "displayName": "", "group": "out", "label": "out", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The value of this header is the _exit value_ of the executable. Non-zero exit values typically indicate abnormal termination. Note that the exit value is OS-dependent." },
     "CamelExecUseStderrOnEmptyStdout": { "kind": "header", "displayName": "", "group": "in", "label": "in", "required": false, "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Indicates that when stdout is empty, this component will populate the Camel Message Body with stderr. This behavior is disabled (false) by default." },
@@ -42,6 +43,7 @@
     "binding": { "kind": "parameter", "displayName": "Binding", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecBinding in the Registry." },
     "commandExecutor": { "kind": "parameter", "displayName": "Command Executor", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecCommandExecutor in the Registry that customizes the command execution. The default command executor utilizes the commons-exec library, which adds a shutdo [...]
     "commandLogLevel": { "kind": "parameter", "displayName": "Command Log Level", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "DEBUG", "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERR [...]
+    "exitValues": { "kind": "parameter", "displayName": "Exit Values", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The exit values of successful executions. If the process exits with another value, an exception is raised. Comma-separated list of exit values. And empty list (the default) sets no expected exit values and disables the check." },
     "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during sta [...]
     "outFile": { "kind": "parameter", "displayName": "Out File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. If no outFile is set, the standard output (stdout) of the executable will be used instead." },
     "timeout": { "kind": "parameter", "displayName": "Timeout", "group": "producer", "label": "", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "description": "The timeout, in milliseconds, after which the executable should be terminated. If execution has not completed within the timeout, the component will send a termination request." },
diff --git a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
index dfcee03f0bf..a481088837f 100644
--- a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
+++ b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
@@ -81,6 +81,16 @@ public interface ExecBinding {
               javaType = "long")
     String EXEC_COMMAND_TIMEOUT = "CamelExecCommandTimeout";
 
+    /**
+     * Which exit values of the process are considered a success. When the process exits with a value not in this list,
+     * an ExecuteException is raised. When the list is empty (the default), no exception is raised based on the exit
+     * value. Example:
+     */
+    @Metadata(label = "in", description = "The exit values for successful execution of the process.\n" +
+                                          "Overrides any existing `exitValues` in the URI.",
+              javaType = "String")
+    String EXEC_COMMAND_EXIT_VALUES = "CamelExecExitValues";
+
     /**
      * The value of this header is a {@link InputStream} with the standard error stream of the executable.
      */
diff --git a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecCommand.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecCommand.java
index 7ed02c7c6cb..a0d256d4f5f 100644
--- a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecCommand.java
+++ b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecCommand.java
@@ -22,6 +22,8 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.camel.LoggingLevel;
 
@@ -39,6 +41,11 @@ public class ExecCommand implements Serializable {
      */
     private final String executable;
 
+    /**
+     * @see ExecBinding#EXEC_COMMAND_EXIT_VALUES
+     */
+    private final Set<Integer> exitValues;
+
     /**
      * @see ExecBinding#EXEC_COMMAND_ARGS
      */
@@ -71,10 +78,11 @@ public class ExecCommand implements Serializable {
 
     private final boolean useStderrOnEmptyStdout;
 
-    public ExecCommand(String executable, List<String> args, String workingDir, Long timeout,
+    public ExecCommand(String executable, List<String> args, String workingDir, Long timeout, Set<Integer> exitValues,
                        InputStream input, File outFile, boolean useStderrOnEmptyStdout, LoggingLevel commandLogLevel) {
         notNull(executable, "command executable");
         this.executable = executable;
+        this.exitValues = exitValues;
         this.args = unmodifiableOrEmptyList(args);
         this.workingDir = workingDir;
         this.timeout = timeout;
@@ -92,6 +100,10 @@ public class ExecCommand implements Serializable {
         return executable;
     }
 
+    public Set<Integer> getExitValues() {
+        return exitValues;
+    }
+
     public InputStream getInput() {
         return input;
     }
@@ -120,7 +132,10 @@ public class ExecCommand implements Serializable {
     public String toString() {
         String dirToPrint = workingDir == null ? "null" : workingDir;
         String outFileToPrint = outFile == null ? "null" : outFile.getPath();
-        return "ExecCommand [args=" + args + ", executable=" + executable + ", timeout=" + timeout + ", outFile="
+        String exitValuesString = exitValues.stream().map(Object::toString)
+                .collect(Collectors.joining(","));
+        return "ExecCommand [args=" + args + ", executable=" + executable + ", timeout=" + timeout
+               + ", exitValues=" + exitValuesString + ", outFile="
                + outFileToPrint + ", workingDir=" + dirToPrint + ", commandLogLevel=" + commandLogLevel
                + ", useStderrOnEmptyStdout=" + useStderrOnEmptyStdout + "]";
     }
diff --git a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
index 5a54d777fec..f8219de8e9c 100644
--- a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
+++ b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
@@ -52,6 +52,8 @@ public class ExecEndpoint extends DefaultEndpoint {
     @UriParam(javaType = "java.time.Duration")
     private long timeout;
     @UriParam
+    private String exitValues;
+    @UriParam
     private String outFile;
     @UriParam
     private ExecCommandExecutor commandExecutor;
@@ -128,6 +130,19 @@ public class ExecEndpoint extends DefaultEndpoint {
         this.timeout = timeout;
     }
 
+    public String getExitValues() {
+        return exitValues;
+    }
+
+    /**
+     * The exit values of successful executions. If the process exits with another value, an exception is raised.
+     * Comma-separated list of exit values. And empty list (the default) sets no expected exit values and disables the
+     * check.
+     */
+    public void setExitValues(String exitValues) {
+        this.exitValues = exitValues;
+    }
+
     public String getOutFile() {
         return outFile;
     }
diff --git a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
index fe93cff6fda..d15dd9eb19d 100644
--- a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
+++ b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
@@ -18,7 +18,9 @@ package org.apache.camel.component.exec.impl;
 
 import java.io.File;
 import java.io.InputStream;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.LoggingLevel;
@@ -31,11 +33,12 @@ import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.camel.component.exec.impl.ExecParseUtils.splitCommaSeparatedToListOfInts;
 import static org.apache.camel.component.exec.impl.ExecParseUtils.splitToWhiteSpaceSeparatedTokens;
 
 /**
  * Default implementation of {@link ExecBinding}.
- * 
+ *
  * @see DefaultExecBinding#writeOutputInMessage(Message, ExecResult)
  */
 public class DefaultExecBinding implements ExecBinding {
@@ -53,6 +56,8 @@ public class DefaultExecBinding implements ExecBinding {
         String cmd = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_EXECUTABLE, endpoint.getExecutable(), String.class);
         String dir = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_WORKING_DIR, endpoint.getWorkingDir(), String.class);
         long timeout = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_TIMEOUT, endpoint.getTimeout(), Long.class);
+        String exitValuesString
+                = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_EXIT_VALUES, endpoint.getExitValues(), String.class);
         String outFilePath = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_OUT_FILE, endpoint.getOutFile(), String.class);
         boolean useStderrOnEmptyStdout = getAndRemoveHeader(exchange.getIn(), EXEC_USE_STDERR_ON_EMPTY_STDOUT,
                 endpoint.isUseStderrOnEmptyStdout(), Boolean.class);
@@ -77,8 +82,14 @@ public class DefaultExecBinding implements ExecBinding {
             argsList = splitToWhiteSpaceSeparatedTokens(s);
         }
 
+        Set<Integer> exitValues = new HashSet<Integer>();
+        if (exitValuesString != null && exitValuesString.length() > 0) {
+            exitValues = new HashSet<Integer>(splitCommaSeparatedToListOfInts(exitValuesString));
+        }
+
         File outFile = outFilePath == null ? null : new File(outFilePath);
-        return new ExecCommand(cmd, argsList, dir, timeout, input, outFile, useStderrOnEmptyStdout, commandLogLevel);
+        return new ExecCommand(
+                cmd, argsList, dir, timeout, exitValues, input, outFile, useStderrOnEmptyStdout, commandLogLevel);
     }
 
     private boolean isListOfStrings(Object o) {
diff --git a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
index 91bd423c839..dd50d7dfc2f 100644
--- a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
+++ b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 import org.apache.camel.component.exec.ExecCommand;
 import org.apache.camel.component.exec.ExecCommandExecutor;
@@ -112,7 +113,12 @@ public class DefaultExecCommandExecutor implements ExecCommandExecutor {
 
     protected DefaultExecutor prepareDefaultExecutor(ExecCommand execCommand) {
         DefaultExecutor executor = new ExecDefaultExecutor();
+
         executor.setExitValues(null);
+        Set<Integer> exitValues = execCommand.getExitValues();
+        if (exitValues.size() > 0) {
+            executor.setExitValues(exitValues.stream().mapToInt(Integer::intValue).toArray());
+        }
 
         if (execCommand.getWorkingDir() != null) {
             executor.setWorkingDirectory(new File(execCommand.getWorkingDir()).getAbsoluteFile());
diff --git a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/ExecParseUtils.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/ExecParseUtils.java
index ee5c9ae101d..cf1777ec408 100644
--- a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/ExecParseUtils.java
+++ b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/ExecParseUtils.java
@@ -109,4 +109,13 @@ public final class ExecParseUtils {
         }
         return input.matches("(^" + QUOTE_CHAR + "{2}([^" + QUOTE_CHAR + "]+)" + QUOTE_CHAR + "{2})");
     }
+
+    public static List<Integer> splitCommaSeparatedToListOfInts(String commaSeparatedInts) {
+        List<Integer> exitValues = new ArrayList<>();
+        StringTokenizer st = new StringTokenizer(commaSeparatedInts, ",");
+        while (st.hasMoreTokens()) {
+            exitValues.add(Integer.valueOf(st.nextToken()));
+        }
+        return exitValues;
+    }
 }
diff --git a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
index a34e8010d00..81c4c304a6b 100644
--- a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
+++ b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
@@ -140,6 +140,20 @@ public class ExecEndpointTest {
         assertEquals(timeout, e.getTimeout());
     }
 
+    @Test
+    @DirtiesContext
+    public void testCreateEndpointWithExitValues() throws Exception {
+        ExecEndpoint e = createExecEndpoint("exec:test?exitValues=1,2,3");
+        assertEquals("1,2,3", e.getExitValues());
+    }
+
+    @Test
+    @DirtiesContext
+    public void testCreateEndpointWithEmptyExitValues() throws Exception {
+        ExecEndpoint e = createExecEndpoint("exec:test?exitValues=");
+        assertEquals("", e.getExitValues());
+    }
+
     @Test
     @DirtiesContext
     public void testCreateEndpointWithOutFile() throws Exception {
diff --git a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
index 23f3c81ce12..3372d5a1406 100644
--- a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
+++ b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
@@ -36,11 +36,13 @@ import org.springframework.test.context.ContextConfiguration;
 
 import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_ARGS;
 import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_EXECUTABLE;
+import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_EXIT_VALUES;
 import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_TIMEOUT;
 import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_WORKING_DIR;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Test the functionality of {@link ExecProducer}
@@ -112,6 +114,32 @@ public class ExecProducerTest {
         assertEquals(1000, execCommandExecutorMock.lastCommandResult.getCommand().getTimeout());
     }
 
+    @Test
+    @DirtiesContext
+    public void testExitValues() {
+        producerTemplate.send(new Processor() {
+
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setBody("noinput");
+                exchange.getIn().setHeader(EXEC_COMMAND_EXIT_VALUES, "0,1");
+            }
+        });
+        assertTrue(execCommandExecutorMock.lastCommandResult.getCommand().getExitValues().contains(1));
+    }
+
+    @Test
+    @DirtiesContext
+    public void testExitValueNone() {
+        producerTemplate.send(new Processor() {
+
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setBody("noinput");
+                exchange.getIn().setHeader(EXEC_COMMAND_EXIT_VALUES, "");
+            }
+        });
+        assertEquals(0, execCommandExecutorMock.lastCommandResult.getCommand().getExitValues().size());
+    }
+
     @Test
     @DirtiesContext
     public void testInputLines() throws IOException {
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
index f5418e0df3d..9d91296a8a5 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
@@ -159,6 +159,23 @@ public interface ExecEndpointBuilderFactory {
             doSetProperty("commandLogLevel", commandLogLevel);
             return this;
         }
+        /**
+         * The exit values of successful executions. If the process exits with
+         * another value, an exception is raised. Comma-separated list of exit
+         * values. And empty list (the default) sets no expected exit values and
+         * disables the check.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: producer
+         * 
+         * @param exitValues the value to set
+         * @return the dsl builder
+         */
+        default ExecEndpointBuilder exitValues(String exitValues) {
+            doSetProperty("exitValues", exitValues);
+            return this;
+        }
         /**
          * Whether the producer should be started lazy (on the first message).
          * By starting lazy you can use this to allow CamelContext and routes to