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

davsclaus pushed a commit to branch feature/CAMEL-22616-cluster-config
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 4f3794acbe646866ff436a45369dfd969d880431
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Jul 6 16:09:45 2026 +0200

    CAMEL-22616: camel-main - Add cluster service configuration metadata for 
tooling
    
    Co-Authored-By: Claude <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel-main-configuration-metadata.json         |  77 ++++++++++-
 core/camel-main/src/main/docs/main.adoc            | 123 +++++++++++++++++
 .../maven/packaging/PrepareCamelMainMojo.java      | 147 +++++++++++++++++++++
 3 files changed, 346 insertions(+), 1 deletion(-)

diff --git 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index ebf86b1e192c..ca9332ad5c6f 100644
--- 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -28,7 +28,13 @@
     { "name": "camel.faulttolerance", "description": "Fault Tolerance EIP 
Circuit Breaker configurations", "sourceType": 
"org.apache.camel.main.FaultToleranceConfigurationProperties" },
     { "name": "camel.resilience4j", "description": "Resilience4j EIP Circuit 
Breaker configurations", "sourceType": 
"org.apache.camel.main.Resilience4jConfigurationProperties" },
     { "name": "camel.lra", "description": "Camel Saga EIP (Long Running 
Actions) configurations", "sourceType": 
"org.apache.camel.main.LraConfigurationProperties" },
-    { "name": "camel.errorRegistry", "description": "Camel Error Registry 
configurations", "sourceType": 
"org.apache.camel.main.ErrorRegistryConfigurationProperties" }
+    { "name": "camel.errorRegistry", "description": "Camel Error Registry 
configurations", "sourceType": 
"org.apache.camel.main.ErrorRegistryConfigurationProperties" },
+    { "name": "camel.cluster.file", "description": "Camel File Cluster Service 
configurations", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService" },
+    { "name": "camel.cluster.kubernetes", "description": "Camel Kubernetes 
Cluster Service configurations", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService" },
+    { "name": "camel.cluster.zookeeper", "description": "Camel ZooKeeper 
Cluster Service configurations", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService" },
+    { "name": "camel.cluster.consul", "description": "Camel Consul Cluster 
Service configurations", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService" },
+    { "name": "camel.cluster.jgroups-raft", "description": "Camel JGroups Raft 
Cluster Service configurations", "sourceType": 
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService" },
+    { "name": "camel.cluster.infinispan", "description": "Camel Infinispan 
Remote Cluster Service configurations", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService"
 }
   ],
   "properties": [
     { "name": "camel.main.additionalSensitiveKeywords", "required": false, 
"description": "Camel comes with a default set of sensitive keywords which are 
automatically masked. This option allows to add additional custom keywords to 
be masked as well. Multiple keywords can be separated by comma.", "sourceType": 
"org.apache.camel.main.DefaultConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "secret": false },
@@ -160,6 +166,75 @@
     { "name": "camel.main.uuidGenerator", "required": false, "description": 
"UUID generator to use. default (32 bytes), short (16 bytes), classic (32 bytes 
or longer), simple (long incrementing counter), off (turned off for exchanges - 
only intended for performance profiling)", "sourceType": 
"org.apache.camel.main.DefaultConfigurationProperties", "type": "enum", 
"javaType": "java.lang.String", "defaultValue": "default", "secret": false, 
"enum": [ "classic", "default", "short", "simple",  [...]
     { "name": "camel.main.virtualThreadsEnabled", "required": false, 
"description": "Whether to enable virtual threads when creating thread pools. 
When enabled, Camel will use virtual threads instead of platform threads for 
its thread pools. This can also be enabled via the JVM system property {code 
camel.threads.virtual.enabled=true} . This option must be read early during 
bootstrap, so it is set as a system property before thread pools are created.", 
"sourceType": "org.apache.camel.mai [...]
     { "name": "camel.main.yamlDslCompactNotationWarn", "required": false, 
"description": "Whether to log a WARN when YAML DSL routes use compact 
(shorthand) notation instead of the canonical (explicit\/normalized) form. The 
canonical style is recommended as it is more tooling and AI friendly. Use Camel 
CLI to normalize existing routes: camel validate normalize &lt;file&gt;", 
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": 
"boolean", "javaType": "boolean", "d [...]
+    { "name": "camel.cluster.consul.aclToken", "required": false, 
"description": "Sets the ACL token to be used with Consul", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": true, "security": "secret" 
},
+    { "name": "camel.cluster.consul.blockSeconds", "required": false, 
"description": "The second to wait for a watch event, default 10 seconds", 
"sourceType": "org.apache.camel.component.consul.cluster.ConsulClusterService", 
"type": "integer", "javaType": "int", "defaultValue": 10, "secret": false },
+    { "name": "camel.cluster.consul.connectTimeout", "required": false, 
"description": "Connect timeout in millis", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"integer", "javaType": "int", "secret": false },
+    { "name": "camel.cluster.consul.datacenter", "required": false, 
"description": "The data center", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.consul.enabled", "required": false, 
"description": "Whether to enable this cluster service.", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"boolean", "javaType": "boolean", "defaultValue": true, "secret": false },
+    { "name": "camel.cluster.consul.id", "required": true, "description": 
"Client id registered as _consul.service.registry.id", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.consul.password", "required": false, 
"description": "Sets the password to be used for basic authentication", 
"sourceType": "org.apache.camel.component.consul.cluster.ConsulClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": true, "security": 
"secret" },
+    { "name": "camel.cluster.consul.readTimeout", "required": false, 
"description": "Read timeout in millis", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"integer", "javaType": "int", "secret": false },
+    { "name": "camel.cluster.consul.rootPath", "required": false, 
"description": "The Consul cluster root directory path", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"string", "javaType": "java.lang.String", "defaultValue": "\/camel", "secret": 
false },
+    { "name": "camel.cluster.consul.sessionLockDelay", "required": false, 
"description": "Session lock delay in seconds", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"integer", "javaType": "int", "defaultValue": 5, "secret": false },
+    { "name": "camel.cluster.consul.sessionRefreshInterval", "required": 
false, "description": "Session refresh interval in seconds", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"integer", "javaType": "int", "defaultValue": 5, "secret": false },
+    { "name": "camel.cluster.consul.sessionTtl", "required": false, 
"description": "Session time to live in seconds", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"integer", "javaType": "int", "defaultValue": 60, "secret": false },
+    { "name": "camel.cluster.consul.sslContextParameters", "required": false, 
"description": "SSL configuration for advanced security configuration", 
"sourceType": "org.apache.camel.component.consul.cluster.ConsulClusterService", 
"type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "secret": false },
+    { "name": "camel.cluster.consul.url", "required": false, "description": 
"The Consul agent URL", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.consul.userName", "required": false, 
"description": "Sets the username to be used for basic authentication", 
"sourceType": "org.apache.camel.component.consul.cluster.ConsulClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": true, "security": 
"secret" },
+    { "name": "camel.cluster.consul.writeTimeout", "required": false, 
"description": "Write timeout in mills", "sourceType": 
"org.apache.camel.component.consul.cluster.ConsulClusterService", "type": 
"integer", "javaType": "int", "secret": false },
+    { "name": "camel.cluster.file.acquireLockDelay", "required": false, 
"description": "The time to wait before starting to try to acquire the cluster 
lock. Note that if FileLockClusterService determines no cluster members are 
running or cannot reliably determine the cluster state, the initial delay is 
computed from the acquireLockInterval.", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService", "type": 
"integer", "javaType": "long", "defaultValue": 1, "secret": [...]
+    { "name": "camel.cluster.file.acquireLockDelayUnit", "required": false, 
"description": "The time unit for the acquireLockDelay", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService", "type": 
"object", "javaType": "java.util.concurrent.TimeUnit", "defaultValue": 
"SECONDS", "secret": false },
+    { "name": "camel.cluster.file.acquireLockInterval", "required": false, 
"description": "The time to wait between attempts to try to acquire the cluster 
lock evaluated using wall-clock time. All cluster members must use the same 
value so leadership checks and leader liveness detection remain consistent.", 
"sourceType": "org.apache.camel.component.file.cluster.FileLockClusterService", 
"type": "integer", "javaType": "long", "defaultValue": 10, "secret": false },
+    { "name": "camel.cluster.file.acquireLockIntervalUnit", "required": false, 
"description": "The time unit for the acquireLockInterval", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService", "type": 
"object", "javaType": "java.util.concurrent.TimeUnit", "defaultValue": 
"SECONDS", "secret": false },
+    { "name": "camel.cluster.file.clusterDataTaskMaxAttempts", "required": 
false, "description": "Sets how many times a cluster data task will run, 
counting both the first execution and subsequent retries in case of failure or 
timeout. The default is 5 attempts. This can be useful when the cluster data 
root is on network based file storage, where I\/O operations may occasionally 
block for long or unpredictable periods.", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClus [...]
+    { "name": "camel.cluster.file.clusterDataTaskTimeout", "required": false, 
"description": "Sets the timeout for a cluster data task (reading or writing 
cluster data). The default is 10 seconds. Timeouts are useful when the cluster 
data root is on network storage, where I\/O operations may occasionally block 
for long or unpredictable periods.", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService", "type": 
"integer", "javaType": "long", "defaultValue": 10, "sec [...]
+    { "name": "camel.cluster.file.clusterDataTaskTimeoutUnit", "required": 
false, "description": "The time unit for the clusterDataTaskTimeoutUnit", 
"sourceType": "org.apache.camel.component.file.cluster.FileLockClusterService", 
"type": "object", "javaType": "java.util.concurrent.TimeUnit", "defaultValue": 
"SECONDS", "secret": false },
+    { "name": "camel.cluster.file.enabled", "required": false, "description": 
"Whether to enable this cluster service.", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService", "type": 
"boolean", "javaType": "boolean", "defaultValue": true, "secret": false },
+    { "name": "camel.cluster.file.heartbeatTimeoutMultiplier", "required": 
false, "description": "Multiplier applied to the cluster leader 
acquireLockInterval to determine how long followers should wait before 
considering the leader stale. For example, if the leader updates its heartbeat 
every 2 seconds and the heartbeatTimeoutMultiplier is 3, followers will 
tolerate up to 2s * 3 = 6s of silence before declaring the leader 
unavailable.", "sourceType": "org.apache.camel.component.file.clu [...]
+    { "name": "camel.cluster.file.root", "required": true, "description": "The 
root file path", "sourceType": 
"org.apache.camel.component.file.cluster.FileLockClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.cacheContainer", "required": false, 
"description": "Specifies the cache Container to connect", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "object", "javaType": 
"org.infinispan.client.hotrod.RemoteCacheManager", "secret": false },
+    { "name": "camel.cluster.infinispan.configurationProperties", "required": 
false, "description": "Implementation specific properties for the 
CacheManager", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "object", "javaType": "java.util.Map", "secret": false },
+    { "name": "camel.cluster.infinispan.configurationUri", "required": false, 
"description": "An implementation specific URI for the CacheManager", 
"sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.enabled", "required": false, 
"description": "Whether to enable this cluster service.", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "boolean", "javaType": "boolean", "defaultValue": true, "secret": 
false },
+    { "name": "camel.cluster.infinispan.hosts", "required": false, 
"description": "Specifies the host of the cache on Infinispan instance. 
Multiple hosts can be separated by semicolon.", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.id", "required": true, "description": 
"Node id", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.lifespan", "required": false, 
"description": "The lifespan of the cache entry for the local cluster member 
registered to the inventory", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "integer", "javaType": "long", "defaultValue": 30, "secret": false },
+    { "name": "camel.cluster.infinispan.lifespanTimeUnit", "required": false, 
"description": "The TimeUnit of the lifespan", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "object", "javaType": "java.util.concurrent.TimeUnit", "defaultValue": 
"SECONDS", "secret": false },
+    { "name": "camel.cluster.infinispan.password", "required": false, 
"description": "Define the password to access the infinispan instance", 
"sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": true, "security": 
"secret" },
+    { "name": "camel.cluster.infinispan.saslMechanism", "required": false, 
"description": "Define the SASL Mechanism to access the infinispan instance", 
"sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.secure", "required": false, 
"description": "Define if we are connecting to a secured Infinispan instance", 
"sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "boolean", "javaType": "boolean", "defaultValue": false, "secret": 
false },
+    { "name": "camel.cluster.infinispan.securityRealm", "required": false, 
"description": "Define the security realm to access the infinispan instance", 
"sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.securityServerName", "required": 
false, "description": "Define the security server name to access the infinispan 
instance", "sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.infinispan.username", "required": false, 
"description": "Define the username to access the infinispan instance", 
"sourceType": 
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService",
 "type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.jgroups-raft.enabled", "required": false, 
"description": "Whether to enable this cluster service.", "sourceType": 
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService", 
"type": "boolean", "javaType": "boolean", "defaultValue": true, "secret": false 
},
+    { "name": "camel.cluster.jgroups-raft.id", "required": true, 
"description": "Unique Raft id", "sourceType": 
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.jgroups-raft.jgroupsClusterName", "required": 
false, "description": "The name of the cluster", "sourceType": 
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService", 
"type": "string", "javaType": "java.lang.String", "defaultValue": 
"jgroupsraft-master", "secret": false },
+    { "name": "camel.cluster.jgroups-raft.jgroupsConfig", "required": false, 
"description": "The path to the JGroups Raft configuration", "sourceType": 
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService", 
"type": "string", "javaType": "java.lang.String", "defaultValue": "raft.xml", 
"secret": false },
+    { "name": "camel.cluster.kubernetes.clusterLabels", "required": false, 
"description": "Labels used to identify the members of the cluster.", 
"sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "object", "javaType": "java.util.Map", "secret": false, "deprecated": 
true },
+    { "name": "camel.cluster.kubernetes.connectionTimeout", "required": false, 
"description": "Connection timeout in milliseconds to use when making requests 
to the Kubernetes API server.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "integer", "javaType": "java.lang.Integer", "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.enabled", "required": false, 
"description": "Whether to enable this cluster service.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "boolean", "javaType": "boolean", "defaultValue": true, "secret": false 
},
+    { "name": "camel.cluster.kubernetes.groupName", "required": false, 
"description": "Name of the lock group (or namespace according to the Camel 
cluster convention) within the chosen ConfigMap.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.jitterFactor", "required": false, 
"description": "A jitter factor to apply in order to prevent all pods to call 
Kubernetes APIs in the same instant.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "number", "javaType": "double", "defaultValue": "1.2", "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.kubernetesResourceName", "required": 
false, "description": "Name of the resource used for locking (or prefix, in 
case multiple ones are used).", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "string", "javaType": "java.lang.String", "defaultValue": "leaders", 
"secret": false, "deprecated": true },
+    { "name": "camel.cluster.kubernetes.kubernetesResourcesNamespace", 
"required": false, "description": "Kubernetes namespace containing the pods and 
the ConfigMap used for locking.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.leaseDurationMillis", "required": 
false, "description": "The default duration of the lease for the current 
leader.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "integer", "javaType": "long", "defaultValue": 15000, "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.leaseResourceType", "required": false, 
"description": "Kubernetes resource type used to hold the leases.", 
"sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "object", "javaType": 
"org.apache.camel.component.kubernetes.cluster.LeaseResourceType", 
"defaultValue": "Lease", "secret": false, "deprecated": true },
+    { "name": "camel.cluster.kubernetes.masterUrl", "required": false, 
"description": "URL to a remote Kubernetes API server. This should only be used 
when your Camel application is connecting from outside Kubernetes. If you run 
your Camel application inside Kubernetes, then you can use local or client as 
the URL to tell Camel to run in local mode. If you connect remotely to 
Kubernetes, then you may also need some of the many other configuration options 
for secured connection with certif [...]
+    { "name": "camel.cluster.kubernetes.namespace", "required": false, 
"description": "Kubernetes namespace", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.podName", "required": false, 
"description": "Name of the current pod (defaults to host name).", 
"sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "string", "javaType": "java.lang.String", "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.renewDeadlineMillis", "required": 
false, "description": "The deadline after which the leader must stop its 
services because it may have lost the leadership.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "integer", "javaType": "long", "defaultValue": 10000, "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.kubernetes.retryPeriodMillis", "required": false, 
"description": "The time between two subsequent attempts to check and acquire 
the leadership. It is randomized using the jitter factor.", "sourceType": 
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService", 
"type": "integer", "javaType": "long", "defaultValue": 2000, "secret": false, 
"deprecated": true },
+    { "name": "camel.cluster.zookeeper.basePath", "required": true, 
"description": "The base path to store in ZooKeeper", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.zookeeper.connectTimeout", "required": false, 
"description": "Connect timeout (millis)", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"integer", "javaType": "long", "defaultValue": 15000, "secret": false },
+    { "name": "camel.cluster.zookeeper.enabled", "required": false, 
"description": "Whether to enable this cluster service.", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"boolean", "javaType": "boolean", "defaultValue": true, "secret": false },
+    { "name": "camel.cluster.zookeeper.id", "required": true, "description": 
"Node id", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.zookeeper.maxCloseWait", "required": false, 
"description": "Time to wait (millis) during close to join background threads", 
"sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"integer", "javaType": "long", "defaultValue": 1000, "secret": false },
+    { "name": "camel.cluster.zookeeper.namespace", "required": false, 
"description": "ZooKeeper namespace. If a namespace is set here, all paths will 
get pre-pended with the namespace", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.zookeeper.nodes", "required": true, 
"description": "The Zookeeper server hosts (multiple servers can be separated 
by comma)", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"string", "javaType": "java.lang.String", "secret": false },
+    { "name": "camel.cluster.zookeeper.reconnectBaseSleepTime", "required": 
false, "description": "Initial amount of time (millis) to wait between 
retries", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"integer", "javaType": "long", "defaultValue": 1000, "secret": false },
+    { "name": "camel.cluster.zookeeper.reconnectMaxRetries", "required": 
false, "description": "Max number of times to retry", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"integer", "javaType": "int", "defaultValue": 3, "secret": false },
+    { "name": "camel.cluster.zookeeper.reconnectMaxSleepTime", "required": 
false, "description": "Max time (millis) to sleep on each retry", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"integer", "javaType": "long", "secret": false },
+    { "name": "camel.cluster.zookeeper.sessionTimeout", "required": false, 
"description": "Session timeout (millis)", "sourceType": 
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService", "type": 
"integer", "javaType": "long", "defaultValue": 60000, "secret": false },
     { "name": "camel.debug.bodyIncludeFiles", "required": false, 
"description": "Whether to include the message body of file based messages. The 
overhead is that the file content has to be read from the file.", "sourceType": 
"org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": true, "secret": false },
     { "name": "camel.debug.bodyIncludeStreams", "required": false, 
"description": "Whether to include the message body of stream based messages. 
If enabled then beware the stream may not be re-readable later. See more about 
Stream Caching.", "sourceType": 
"org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": false, "secret": false },
     { "name": "camel.debug.bodyMaxChars", "required": false, "description": 
"To limit the message body to a maximum size in the traced message. Use 0 or 
negative value to use unlimited size.", "sourceType": 
"org.apache.camel.main.DebuggerConfigurationProperties", "type": "integer", 
"javaType": "int", "defaultValue": 32768, "secret": false },
diff --git a/core/camel-main/src/main/docs/main.adoc 
b/core/camel-main/src/main/docs/main.adoc
index 3af1204ab2bc..c2b0aa4f4576 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -734,6 +734,129 @@ The camel.errorRegistry supports 8 options, which are 
listed below.
 | *camel.errorRegistry.maximumEntries* | The maximum number of error entries 
to keep in the registry. When the limit is exceeded, the oldest entries are 
evicted. | 100 | int
 | *camel.errorRegistry.timeToLiveSeconds* | The time-to-live in seconds for 
error entries. Entries older than this are evicted. The default value is 0 
(disabled). | 0 | int
 |===
+
+
+=== Camel File Cluster Service configurations
+The camel.cluster.file supports 10 options, which are listed below.
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.cluster.file.acquireLockDelay* | The time to wait before starting to 
try to acquire the cluster lock. Note that if FileLockClusterService determines 
no cluster members are running or cannot reliably determine the cluster state, 
the initial delay is computed from the acquireLockInterval. | 1 | long
+| *camel.cluster.file.acquireLockDelayUnit* | The time unit for the 
acquireLockDelay | SECONDS | TimeUnit
+| *camel.cluster.file.acquireLockInterval* | The time to wait between attempts 
to try to acquire the cluster lock evaluated using wall-clock time. All cluster 
members must use the same value so leadership checks and leader liveness 
detection remain consistent. | 10 | long
+| *camel.cluster.file.acquireLockIntervalUnit* | The time unit for the 
acquireLockInterval | SECONDS | TimeUnit
+| *camel.cluster.file.clusterDataTaskMaxAttempts* | Sets how many times a 
cluster data task will run, counting both the first execution and subsequent 
retries in case of failure or timeout. The default is 5 attempts. This can be 
useful when the cluster data root is on network based file storage, where I/O 
operations may occasionally block for long or unpredictable periods. | 5 | int
+| *camel.cluster.file.clusterDataTaskTimeout* | Sets the timeout for a cluster 
data task (reading or writing cluster data). The default is 10 seconds. 
Timeouts are useful when the cluster data root is on network storage, where I/O 
operations may occasionally block for long or unpredictable periods. | 10 | long
+| *camel.cluster.file.clusterDataTaskTimeoutUnit* | The time unit for the 
clusterDataTaskTimeoutUnit | SECONDS | TimeUnit
+| *camel.cluster.file.enabled* | Whether to enable this cluster service. | 
true | boolean
+| *camel.cluster.file.heartbeatTimeoutMultiplier* | Multiplier applied to the 
cluster leader acquireLockInterval to determine how long followers should wait 
before considering the leader stale. For example, if the leader updates its 
heartbeat every 2 seconds and the heartbeatTimeoutMultiplier is 3, followers 
will tolerate up to 2s * 3 = 6s of silence before declaring the leader 
unavailable. | 5 | int
+| *camel.cluster.file.root* | The root file path |  | String
+|===
+
+
+=== Camel Kubernetes Cluster Service configurations
+The camel.cluster.kubernetes supports 14 options, which are listed below.
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.cluster.kubernetes.clusterLabels* | Labels used to identify the 
members of the cluster. |  | Map
+| *camel.cluster.kubernetes.connectionTimeout* | Connection timeout in 
milliseconds to use when making requests to the Kubernetes API server. |  | 
Integer
+| *camel.cluster.kubernetes.enabled* | Whether to enable this cluster service. 
| true | boolean
+| *camel.cluster.kubernetes.groupName* | Name of the lock group (or namespace 
according to the Camel cluster convention) within the chosen ConfigMap. |  | 
String
+| *camel.cluster.kubernetes.jitterFactor* | A jitter factor to apply in order 
to prevent all pods to call Kubernetes APIs in the same instant. | 1.2 | double
+| *camel.cluster.kubernetes.kubernetesResourceName* | Name of the resource 
used for locking (or prefix, in case multiple ones are used). | leaders | String
+| *camel.cluster.kubernetes.kubernetesResourcesNamespace* | Kubernetes 
namespace containing the pods and the ConfigMap used for locking. |  | String
+| *camel.cluster.kubernetes.leaseDurationMillis* | The default duration of the 
lease for the current leader. | 15000 | long
+| *camel.cluster.kubernetes.leaseResourceType* | Kubernetes resource type used 
to hold the leases. | Lease | LeaseResourceType
+| *camel.cluster.kubernetes.masterUrl* | URL to a remote Kubernetes API 
server. This should only be used when your Camel application is connecting from 
outside Kubernetes. If you run your Camel application inside Kubernetes, then 
you can use local or client as the URL to tell Camel to run in local mode. If 
you connect remotely to Kubernetes, then you may also need some of the many 
other configuration options for secured connection with certificates, etc. |  | 
String
+| *camel.cluster.kubernetes.namespace* | Kubernetes namespace |  | String
+| *camel.cluster.kubernetes.podName* | Name of the current pod (defaults to 
host name). |  | String
+| *camel.cluster.kubernetes.renewDeadlineMillis* | The deadline after which 
the leader must stop its services because it may have lost the leadership. | 
10000 | long
+| *camel.cluster.kubernetes.retryPeriodMillis* | The time between two 
subsequent attempts to check and acquire the leadership. It is randomized using 
the jitter factor. | 2000 | long
+|===
+
+
+=== Camel ZooKeeper Cluster Service configurations
+The camel.cluster.zookeeper supports 11 options, which are listed below.
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.cluster.zookeeper.basePath* | The base path to store in ZooKeeper |  
| String
+| *camel.cluster.zookeeper.connectTimeout* | Connect timeout (millis) | 15000 
| long
+| *camel.cluster.zookeeper.enabled* | Whether to enable this cluster service. 
| true | boolean
+| *camel.cluster.zookeeper.id* | Node id |  | String
+| *camel.cluster.zookeeper.maxCloseWait* | Time to wait (millis) during close 
to join background threads | 1000 | long
+| *camel.cluster.zookeeper.namespace* | ZooKeeper namespace. If a namespace is 
set here, all paths will get pre-pended with the namespace |  | String
+| *camel.cluster.zookeeper.nodes* | The Zookeeper server hosts (multiple 
servers can be separated by comma) |  | String
+| *camel.cluster.zookeeper.reconnectBaseSleepTime* | Initial amount of time 
(millis) to wait between retries | 1000 | long
+| *camel.cluster.zookeeper.reconnectMaxRetries* | Max number of times to retry 
| 3 | int
+| *camel.cluster.zookeeper.reconnectMaxSleepTime* | Max time (millis) to sleep 
on each retry |  | long
+| *camel.cluster.zookeeper.sessionTimeout* | Session timeout (millis) | 60000 
| long
+|===
+
+
+=== Camel Consul Cluster Service configurations
+The camel.cluster.consul supports 16 options, which are listed below.
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.cluster.consul.aclToken* | Sets the ACL token to be used with Consul 
|  | String
+| *camel.cluster.consul.blockSeconds* | The second to wait for a watch event, 
default 10 seconds | 10 | int
+| *camel.cluster.consul.connectTimeout* | Connect timeout in millis |  | int
+| *camel.cluster.consul.datacenter* | The data center |  | String
+| *camel.cluster.consul.enabled* | Whether to enable this cluster service. | 
true | boolean
+| *camel.cluster.consul.id* | Client id registered as 
_consul.service.registry.id |  | String
+| *camel.cluster.consul.password* | Sets the password to be used for basic 
authentication |  | String
+| *camel.cluster.consul.readTimeout* | Read timeout in millis |  | int
+| *camel.cluster.consul.rootPath* | The Consul cluster root directory path | 
/camel | String
+| *camel.cluster.consul.sessionLockDelay* | Session lock delay in seconds | 5 
| int
+| *camel.cluster.consul.sessionRefreshInterval* | Session refresh interval in 
seconds | 5 | int
+| *camel.cluster.consul.sessionTtl* | Session time to live in seconds | 60 | 
int
+| *camel.cluster.consul.sslContextParameters* | SSL configuration for advanced 
security configuration |  | SSLContextParameters
+| *camel.cluster.consul.url* | The Consul agent URL |  | String
+| *camel.cluster.consul.userName* | Sets the username to be used for basic 
authentication |  | String
+| *camel.cluster.consul.writeTimeout* | Write timeout in mills |  | int
+|===
+
+
+=== Camel JGroups Raft Cluster Service configurations
+The camel.cluster.jgroups-raft supports 4 options, which are listed below.
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.cluster.jgroups-raft.enabled* | Whether to enable this cluster 
service. | true | boolean
+| *camel.cluster.jgroups-raft.id* | Unique Raft id |  | String
+| *camel.cluster.jgroups-raft.jgroupsClusterName* | The name of the cluster | 
jgroupsraft-master | String
+| *camel.cluster.jgroups-raft.jgroupsConfig* | The path to the JGroups Raft 
configuration | raft.xml | String
+|===
+
+
+=== Camel Infinispan Remote Cluster Service configurations
+The camel.cluster.infinispan supports 14 options, which are listed below.
+
+[width="100%",cols="2,5,^1,2",options="header"]
+|===
+| Name | Description | Default | Type
+| *camel.cluster.infinispan.cacheContainer* | Specifies the cache Container to 
connect |  | RemoteCacheManager
+| *camel.cluster.infinispan.configurationProperties* | Implementation specific 
properties for the CacheManager |  | Map
+| *camel.cluster.infinispan.configurationUri* | An implementation specific URI 
for the CacheManager |  | String
+| *camel.cluster.infinispan.enabled* | Whether to enable this cluster service. 
| true | boolean
+| *camel.cluster.infinispan.hosts* | Specifies the host of the cache on 
Infinispan instance. Multiple hosts can be separated by semicolon. |  | String
+| *camel.cluster.infinispan.id* | Node id |  | String
+| *camel.cluster.infinispan.lifespan* | The lifespan of the cache entry for 
the local cluster member registered to the inventory | 30 | long
+| *camel.cluster.infinispan.lifespanTimeUnit* | The TimeUnit of the lifespan | 
SECONDS | TimeUnit
+| *camel.cluster.infinispan.password* | Define the password to access the 
infinispan instance |  | String
+| *camel.cluster.infinispan.saslMechanism* | Define the SASL Mechanism to 
access the infinispan instance |  | String
+| *camel.cluster.infinispan.secure* | Define if we are connecting to a secured 
Infinispan instance | false | boolean
+| *camel.cluster.infinispan.securityRealm* | Define the security realm to 
access the infinispan instance |  | String
+| *camel.cluster.infinispan.securityServerName* | Define the security server 
name to access the infinispan instance |  | String
+| *camel.cluster.infinispan.username* | Define the username to access the 
infinispan instance |  | String
+|===
 // main options: END
 
 == Package Scanning
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java
index c2ada8247cb2..259740416d85 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelMainMojo.java
@@ -18,9 +18,12 @@ package org.apache.camel.maven.packaging;
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
@@ -31,6 +34,8 @@ import org.apache.camel.tooling.model.MainModel;
 import org.apache.camel.tooling.model.MainModel.MainGroupModel;
 import org.apache.camel.tooling.util.JavadocHelper;
 import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
@@ -150,6 +155,98 @@ public class PrepareCamelMainMojo extends 
AbstractGeneratorMojo {
         return answer;
     }
 
+    private static final Set<String> SETTABLE_JAVA_TYPES = Set.of(
+            "java.lang.String", "java.lang.Integer", "java.lang.Long", 
"java.lang.Boolean",
+            "java.lang.Double", "java.lang.Float", "java.lang.Short", 
"java.lang.Byte",
+            "int", "long", "boolean", "double", "float", "short", "byte",
+            "java.util.Map", "java.util.concurrent.TimeUnit",
+            "org.apache.camel.support.jsse.SSLContextParameters");
+
+    /**
+     * Loads cluster service configuration metadata from a bean JSON file.
+     */
+    public static List<MainModel.MainOptionModel> loadClusterServiceMetadata(
+            String prefix, File beanJsonFile)
+            throws Exception {
+
+        final List<MainModel.MainOptionModel> answer = new ArrayList<>();
+
+        String json = Files.readString(beanJsonFile.toPath());
+        JsonObject root = (JsonObject) Jsoner.deserialize(json);
+        JsonObject bean = root.getMap("bean");
+        String sourceType = bean.getString("javaType");
+        boolean classDeprecated = Boolean.TRUE.equals(bean.get("deprecated"));
+
+        // add synthetic "enabled" property
+        MainModel.MainOptionModel enabledModel = new 
MainModel.MainOptionModel();
+        enabledModel.setName(prefix + "enabled");
+        enabledModel.setJavaType("boolean");
+        enabledModel.setType("boolean");
+        enabledModel.setDefaultValue(true);
+        enabledModel.setDescription("Whether to enable this cluster service.");
+        enabledModel.setSourceType(sourceType);
+        answer.add(enabledModel);
+
+        JsonObject properties = bean.getMap("properties");
+        if (properties != null) {
+            for (Map.Entry<String, Object> entry : properties.entrySet()) {
+                String name = entry.getKey();
+                JsonObject prop = (JsonObject) entry.getValue();
+
+                String javaType = prop.getString("javaType");
+                String label = prop.getString("label");
+
+                // skip advanced properties with component-specific types
+                if (label != null && label.contains("advanced") && 
!SETTABLE_JAVA_TYPES.contains(javaType)) {
+                    continue;
+                }
+
+                MainModel.MainOptionModel model = new 
MainModel.MainOptionModel();
+                model.setName(prefix + name);
+                model.setJavaType(javaType);
+                model.setSourceType(sourceType);
+                model.setRequired(Boolean.TRUE.equals(prop.get("required")));
+                model.setDeprecated(classDeprecated || 
Boolean.TRUE.equals(prop.get("deprecated")));
+                model.setSecret(Boolean.TRUE.equals(prop.get("secret")));
+                model.setDescription(prop.getString("description"));
+
+                String security = prop.getString("security");
+                if (security != null) {
+                    model.setSecurity(security);
+                    if ("secret".equals(security) && !model.isSecret()) {
+                        model.setSecret(true);
+                    }
+                }
+
+                // determine type and handle enums
+                String type = prop.getString("type");
+                List<String> enums = null;
+                Object enumObj = prop.get("enum");
+                if (enumObj instanceof List) {
+                    enums = ((List<?>) 
enumObj).stream().map(Object::toString).collect(Collectors.toList());
+                    type = "enum";
+                }
+                if (type == null) {
+                    type = MojoHelper.getType(javaType, enums != null, false);
+                }
+                model.setType(type);
+                model.setEnums(enums);
+
+                // default value
+                Object defaultValue = prop.get("defaultValue");
+                if (defaultValue != null) {
+                    model.setDefaultValue(defaultValue);
+                } else if ("boolean".equals(type)) {
+                    model.setDefaultValue("false");
+                }
+
+                answer.add(model);
+            }
+        }
+
+        return answer;
+    }
+
     private static Object asDefaultValue(String type, String defaultValue) {
         if (defaultValue != null) {
             if ("boolean".equals(type)) {
@@ -359,6 +456,38 @@ public class PrepareCamelMainMojo extends 
AbstractGeneratorMojo {
             throw new MojoFailureException("Error parsing file " + 
cyberarkVaultConfig + " due " + e.getMessage(), e);
         }
 
+        // include cluster service configuration from component bean metadata
+        String[][] clusterServices = {
+                { "file", "components/camel-file", "FileLockClusterService" },
+                { "kubernetes", "components/camel-kubernetes", 
"KubernetesClusterService" },
+                { "zookeeper", "components/camel-zookeeper", 
"ZooKeeperClusterService" },
+                { "consul", "components/camel-consul", "ConsulClusterService" 
},
+                { "jgroups-raft", "components/camel-jgroups-raft", 
"JGroupsRaftClusterService" },
+                { "infinispan", 
"components/camel-infinispan/camel-infinispan", 
"InfinispanRemoteClusterService" },
+        };
+        for (String[] cs : clusterServices) {
+            String type = cs[0];
+            String componentPath = cs[1];
+            String beanName = cs[2];
+            try {
+                File componentDir = 
PackageHelper.findCamelDirectory(project.getBasedir(), componentPath);
+                File beanJson = new File(
+                        componentDir,
+                        
"src/generated/resources/META-INF/services/org/apache/camel/bean/" + beanName + 
".json");
+                if (beanJson.isFile()) {
+                    String prefix = "camel.cluster." + type + ".";
+                    List<MainModel.MainOptionModel> clusterModel = 
loadClusterServiceMetadata(prefix, beanJson);
+                    data.addAll(clusterModel);
+                    getLog().info("Loaded cluster service metadata: " + type + 
" (" + clusterModel.size() + " options)");
+                } else {
+                    getLog().warn("Cluster service bean metadata not found: " 
+ beanJson);
+                }
+            } catch (Exception e) {
+                throw new MojoFailureException(
+                        "Error loading cluster service metadata for " + type + 
" due " + e.getMessage(), e);
+            }
+        }
+
         // lets sort so they are always ordered (but camel.main in top)
         data.sort((o1, o2) -> {
             if (o1.getName().startsWith("camel.main.") && 
!o2.getName().startsWith("camel.main.")) {
@@ -477,6 +606,24 @@ public class PrepareCamelMainMojo extends 
AbstractGeneratorMojo {
             model.getGroups().add(new MainGroupModel(
                     "camel.errorRegistry", "Camel Error Registry 
configurations",
                     
"org.apache.camel.main.ErrorRegistryConfigurationProperties"));
+            model.getGroups().add(new MainGroupModel(
+                    "camel.cluster.file", "Camel File Cluster Service 
configurations",
+                    
"org.apache.camel.component.file.cluster.FileLockClusterService"));
+            model.getGroups().add(new MainGroupModel(
+                    "camel.cluster.kubernetes", "Camel Kubernetes Cluster 
Service configurations",
+                    
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService"));
+            model.getGroups().add(new MainGroupModel(
+                    "camel.cluster.zookeeper", "Camel ZooKeeper Cluster 
Service configurations",
+                    
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService"));
+            model.getGroups().add(new MainGroupModel(
+                    "camel.cluster.consul", "Camel Consul Cluster Service 
configurations",
+                    
"org.apache.camel.component.consul.cluster.ConsulClusterService"));
+            model.getGroups().add(new MainGroupModel(
+                    "camel.cluster.jgroups-raft", "Camel JGroups Raft Cluster 
Service configurations",
+                    
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService"));
+            model.getGroups().add(new MainGroupModel(
+                    "camel.cluster.infinispan", "Camel Infinispan Remote 
Cluster Service configurations",
+                    
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService"));
 
             String json = JsonMapper.createJsonSchema(model);
 

Reply via email to