This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 9412c6eda27d CAMEL-25652: Component metadata - an internal flag, next
to remote, for components that only reach the CamelContext (#27833)
9412c6eda27d is described below
commit 9412c6eda27da3044267b4ef5a68f5ce7ed85578
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 14 21:33:39 2026 +0200
CAMEL-25652: Component metadata - an internal flag, next to remote, for
components that only reach the CamelContext (#27833)
* CAMEL-25652: Component metadata - an internal flag, next to remote, for
components that only reach the CamelContext
@UriEndpoint(internal = true) marks a component that stays inside the
CamelContext: its routes, in-memory state and log, and never a file, the
network, a process, or code or a resource named in its URI. A capability
counts even behind a seldom used option or a feature the component ships
with. It implies remote = false (the build fails otherwise).
The catalog JSON has "internal": true on the 13 internal components only
(direct, seda, stub, disruptor, disruptor-vm, log, mock, browse,
caffeine-cache, caffeine-loadcache, metrics, event, timer), and
ComponentModel.isInternal(). The AI tools of the TUI read it from the
catalog instead of their own list; dataset-test now asks first.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
* CAMEL-25652: camel-jbang - the AI send tool asks first when the body
reads a file outside the project
The send dev console sends the content of the file that a body starting
with file: names, read by the integration relative to its working
directory. tui_send_message now asks first when that file is outside the
project folder (the integration's source directory, where the AI tools
write), wherever the message goes; a file in the project goes ahead.
Symlinks and .. are resolved before the check.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
* CAMEL-25652: camel-jbang - a blank or relative working directory cannot
place a file: body either
A relative file: body is resolved against the integration's working
directory. When that is blank or relative, Path resolution fell back to
the TUI's own working directory; it now counts as not known, so the send
asks first. From the review of gnodet-bot.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---------
Signed-off-by: Claus Ibsen <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
AGENTS.md | 5 +-
.../apache/camel/catalog/components/browse.json | 3 +-
.../camel/catalog/components/caffeine-cache.json | 3 +-
.../catalog/components/caffeine-loadcache.json | 3 +-
.../apache/camel/catalog/components/direct.json | 3 +-
.../camel/catalog/components/disruptor-vm.json | 3 +-
.../apache/camel/catalog/components/disruptor.json | 3 +-
.../org/apache/camel/catalog/components/event.json | 3 +-
.../org/apache/camel/catalog/components/log.json | 3 +-
.../apache/camel/catalog/components/metrics.json | 3 +-
.../org/apache/camel/catalog/components/mock.json | 3 +-
.../org/apache/camel/catalog/components/seda.json | 3 +-
.../org/apache/camel/catalog/components/stub.json | 3 +-
.../org/apache/camel/catalog/components/timer.json | 3 +-
.../org/apache/camel/catalog/CamelCatalogTest.java | 14 +++
.../org/apache/camel/component/browse/browse.json | 3 +-
.../camel/component/browse/BrowseEndpoint.java | 2 +-
.../component/caffeine/cache/caffeine-cache.json | 3 +-
.../caffeine/load/caffeine-loadcache.json | 3 +-
.../caffeine/cache/CaffeineCacheEndpoint.java | 3 +-
.../caffeine/load/CaffeineLoadCacheEndpoint.java | 3 +-
.../org/apache/camel/component/direct/direct.json | 3 +-
.../camel/component/direct/DirectEndpoint.java | 2 +-
.../camel/component/disruptor/disruptor-vm.json | 3 +-
.../camel/component/disruptor/disruptor.json | 3 +-
.../component/disruptor/DisruptorEndpoint.java | 2 +-
.../apache/camel/component/camelevent/event.json | 3 +-
.../component/camelevent/CamelEventEndpoint.java | 2 +-
.../org/apache/camel/component/log/log.json | 3 +-
.../apache/camel/component/log/LogEndpoint.java | 3 +-
.../apache/camel/component/metrics/metrics.json | 3 +-
.../camel/component/metrics/MetricsEndpoint.java | 3 +-
.../org/apache/camel/component/mock/mock.json | 3 +-
.../apache/camel/component/mock/MockEndpoint.java | 2 +-
.../org/apache/camel/component/seda/seda.json | 3 +-
.../apache/camel/component/seda/SedaEndpoint.java | 2 +-
.../org/apache/camel/component/stub/stub.json | 3 +-
.../apache/camel/component/stub/StubEndpoint.java | 2 +-
.../org/apache/camel/component/timer/timer.json | 3 +-
.../camel/component/timer/TimerEndpoint.java | 3 +-
.../java/org/apache/camel/spi/UriEndpoint.java | 22 ++++
.../modules/ROOT/pages/camel-jbang-tui-ai.adoc | 8 +-
.../modules/ROOT/pages/security-model.adoc | 12 +-
dsl/camel-jbang/camel-jbang-plugin-tui/AGENTS.md | 2 +-
.../dsl/jbang/core/commands/tui/McpFacade.java | 6 +-
.../dsl/jbang/core/commands/tui/ToolConfirms.java | 105 +++++++++++++++--
.../core/commands/tui/McpFacadeConfirmTest.java | 21 +++-
.../core/commands/tui/ToolConfirmPopupTest.java | 2 +-
.../jbang/core/commands/tui/ToolConfirmsTest.java | 124 +++++++++++++++++----
.../apache/camel/tooling/model/ComponentModel.java | 13 +++
.../org/apache/camel/tooling/model/JsonMapper.java | 5 +
.../apache/camel/tooling/model/JsonMapperTest.java | 13 +++
.../packaging/EndpointSchemaGeneratorMojo.java | 6 +
.../packaging/EndpointSchemaGeneratorMojoTest.java | 12 ++
.../endpoint/SomeInternalRemoteEndpoint.java | 24 ++++
.../java/org/apache/camel/spi/UriEndpoint.java | 22 ++++
56 files changed, 437 insertions(+), 86 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index e19d3f786154..daedf0513373 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -457,8 +457,9 @@ The following are explicitly **not** framework
vulnerabilities and will be close
- An **AI assistant in Camel TUI** (the AI panel's model, an ACP agent, an MCP
client) using the
TUI's tools without asking, even after a prompt injection. Camel CLI and
Camel TUI are developer
tools on the developer's own machine; the TUI asks first only before a file
write (its write
- mode), a message to an endpoint the integration does not have, SQL that is
not a `SELECT`, and a
- code-running expression language such as Groovy. A tool breaking that
contract is in scope. See
+ mode), a message to an endpoint the integration does not have or with a body
read from a file
+ outside the project, SQL that is not a `SELECT`, and a code-running
expression language such as
+ Groovy. A tool breaking that contract is in scope. See
"Developer tools" in the security model.
### Operator hardening checklist
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/browse.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/browse.json
index 282c9b11e150..208f04911301 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/browse.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/browse.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-cache.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-cache.json
index 4a1f35b7cb1a..2306d19ed008 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-cache.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-cache.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"action": { "index": 0, "kind": "property", "displayName": "Action",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "GET", "GET_ALL", "PUT", "PUT_ALL",
"INVALIDATE", "INVALIDATE_ALL", "CLEANUP", "AS_MAP" ], "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.caffeine.CaffeineConfiguration",
"configurationField": "configuration", "description": "To configure the defa
[...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-loadcache.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-loadcache.json
index 0eaeaed09173..0da3d147ff1c 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-loadcache.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/caffeine-loadcache.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"action": { "index": 0, "kind": "property", "displayName": "Action",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "GET", "GET_ALL", "PUT", "PUT_ALL",
"INVALIDATE", "INVALIDATE_ALL", "CLEANUP", "AS_MAP" ], "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.caffeine.CaffeineConfiguration",
"configurationField": "configuration", "description": "To configure the defa
[...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json
index e0866218e2e9..bf84bbc045c0 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/direct.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor-vm.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor-vm.json
index 16e4ff1b865f..83bda4488248 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor-vm.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor-vm.json
@@ -22,7 +22,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bufferSize": { "index": 0, "kind": "property", "displayName": "Buffer
Size", "group": "common", "label": "", "required": false, "type": "integer",
"javaType": "int", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 1024, "description": "To configure the ring buffer size" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor.json
index 90a59fa80dc2..9cf585a052c5 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/disruptor.json
@@ -22,7 +22,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bufferSize": { "index": 0, "kind": "property", "displayName": "Buffer
Size", "group": "common", "label": "", "required": false, "type": "integer",
"javaType": "int", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 1024, "description": "To configure the ring buffer size" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
index 9ef15d1462f1..e0fcda0198bd 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json
index 553eac237c7d..30b8b7326c6b 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/log.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/metrics.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/metrics.json
index 6927e608305f..952ca0a8b5d9 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/metrics.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/metrics.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json
index f22d774f197c..d8a7f8807de2 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mock.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": true,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
index 6681f3cbd6c3..0a1a4bb65b6d 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
index 32d642f6acce..43c75bea1a28 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": true,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json
index 28e78be1017b..88a27c1c699a 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/timer.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
index e868ae64bb34..b167e18c4e83 100644
---
a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
+++
b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java
@@ -206,6 +206,20 @@ public class CamelCatalogTest {
assertTrue(catalog.componentModel("activemq").getAliases().contains("amq"));
}
+ @Test
+ public void testComponentInternal() {
+ // internal: stays inside the CamelContext, set with
@UriEndpoint(internal = true)
+ assertTrue(catalog.componentModel("direct").isInternal());
+ assertTrue(catalog.componentModel("log").isInternal());
+ assertTrue(catalog.componentModel("disruptor-vm").isInternal());
+ assertFalse(catalog.componentModel("kafka").isInternal());
+ // not remote, but not internal either: exec runs a process,
dataset-test polls the endpoint in its URI
+ for (String name : List.of("exec", "dataset-test")) {
+ assertFalse(catalog.componentModel(name).isRemote(), name);
+ assertFalse(catalog.componentModel(name).isInternal(), name);
+ }
+ }
+
@Test
public void testSuggestComponentNames() {
// exact scheme first, then words of the title or scheme
diff --git
a/components/camel-browse/src/generated/resources/META-INF/org/apache/camel/component/browse/browse.json
b/components/camel-browse/src/generated/resources/META-INF/org/apache/camel/component/browse/browse.json
index 282c9b11e150..208f04911301 100644
---
a/components/camel-browse/src/generated/resources/META-INF/org/apache/camel/component/browse/browse.json
+++
b/components/camel-browse/src/generated/resources/META-INF/org/apache/camel/component/browse/browse.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/components/camel-browse/src/main/java/org/apache/camel/component/browse/BrowseEndpoint.java
b/components/camel-browse/src/main/java/org/apache/camel/component/browse/BrowseEndpoint.java
index e6c657484c92..5462f9dae33f 100644
---
a/components/camel-browse/src/main/java/org/apache/camel/component/browse/BrowseEndpoint.java
+++
b/components/camel-browse/src/main/java/org/apache/camel/component/browse/BrowseEndpoint.java
@@ -42,7 +42,7 @@ import org.apache.camel.support.DefaultProducer;
* available to be browsed.
*/
@UriEndpoint(firstVersion = "1.3.0", scheme = "browse", title = "Browse",
syntax = "browse:name",
- remote = false, category = { Category.CORE, Category.MONITORING })
+ remote = false, internal = true, category = { Category.CORE,
Category.MONITORING })
public class BrowseEndpoint extends DefaultEndpoint implements
BrowsableEndpoint {
@UriPath(description = "A name which can be any string to uniquely
identify the endpoint")
diff --git
a/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/cache/caffeine-cache.json
b/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/cache/caffeine-cache.json
index 4a1f35b7cb1a..2306d19ed008 100644
---
a/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/cache/caffeine-cache.json
+++
b/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/cache/caffeine-cache.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"action": { "index": 0, "kind": "property", "displayName": "Action",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "GET", "GET_ALL", "PUT", "PUT_ALL",
"INVALIDATE", "INVALIDATE_ALL", "CLEANUP", "AS_MAP" ], "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.caffeine.CaffeineConfiguration",
"configurationField": "configuration", "description": "To configure the defa
[...]
diff --git
a/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/load/caffeine-loadcache.json
b/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/load/caffeine-loadcache.json
index 0eaeaed09173..0da3d147ff1c 100644
---
a/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/load/caffeine-loadcache.json
+++
b/components/camel-caffeine/src/generated/resources/META-INF/org/apache/camel/component/caffeine/load/caffeine-loadcache.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"action": { "index": 0, "kind": "property", "displayName": "Action",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "GET", "GET_ALL", "PUT", "PUT_ALL",
"INVALIDATE", "INVALIDATE_ALL", "CLEANUP", "AS_MAP" ], "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.caffeine.CaffeineConfiguration",
"configurationField": "configuration", "description": "To configure the defa
[...]
diff --git
a/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/cache/CaffeineCacheEndpoint.java
b/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/cache/CaffeineCacheEndpoint.java
index 38da56f487fa..8ed4bb8c919c 100644
---
a/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/cache/CaffeineCacheEndpoint.java
+++
b/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/cache/CaffeineCacheEndpoint.java
@@ -35,7 +35,8 @@ import org.apache.camel.support.DefaultEndpoint;
* Perform caching operations using Caffeine Cache.
*/
@UriEndpoint(firstVersion = "2.20.0", scheme = "caffeine-cache", title =
"Caffeine Cache",
- remote = false, syntax = "caffeine-cache:cacheName", category = {
Category.CACHE, Category.CLUSTERING },
+ remote = false, internal = true, syntax =
"caffeine-cache:cacheName",
+ category = { Category.CACHE, Category.CLUSTERING },
producerOnly = true, headersClass = CaffeineConstants.class)
public class CaffeineCacheEndpoint extends DefaultEndpoint {
diff --git
a/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/load/CaffeineLoadCacheEndpoint.java
b/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/load/CaffeineLoadCacheEndpoint.java
index fb8c4acde8cc..755d0b779957 100644
---
a/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/load/CaffeineLoadCacheEndpoint.java
+++
b/components/camel-caffeine/src/main/java/org/apache/camel/component/caffeine/load/CaffeineLoadCacheEndpoint.java
@@ -35,7 +35,8 @@ import org.apache.camel.support.DefaultEndpoint;
* Perform caching operations using Caffeine Cache with an attached
CacheLoader.
*/
@UriEndpoint(firstVersion = "2.20.0", scheme = "caffeine-loadcache", title =
"Caffeine LoadCache",
- remote = false, syntax = "caffeine-loadcache:cacheName", category
= { Category.CACHE, Category.CLUSTERING },
+ remote = false, internal = true, syntax =
"caffeine-loadcache:cacheName",
+ category = { Category.CACHE, Category.CLUSTERING },
producerOnly = true, headersClass = CaffeineConstants.class)
public class CaffeineLoadCacheEndpoint extends DefaultEndpoint {
@UriPath(description = "the cache name")
diff --git
a/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json
b/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json
index e0866218e2e9..bf84bbc045c0 100644
---
a/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json
+++
b/components/camel-direct/src/generated/resources/META-INF/org/apache/camel/component/direct/direct.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java
b/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java
index 733aa2a71632..1452087ad592 100644
---
a/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java
+++
b/components/camel-direct/src/main/java/org/apache/camel/component/direct/DirectEndpoint.java
@@ -33,7 +33,7 @@ import org.apache.camel.util.StringHelper;
* This endpoint can be used to connect existing routes in the same
CamelContext.
*/
@UriEndpoint(firstVersion = "1.0.0", scheme = "direct", title = "Direct",
syntax = "direct:name",
- remote = false, category = { Category.CORE, Category.MESSAGING })
+ remote = false, internal = true, category = { Category.CORE,
Category.MESSAGING })
public class DirectEndpoint extends DefaultEndpoint {
private final DirectComponent component;
diff --git
a/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor-vm.json
b/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor-vm.json
index 16e4ff1b865f..83bda4488248 100644
---
a/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor-vm.json
+++
b/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor-vm.json
@@ -22,7 +22,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bufferSize": { "index": 0, "kind": "property", "displayName": "Buffer
Size", "group": "common", "label": "", "required": false, "type": "integer",
"javaType": "int", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 1024, "description": "To configure the ring buffer size" },
diff --git
a/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor.json
b/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor.json
index 90a59fa80dc2..9cf585a052c5 100644
---
a/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor.json
+++
b/components/camel-disruptor/src/generated/resources/META-INF/org/apache/camel/component/disruptor/disruptor.json
@@ -22,7 +22,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bufferSize": { "index": 0, "kind": "property", "displayName": "Buffer
Size", "group": "common", "label": "", "required": false, "type": "integer",
"javaType": "int", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": 1024, "description": "To configure the ring buffer size" },
diff --git
a/components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorEndpoint.java
b/components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorEndpoint.java
index ab83f1e0dfc1..aa236f9be717 100644
---
a/components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorEndpoint.java
+++
b/components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorEndpoint.java
@@ -52,7 +52,7 @@ import org.slf4j.LoggerFactory;
*/
@ManagedResource(description = "Managed Disruptor Endpoint")
@UriEndpoint(firstVersion = "2.12.0", scheme = "disruptor,disruptor-vm", title
= "Disruptor,Disruptor VM",
- remote = false, syntax = "disruptor:name", category = {
Category.MESSAGING })
+ remote = false, internal = true, syntax = "disruptor:name",
category = { Category.MESSAGING })
public class DisruptorEndpoint extends DefaultEndpoint implements
AsyncEndpoint, MultipleConsumersSupport {
/**
* Property on the exchange published by a producer that waits for the
reply. Its value is set to true when the
diff --git
a/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
b/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
index 9ef15d1462f1..e0fcda0198bd 100644
---
a/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
+++
b/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/components/camel-event/src/main/java/org/apache/camel/component/camelevent/CamelEventEndpoint.java
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/CamelEventEndpoint.java
index 528f897f42f3..db7538ca9557 100644
---
a/components/camel-event/src/main/java/org/apache/camel/component/camelevent/CamelEventEndpoint.java
+++
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/CamelEventEndpoint.java
@@ -46,7 +46,7 @@ import org.apache.camel.support.DefaultEndpoint;
* {@code event:Exchange*} matches all exchange events, and {@code event:*}
matches all events.
*/
@UriEndpoint(firstVersion = "4.19.0", scheme = "event", title = "Event",
syntax = "event:events",
- consumerOnly = true, remote = false,
+ consumerOnly = true, remote = false, internal = true,
category = { Category.CORE, Category.MONITORING },
headersClass = CamelEventConstants.class)
public class CamelEventEndpoint extends DefaultEndpoint {
diff --git
a/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json
b/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json
index 553eac237c7d..30b8b7326c6b 100644
---
a/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json
+++
b/components/camel-log/src/generated/resources/META-INF/org/apache/camel/component/log/log.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
diff --git
a/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java
b/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java
index 13e54397b0f6..3f8eff912721 100644
---
a/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java
+++
b/components/camel-log/src/main/java/org/apache/camel/component/log/LogEndpoint.java
@@ -43,7 +43,8 @@ import static
org.apache.camel.support.LoggerHelper.getLineNumberLoggerName;
* Prints data from the routed message (such as body and headers) to the
logger.
*/
@UriEndpoint(firstVersion = "1.1.0", scheme = "log", title = "Log Data",
- remote = false, syntax = "log:loggerName", producerOnly = true,
category = { Category.CORE, Category.MONITORING })
+ remote = false, internal = true, syntax = "log:loggerName",
producerOnly = true,
+ category = { Category.CORE, Category.MONITORING })
public class LogEndpoint extends ProcessorEndpoint implements LineNumberAware {
private volatile Processor logger;
diff --git
a/components/camel-metrics/src/generated/resources/META-INF/org/apache/camel/component/metrics/metrics.json
b/components/camel-metrics/src/generated/resources/META-INF/org/apache/camel/component/metrics/metrics.json
index 6927e608305f..952ca0a8b5d9 100644
---
a/components/camel-metrics/src/generated/resources/META-INF/org/apache/camel/component/metrics/metrics.json
+++
b/components/camel-metrics/src/generated/resources/META-INF/org/apache/camel/component/metrics/metrics.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
diff --git
a/components/camel-metrics/src/main/java/org/apache/camel/component/metrics/MetricsEndpoint.java
b/components/camel-metrics/src/main/java/org/apache/camel/component/metrics/MetricsEndpoint.java
index baef89738240..d8987cf49d47 100644
---
a/components/camel-metrics/src/main/java/org/apache/camel/component/metrics/MetricsEndpoint.java
+++
b/components/camel-metrics/src/main/java/org/apache/camel/component/metrics/MetricsEndpoint.java
@@ -33,7 +33,8 @@ import org.apache.camel.support.DefaultEndpoint;
* Collect various metrics directly from Camel routes using the DropWizard
metrics library.
*/
@UriEndpoint(firstVersion = "2.14.0", scheme = "metrics", title = "Metrics",
syntax = "metrics:metricsType:metricsName",
- remote = false, producerOnly = true, category = {
Category.MONITORING }, headersClass = MetricsConstants.class)
+ remote = false, internal = true, producerOnly = true, category =
{ Category.MONITORING },
+ headersClass = MetricsConstants.class)
public class MetricsEndpoint extends DefaultEndpoint {
protected final MetricRegistry registry;
diff --git
a/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json
b/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json
index f22d774f197c..d8a7f8807de2 100644
---
a/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json
+++
b/components/camel-mock/src/generated/resources/META-INF/org/apache/camel/component/mock/mock.json
@@ -21,7 +21,8 @@
"producerOnly": true,
"lenientProperties": true,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
diff --git
a/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java
b/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java
index 187c6317a126..6ac3b02ca1e5 100644
---
a/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java
+++
b/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockEndpoint.java
@@ -96,7 +96,7 @@ import org.slf4j.LoggerFactory;
* overrides the number of expected messages based on the number of values
provided in the bodies/headers.
*/
@UriEndpoint(firstVersion = "1.0.0", scheme = "mock", title = "Mock", syntax =
"mock:name", producerOnly = true,
- remote = false, category = { Category.CORE, Category.TESTING },
lenientProperties = true)
+ remote = false, internal = true, category = { Category.CORE,
Category.TESTING }, lenientProperties = true)
public class MockEndpoint extends DefaultEndpoint implements
BrowsableEndpoint, NotifyBuilderMatcher {
private static final Logger LOG =
LoggerFactory.getLogger(MockEndpoint.class);
diff --git
a/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json
b/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json
index 6681f3cbd6c3..0a1a4bb65b6d 100644
---
a/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json
+++
b/components/camel-seda/src/generated/resources/META-INF/org/apache/camel/component/seda/seda.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java
b/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java
index 6ce9d7ec6510..0da80bde3d08 100644
---
a/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java
+++
b/components/camel-seda/src/main/java/org/apache/camel/component/seda/SedaEndpoint.java
@@ -58,7 +58,7 @@ import org.slf4j.LoggerFactory;
*/
@ManagedResource(description = "Managed SedaEndpoint")
@UriEndpoint(firstVersion = "1.1.0", scheme = "seda", title = "SEDA", syntax =
"seda:name",
- remote = false, category = { Category.CORE, Category.MESSAGING })
+ remote = false, internal = true, category = { Category.CORE,
Category.MESSAGING })
public class SedaEndpoint extends DefaultEndpoint implements AsyncEndpoint,
BrowsableEndpoint, MultipleConsumersSupport {
private static final Logger LOG =
LoggerFactory.getLogger(SedaEndpoint.class);
diff --git
a/components/camel-stub/src/generated/resources/META-INF/org/apache/camel/component/stub/stub.json
b/components/camel-stub/src/generated/resources/META-INF/org/apache/camel/component/stub/stub.json
index 32d642f6acce..43c75bea1a28 100644
---
a/components/camel-stub/src/generated/resources/META-INF/org/apache/camel/component/stub/stub.json
+++
b/components/camel-stub/src/generated/resources/META-INF/org/apache/camel/component/stub/stub.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": true,
"browsable": true,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubEndpoint.java
b/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubEndpoint.java
index 9265d549e7eb..e745e04d14ba 100644
---
a/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubEndpoint.java
+++
b/components/camel-stub/src/main/java/org/apache/camel/component/stub/StubEndpoint.java
@@ -37,7 +37,7 @@ import org.apache.camel.spi.UriEndpoint;
* parameters to let you quickly stub out one or more endpoints in your route
temporarily.
*/
@UriEndpoint(firstVersion = "2.10.0", scheme = "stub", title = "Stub", syntax
= "stub:name",
- remote = false, category = { Category.CORE, Category.TESTING },
lenientProperties = true)
+ remote = false, internal = true, category = { Category.CORE,
Category.TESTING }, lenientProperties = true)
public class StubEndpoint extends SedaEndpoint {
public StubEndpoint(String endpointUri, Component component,
BlockingQueue<Exchange> queue) {
diff --git
a/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json
b/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json
index 28e78be1017b..88a27c1c699a 100644
---
a/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json
+++
b/components/camel-timer/src/generated/resources/META-INF/org/apache/camel/component/timer/timer.json
@@ -21,7 +21,8 @@
"producerOnly": false,
"lenientProperties": false,
"browsable": false,
- "remote": false
+ "remote": false,
+ "internal": true
},
"componentProperties": {
"bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "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 messages, or the like [...]
diff --git
a/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java
b/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java
index e6a80deaa9ca..943e98c3783b 100644
---
a/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java
+++
b/components/camel-timer/src/main/java/org/apache/camel/component/timer/TimerEndpoint.java
@@ -42,7 +42,8 @@ import org.apache.camel.support.DefaultEndpoint;
*/
@ManagedResource(description = "Managed TimerEndpoint")
@UriEndpoint(firstVersion = "1.0.0", scheme = "timer", title = "Timer", syntax
= "timer:timerName", consumerOnly = true,
- remote = false, category = { Category.CORE, Category.SCHEDULING
}, headersClass = TimerConstants.class)
+ remote = false, internal = true, category = { Category.CORE,
Category.SCHEDULING },
+ headersClass = TimerConstants.class)
public class TimerEndpoint extends DefaultEndpoint implements
MultipleConsumersSupport {
@UriPath
@Metadata(required = true)
diff --git
a/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
b/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
index 048bd736794a..fc1c94f5ab92 100644
--- a/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
+++ b/core/camel-api/src/generated/java/org/apache/camel/spi/UriEndpoint.java
@@ -201,6 +201,28 @@ public @interface UriEndpoint {
/**
* Whether the component does remote communication such as connecting to
an external system over the network. Set
* this to false for internal components such as log, message
transformations and other kinds.
+ * <p/>
+ * Not remote does not mean that the component stays inside the
CamelContext: a component that runs code, or reads a
+ * file or a template, is not remote either. See {@link #internal()}.
*/
boolean remote() default true;
+
+ /**
+ * Whether the component stays inside the CamelContext: it only reaches
the routes, the in-memory state (a queue, a
+ * cache, metrics) and the log of the CamelContext. It never reads or
writes a file, uses the network, runs a
+ * process, or runs code or loads a resource named in its URI. A message
it hands to a route (direct, seda) still
+ * does whatever that route does.
+ * <p/>
+ * This is about what the component can do, not what it is usually used
for: a capability counts even when it sits
+ * behind an option that is off by default and seldom used, or in a
feature that ships with the component. So
+ * dataset-test is not internal (it polls the endpoint named in its URI),
nor is once (it can read its body from a
+ * file), nor micrometer (it evaluates its options with the simple
language, which can load its text from a file or
+ * a URL).
+ * <p/>
+ * An internal component is not remote, so it sets {@code remote = false}
too. Tooling uses this to tell what stays
+ * inside the integration, for example to send a test message to log or
direct without asking the user first.
+ *
+ * @since 4.24
+ */
+ boolean internal() default false;
}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-ai.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-ai.adoc
index 4ded91aaf6ff..d39ee1e4be2f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-ai.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-tui-ai.adoc
@@ -196,8 +196,12 @@ code ask you first, in a dialog that shows the call:
* writing a file: the diff dialog, or the live replay in the Source editor
(see `/write` above);
* sending a message to an endpoint the integration does not have, such as a
file, a command, an HTTP service or
- another topic; a message to a route, to an internal endpoint (`direct:`,
`seda:`, `log:`, `mock:`, `stub:`
- and the like), or to an endpoint the integration uses goes ahead;
+ another topic; a message to a route, to an endpoint the integration uses, or
to an internal endpoint goes ahead.
+ Internal is what the Camel catalog marks so: a component that stays inside
the integration (`direct:`, `seda:`,
+ `log:`, `mock:`, `stub:` and the like), with no option or feature that
reaches a file or the network, even one
+ seldom used;
+* sending a file from outside the project folder as the message body (a body
that is `file:` and a path), wherever
+ the message goes; a file in the project, such as `file:data/order.json`,
goes ahead;
* SQL other than a single `SELECT`, `SHOW` or `DESCRIBE`, and updating a row;
* evaluating an expression in a language that runs code, such as Groovy, Java,
JavaScript or Python.
diff --git a/docs/user-manual/modules/ROOT/pages/security-model.adoc
b/docs/user-manual/modules/ROOT/pages/security-model.adoc
index 712c68e3ad85..b659add2c9b2 100644
--- a/docs/user-manual/modules/ROOT/pages/security-model.adoc
+++ b/docs/user-manual/modules/ROOT/pages/security-model.adoc
@@ -152,11 +152,13 @@ deliberately few, and they are the contract:
unless the developer chose `/write auto`.
* The developer confirms a call that reaches beyond the integration, may
destroy data or runs code: a message to an endpoint the integration does not
- have (a route, an internal endpoint such as `direct:`, `seda:` or `log:`,
- and the integration's own endpoints are inside it), a SQL statement other
- than a single `SELECT`, `SHOW` or `DESCRIBE`, a row update, and an
- expression in a language that runs code, such as Groovy. An HTTP request
- goes only to the integration's own server on `localhost`.
+ have (a route, an endpoint of a component the Camel catalog marks internal,
+ such as `direct:`, `seda:` or `log:`, and the integration's own endpoints are
+ inside it), a message whose body the integration reads from a file outside
+ the project folder, a SQL statement other than a single `SELECT`, `SHOW` or
+ `DESCRIBE`, a row update, and an expression in a language that runs code,
+ such as Groovy. An HTTP request goes only to the integration's own server on
+ `localhost`.
* A coding agent's own tools - its shell commands, its file changes, its
network fetches - are put in front of the developer in the TUI.
* Keys sent over MCP never answer a dialog that asks the developer to confirm
diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/AGENTS.md
b/dsl/camel-jbang/camel-jbang-plugin-tui/AGENTS.md
index 2f81ab59802d..412984c081b5 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-tui/AGENTS.md
+++ b/dsl/camel-jbang/camel-jbang-plugin-tui/AGENTS.md
@@ -37,7 +37,7 @@ and use each other's package-private members, so there are no
sub-packages. Clas
| `AbstractDiagramTab` | what the Diagram and Routes tabs share of the route
diagram: its state, keys, drilling into a route and back |
| `AiPanel`, `AiSlashCommandRegistry` | the F8 AI panel; its input line is
`AiInputLine`, its tools and system prompt `AiToolSet`, its usage statistics
`AiUsageStats`, and an external coding agent (ACP) `AcpSession` over
`AcpAgentClient` |
| `LiveEdit`, `EditReplay` | `/write live`: a file an AI tool writes is typed
into the source editor, to watch and then save or discard |
-| `ToolConfirms`, `ToolConfirmPopup` | the AI tools run without asking (a
developer tool); these are the few calls the user confirms first (a file write,
a message to an endpoint the integration does not have, SQL that is not a
SELECT, Groovy), and the popup that asks |
+| `ToolConfirms`, `ToolConfirmPopup` | the AI tools run without asking (a
developer tool); these are the few calls the user confirms first (a file write,
a message to an endpoint the integration does not have or with a body read from
a file outside the project, SQL that is not a SELECT, Groovy), and the popup
that asks |
| `TuiMcpServer`, `TuiToolDefinitions`, `TuiToolRegistry`, `McpFacade` | the
MCP server: the tools (`TuiTool`: schema, handler, flags), their
implementations, the bridge to the screen; the shared file tools are
`TuiFileTools`, which writes only to an integration's source directory, through
the AI panel's write mode |
| `Theme`, `TuiIcons` | colours (themes in `src/main/resources/tui/themes`)
and icons |
| `TuiSettings`, `SettingsPopup` | persisted settings |
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
index 76f9c7b08612..b30301ac9fb7 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
@@ -1631,7 +1631,11 @@ class McpFacade {
} catch (NumberFormatException e) {
return null;
}
- JsonObject rejected = confirm(ToolConfirms.sendMessage(endpoint,
selectedEndpointUris()));
+ // a body file:... makes the integration read that file, relative to
its working directory
+ IntegrationInfo info = findIntegration(null);
+ String fileOutside = ToolConfirms.fileOutsideProject(
+ body, info != null ? info.directory : null,
getSelectedSourceDirectory());
+ JsonObject rejected = confirm(ToolConfirms.sendMessage(endpoint,
selectedEndpointUris(), fileOutside));
return rejected != null ? rejected : RuntimeHelper.sendMessage(pid,
endpoint, body, headers);
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirms.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirms.java
index 876235757f41..2df741748f6e 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirms.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirms.java
@@ -16,13 +16,19 @@
*/
package org.apache.camel.dsl.jbang.core.commands.tui;
+import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
import java.util.Collection;
import java.util.Locale;
import java.util.Set;
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.util.URISupport;
/**
@@ -30,8 +36,9 @@ import org.apache.camel.util.URISupport;
* agent and an MCP client run its tools without asking, so the AI can show
and tell at once (see "Developer tools" in
* the security model). The exceptions are the calls that reach beyond the
integration, may destroy data or run code:
* <ul>
- * <li>tui_send_message to an endpoint the integration does not have; a route
id, a pattern and an internal endpoint
- * (direct:, seda:, log:, mock: and the like) stay inside it;</li>
+ * <li>tui_send_message to an endpoint the integration does not have; a route
id, a pattern and an endpoint of a
+ * component the catalog marks internal (direct:, seda:, log:, mock: and the
like) stay inside it. Also a message whose
+ * body the integration reads from a file outside the project folder (a body
that starts with file:);</li>
* <li>tui_execute_sql with a statement that is not a single SELECT (or SHOW,
DESCRIBE), and tui_update_row;</li>
* <li>camel_eval_expression in a language that runs code, such as Groovy.</li>
* </ul>
@@ -48,20 +55,51 @@ final class ToolConfirms {
static final Set<String> CODE_LANGUAGES
= Set.of("groovy", "java", "joor", "js", "javascript", "python",
"mvel", "ognl", "spel");
+ private ToolConfirms() {
+ }
+
/**
- * The components that stay inside the integration: its routes and
in-memory queues, its log and the test endpoints.
- * Not every component the catalog marks remote=false: exec, class, bean,
language and the components that load a
- * template or a stylesheet by URI are not remote either, but run code, or
read a file or a URL.
+ * Whether a component stays inside the integration, as the catalog marks
it internal: its routes, in-memory state
+ * and log, and no file, network, process, or code or resource named in
its URI, by any option. This is narrower
+ * than remote=false, which exec, class, bean, language and the components
that load a template or a stylesheet by
+ * URI are too. The catalog is the TUI's own, so it has the flag; a
component it does not know (a custom one, or one
+ * newer than the TUI) is not internal.
*/
- static final Set<String> INTERNAL_SCHEMES = Set.of(
- "direct", "seda", "disruptor", "disruptor-vm", "log", "mock",
"stub", "browse", "dataset", "dataset-test");
+ static boolean isInternal(String scheme) {
+ if (!scheme.matches("[a-z0-9-]+")) {
+ return false;
+ }
+ try {
+ ComponentModel model = Catalog.INSTANCE.componentModel(scheme);
+ return model != null && model.isInternal();
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
- private ToolConfirms() {
+ // loaded on the first send, not when a call only needs the SQL or
language checks
+ private static final class Catalog {
+ static final CamelCatalog INSTANCE = new DefaultCamelCatalog(true);
}
- /** A message to an endpoint the integration does not have, or null when
it stays inside the integration. */
- static Call sendMessage(String endpoint, Collection<String> endpointUris) {
- if (staysInside(endpoint, endpointUris)) {
+ /**
+ * A message to an endpoint the integration does not have, or with a body
read from a file outside the project; null
+ * when it stays inside the integration and the project.
+ *
+ * @param fileOutside the file outside the project that the body makes the
integration read (see
+ * {@link #fileOutsideProject}), or null
+ */
+ static Call sendMessage(String endpoint, Collection<String> endpointUris,
String fileOutside) {
+ boolean inside = staysInside(endpoint, endpointUris);
+ if (fileOutside != null) {
+ return new Call(
+ inside
+ ? "send a file from outside the project"
+ : "send a file from outside the project to an
endpoint the integration does not have",
+ endpoint + "\nbody: file:" + fileOutside,
+ "The integration reads the file and sends its content,
which may hold secrets");
+ }
+ if (inside) {
return null;
}
return new Call(
@@ -69,6 +107,49 @@ final class ToolConfirms {
"The message may leave the integration: to a file, a broker or
a service");
}
+ /**
+ * The file a message body makes the integration read when it is outside
the project folder, or null. The send
+ * console sends the content of the file that a body starting with file:
names, relative to the working directory of
+ * the integration. The project folder is the integration's source
directory, where the AI tools write. Symlinks and
+ * .. are resolved as the integration would; a file that cannot be placed
counts as outside.
+ */
+ static String fileOutsideProject(String body, String workingDirectory,
Path projectDirectory) {
+ if (body == null || !body.startsWith("file:")) {
+ return null;
+ }
+ String name = body.substring(5);
+ Path file;
+ try {
+ Path path = Path.of(name);
+ if (!path.isAbsolute()) {
+ // not known, blank or relative, it cannot place the file: the
JVM of the TUI is elsewhere
+ Path work = workingDirectory != null ?
Path.of(workingDirectory) : null;
+ if (work == null || !work.isAbsolute()) {
+ return name;
+ }
+ path = work.resolve(path);
+ }
+ file = realPath(path);
+ } catch (InvalidPathException e) {
+ return name;
+ }
+ return projectDirectory != null &&
file.startsWith(realPath(projectDirectory)) ? null : file.toString();
+ }
+
+ /**
+ * The path with symlinks and .. resolved as the file system does. A file
that is not there is placed under the real
+ * path of its nearest parent that is.
+ */
+ private static Path realPath(Path path) {
+ Path absolute = path.toAbsolutePath();
+ try {
+ return absolute.toRealPath();
+ } catch (IOException e) {
+ Path parent = absolute.getParent();
+ return parent != null ?
realPath(parent).resolve(absolute.getFileName()).normalize() :
absolute.normalize();
+ }
+ }
+
/**
* Whether a send stays inside the integration: a route id or a pattern
(the send console picks one of the routes),
* an internal endpoint, or an endpoint the integration has (its endpoint
registry, as its status lists it).
@@ -81,7 +162,7 @@ final class ToolConfirms {
// an option can still reach out: #class:...(args) runs any
constructor (a FileOutputStream truncates a file),
// and a {{placeholder}} can put a secret in the log
String decoded = decode(endpoint);
- if (INTERNAL_SCHEMES.contains(endpoint.substring(0,
endpoint.indexOf(':')))
+ if (isInternal(endpoint.substring(0, endpoint.indexOf(':')))
&& !decoded.contains("#") && !decoded.contains("{{")) {
return true;
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacadeConfirmTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacadeConfirmTest.java
index 276cddebfa99..eb934f5a481c 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacadeConfirmTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacadeConfirmTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.dsl.jbang.core.commands.tui;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -24,6 +25,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.camel.util.json.JsonObject;
import org.apache.camel.util.json.Jsoner;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
@@ -80,10 +82,27 @@ class McpFacadeConfirmTest {
// the endpoints of the integration come from its status
assertThat(facade.selectedEndpointUris()).containsExactly("kafka://orders?brokers=localhost%3A9092");
assertThat(facade.confirm(ToolConfirms.sendMessage("kafka:orders?brokers=localhost:9092",
- facade.selectedEndpointUris()))).isNull();
+ facade.selectedEndpointUris(), null))).isNull();
assertThat(bridge.calls).hasSize(1);
}
+ @Test
+ void aBodyReadFromAFileOutsideTheProjectAsksEvenForTheLog(@TempDir Path
project) {
+ AnsweringBridge bridge = new AnsweringBridge(false);
+ MonitorContext ctx = context();
+ // the integration runs in the project folder, which is also its
source directory
+ ctx.findSelectedIntegration().directory = project.toString();
+ McpFacade facade = facade(ctx, bridge);
+
+ JsonObject result = facade.sendMessage("log:demo", "file:/etc/hosts",
null);
+
+ assertThat(result.getString("status")).isEqualTo("rejected");
+ assertThat(bridge.calls).singleElement().satisfies(call -> {
+ assertThat(call.title()).isEqualTo("send a file from outside the
project");
+ assertThat(call.target()).startsWith("log:demo\nbody:
file:").endsWith("hosts");
+ });
+ }
+
@Test
void sqlThatIsNotASelectAndARowUpdateAskFirst() {
AnsweringBridge bridge = new AnsweringBridge(false);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmPopupTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmPopupTest.java
index 2538404809e6..40f2cbed59e1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmPopupTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmPopupTest.java
@@ -110,7 +110,7 @@ class ToolConfirmPopupTest {
void aCallShowsWhatTheAiWantsToRunAndEnterRunsIt() {
ToolConfirmPopup popup = new ToolConfirmPopup();
CompletableFuture<Boolean> answer = new CompletableFuture<>();
- popup.open(ToolConfirms.sendMessage("file:/home/me?fileName=.zshrc",
List.of("kafka:orders")), answer);
+ popup.open(ToolConfirms.sendMessage("file:/home/me?fileName=.zshrc",
List.of("kafka:orders"), null), answer);
assertThat(render(popup, 100, 14))
.contains("AI wants to send a message to an endpoint the
integration does not have")
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmsTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmsTest.java
index f1b64d82e52f..58e5a4922867 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmsTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/ToolConfirmsTest.java
@@ -16,11 +16,16 @@
*/
package org.apache.camel.dsl.jbang.core.commands.tui;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.List;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assumptions.assumeThat;
/**
* The AI tools run without asking, except a call that reaches beyond the
integration, may destroy data or runs code.
@@ -34,47 +39,120 @@ class ToolConfirmsTest {
@Test
void aMessageToAnEndpointOfTheIntegrationIsSentWithoutAsking() {
// the same endpoint, written another way: the URIs are compared
normalized
-
assertThat(ToolConfirms.sendMessage("kafka:orders?groupId=shop&brokers=localhost:9092",
ENDPOINTS)).isNull();
- assertThat(ToolConfirms.sendMessage("timer:tick?period=1000",
ENDPOINTS)).isNull();
+
assertThat(ToolConfirms.sendMessage("kafka:orders?groupId=shop&brokers=localhost:9092",
ENDPOINTS, null)).isNull();
+ assertThat(ToolConfirms.sendMessage("timer:tick?period=1000",
ENDPOINTS, null)).isNull();
// a route id or a pattern: the send console picks one of the
integration's routes
- assertThat(ToolConfirms.sendMessage("checkout", ENDPOINTS)).isNull();
- assertThat(ToolConfirms.sendMessage("kafka:*", ENDPOINTS)).isNull();
- // an internal endpoint stays inside the integration: its routes, its
log, the test endpoints
- assertThat(ToolConfirms.sendMessage("direct:unknown",
ENDPOINTS)).isNull();
- assertThat(ToolConfirms.sendMessage("seda:work", List.of())).isNull();
- assertThat(ToolConfirms.sendMessage("log:demo?showAll=true",
List.of())).isNull();
- assertThat(ToolConfirms.sendMessage("mock:result",
List.of())).isNull();
- assertThat(ToolConfirms.sendMessage("stub:kafka:payments",
List.of())).isNull();
+ assertThat(ToolConfirms.sendMessage("checkout", ENDPOINTS,
null)).isNull();
+ assertThat(ToolConfirms.sendMessage("kafka:*", ENDPOINTS,
null)).isNull();
+ // an endpoint of a component the catalog marks internal stays inside
the integration: its routes, in-memory
+ // state, log and test endpoints
+ assertThat(ToolConfirms.sendMessage("direct:unknown", ENDPOINTS,
null)).isNull();
+ assertThat(ToolConfirms.sendMessage("seda:work", List.of(),
null)).isNull();
+ assertThat(ToolConfirms.sendMessage("log:demo?showAll=true",
List.of(), null)).isNull();
+ assertThat(ToolConfirms.sendMessage("mock:result", List.of(),
null)).isNull();
+ assertThat(ToolConfirms.sendMessage("stub:kafka:payments", List.of(),
null)).isNull();
+
assertThat(ToolConfirms.sendMessage("caffeine-cache:orders?action=PUT&key=1",
List.of(), null)).isNull();
}
@Test
void aMessageToAnEndpointTheIntegrationDoesNotHaveAsks() {
- ToolConfirms.Call call =
ToolConfirms.sendMessage("file:/Users/me?fileName=.zshrc", ENDPOINTS);
+ ToolConfirms.Call call =
ToolConfirms.sendMessage("file:/Users/me?fileName=.zshrc", ENDPOINTS, null);
assertThat(call).isNotNull();
assertThat(call.target()).isEqualTo("file:/Users/me?fileName=.zshrc");
assertThat(call.title()).isEqualTo("send a message to an endpoint the
integration does not have");
- assertThat(ToolConfirms.sendMessage("exec:rm?args=-rf /tmp/x",
ENDPOINTS)).isNotNull();
- assertThat(ToolConfirms.sendMessage("http://example.com/hook",
ENDPOINTS)).isNotNull();
+ assertThat(ToolConfirms.sendMessage("exec:rm?args=-rf /tmp/x",
ENDPOINTS, null)).isNotNull();
+ assertThat(ToolConfirms.sendMessage("http://example.com/hook",
ENDPOINTS, null)).isNotNull();
// another topic, or the endpoint of the integration with an option of
its own, is not the same endpoint
-
assertThat(ToolConfirms.sendMessage("kafka:payments?brokers=localhost:9092&groupId=shop",
ENDPOINTS)).isNotNull();
-
assertThat(ToolConfirms.sendMessage("kafka:orders?brokers=evil:9092&groupId=shop",
ENDPOINTS)).isNotNull();
+
assertThat(ToolConfirms.sendMessage("kafka:payments?brokers=localhost:9092&groupId=shop",
ENDPOINTS, null)).isNotNull();
+
assertThat(ToolConfirms.sendMessage("kafka:orders?brokers=evil:9092&groupId=shop",
ENDPOINTS, null)).isNotNull();
// nothing known of the integration: everything outside it asks
- assertThat(ToolConfirms.sendMessage("kafka:orders",
List.of())).isNotNull();
+ assertThat(ToolConfirms.sendMessage("kafka:orders", List.of(),
null)).isNotNull();
+ }
+
+ @Test
+ void aBodyReadFromAFileInTheProjectIsSentWithoutAsking(@TempDir Path
project) throws IOException {
+ Files.createDirectories(project.resolve("data"));
+ Files.writeString(project.resolve("data/order.json"), "{}");
+ String work = project.toString();
+
+ // relative to the working directory of the integration, or absolute
+ assertThat(ToolConfirms.fileOutsideProject("file:data/order.json",
work, project)).isNull();
+ assertThat(ToolConfirms.fileOutsideProject("file:" +
project.resolve("data/order.json"), work, project)).isNull();
+ // a file that is not there yet
+ assertThat(ToolConfirms.fileOutsideProject("file:data/new.json", work,
project)).isNull();
+ // not a file
+ assertThat(ToolConfirms.fileOutsideProject("hello file:x", work,
project)).isNull();
+ assertThat(ToolConfirms.fileOutsideProject(null, work,
project)).isNull();
+ }
+
+ @Test
+ void aBodyReadFromAFileOutsideTheProjectAsks(@TempDir Path tmp) throws
IOException {
+ Path project = Files.createDirectories(tmp.resolve("project"));
+ Path secret = Files.writeString(tmp.resolve("secret.txt"), "s3cr3t");
+ String work = project.toString();
+
+ assertThat(ToolConfirms.fileOutsideProject("file:" + secret, work,
project))
+ .isEqualTo(secret.toRealPath().toString());
+ // out of the project with .., also through a folder that is not there
+ assertThat(ToolConfirms.fileOutsideProject("file:../secret.txt", work,
project)).isNotNull();
+
assertThat(ToolConfirms.fileOutsideProject("file:missing/../../secret.txt",
work, project))
+ .isEqualTo(secret.toRealPath().toString());
+ // a relative file when the working directory is not known (or blank
or relative, which the JVM of the TUI
+ // would resolve), any file when the project is not known
+ assertThat(ToolConfirms.fileOutsideProject("file:data/order.json",
null, project)).isEqualTo("data/order.json");
+ assertThat(ToolConfirms.fileOutsideProject("file:data/order.json", "",
project)).isEqualTo("data/order.json");
+ assertThat(ToolConfirms.fileOutsideProject("file:data/order.json", "
", project)).isEqualTo("data/order.json");
+ assertThat(ToolConfirms.fileOutsideProject("file:data/order.json",
"project", project))
+ .isEqualTo("data/order.json");
+ assertThat(ToolConfirms.fileOutsideProject("file:data/order.json",
work, null)).isNotNull();
+
+ // wherever the message goes, even to the log
+ ToolConfirms.Call call = ToolConfirms.sendMessage("log:demo",
List.of(), secret.toString());
+ assertThat(call.title()).isEqualTo("send a file from outside the
project");
+ assertThat(call.target()).isEqualTo("log:demo\nbody: file:" + secret);
+ assertThat(ToolConfirms.sendMessage("http://example.com/hook",
List.of(), secret.toString()).title())
+ .isEqualTo("send a file from outside the project to an
endpoint the integration does not have");
+ }
+
+ @Test
+ void aSymlinkInTheProjectToAFileOutsideAsks(@TempDir Path tmp) throws
IOException {
+ Path project = Files.createDirectories(tmp.resolve("project"));
+ Path outside = Files.createDirectories(tmp.resolve("outside"));
+ Files.writeString(outside.resolve("secret.txt"), "s3cr3t");
+ boolean linked;
+ try {
+ Files.createSymbolicLink(project.resolve("link"), outside);
+ linked = true;
+ } catch (UnsupportedOperationException | IOException e) {
+ linked = false;
+ }
+ assumeThat(linked).as("symbolic links").isTrue();
+
+ assertThat(ToolConfirms.fileOutsideProject("file:link/secret.txt",
project.toString(), project)).isNotNull();
}
@Test
void anInternalEndpointThatReachesOutAsks() {
// a bean option runs any constructor, a placeholder can log a secret
assertThat(ToolConfirms.sendMessage("log:x?exchangeFormatter=#class:java.io.FileOutputStream('/tmp/x')",
- List.of())).isNotNull();
- assertThat(ToolConfirms.sendMessage("seda:x?queue=%23bean:queue",
List.of())).isNotNull();
- assertThat(ToolConfirms.sendMessage("log:{{aws.secretKey}}",
List.of())).isNotNull();
+ List.of(), null)).isNotNull();
+ assertThat(ToolConfirms.sendMessage("seda:x?queue=%23bean:queue",
List.of(), null)).isNotNull();
+ assertThat(ToolConfirms.sendMessage("log:{{aws.secretKey}}",
List.of(), null)).isNotNull();
// not remote, but not internal: they run code, or read a file or a URL
-
assertThat(ToolConfirms.sendMessage("bean:orderService?method=deleteAll",
List.of())).isNotNull();
- assertThat(ToolConfirms.sendMessage("class:java.lang.Runtime",
List.of())).isNotNull();
- assertThat(ToolConfirms.sendMessage("language:groovy:'touch
/tmp/x'.execute()", List.of())).isNotNull();
-
assertThat(ToolConfirms.sendMessage("velocity:file:/Users/me/.ssh/id_rsa",
List.of())).isNotNull();
+
assertThat(ToolConfirms.sendMessage("bean:orderService?method=deleteAll",
List.of(), null)).isNotNull();
+ assertThat(ToolConfirms.sendMessage("class:java.lang.Runtime",
List.of(), null)).isNotNull();
+ assertThat(ToolConfirms.sendMessage("language:groovy:'touch
/tmp/x'.execute()", List.of(), null)).isNotNull();
+
assertThat(ToolConfirms.sendMessage("velocity:file:/Users/me/.ssh/id_rsa",
List.of(), null)).isNotNull();
+ // they look internal but are not, as a seldom used option or feature
reaches out: dataset-test polls the
+ // endpoint in its URI (a file consumer moves the files it reads),
micrometer evaluates its options with simple,
+ // which loads a resource: text from a file or a URL
+
assertThat(ToolConfirms.sendMessage("dataset-test:file:/Users/me/work",
List.of(), null)).isNotNull();
+
assertThat(ToolConfirms.sendMessage("micrometer:counter:resource:file:/Users/me/.aws/credentials",
List.of(), null))
+ .isNotNull();
+ // a component the catalog does not know, such as a custom one
+ assertThat(ToolConfirms.sendMessage("acme:orders", List.of(),
null)).isNotNull();
+ assertThat(ToolConfirms.isInternal("../components/log")).isFalse();
}
@Test
diff --git
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
index 561849cf3906..bfce0ec3818b 100644
---
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
+++
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
@@ -37,6 +37,7 @@ public class ComponentModel extends
ArtifactModel<ComponentModel.ComponentOption
protected boolean lenientProperties;
protected boolean browsable;
protected boolean remote;
+ protected boolean internal;
protected String verifiers;
protected final List<EndpointOptionModel> endpointOptions = new
ArrayList<>();
protected final List<EndpointHeaderModel> headers = new ArrayList<>();
@@ -155,6 +156,18 @@ public class ComponentModel extends
ArtifactModel<ComponentModel.ComponentOption
this.remote = remote;
}
+ /**
+ * Whether the component stays inside the CamelContext: no file, network,
process, or code or resource named in its
+ * URI, by any option or feature (see {@code UriEndpoint.internal()}).
+ */
+ public boolean isInternal() {
+ return internal;
+ }
+
+ public void setInternal(boolean internal) {
+ this.internal = internal;
+ }
+
public String getVerifiers() {
return verifiers;
}
diff --git
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
index 8921f15869fc..3e8474dea729 100644
---
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
+++
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
@@ -205,6 +205,7 @@ public final class JsonMapper {
model.setLenientProperties(mobj.getBooleanOrDefault("lenientProperties",
false));
model.setBrowsable(mobj.getBooleanOrDefault("browsable", false));
model.setRemote(mobj.getBooleanOrDefault("remote", false));
+ model.setInternal(mobj.getBooleanOrDefault("internal", false));
parseArtifact(mobj, model);
}
@@ -238,6 +239,10 @@ public final class JsonMapper {
obj.put("lenientProperties", model.isLenientProperties());
obj.put("browsable", model.isBrowsable());
obj.put("remote", model.isRemote());
+ // only written for the few internal components; a catalog without it
reads as not internal
+ if (model.isInternal()) {
+ obj.put("internal", true);
+ }
obj.put("verifiers", model.getVerifiers());
obj.entrySet().removeIf(e -> e.getValue() == null);
JsonObject wrapper = new JsonObject();
diff --git
a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java
b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java
index 18c4d56cc2ab..8112520b1d6a 100644
---
a/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java
+++
b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/JsonMapperTest.java
@@ -39,6 +39,19 @@ class JsonMapperTest {
assertTrue(model2.getEndpointHeaders().isEmpty());
}
+ @Test
+ void testShouldWriteInternalOnlyWhenSet() {
+ ComponentModel model = new ComponentModel();
+ String json = JsonMapper.createParameterJsonSchema(model);
+ assertFalse(json.contains("\"internal\""));
+ assertFalse(JsonMapper.generateComponentModel(json).isInternal());
+
+ model.setInternal(true);
+ json = JsonMapper.createParameterJsonSchema(model);
+ assertTrue(json.contains("\"internal\": true"));
+ assertTrue(JsonMapper.generateComponentModel(json).isInternal());
+ }
+
@Test
void testShouldSerializeAndDeserializeComponentWithOneHeader() {
ComponentModel model = new ComponentModel();
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
index 919af18c16a0..5c0db7ec11a5 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
@@ -824,6 +824,11 @@ public class EndpointSchemaGeneratorMojo extends
AbstractGeneratorMojo {
UriEndpoint uriEndpoint, Class<?> endpointClassElement, String
title, String scheme,
String extendsScheme, String label, String[] schemes)
throws MojoExecutionException {
+ if (uriEndpoint.internal() && uriEndpoint.remote()) {
+ throw new MojoExecutionException(
+ "@UriEndpoint(internal = true) on " +
endpointClassElement.getName()
+ + " needs remote = false too: an
internal component does not reach the network");
+ }
ComponentModel model = new ComponentModel();
model.setScheme(scheme);
model.setName(scheme);
@@ -847,6 +852,7 @@ public class EndpointSchemaGeneratorMojo extends
AbstractGeneratorMojo {
model.setProducerOnly(uriEndpoint.producerOnly());
model.setLenientProperties(uriEndpoint.lenientProperties());
model.setRemote(uriEndpoint.remote());
+ model.setInternal(uriEndpoint.internal());
model.setAsync(loadClass("org.apache.camel.AsyncEndpoint").isAssignableFrom(endpointClassElement));
model.setApi(loadClass("org.apache.camel.ApiEndpoint").isAssignableFrom(endpointClassElement));
model.setBrowsable(loadClass("org.apache.camel.spi.BrowsableEndpoint").isAssignableFrom(endpointClassElement));
diff --git
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
index b717f0343e00..562fce01273e 100644
---
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
+++
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
@@ -31,9 +31,11 @@ import
org.apache.camel.maven.packaging.endpoint.SomeEndpointWithHeaderInterface
import
org.apache.camel.maven.packaging.endpoint.SomeEndpointWithJavadocAsDescription;
import org.apache.camel.maven.packaging.endpoint.SomeEndpointWithoutHeaders;
import org.apache.camel.maven.packaging.endpoint.SomeExtendingEndpoint;
+import org.apache.camel.maven.packaging.endpoint.SomeInternalRemoteEndpoint;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.tooling.model.ComponentModel.EndpointHeaderModel;
+import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -44,6 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -131,6 +134,15 @@ class EndpointSchemaGeneratorMojoTest {
assertEquals(0, model.getEndpointHeaders().size());
}
+ @Test
+ void testInternalEndpointMustNotBeRemote() {
+ UriEndpoint endpoint =
SomeInternalRemoteEndpoint.class.getAnnotation(UriEndpoint.class);
+ MojoExecutionException e = assertThrows(MojoExecutionException.class,
+ () -> mojo.findComponentProperties(endpoint,
SomeInternalRemoteEndpoint.class, "some", "some", null, null,
+ null));
+ assertTrue(e.getMessage().contains("needs remote = false"),
e.getMessage());
+ }
+
@Test
void testEndpointWithoutHeadersAreIgnored() {
mojo.addEndpointHeaders(model,
SomeEndpointWithoutHeaders.class.getAnnotation(UriEndpoint.class), "some");
diff --git
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeInternalRemoteEndpoint.java
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeInternalRemoteEndpoint.java
new file mode 100644
index 000000000000..83b90a7e5b0c
--- /dev/null
+++
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeInternalRemoteEndpoint.java
@@ -0,0 +1,24 @@
+/*
+ * 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.maven.packaging.endpoint;
+
+import org.apache.camel.spi.UriEndpoint;
+
+// internal without remote = false, which the generator refuses
+@UriEndpoint(scheme = "some", syntax = "some", title = "some", internal = true)
+public class SomeInternalRemoteEndpoint {
+}
diff --git
a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
index 048bd736794a..fc1c94f5ab92 100644
---
a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
+++
b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/UriEndpoint.java
@@ -201,6 +201,28 @@ public @interface UriEndpoint {
/**
* Whether the component does remote communication such as connecting to
an external system over the network. Set
* this to false for internal components such as log, message
transformations and other kinds.
+ * <p/>
+ * Not remote does not mean that the component stays inside the
CamelContext: a component that runs code, or reads a
+ * file or a template, is not remote either. See {@link #internal()}.
*/
boolean remote() default true;
+
+ /**
+ * Whether the component stays inside the CamelContext: it only reaches
the routes, the in-memory state (a queue, a
+ * cache, metrics) and the log of the CamelContext. It never reads or
writes a file, uses the network, runs a
+ * process, or runs code or loads a resource named in its URI. A message
it hands to a route (direct, seda) still
+ * does whatever that route does.
+ * <p/>
+ * This is about what the component can do, not what it is usually used
for: a capability counts even when it sits
+ * behind an option that is off by default and seldom used, or in a
feature that ships with the component. So
+ * dataset-test is not internal (it polls the endpoint named in its URI),
nor is once (it can read its body from a
+ * file), nor micrometer (it evaluates its options with the simple
language, which can load its text from a file or
+ * a URL).
+ * <p/>
+ * An internal component is not remote, so it sets {@code remote = false}
too. Tooling uses this to tell what stays
+ * inside the integration, for example to send a test message to log or
direct without asking the user first.
+ *
+ * @since 4.24
+ */
+ boolean internal() default false;
}