This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new 01a5a1c4cf45 [backport camel-4.18.x] CAMEL-25459, CAMEL-25460,
CAMEL-25461: camel-thrift - apply the SSL settings of the consumer and mute its
exceptions (#28236)
01a5a1c4cf45 is described below
commit 01a5a1c4cf456676fe4c9beed4c3381c177b8fad
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Oct 21 21:57:00 2026 +0200
[backport camel-4.18.x] CAMEL-25459, CAMEL-25460, CAMEL-25461: camel-thrift
- apply the SSL settings of the consumer and mute its exceptions (#28236)
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 ++--
.../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 | 123 +++++++++++++++++++++
.../thrift/ThriftConsumerMuteExceptionTest.java | 108 ++++++++++++++++++
.../thrift/ThriftConsumerSecurityTest.java | 1 +
.../thrift/ThriftNegotiationTypeStartupTest.java | 90 +++++++++++++++
.../endpoint/dsl/ThriftEndpointBuilderFactory.java | 36 ++++++
14 files changed, 522 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 69c822ea421c..7855612b0b0e 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/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 176cbdf2b8e5..68fca07b45f8 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
@@ -23,7 +23,7 @@ public class ThriftEndpointUriFactory extends
org.apache.camel.support.component
private static final Set<String> SECRET_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");
@@ -34,6 +34,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 69c822ea421c..7855612b0b0e 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 853cee90a43b..8d4814223ac0 100644
--- a/components/camel-thrift/src/main/docs/thrift-component.adoc
+++ b/components/camel-thrift/src/main/docs/thrift-component.adoc
@@ -102,6 +102,55 @@
from("thrift://localhost:1101/org.apache.camel.component.thrift.generated.Calcul
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 11e8e2cff601..2b1d9eea270e 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;
@@ -106,6 +111,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;
@@ -138,39 +152,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..d3dc9fe7fb3b
--- /dev/null
+++
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerClientAuthenticationTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.AvailablePortFinder;
+import org.apache.camel.test.junit5.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_TEST_PORT =
AvailablePortFinder.getNextAvailable();
+ 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 {
+ return TSSLTransportFactory.getClientSocket("localhost",
THRIFT_TEST_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:" + THRIFT_TEST_PORT
+ +
"/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..3df7ac1a0caa
--- /dev/null
+++
b/components/camel-thrift/src/test/java/org/apache/camel/component/thrift/ThriftConsumerMuteExceptionTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.AvailablePortFinder;
+import org.apache.camel.test.junit5.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";
+ private static final int THRIFT_MUTED_PORT =
AvailablePortFinder.getNextAvailable();
+ private static final int THRIFT_NOT_MUTED_PORT =
AvailablePortFinder.getNextAvailable();
+
+ @Test
+ void asyncServerMutesTheException() throws Exception {
+ TApplicationException e = callAdd(THRIFT_MUTED_PORT);
+ assertEquals(TApplicationException.INTERNAL_ERROR, e.getType());
+ assertEquals("Internal error processing add", e.getMessage());
+ }
+
+ @Test
+ void asyncServerSendsTheExceptionWhenNotMuted() throws Exception {
+
assertTrue(callAdd(THRIFT_NOT_MUTED_PORT).getMessage().contains(DETAIL));
+ }
+
+ @Test
+ void declaredExceptionIsSentWhenMuted() throws Exception {
+ TTransport transport = open(THRIFT_MUTED_PORT);
+ 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(int port) throws Exception {
+ TTransport transport = open(port);
+ 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(int port) throws Exception {
+ TTransport transport = new TSocket("localhost", port);
+ transport.open();
+ return transport;
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ String service =
"/org.apache.camel.component.thrift.generated.Calculator";
+ from("thrift://localhost:" + THRIFT_MUTED_PORT +
service).to("direct:service");
+ from("thrift://localhost:" + THRIFT_NOT_MUTED_PORT + service +
"?muteException=false")
+ .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 1d109491a7c6..90475462adf5 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
@@ -92,6 +92,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 9c48dd286f0a..f010a230a4cb 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.
*