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

gnodet pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.22.x by this push:
     new 9d60fd8a2109 [backport camel-4.22.x] CAMEL-25459, CAMEL-25460, 
CAMEL-25461: camel-thrift - apply the SSL settings of the consumer and mute its 
exceptions (#28226)
9d60fd8a2109 is described below

commit 9d60fd8a21090d91e2180b2baa450dce095f892d
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Oct 21 14:15:58 2026 +0200

    [backport camel-4.22.x] CAMEL-25459, CAMEL-25460, CAMEL-25461: camel-thrift 
- apply the SSL settings of the consumer and mute its exceptions (#28226)
    
    CAMEL-25459, CAMEL-25460, CAMEL-25461: camel-thrift - apply the SSL 
settings of the consumer and mute its exceptions (#28154)
    
    CAMEL-25459: a consumer with negotiationType=SSL and synchronous=false now
    fails to start, as the producer does, instead of starting a server without
    SSL/TLS. The SASL negotiation type, which is not implemented, now makes the
    producer and the consumer fail to start instead of behaving as PLAINTEXT.
    
    CAMEL-25461: the SSL consumer creates its server socket from the SSL context
    of sslParameters, so all of its settings apply, including the trust managers
    and the server parameters such as clientAuthentication. As in the other
    components, the key is read with the keyPassword of the key managers.
    
    CAMEL-25460: a new muteException consumer option, enabled by default, sends 
a
    generic TApplicationException to the client instead of an exception that the
    Thrift method does not declare. Exceptions declared in the IDL are still 
sent
    as they are. On this branch the synchronous server does not send the 
exception
    of a failed exchange to the client, so the option applies to the 
asynchronous
    server.
    
    Ported from commit 3c848184a78d044c6e879b1acfca210e1646185c.
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../apache/camel/catalog/components/thrift.json    |  19 ++--
 .../camel/catalog/docs/thrift-component.adoc       |  49 +++++++++
 .../component/thrift/ThriftEndpointConfigurer.java |   6 +
 .../component/thrift/ThriftEndpointUriFactory.java |   3 +-
 .../org/apache/camel/component/thrift/thrift.json  |  19 ++--
 .../src/main/docs/thrift-component.adoc            |  49 +++++++++
 .../component/thrift/ThriftConfiguration.java      |  16 +++
 .../camel/component/thrift/ThriftConsumer.java     |  65 ++++++-----
 .../camel/component/thrift/ThriftProducer.java     |   3 +
 .../thrift/server/ThriftMethodHandler.java         |  34 +++++-
 .../ThriftConsumerClientAuthenticationTest.java    | 121 +++++++++++++++++++++
 .../thrift/ThriftConsumerMuteExceptionTest.java    | 105 ++++++++++++++++++
 .../thrift/ThriftConsumerSecurityTest.java         |   1 +
 .../thrift/ThriftNegotiationTypeStartupTest.java   |  90 +++++++++++++++
 .../endpoint/dsl/ThriftEndpointBuilderFactory.java |  36 ++++++
 15 files changed, 566 insertions(+), 50 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thrift.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thrift.json
index 51faccf61dac..3596e22bb5a8 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thrift.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thrift.json
@@ -40,14 +40,15 @@
     "exchangeProtocol": { "index": 4, "kind": "parameter", "displayName": 
"Exchange Protocol", "group": "common", "label": "", "required": false, "type": 
"enum", "javaType": "org.apache.camel.component.thrift.ThriftExchangeProtocol", 
"enum": [ "BINARY", "JSON", "SJSON", "COMPACT" ], "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "BINARY", 
"configurationClass": "org.apache.camel.component.thrift.ThriftConfiguration", 
"configurationField": "configuration", "descr [...]
     "clientTimeout": { "index": 5, "kind": "parameter", "displayName": "Client 
Timeout", "group": "consumer", "label": "consumer", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "Client timeout for consumers" },
     "maxPoolSize": { "index": 6, "kind": "parameter", "displayName": "Max Pool 
Size", "group": "consumer", "label": "consumer", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 10, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift server consumer max thread pool 
size" },
-    "poolSize": { "index": 7, "kind": "parameter", "displayName": "Pool Size", 
"group": "consumer", "label": "consumer", "required": false, "type": "integer", 
"javaType": "int", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift server consumer initial thread pool 
size" },
-    "bridgeErrorHandler": { "index": 8, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
-    "exceptionHandler": { "index": 9, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
-    "exchangePattern": { "index": 10, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
-    "method": { "index": 11, "kind": "parameter", "displayName": "Method", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift invoked method name" },
-    "lazyStartProducer": { "index": 12, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "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 produ [...]
-    "synchronous": { "index": 13, "kind": "parameter", "displayName": 
"Synchronous", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "Sets whether 
synchronous processing should be strictly used" },
-    "negotiationType": { "index": 14, "kind": "parameter", "displayName": 
"Negotiation Type", "group": "security", "label": "security", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.thrift.ThriftNegotiationType", "enum": [ 
"PLAINTEXT", "SSL", "SASL" ], "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "PLAINTEXT", "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "desc [...]
-    "sslParameters": { "index": 15, "kind": "parameter", "displayName": "Ssl 
Parameters", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "Configuration parameters for SSL\/TLS security 
negotiation" }
+    "muteException": { "index": 7, "kind": "parameter", "displayName": "Mute 
Exception", "group": "consumer", "label": "consumer", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "If enabled and an Exchange failed processing 
on the consumer side, the Thrift client r [...]
+    "poolSize": { "index": 8, "kind": "parameter", "displayName": "Pool Size", 
"group": "consumer", "label": "consumer", "required": false, "type": "integer", 
"javaType": "int", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift server consumer initial thread pool 
size" },
+    "bridgeErrorHandler": { "index": 9, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
+    "exceptionHandler": { "index": 10, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
+    "exchangePattern": { "index": 11, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
+    "method": { "index": 12, "kind": "parameter", "displayName": "Method", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift invoked method name" },
+    "lazyStartProducer": { "index": 13, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "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 produ [...]
+    "synchronous": { "index": 14, "kind": "parameter", "displayName": 
"Synchronous", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "Sets whether 
synchronous processing should be strictly used" },
+    "negotiationType": { "index": 15, "kind": "parameter", "displayName": 
"Negotiation Type", "group": "security", "label": "security", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.thrift.ThriftNegotiationType", "enum": [ 
"PLAINTEXT", "SSL", "SASL" ], "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "PLAINTEXT", "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "desc [...]
+    "sslParameters": { "index": 16, "kind": "parameter", "displayName": "Ssl 
Parameters", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "Configuration parameters for SSL\/TLS security 
negotiation" }
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/thrift-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/thrift-component.adoc
index a197211ba640..7128b1d31b88 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/thrift-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/thrift-component.adoc
@@ -147,6 +147,55 @@ YAML::
 
 It's possible to automate Java code generation for .thrift files using 
*thrift-maven-plugin*, but before start the thrift compiler binary distribution 
for your operating system must be present on the running host.
 
+== SSL/TLS
+
+Set `negotiationType=SSL` and configure `sslParameters` to use SSL/TLS. 
SSL/TLS requires the synchronous communication
+mode (`synchronous=true`), on the producer and on the consumer: an endpoint 
that combines `negotiationType=SSL` with
+`synchronous=false` (the default) fails to start. The `SASL` negotiation type 
is not supported, and an endpoint
+configured with it fails to start.
+
+The consumer creates its server socket from the SSL context of 
`sslParameters`, so all of its settings apply: the key
+managers (required, with the `keyPassword` of a key protected by a password), 
the trust managers, the secure socket
+protocols and cipher suites, and the server parameters. For example, require a 
client certificate with `serverParameters.clientAuthentication=REQUIRE` and 
trust managers that
+trust the certificates of the clients:
+
+[source,java]
+----
+KeyStoreParameters keyStore = new KeyStoreParameters();
+keyStore.setResource("file:server-keystore.jks");
+keyStore.setPassword("changeit");
+KeyManagersParameters keyManagers = new KeyManagersParameters();
+keyManagers.setKeyStore(keyStore);
+keyManagers.setKeyPassword("changeit");
+
+KeyStoreParameters trustStore = new KeyStoreParameters();
+trustStore.setResource("file:client-truststore.jks");
+trustStore.setPassword("changeit");
+TrustManagersParameters trustManagers = new TrustManagersParameters();
+trustManagers.setKeyStore(trustStore);
+
+SSLContextServerParameters serverParameters = new SSLContextServerParameters();
+serverParameters.setClientAuthentication("REQUIRE");
+
+SSLContextParameters sslParameters = new SSLContextParameters();
+sslParameters.setKeyManagers(keyManagers);
+sslParameters.setTrustManagers(trustManagers);
+sslParameters.setServerParameters(serverParameters);
+context.getRegistry().bind("sslParams", sslParameters);
+
+from("thrift://0.0.0.0:1101/org.apache.camel.component.thrift.generated.Calculator?negotiationType=SSL&sslParameters=#sslParams&synchronous=true")
+    .to("direct:thrift-service");
+----
+
+== Errors sent to the client
+
+When the exchange of an asynchronous consumer (the default) fails, the Thrift 
client receives an error. An exception
+that the Thrift method declares in the IDL (such as `throws 
(1:InvalidOperation ouch)`) belongs to the service contract
+and is sent as it is.
+Any other exception is sent as a generic `TApplicationException` of type 
`INTERNAL_ERROR`, without the message of the
+exception, as long as the `muteException` option is enabled (the default). Set 
`muteException=false` to send the
+message of the exception to the client.
+
 == For more information, see these resources
 
 https://github.com/apache/thrift/[Thrift project GitHub]
diff --git 
a/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointConfigurer.java
 
b/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointConfigurer.java
index 732e8fe83362..896c2fc5da71 100644
--- 
a/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointConfigurer.java
+++ 
b/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointConfigurer.java
@@ -40,6 +40,8 @@ public class ThriftEndpointConfigurer extends 
PropertyConfigurerSupport implemen
         case "maxpoolsize":
         case "maxPoolSize": 
target.getConfiguration().setMaxPoolSize(property(camelContext, int.class, 
value)); return true;
         case "method": 
target.getConfiguration().setMethod(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "muteexception":
+        case "muteException": 
target.getConfiguration().setMuteException(property(camelContext, 
boolean.class, value)); return true;
         case "negotiationtype":
         case "negotiationType": 
target.getConfiguration().setNegotiationType(property(camelContext, 
org.apache.camel.component.thrift.ThriftNegotiationType.class, value)); return 
true;
         case "poolsize":
@@ -71,6 +73,8 @@ public class ThriftEndpointConfigurer extends 
PropertyConfigurerSupport implemen
         case "maxpoolsize":
         case "maxPoolSize": return int.class;
         case "method": return java.lang.String.class;
+        case "muteexception":
+        case "muteException": return boolean.class;
         case "negotiationtype":
         case "negotiationType": return 
org.apache.camel.component.thrift.ThriftNegotiationType.class;
         case "poolsize":
@@ -103,6 +107,8 @@ public class ThriftEndpointConfigurer extends 
PropertyConfigurerSupport implemen
         case "maxpoolsize":
         case "maxPoolSize": return target.getConfiguration().getMaxPoolSize();
         case "method": return target.getConfiguration().getMethod();
+        case "muteexception":
+        case "muteException": return 
target.getConfiguration().isMuteException();
         case "negotiationtype":
         case "negotiationType": return 
target.getConfiguration().getNegotiationType();
         case "poolsize":
diff --git 
a/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointUriFactory.java
 
b/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointUriFactory.java
index 44206910e706..964aacf0de36 100644
--- 
a/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointUriFactory.java
+++ 
b/components/camel-thrift/src/generated/java/org/apache/camel/component/thrift/ThriftEndpointUriFactory.java
@@ -24,7 +24,7 @@ public class ThriftEndpointUriFactory extends 
org.apache.camel.support.component
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(16);
+        Set<String> props = new HashSet<>(17);
         props.add("bridgeErrorHandler");
         props.add("clientTimeout");
         props.add("compressionType");
@@ -35,6 +35,7 @@ public class ThriftEndpointUriFactory extends 
org.apache.camel.support.component
         props.add("lazyStartProducer");
         props.add("maxPoolSize");
         props.add("method");
+        props.add("muteException");
         props.add("negotiationType");
         props.add("poolSize");
         props.add("port");
diff --git 
a/components/camel-thrift/src/generated/resources/META-INF/org/apache/camel/component/thrift/thrift.json
 
b/components/camel-thrift/src/generated/resources/META-INF/org/apache/camel/component/thrift/thrift.json
index 51faccf61dac..3596e22bb5a8 100644
--- 
a/components/camel-thrift/src/generated/resources/META-INF/org/apache/camel/component/thrift/thrift.json
+++ 
b/components/camel-thrift/src/generated/resources/META-INF/org/apache/camel/component/thrift/thrift.json
@@ -40,14 +40,15 @@
     "exchangeProtocol": { "index": 4, "kind": "parameter", "displayName": 
"Exchange Protocol", "group": "common", "label": "", "required": false, "type": 
"enum", "javaType": "org.apache.camel.component.thrift.ThriftExchangeProtocol", 
"enum": [ "BINARY", "JSON", "SJSON", "COMPACT" ], "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": "BINARY", 
"configurationClass": "org.apache.camel.component.thrift.ThriftConfiguration", 
"configurationField": "configuration", "descr [...]
     "clientTimeout": { "index": 5, "kind": "parameter", "displayName": "Client 
Timeout", "group": "consumer", "label": "consumer", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "Client timeout for consumers" },
     "maxPoolSize": { "index": 6, "kind": "parameter", "displayName": "Max Pool 
Size", "group": "consumer", "label": "consumer", "required": false, "type": 
"integer", "javaType": "int", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": 10, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift server consumer max thread pool 
size" },
-    "poolSize": { "index": 7, "kind": "parameter", "displayName": "Pool Size", 
"group": "consumer", "label": "consumer", "required": false, "type": "integer", 
"javaType": "int", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift server consumer initial thread pool 
size" },
-    "bridgeErrorHandler": { "index": 8, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
-    "exceptionHandler": { "index": 9, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By def [...]
-    "exchangePattern": { "index": 10, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
-    "method": { "index": 11, "kind": "parameter", "displayName": "Method", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift invoked method name" },
-    "lazyStartProducer": { "index": 12, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "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 produ [...]
-    "synchronous": { "index": 13, "kind": "parameter", "displayName": 
"Synchronous", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "Sets whether 
synchronous processing should be strictly used" },
-    "negotiationType": { "index": 14, "kind": "parameter", "displayName": 
"Negotiation Type", "group": "security", "label": "security", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.thrift.ThriftNegotiationType", "enum": [ 
"PLAINTEXT", "SSL", "SASL" ], "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "PLAINTEXT", "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "desc [...]
-    "sslParameters": { "index": 15, "kind": "parameter", "displayName": "Ssl 
Parameters", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "Configuration parameters for SSL\/TLS security 
negotiation" }
+    "muteException": { "index": 7, "kind": "parameter", "displayName": "Mute 
Exception", "group": "consumer", "label": "consumer", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": true, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "If enabled and an Exchange failed processing 
on the consumer side, the Thrift client r [...]
+    "poolSize": { "index": 8, "kind": "parameter", "displayName": "Pool Size", 
"group": "consumer", "label": "consumer", "required": false, "type": "integer", 
"javaType": "int", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift server consumer initial thread pool 
size" },
+    "bridgeErrorHandler": { "index": 9, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming  [...]
+    "exceptionHandler": { "index": 10, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
+    "exchangePattern": { "index": 11, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
+    "method": { "index": 12, "kind": "parameter", "displayName": "Method", 
"group": "producer", "label": "producer", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "The Thrift invoked method name" },
+    "lazyStartProducer": { "index": 13, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "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 produ [...]
+    "synchronous": { "index": 14, "kind": "parameter", "displayName": 
"Synchronous", "group": "advanced", "label": "advanced", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "description": "Sets whether 
synchronous processing should be strictly used" },
+    "negotiationType": { "index": 15, "kind": "parameter", "displayName": 
"Negotiation Type", "group": "security", "label": "security", "required": 
false, "type": "enum", "javaType": 
"org.apache.camel.component.thrift.ThriftNegotiationType", "enum": [ 
"PLAINTEXT", "SSL", "SASL" ], "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "PLAINTEXT", "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "desc [...]
+    "sslParameters": { "index": 16, "kind": "parameter", "displayName": "Ssl 
Parameters", "group": "security", "label": "security", "required": false, 
"type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.thrift.ThriftConfiguration", "configurationField": 
"configuration", "description": "Configuration parameters for SSL\/TLS security 
negotiation" }
   }
 }
diff --git a/components/camel-thrift/src/main/docs/thrift-component.adoc 
b/components/camel-thrift/src/main/docs/thrift-component.adoc
index a197211ba640..7128b1d31b88 100644
--- a/components/camel-thrift/src/main/docs/thrift-component.adoc
+++ b/components/camel-thrift/src/main/docs/thrift-component.adoc
@@ -147,6 +147,55 @@ YAML::
 
 It's possible to automate Java code generation for .thrift files using 
*thrift-maven-plugin*, but before start the thrift compiler binary distribution 
for your operating system must be present on the running host.
 
+== SSL/TLS
+
+Set `negotiationType=SSL` and configure `sslParameters` to use SSL/TLS. 
SSL/TLS requires the synchronous communication
+mode (`synchronous=true`), on the producer and on the consumer: an endpoint 
that combines `negotiationType=SSL` with
+`synchronous=false` (the default) fails to start. The `SASL` negotiation type 
is not supported, and an endpoint
+configured with it fails to start.
+
+The consumer creates its server socket from the SSL context of 
`sslParameters`, so all of its settings apply: the key
+managers (required, with the `keyPassword` of a key protected by a password), 
the trust managers, the secure socket
+protocols and cipher suites, and the server parameters. For example, require a 
client certificate with `serverParameters.clientAuthentication=REQUIRE` and 
trust managers that
+trust the certificates of the clients:
+
+[source,java]
+----
+KeyStoreParameters keyStore = new KeyStoreParameters();
+keyStore.setResource("file:server-keystore.jks");
+keyStore.setPassword("changeit");
+KeyManagersParameters keyManagers = new KeyManagersParameters();
+keyManagers.setKeyStore(keyStore);
+keyManagers.setKeyPassword("changeit");
+
+KeyStoreParameters trustStore = new KeyStoreParameters();
+trustStore.setResource("file:client-truststore.jks");
+trustStore.setPassword("changeit");
+TrustManagersParameters trustManagers = new TrustManagersParameters();
+trustManagers.setKeyStore(trustStore);
+
+SSLContextServerParameters serverParameters = new SSLContextServerParameters();
+serverParameters.setClientAuthentication("REQUIRE");
+
+SSLContextParameters sslParameters = new SSLContextParameters();
+sslParameters.setKeyManagers(keyManagers);
+sslParameters.setTrustManagers(trustManagers);
+sslParameters.setServerParameters(serverParameters);
+context.getRegistry().bind("sslParams", sslParameters);
+
+from("thrift://0.0.0.0:1101/org.apache.camel.component.thrift.generated.Calculator?negotiationType=SSL&sslParameters=#sslParams&synchronous=true")
+    .to("direct:thrift-service");
+----
+
+== Errors sent to the client
+
+When the exchange of an asynchronous consumer (the default) fails, the Thrift 
client receives an error. An exception
+that the Thrift method declares in the IDL (such as `throws 
(1:InvalidOperation ouch)`) belongs to the service contract
+and is sent as it is.
+Any other exception is sent as a generic `TApplicationException` of type 
`INTERNAL_ERROR`, without the message of the
+exception, as long as the `muteException` option is enabled (the default). Set 
`muteException=false` to send the
+message of the exception to the client.
+
 == For more information, see these resources
 
 https://github.com/apache/thrift/[Thrift project GitHub]
diff --git 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConfiguration.java
 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConfiguration.java
index 85cf4d5a8144..470b193b040b 100644
--- 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConfiguration.java
+++ 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConfiguration.java
@@ -63,6 +63,9 @@ public class ThriftConfiguration {
     @UriParam(label = "consumer", defaultValue = "" + 
ThriftConstants.THRIFT_CONSUMER_MAX_POOL_SIZE)
     private int maxPoolSize = ThriftConstants.THRIFT_CONSUMER_MAX_POOL_SIZE;
 
+    @UriParam(label = "consumer", defaultValue = "true")
+    private boolean muteException = true;
+
     /**
      * Fully qualified service name from the thrift descriptor file (package 
dot service definition name)
      */
@@ -185,6 +188,19 @@ public class ThriftConfiguration {
         this.maxPoolSize = maxPoolSize;
     }
 
+    /**
+     * If enabled and an Exchange failed processing on the consumer side, the 
Thrift client receives a generic error
+     * that does not contain the exception's message. An exception that the 
Thrift method declares in the IDL is still
+     * sent as it is.
+     */
+    public boolean isMuteException() {
+        return muteException;
+    }
+
+    public void setMuteException(boolean muteException) {
+        this.muteException = muteException;
+    }
+
     public void parseURI(URI uri) {
         setHost(uri.getHost());
 
diff --git 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConsumer.java
 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConsumer.java
index 0845099cabff..5574f52a6d4e 100644
--- 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConsumer.java
+++ 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftConsumer.java
@@ -21,6 +21,10 @@ import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
 
 import javassist.util.proxy.MethodHandler;
 import javassist.util.proxy.Proxy;
@@ -32,13 +36,11 @@ import 
org.apache.camel.component.thrift.server.ThriftHsHaServer;
 import org.apache.camel.component.thrift.server.ThriftMethodHandler;
 import org.apache.camel.component.thrift.server.ThriftThreadPoolServer;
 import org.apache.camel.support.DefaultConsumer;
-import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.support.jsse.SSLContextParameters;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.thrift.TProcessor;
 import org.apache.thrift.server.TServer;
 import org.apache.thrift.transport.TNonblockingServerSocket;
-import org.apache.thrift.transport.TSSLTransportFactory;
 import org.apache.thrift.transport.TServerSocket;
 import org.apache.thrift.transport.TTransportException;
 import org.apache.thrift.transport.TZlibTransport;
@@ -52,6 +54,9 @@ public class ThriftConsumer extends DefaultConsumer {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(ThriftConsumer.class);
 
+    // the backlog of the server sockets of TSSLTransportFactory
+    private static final int SERVER_SOCKET_BACKLOG = 100;
+
     private TNonblockingServerSocket asyncServerTransport;
     private TServerSocket syncServerTransport;
     private TServer server;
@@ -116,6 +121,15 @@ public class ThriftConsumer extends DefaultConsumer {
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
     protected void initializeServer() throws TTransportException, IOException {
+        if (configuration.getNegotiationType() == ThriftNegotiationType.SASL) {
+            throw new IllegalArgumentException("The SASL negotiation type is 
not supported");
+        }
+        if (configuration.getNegotiationType() == ThriftNegotiationType.SSL && 
!endpoint.isSynchronous()) {
+            // only the synchronous server is built on a TLS server socket
+            throw new IllegalArgumentException(
+                    "The SSL negotiation type requires to set synchronous 
communication mode (synchronous=true)");
+        }
+
         Class serverImplementationClass;
         Object serverImplementationInstance;
         Object serverProcessor;
@@ -148,39 +162,30 @@ public class ThriftConsumer extends DefaultConsumer {
                                                    + 
configuration.getNegotiationType());
             }
 
-            ObjectHelper.notNull(sslParameters.getSecureSocketProtocol(), 
"Security protocol");
-            
ObjectHelper.notNull(sslParameters.getKeyManagers().getKeyStore().getResource(),
 "Keystore path");
-            
ObjectHelper.notNull(sslParameters.getKeyManagers().getKeyStore().getPassword(),
 "Keystore password");
-
-            TSSLTransportFactory.TSSLTransportParameters sslParams;
-            sslParams = new TSSLTransportFactory.TSSLTransportParameters(
-                    sslParameters.getSecureSocketProtocol(),
-                    sslParameters.getCipherSuites() == null
-                            ? null
-                            : 
sslParameters.getCipherSuites().getCipherSuite().stream().toArray(String[]::new));
-
-            if 
(ObjectHelper.isNotEmpty(sslParameters.getKeyManagers().getKeyStore().getProvider())
-                    && 
ObjectHelper.isNotEmpty(sslParameters.getKeyManagers().getKeyStore().getType()))
 {
-                sslParams.setKeyStore(
-                        
ResourceHelper.resolveResourceAsInputStream(endpoint.getCamelContext(),
-                                
sslParameters.getKeyManagers().getKeyStore().getResource()),
-                        
sslParameters.getKeyManagers().getKeyStore().getPassword(),
-                        
sslParameters.getKeyManagers().getKeyStore().getProvider(),
-                        
sslParameters.getKeyManagers().getKeyStore().getType());
-            } else {
-                sslParams.setKeyStore(
-                        
ResourceHelper.resolveResourceAsInputStream(endpoint.getCamelContext(),
-                                
sslParameters.getKeyManagers().getKeyStore().getResource()),
-                        
sslParameters.getKeyManagers().getKeyStore().getPassword());
-            }
+            ObjectHelper.notNull(sslParameters.getKeyManagers(), "Key 
managers");
 
+            // the server socket factory of the SSL context applies all the 
sslParameters: the key and trust managers,
+            // the protocols and cipher suites, and the server parameters such 
as the client authentication
+            SSLServerSocket serverSocket;
             try {
-                syncServerTransport = 
TSSLTransportFactory.getServerSocket(configuration.getPort(),
-                        configuration.getClientTimeout(), 
InetAddress.getByName(configuration.getHost()),
-                        sslParams);
+                SSLContext sslContext = 
sslParameters.createSSLContext(endpoint.getCamelContext());
+                serverSocket = (SSLServerSocket) 
sslContext.getServerSocketFactory().createServerSocket(
+                        configuration.getPort(), SERVER_SOCKET_BACKLOG, 
InetAddress.getByName(configuration.getHost()));
             } catch (UnknownHostException e) {
                 throw new IllegalArgumentException("Unknown host defined: " + 
configuration.getHost());
+            } catch (GeneralSecurityException e) {
+                throw new IllegalArgumentException(
+                        "Unable to create the SSL context of the Thrift server"
+                                                   + 
(sslParameters.getKeyManagers().getKeyPassword() == null
+                                                           ? " (set 
keyPassword on the key managers of sslParameters when the"
+                                                             + " key is 
protected by a password)"
+                                                           : ""),
+                        e);
             }
+            serverSocket.setSoTimeout(configuration.getClientTimeout());
+            syncServerTransport = new TServerSocket(
+                    new 
TServerSocket.ServerSocketTransportArgs().serverSocket(serverSocket)
+                            .clientTimeout(configuration.getClientTimeout()));
             ThriftThreadPoolServer.Args args = new 
ThriftThreadPoolServer.Args(syncServerTransport);
             args.processor((TProcessor) serverProcessor);
             
args.executorService(getEndpoint().getCamelContext().getExecutorServiceManager().newThreadPool(this,
diff --git 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftProducer.java
 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftProducer.java
index 444701a6cf8c..1ee34cafd47a 100644
--- 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftProducer.java
+++ 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/ThriftProducer.java
@@ -95,6 +95,9 @@ public class ThriftProducer extends DefaultAsyncProducer 
implements AsyncProduce
     protected void doStart() throws Exception {
         super.doStart();
 
+        if (configuration.getNegotiationType() == ThriftNegotiationType.SASL) {
+            throw new IllegalArgumentException("The SASL negotiation type is 
not supported");
+        }
         if (configuration.getNegotiationType() == ThriftNegotiationType.SSL) {
             if (!endpoint.isSynchronous()) {
                 throw new IllegalArgumentException("The SSL negotiation type 
requires to set syncronous communication mode");
diff --git 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/server/ThriftMethodHandler.java
 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/server/ThriftMethodHandler.java
index f7f928737fa7..05397b6cca55 100644
--- 
a/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/server/ThriftMethodHandler.java
+++ 
b/components/camel-thrift/src/main/java/org/apache/camel/component/thrift/server/ThriftMethodHandler.java
@@ -24,6 +24,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.component.thrift.ThriftConstants;
 import org.apache.camel.component.thrift.ThriftConsumer;
+import org.apache.camel.component.thrift.ThriftEndpoint;
 import org.apache.camel.component.thrift.ThriftUtils;
 import org.apache.thrift.TApplicationException;
 import org.apache.thrift.TException;
@@ -61,7 +62,7 @@ public class ThriftMethodHandler implements MethodHandler {
                     Exception exception = exchange.getException();
 
                     if (exception != null) {
-                        callback.onError(exception);
+                        callback.onError(toClientException(thisMethod, 
exception));
                     }
 
                     message = exchange.getMessage();
@@ -104,4 +105,35 @@ public class ThriftMethodHandler implements MethodHandler {
             return proceed.invoke(self, args);
         }
     }
+
+    /**
+     * The error to send to the client for a failed exchange. An exception 
that the Thrift method declares belongs to
+     * the service contract and is sent as it is; any other one is replaced by 
a generic error unless muteException is
+     * disabled.
+     */
+    private Exception toClientException(Method method, Exception exception) {
+        if (!consumer.getConfiguration().isMuteException() || 
isDeclared(method, exception)) {
+            return exception;
+        }
+        return new TApplicationException(
+                TApplicationException.INTERNAL_ERROR, "Internal error 
processing " + method.getName());
+    }
+
+    private boolean isDeclared(Method method, Exception exception) {
+        // the methods of the asynchronous interface only declare TException, 
so read the exceptions of the IDL from
+        // the method of the synchronous interface (a Thrift service has no 
overloaded methods)
+        ThriftEndpoint endpoint = (ThriftEndpoint) consumer.getEndpoint();
+        Class<?> serverInterface = 
ThriftUtils.getServerInterface(endpoint.getServicePackage(), 
endpoint.getServiceName(),
+                true, endpoint.getCamelContext());
+        for (Method declaring : serverInterface.getMethods()) {
+            if (declaring.getName().equals(method.getName())) {
+                for (Class<?> declared : declaring.getExceptionTypes()) {
+                    if (declared != TException.class && 
declared.isInstance(exception)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
 }
diff --git 
a/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerClientAuthenticationTest.java
 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerClientAuthenticationTest.java
new file mode 100644
index 000000000000..b186b0240a9b
--- /dev/null
+++ 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerClientAuthenticationTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.thrift;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.thrift.generated.Calculator;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.jsse.ClientAuthentication;
+import org.apache.camel.support.jsse.KeyManagersParameters;
+import org.apache.camel.support.jsse.KeyStoreParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.SSLContextServerParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.transport.TSSLTransportFactory;
+import org.apache.thrift.transport.TTransport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * The SSL consumer applies the server parameters of sslParameters, such as a 
required client certificate.
+ */
+class ThriftConsumerClientAuthenticationTest extends CamelTestSupport {
+
+    private static final String TRUST_STORE_RESOURCE = 
"file:src/test/resources/certs/truststore.jks";
+    private static final String KEY_STORE_RESOURCE = 
"file:src/test/resources/certs/keystore.jks";
+    private static final String SECURITY_STORE_PASSWORD = "camelinaction";
+    private static final int THRIFT_CLIENT_TIMEOUT = 2000;
+
+    @Test
+    void clientWithoutCertificateIsRejected() throws Exception {
+        TSSLTransportFactory.TSSLTransportParameters params = new 
TSSLTransportFactory.TSSLTransportParameters();
+        params.setTrustStore(TRUST_STORE_RESOURCE, SECURITY_STORE_PASSWORD);
+
+        // depending on the TLS version the handshake fails when the 
connection is opened or on the first call
+        assertThrows(TException.class, () -> {
+            TTransport transport = open(params);
+            try {
+                new Calculator.Client(new TBinaryProtocol(transport)).add(12, 
13);
+            } finally {
+                transport.close();
+            }
+        });
+    }
+
+    @Test
+    void clientWithTrustedCertificateIsServed() throws Exception {
+        TSSLTransportFactory.TSSLTransportParameters params = new 
TSSLTransportFactory.TSSLTransportParameters();
+        params.setTrustStore(TRUST_STORE_RESOURCE, SECURITY_STORE_PASSWORD);
+        params.setKeyStore(KEY_STORE_RESOURCE, SECURITY_STORE_PASSWORD);
+
+        TTransport transport = open(params);
+        try {
+            Calculator.Client client = new Calculator.Client(new 
TBinaryProtocol(transport));
+            assertEquals(25, client.add(12, 13));
+        } finally {
+            transport.close();
+        }
+    }
+
+    private TTransport open(TSSLTransportFactory.TSSLTransportParameters 
params) throws Exception {
+        int port = ((ThriftConsumer) 
context.getRoutes().get(0).getConsumer()).getLocalPort();
+        return TSSLTransportFactory.getClientSocket("localhost", port, 
THRIFT_CLIENT_TIMEOUT, params);
+    }
+
+    @Override
+    protected void bindToRegistry(Registry registry) {
+        KeyStoreParameters keyStore = new KeyStoreParameters();
+        keyStore.setResource(KEY_STORE_RESOURCE);
+        keyStore.setPassword(SECURITY_STORE_PASSWORD);
+        KeyManagersParameters keyManagers = new KeyManagersParameters();
+        keyManagers.setKeyStore(keyStore);
+        keyManagers.setKeyPassword(SECURITY_STORE_PASSWORD);
+
+        KeyStoreParameters trustStore = new KeyStoreParameters();
+        trustStore.setResource(TRUST_STORE_RESOURCE);
+        trustStore.setPassword(SECURITY_STORE_PASSWORD);
+        TrustManagersParameters trustManagers = new TrustManagersParameters();
+        trustManagers.setKeyStore(trustStore);
+
+        SSLContextServerParameters serverParameters = new 
SSLContextServerParameters();
+        
serverParameters.setClientAuthentication(ClientAuthentication.REQUIRE.name());
+
+        SSLContextParameters sslParameters = new SSLContextParameters();
+        sslParameters.setKeyManagers(keyManagers);
+        sslParameters.setTrustManagers(trustManagers);
+        sslParameters.setServerParameters(serverParameters);
+
+        registry.bind("sslParams", sslParameters);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                
from("thrift://localhost:0/org.apache.camel.component.thrift.generated.Calculator"
+                     + 
"?negotiationType=SSL&sslParameters=#sslParams&synchronous=true")
+                        .setBody(constant(25));
+            }
+        };
+    }
+}
diff --git 
a/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerMuteExceptionTest.java
 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerMuteExceptionTest.java
new file mode 100644
index 000000000000..15a3f4ab9b7c
--- /dev/null
+++ 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerMuteExceptionTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.thrift;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.thrift.generated.Calculator;
+import org.apache.camel.component.thrift.generated.InvalidOperation;
+import org.apache.camel.component.thrift.generated.Operation;
+import org.apache.camel.component.thrift.generated.Work;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.thrift.TApplicationException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.layered.TFramedTransport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The consumer sends a generic error for an exception that the Thrift method 
does not declare, unless muteException is
+ * disabled.
+ */
+class ThriftConsumerMuteExceptionTest extends CamelTestSupport {
+
+    private static final String DETAIL = "Detail of the failure";
+
+    @Test
+    void asyncServerMutesTheException() throws Exception {
+        TApplicationException e = callAdd("async");
+        assertEquals(TApplicationException.INTERNAL_ERROR, e.getType());
+        assertEquals("Internal error processing add", e.getMessage());
+    }
+
+    @Test
+    void asyncServerSendsTheExceptionWhenNotMuted() throws Exception {
+        assertTrue(callAdd("async-not-muted").getMessage().contains(DETAIL));
+    }
+
+    @Test
+    void declaredExceptionIsSentWhenMuted() throws Exception {
+        TTransport transport = open("async");
+        try {
+            Calculator.Client client = new Calculator.Client(new 
TBinaryProtocol(new TFramedTransport(transport)));
+            InvalidOperation e = assertThrows(InvalidOperation.class,
+                    () -> client.calculate(1, new Work(12, 13, 
Operation.DIVIDE)));
+            assertEquals(DETAIL, e.getWhy());
+        } finally {
+            transport.close();
+        }
+    }
+
+    private TApplicationException callAdd(String routeId) throws Exception {
+        TTransport transport = open(routeId);
+        try {
+            Calculator.Client client = new Calculator.Client(new 
TBinaryProtocol(new TFramedTransport(transport)));
+            return assertThrows(TApplicationException.class, () -> 
client.add(12, 13));
+        } finally {
+            transport.close();
+        }
+    }
+
+    private TTransport open(String routeId) throws Exception {
+        TTransport transport
+                = new TSocket("localhost", ((ThriftConsumer) 
context.getRoute(routeId).getConsumer()).getLocalPort());
+        transport.open();
+        return transport;
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                String uri = 
"thrift://localhost:0/org.apache.camel.component.thrift.generated.Calculator";
+                from(uri).routeId("async").to("direct:service");
+                from(uri + 
"?muteException=false").routeId("async-not-muted").to("direct:service");
+
+                from("direct:service")
+                        .choice()
+                        
.when(header(ThriftConstants.THRIFT_METHOD_NAME_HEADER).isEqualTo("calculate"))
+                        .throwException(new InvalidOperation(4, DETAIL))
+                        .otherwise()
+                        // such as the error of a Thrift service the route 
called
+                        .throwException(new 
TApplicationException(TApplicationException.INTERNAL_ERROR, DETAIL));
+            }
+        };
+    }
+}
diff --git 
a/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerSecurityTest.java
 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerSecurityTest.java
index 3f1f6fe4b729..209e031f1b94 100644
--- 
a/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerSecurityTest.java
+++ 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerSecurityTest.java
@@ -97,6 +97,7 @@ public class ThriftConsumerSecurityTest extends 
CamelTestSupport {
 
         KeyManagersParameters keyManagerParams = new KeyManagersParameters();
         keyManagerParams.setKeyStore(keyStoreParams);
+        keyManagerParams.setKeyPassword(SECURITY_STORE_PASSWORD);
 
         sslParameters.setKeyManagers(keyManagerParams);
 
diff --git 
a/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftNegotiationTypeStartupTest.java
 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftNegotiationTypeStartupTest.java
new file mode 100644
index 000000000000..fd419b4463d5
--- /dev/null
+++ 
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftNegotiationTypeStartupTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.thrift;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.jsse.KeyManagersParameters;
+import org.apache.camel.support.jsse.KeyStoreParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * A negotiation type that the consumer or the producer cannot apply makes it 
fail to start.
+ */
+class ThriftNegotiationTypeStartupTest {
+
+    private static final String SERVICE = 
"org.apache.camel.component.thrift.generated.Calculator";
+
+    @Test
+    void sslConsumerRequiresSynchronousMode() throws Exception {
+        String message = startFailure(
+                "thrift://localhost:0/" + SERVICE + 
"?negotiationType=SSL&sslParameters=#sslParams", "mock:result");
+        assertTrue(message.contains("synchronous"), message);
+    }
+
+    @Test
+    void saslConsumerIsRejected() throws Exception {
+        String message = startFailure(
+                "thrift://localhost:0/" + SERVICE + 
"?negotiationType=SASL&synchronous=true", "mock:result");
+        assertTrue(message.contains("SASL"), message);
+    }
+
+    @Test
+    void saslProducerIsRejected() throws Exception {
+        String message = startFailure("direct:start",
+                "thrift://localhost:1101/" + SERVICE + 
"?method=ping&negotiationType=SASL&synchronous=true");
+        assertTrue(message.contains("SASL"), message);
+    }
+
+    private static String startFailure(String from, String to) throws 
Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            context.getRegistry().bind("sslParams", sslParameters());
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from(from).to(to);
+                }
+            });
+
+            Exception e = assertThrows(Exception.class, context::start);
+            Throwable cause = e;
+            while (cause != null && !(cause instanceof 
IllegalArgumentException)) {
+                cause = cause.getCause();
+            }
+            assertNotNull(cause, "Expected an IllegalArgumentException, got: " 
+ e);
+            return cause.getMessage();
+        }
+    }
+
+    private static SSLContextParameters sslParameters() {
+        KeyStoreParameters keyStore = new KeyStoreParameters();
+        keyStore.setResource("file:src/test/resources/certs/keystore.jks");
+        keyStore.setPassword("camelinaction");
+        KeyManagersParameters keyManagers = new KeyManagersParameters();
+        keyManagers.setKeyStore(keyStore);
+        keyManagers.setKeyPassword("camelinaction");
+        SSLContextParameters sslParameters = new SSLContextParameters();
+        sslParameters.setKeyManagers(keyManagers);
+        return sslParameters;
+    }
+}
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThriftEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThriftEndpointBuilderFactory.java
index 7dc9270cf800..b2ecdaff9c57 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThriftEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThriftEndpointBuilderFactory.java
@@ -170,6 +170,42 @@ public interface ThriftEndpointBuilderFactory {
             doSetProperty("maxPoolSize", maxPoolSize);
             return this;
         }
+        /**
+         * If enabled and an Exchange failed processing on the consumer side,
+         * the Thrift client receives a generic error that does not contain the
+         * exception's message. An exception that the Thrift method declares in
+         * the IDL is still sent as it is.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: consumer
+         * 
+         * @param muteException the value to set
+         * @return the dsl builder
+         */
+        default ThriftEndpointConsumerBuilder muteException(boolean 
muteException) {
+            doSetProperty("muteException", muteException);
+            return this;
+        }
+        /**
+         * If enabled and an Exchange failed processing on the consumer side,
+         * the Thrift client receives a generic error that does not contain the
+         * exception's message. An exception that the Thrift method declares in
+         * the IDL is still sent as it is.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Default: true
+         * Group: consumer
+         * 
+         * @param muteException the value to set
+         * @return the dsl builder
+         */
+        default ThriftEndpointConsumerBuilder muteException(String 
muteException) {
+            doSetProperty("muteException", muteException);
+            return this;
+        }
         /**
          * The Thrift server consumer initial thread pool size.
          * 

Reply via email to