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 130d3624133e CAMEL-24752: camel-core - generated name to artifact
table so unknown component, language and data format errors say which jar to
add (#26459)
130d3624133e is described below
commit 130d3624133efdce3bfcf09dcf43b7b9e8cec701
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 15 15:07:37 2026 +0200
CAMEL-24752: camel-core - generated name to artifact table so unknown
component, language and data format errors say which jar to add (#26459)
ArtifactUtils is generated from the catalog at build time
(update-artifact-helper goal) with the artifact of every component scheme,
language, data format and built-in bean. NoSuchEndpointException,
NoSuchLanguageException, DataFormatReifier and the YAML DSL use it to say which
jar to add, or the nearest built-in name for a typo.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
catalog/camel-catalog/pom.xml | 2 +
...gDeadLetterChannelInvalidDeadLetterUriTest.java | 3 +-
.../org/apache/camel/NoSuchEndpointException.java | 22 +-
.../org/apache/camel/NoSuchLanguageException.java | 5 +-
.../reifier/dataformat/DataFormatReifier.java | 14 +-
.../camel/impl/engine/DefaultCamelContextTest.java | 29 +-
.../language/simple/SimpleSyntaxHintsTest.java | 17 +-
.../processor/DataFormatMissingJarHintTest.java | 90 +++
.../java/org/apache/camel/util/ArtifactUtils.java | 892 +++++++++++++++++++++
.../org/apache/camel/util/ArtifactUtilsTest.java | 140 ++++
.../deserializers/ExpressionDeserializers.java | 2 +-
.../dsl/yaml/GenerateYamlDeserializersMojo.java | 2 +-
.../apache/camel/dsl/yaml/ExpressionTest.groovy | 20 +
.../maven/packaging/UpdateArtifactHelper.java | 170 ++++
14 files changed, 1396 insertions(+), 12 deletions(-)
diff --git a/catalog/camel-catalog/pom.xml b/catalog/camel-catalog/pom.xml
index b17d57f913da..4b48280945d3 100644
--- a/catalog/camel-catalog/pom.xml
+++ b/catalog/camel-catalog/pom.xml
@@ -157,6 +157,8 @@
<goal>prepare-catalog</goal>
<!-- update secrets in camel-util -->
<goal>update-sensitive-helper</goal>
+ <!-- update artifact names in camel-util -->
+ <goal>update-artifact-helper</goal>
<!-- update mime-types in camel-util -->
<goal>update-mime-type-helper</goal>
<!-- update important headers in camel-util -->
diff --git
a/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringDeadLetterChannelInvalidDeadLetterUriTest.java
b/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringDeadLetterChannelInvalidDeadLetterUriTest.java
index c54afaf2a29e..170f425e5288 100644
---
a/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringDeadLetterChannelInvalidDeadLetterUriTest.java
+++
b/components/camel-spring-parent/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringDeadLetterChannelInvalidDeadLetterUriTest.java
@@ -49,7 +49,8 @@ class SpringDeadLetterChannelInvalidDeadLetterUriTest extends
SpringTestSupport
FailedToCreateRouteException ftcre =
assertIsInstanceOf(FailedToCreateRouteException.class, e);
NoSuchEndpointException cause =
assertIsInstanceOf(NoSuchEndpointException.class, ftcre.getCause());
assertEquals(
- "No endpoint could be found for: xxx, please check your
classpath contains the needed Camel component jar.",
+ "No endpoint could be found for: xxx, please check your
classpath contains the needed Camel component jar"
+ + " (not a built-in Camel component).",
cause.getMessage());
}
diff --git
a/core/camel-api/src/main/java/org/apache/camel/NoSuchEndpointException.java
b/core/camel-api/src/main/java/org/apache/camel/NoSuchEndpointException.java
index 484abd52f085..da0bbabd0d0d 100644
--- a/core/camel-api/src/main/java/org/apache/camel/NoSuchEndpointException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/NoSuchEndpointException.java
@@ -18,6 +18,8 @@ package org.apache.camel;
import java.util.Objects;
+import org.apache.camel.util.ArtifactUtils;
+
import static org.apache.camel.util.URISupport.sanitizeUri;
/**
@@ -39,10 +41,28 @@ public class NoSuchEndpointException extends
RuntimeCamelException {
*/
public NoSuchEndpointException(String uri) {
super("No endpoint could be found for: " +
sanitizeUri(Objects.requireNonNull(uri, "uri"))
- + ", please check your classpath contains the needed Camel
component jar.");
+ + ", please check your classpath contains the needed Camel
component jar"
+ + ArtifactUtils.componentHint(scheme(uri)) + ".");
this.uri = sanitizeUri(uri);
}
+ /**
+ * The component scheme the endpoint would be resolved by: the text before
the first colon or question mark, or the
+ * whole uri when it has neither.
+ */
+ private static String scheme(String uri) {
+ int pos1 = uri.indexOf(':');
+ int pos2 = uri.indexOf('?');
+ if (pos1 != -1 && pos2 != -1) {
+ return uri.substring(0, Math.min(pos1, pos2));
+ } else if (pos1 != -1) {
+ return uri.substring(0, pos1);
+ } else if (pos2 != -1) {
+ return uri.substring(0, pos2);
+ }
+ return uri;
+ }
+
/**
* @param uri the endpoint URI that could not be found
* @param resolveMethod a resolution instruction appended after "please"
in the error message
diff --git
a/core/camel-api/src/main/java/org/apache/camel/NoSuchLanguageException.java
b/core/camel-api/src/main/java/org/apache/camel/NoSuchLanguageException.java
index 846593469527..75c3edbe10a3 100644
--- a/core/camel-api/src/main/java/org/apache/camel/NoSuchLanguageException.java
+++ b/core/camel-api/src/main/java/org/apache/camel/NoSuchLanguageException.java
@@ -18,6 +18,8 @@ package org.apache.camel;
import java.util.Objects;
+import org.apache.camel.util.ArtifactUtils;
+
/**
* A runtime exception thrown if an attempt is made to resolve an unknown
language definition.
*
@@ -32,8 +34,7 @@ public class NoSuchLanguageException extends
RuntimeCamelException {
*/
public NoSuchLanguageException(String language) {
super("No language could be found for: " +
Objects.requireNonNull(language, "language")
- + (language.matches("[a-z0-9-]+")
- ? " (a Camel language needs its dependency on the
classpath, e.g. camel-" + language + ")" : ""));
+ + ArtifactUtils.languageHint(language));
this.language = language;
}
diff --git
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java
index 285a3f21845e..6db4f512f39b 100644
---
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java
+++
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/DataFormatReifier.java
@@ -35,6 +35,7 @@ import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.PropertyBindingSupport;
import org.apache.camel.support.PropertyConfigurerHelper;
+import org.apache.camel.util.ArtifactUtils;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import org.slf4j.Logger;
@@ -97,7 +98,12 @@ public abstract class DataFormatReifier<T extends
DataFormatDefinition> extends
if (type == null) {
dataFormat = camelContext.resolveDataFormat(ref);
if (dataFormat == null) {
- throw new IllegalArgumentException("Cannot find data
format in registry with ref: " + ref);
+ // hint only when the ref names a built-in data format
whose jar is missing; a custom
+ // registry ref is a bean name, so it must not get a
did-you-mean for a data format
+ String artifact = ArtifactUtils.dataFormatArtifact(ref);
+ throw new IllegalArgumentException(
+ "Cannot find data format in registry with ref: " +
ref
+ + (artifact != null ?
ArtifactUtils.dataFormatHint(ref) : ""));
}
return dataFormat;
@@ -253,10 +259,12 @@ public abstract class DataFormatReifier<T extends
DataFormatDefinition> extends
// configure the rest of the options
configureDataFormat(dataFormat,
definition.getDataFormatName());
} else {
+ String name = definition.getDataFormatName();
throw new IllegalArgumentException(
- "Data format '" + (definition.getDataFormatName() !=
null ? definition.getDataFormatName() : "<null>")
+ "Data format '" + (name != null ? name : "<null>")
+ "' could not be created. "
- + "Ensure that the data
format is valid and the associated Camel component is present on the
classpath");
+ + "Ensure that the data
format is valid and the associated Camel component is present on the classpath"
+ +
ArtifactUtils.dataFormatHint(name));
}
}
return dataFormat;
diff --git
a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultCamelContextTest.java
b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultCamelContextTest.java
index 6f76552dd33a..c61cd2859be3 100644
---
a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultCamelContextTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultCamelContextTest.java
@@ -139,7 +139,34 @@ public class DefaultCamelContextTest extends TestSupport {
() -> camelContext.getEndpoint("xxx", Endpoint.class));
assertEquals(
- "No endpoint could be found for: xxx, please check your
classpath contains the needed Camel component jar.",
+ "No endpoint could be found for: xxx, please check your
classpath contains the needed Camel component jar"
+ + " (not a built-in Camel component).",
+ e.getMessage());
+ }
+
+ @Test
+ public void testGetEndpointUnknownSchemeSaysWhatToAdd() {
+ DefaultCamelContext camelContext = new DefaultCamelContext();
+
+ // a built-in component whose jar is not on the classpath
+ NoSuchEndpointException e = assertThrows(NoSuchEndpointException.class,
+ () ->
camelContext.getEndpoint("kafka:myTopic?brokers=localhost"));
+ assertEquals(
+ "No endpoint could be found for:
kafka://myTopic?brokers=localhost, please check your classpath contains"
+ + " the needed Camel component jar (the kafka component
is in camel-kafka; add camel-kafka to the"
+ + " classpath).",
+ e.getMessage());
+
+ // an alternative scheme is in the same jar as its main scheme
+ e = assertThrows(NoSuchEndpointException.class, () ->
camelContext.getEndpoint("coaps://localhost/foo"));
+ assertTrue(e.getMessage().contains("the coaps component is in
camel-coap; add camel-coap to the classpath"),
+ e.getMessage());
+
+ // a typo of a built-in scheme
+ e = assertThrows(NoSuchEndpointException.class, () ->
camelContext.getEndpoint("kafak:myTopic"));
+ assertEquals(
+ "No endpoint could be found for: kafak://myTopic, please check
your classpath contains the needed Camel"
+ + " component jar (not a built-in Camel component; did
you mean 'kafka'?).",
e.getMessage());
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
index 73ce4fd93499..ab250e61ce9d 100644
---
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
+++
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleSyntaxHintsTest.java
@@ -146,8 +146,21 @@ public class SimpleSyntaxHintsTest extends
ExchangeTestSupport {
@Test
public void testMissingLanguageNamesTheDependency() {
- Exception e = assertThrows(Exception.class, () ->
context.resolveLanguage("cheese"));
- assertThat(e.getMessage()).contains("No language could be found for:
cheese").contains("camel-cheese");
+ // xquery is a built-in language whose jar (camel-saxon, not
camel-xquery) is not on the classpath
+ Exception e = assertThrows(Exception.class, () ->
context.resolveLanguage("xquery"));
+ assertThat(e.getMessage()).contains("No language could be found for:
xquery")
+ .contains("the xquery language is in camel-saxon; add
camel-saxon to the classpath)");
+ }
+
+ @Test
+ public void testUnknownLanguageSaysDidYouMean() {
+ Exception e = assertThrows(Exception.class, () ->
context.resolveLanguage("simpel"));
+ assertThat(e.getMessage()).contains("No language could be found for:
simpel")
+ .contains("not a built-in Camel language; did you mean
'simple'?");
+
+ e = assertThrows(Exception.class, () ->
context.resolveLanguage("cheese"));
+ assertThat(e.getMessage()).contains("No language could be found for:
cheese")
+ .contains("(not a built-in Camel
language)").doesNotContain("did you mean");
}
@Test
diff --git
a/core/camel-core/src/test/java/org/apache/camel/processor/DataFormatMissingJarHintTest.java
b/core/camel-core/src/test/java/org/apache/camel/processor/DataFormatMissingJarHintTest.java
new file mode 100644
index 000000000000..120581bdf899
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/processor/DataFormatMissingJarHintTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * A data format whose jar is not on the classpath fails with the artifact to
add.
+ */
+public class DataFormatMissingJarHintTest extends ContextTestSupport {
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @Test
+ public void testModelDataFormatSaysWhatToAdd() {
+ String msg = messages(assertThrows(Exception.class, () -> {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:start").unmarshal().jaxb("com.foo").to("mock:result");
+ }
+ });
+ context.start();
+ }));
+
+ assertTrue(msg.contains("Data format 'jaxb' could not be created."),
msg);
+ assertTrue(msg.contains("(the jaxb data format is in camel-jaxb; add
camel-jaxb to the classpath)"), msg);
+ }
+
+ @Test
+ public void testDataFormatByRefSaysWhatToAdd() {
+ String msg = messages(assertThrows(Exception.class, () -> {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:start").unmarshal().custom("jaxb").to("mock:result");
+ }
+ });
+ context.start();
+ }));
+
+ assertTrue(msg.contains("Cannot find data format in registry with ref:
jaxb (the jaxb data format is in camel-jaxb;"),
+ msg);
+ }
+
+ @Test
+ public void testDataFormatByRefThatIsNotBuiltInHasNoHint() {
+ String msg = messages(assertThrows(Exception.class, () -> {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:start").unmarshal().custom("myFormat").to("mock:result");
+ }
+ });
+ context.start();
+ }));
+
+ assertTrue(msg.contains("Cannot find data format in registry with ref:
myFormat\n"), msg);
+ }
+
+ private static String messages(Throwable e) {
+ StringBuilder sb = new StringBuilder();
+ for (Throwable t = e; t != null; t = t.getCause()) {
+ sb.append(t.getMessage()).append('\n');
+ }
+ return sb.toString();
+ }
+}
diff --git
a/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java
b/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java
new file mode 100644
index 000000000000..577a1b4f88fc
--- /dev/null
+++ b/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java
@@ -0,0 +1,892 @@
+/*
+ * 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.util;
+
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Which Camel artifact (Maven artifactId) ships a component, data format,
language or built-in bean.
+ * <p/>
+ * The runtime cannot tell this on its own: the metadata that knows is in the
jar that is missing. So the tables are
+ * generated from the catalog at build time (like {@link SensitiveUtils}) and
used by the error messages that say what
+ * to add to the classpath when a name cannot be resolved, or which known name
was likely meant.
+ */
+public final class ArtifactUtils {
+
+ // scheme -> artifactId, including alternative schemes
+ private static final Map<String, String> COMPONENTS = Map.ofEntries(
+ // Generated by camel build tools - do NOT edit this list!
+ // COMPONENTS: START
+ Map.entry("a2a", "camel-a2a"),
+ Map.entry("activemq", "camel-activemq"),
+ Map.entry("activemq6", "camel-activemq6"),
+ Map.entry("ai-resource", "camel-ai-resource"),
+ Map.entry("ai-tool", "camel-ai-tool"),
+ Map.entry("alibaba-eventbridge", "camel-alibaba-eventbridge"),
+ Map.entry("alibaba-fc", "camel-alibaba-fc"),
+ Map.entry("alibaba-kms", "camel-alibaba-kms"),
+ Map.entry("alibaba-mns", "camel-alibaba-mns"),
+ Map.entry("alibaba-oss", "camel-alibaba-oss"),
+ Map.entry("alibaba-ots", "camel-alibaba-ots"),
+ Map.entry("alibaba-sls", "camel-alibaba-sls"),
+ Map.entry("alibaba-sms", "camel-alibaba-sms"),
+ Map.entry("amqp", "camel-amqp"),
+ Map.entry("arangodb", "camel-arangodb"),
+ Map.entry("as2", "camel-as2"),
+ Map.entry("asterisk", "camel-asterisk"),
+ Map.entry("atmosphere-websocket", "camel-atmosphere-websocket"),
+ Map.entry("atom", "camel-atom"),
+ Map.entry("avro", "camel-avro-rpc"),
+ Map.entry("aws-bedrock", "camel-aws-bedrock"),
+ Map.entry("aws-bedrock-agent", "camel-aws-bedrock"),
+ Map.entry("aws-bedrock-agent-runtime", "camel-aws-bedrock"),
+ Map.entry("aws-cloudtrail", "camel-aws-cloudtrail"),
+ Map.entry("aws-config", "camel-aws-config"),
+ Map.entry("aws-secrets-manager", "camel-aws-secrets-manager"),
+ Map.entry("aws-security-hub", "camel-aws-security-hub"),
+ Map.entry("aws2-athena", "camel-aws2-athena"),
+ Map.entry("aws2-comprehend", "camel-aws2-comprehend"),
+ Map.entry("aws2-cw", "camel-aws2-cw"),
+ Map.entry("aws2-ddb", "camel-aws2-ddb"),
+ Map.entry("aws2-ddbstream", "camel-aws2-ddb"),
+ Map.entry("aws2-ec2", "camel-aws2-ec2"),
+ Map.entry("aws2-ecs", "camel-aws2-ecs"),
+ Map.entry("aws2-eks", "camel-aws2-eks"),
+ Map.entry("aws2-eventbridge", "camel-aws2-eventbridge"),
+ Map.entry("aws2-iam", "camel-aws2-iam"),
+ Map.entry("aws2-kinesis", "camel-aws2-kinesis"),
+ Map.entry("aws2-kinesis-firehose", "camel-aws2-kinesis"),
+ Map.entry("aws2-kms", "camel-aws2-kms"),
+ Map.entry("aws2-lambda", "camel-aws2-lambda"),
+ Map.entry("aws2-mq", "camel-aws2-mq"),
+ Map.entry("aws2-msk", "camel-aws2-msk"),
+ Map.entry("aws2-polly", "camel-aws2-polly"),
+ Map.entry("aws2-redshift-data", "camel-aws2-redshift"),
+ Map.entry("aws2-rekognition", "camel-aws2-rekognition"),
+ Map.entry("aws2-s3", "camel-aws2-s3"),
+ Map.entry("aws2-s3-vectors", "camel-aws2-s3-vectors"),
+ Map.entry("aws2-ses", "camel-aws2-ses"),
+ Map.entry("aws2-sns", "camel-aws2-sns"),
+ Map.entry("aws2-sqs", "camel-aws2-sqs"),
+ Map.entry("aws2-step-functions", "camel-aws2-step-functions"),
+ Map.entry("aws2-sts", "camel-aws2-sts"),
+ Map.entry("aws2-textract", "camel-aws2-textract"),
+ Map.entry("aws2-timestream", "camel-aws2-timestream"),
+ Map.entry("aws2-transcribe", "camel-aws2-transcribe"),
+ Map.entry("aws2-translate", "camel-aws2-translate"),
+ Map.entry("azure-cosmosdb", "camel-azure-cosmosdb"),
+ Map.entry("azure-eventhubs", "camel-azure-eventhubs"),
+ Map.entry("azure-files", "camel-azure-files"),
+ Map.entry("azure-functions", "camel-azure-functions"),
+ Map.entry("azure-key-vault", "camel-azure-key-vault"),
+ Map.entry("azure-servicebus", "camel-azure-servicebus"),
+ Map.entry("azure-storage-blob", "camel-azure-storage-blob"),
+ Map.entry("azure-storage-datalake",
"camel-azure-storage-datalake"),
+ Map.entry("azure-storage-queue", "camel-azure-storage-queue"),
+ Map.entry("bean", "camel-bean"),
+ Map.entry("bean-validator", "camel-bean-validator"),
+ Map.entry("bonita", "camel-bonita"),
+ Map.entry("box", "camel-box"),
+ Map.entry("braintree", "camel-braintree"),
+ Map.entry("browse", "camel-browse"),
+ Map.entry("caffeine-cache", "camel-caffeine"),
+ Map.entry("caffeine-loadcache", "camel-caffeine"),
+ Map.entry("camunda", "camel-camunda"),
+ Map.entry("chatscript", "camel-chatscript"),
+ Map.entry("chunk", "camel-chunk"),
+ Map.entry("class", "camel-bean"),
+ Map.entry("clickhouse", "camel-clickhouse"),
+ Map.entry("clickup", "camel-clickup"),
+ Map.entry("cm-sms", "camel-cm-sms"),
+ Map.entry("coap", "camel-coap"),
+ Map.entry("coap+tcp", "camel-coap"),
+ Map.entry("coaps", "camel-coap"),
+ Map.entry("coaps+tcp", "camel-coap"),
+ Map.entry("cometd", "camel-cometd"),
+ Map.entry("cometds", "camel-cometd"),
+ Map.entry("consul", "camel-consul"),
+ Map.entry("controlbus", "camel-controlbus"),
+ Map.entry("couchbase", "camel-couchbase"),
+ Map.entry("couchdb", "camel-couchdb"),
+ Map.entry("cql", "camel-cassandraql"),
+ Map.entry("cron", "camel-cron"),
+ Map.entry("crypto", "camel-crypto"),
+ Map.entry("cxf", "camel-cxf-soap"),
+ Map.entry("cxfrs", "camel-cxf-rest"),
+ Map.entry("cyberark-vault", "camel-cyberark-vault"),
+ Map.entry("dapr", "camel-dapr"),
+ Map.entry("dataformat", "camel-dataformat"),
+ Map.entry("dataset", "camel-dataset"),
+ Map.entry("dataset-test", "camel-dataset"),
+ Map.entry("debezium-db2", "camel-debezium-db2"),
+ Map.entry("debezium-mongodb", "camel-debezium-mongodb"),
+ Map.entry("debezium-mysql", "camel-debezium-mysql"),
+ Map.entry("debezium-oracle", "camel-debezium-oracle"),
+ Map.entry("debezium-postgres", "camel-debezium-postgres"),
+ Map.entry("debezium-sqlserver", "camel-debezium-sqlserver"),
+ Map.entry("dfdl", "camel-dfdl"),
+ Map.entry("dhis2", "camel-dhis2"),
+ Map.entry("direct", "camel-direct"),
+ Map.entry("disruptor", "camel-disruptor"),
+ Map.entry("disruptor-vm", "camel-disruptor"),
+ Map.entry("djl", "camel-djl"),
+ Map.entry("dns", "camel-dns"),
+ Map.entry("docker", "camel-docker"),
+ Map.entry("docling", "camel-docling"),
+ Map.entry("drill", "camel-drill"),
+ Map.entry("dropbox", "camel-dropbox"),
+ Map.entry("duckdb", "camel-duckdb"),
+ Map.entry("dynamic-router", "camel-dynamic-router"),
+ Map.entry("dynamic-router-control", "camel-dynamic-router"),
+ Map.entry("ehcache", "camel-ehcache"),
+ Map.entry("elasticsearch", "camel-elasticsearch"),
+ Map.entry("elasticsearch-rest-client",
"camel-elasticsearch-rest-client"),
+ Map.entry("event", "camel-event"),
+ Map.entry("exec", "camel-exec"),
+ Map.entry("fhir", "camel-fhir"),
+ Map.entry("file", "camel-file"),
+ Map.entry("file-watch", "camel-file-watch"),
+ Map.entry("flatpack", "camel-flatpack"),
+ Map.entry("flink", "camel-flink"),
+ Map.entry("flowable", "camel-flowable"),
+ Map.entry("fop", "camel-fop"),
+ Map.entry("freemarker", "camel-freemarker"),
+ Map.entry("ftp", "camel-ftp"),
+ Map.entry("ftps", "camel-ftp"),
+ Map.entry("geocoder", "camel-geocoder"),
+ Map.entry("git", "camel-git"),
+ Map.entry("github2", "camel-github2"),
+ Map.entry("google-bigquery", "camel-google-bigquery"),
+ Map.entry("google-bigquery-sql", "camel-google-bigquery"),
+ Map.entry("google-calendar", "camel-google-calendar"),
+ Map.entry("google-calendar-stream", "camel-google-calendar"),
+ Map.entry("google-drive", "camel-google-drive"),
+ Map.entry("google-firestore", "camel-google-firestore"),
+ Map.entry("google-functions", "camel-google-functions"),
+ Map.entry("google-mail", "camel-google-mail"),
+ Map.entry("google-mail-stream", "camel-google-mail"),
+ Map.entry("google-pubsub", "camel-google-pubsub"),
+ Map.entry("google-secret-manager", "camel-google-secret-manager"),
+ Map.entry("google-sheets", "camel-google-sheets"),
+ Map.entry("google-sheets-stream", "camel-google-sheets"),
+ Map.entry("google-speech-to-text", "camel-google-speech-to-text"),
+ Map.entry("google-storage", "camel-google-storage"),
+ Map.entry("google-text-to-speech", "camel-google-text-to-speech"),
+ Map.entry("google-vertexai", "camel-google-vertexai"),
+ Map.entry("google-vision", "camel-google-vision"),
+ Map.entry("graphql", "camel-graphql"),
+ Map.entry("grpc", "camel-grpc"),
+ Map.entry("hashicorp-vault", "camel-hashicorp-vault"),
+ Map.entry("hazelcast-atomicvalue", "camel-hazelcast"),
+ Map.entry("hazelcast-instance", "camel-hazelcast"),
+ Map.entry("hazelcast-list", "camel-hazelcast"),
+ Map.entry("hazelcast-map", "camel-hazelcast"),
+ Map.entry("hazelcast-multimap", "camel-hazelcast"),
+ Map.entry("hazelcast-pncounter", "camel-hazelcast"),
+ Map.entry("hazelcast-queue", "camel-hazelcast"),
+ Map.entry("hazelcast-replicatedmap", "camel-hazelcast"),
+ Map.entry("hazelcast-ringbuffer", "camel-hazelcast"),
+ Map.entry("hazelcast-seda", "camel-hazelcast"),
+ Map.entry("hazelcast-set", "camel-hazelcast"),
+ Map.entry("hazelcast-topic", "camel-hazelcast"),
+ Map.entry("hivemq", "camel-hivemq"),
+ Map.entry("http", "camel-http"),
+ Map.entry("https", "camel-http"),
+ Map.entry("huggingface", "camel-huggingface"),
+ Map.entry("hwcloud-dms", "camel-huaweicloud-dms"),
+ Map.entry("hwcloud-frs", "camel-huaweicloud-frs"),
+ Map.entry("hwcloud-functiongraph",
"camel-huaweicloud-functiongraph"),
+ Map.entry("hwcloud-iam", "camel-huaweicloud-iam"),
+ Map.entry("hwcloud-imagerecognition",
"camel-huaweicloud-imagerecognition"),
+ Map.entry("hwcloud-obs", "camel-huaweicloud-obs"),
+ Map.entry("hwcloud-smn", "camel-huaweicloud-smn"),
+ Map.entry("ibm-cos", "camel-ibm-cos"),
+ Map.entry("ibm-secrets-manager", "camel-ibm-secrets-manager"),
+ Map.entry("ibm-watson-discovery", "camel-ibm-watson-discovery"),
+ Map.entry("ibm-watson-language", "camel-ibm-watson-language"),
+ Map.entry("ibm-watson-speech-to-text",
"camel-ibm-watson-speech-to-text"),
+ Map.entry("ibm-watson-text-to-speech",
"camel-ibm-watson-text-to-speech"),
+ Map.entry("ibm-watsonx-ai", "camel-ibm-watsonx-ai"),
+ Map.entry("ibm-watsonx-data", "camel-ibm-watsonx-data"),
+ Map.entry("iggy", "camel-iggy"),
+ Map.entry("ignite-cache", "camel-ignite"),
+ Map.entry("ignite-compute", "camel-ignite"),
+ Map.entry("ignite-events", "camel-ignite"),
+ Map.entry("ignite-idgen", "camel-ignite"),
+ Map.entry("ignite-messaging", "camel-ignite"),
+ Map.entry("ignite-queue", "camel-ignite"),
+ Map.entry("ignite-set", "camel-ignite"),
+ Map.entry("imap", "camel-mail"),
+ Map.entry("imaps", "camel-mail"),
+ Map.entry("infinispan", "camel-infinispan"),
+ Map.entry("infinispan-embedded", "camel-infinispan-embedded"),
+ Map.entry("influxdb", "camel-influxdb"),
+ Map.entry("influxdb2", "camel-influxdb2"),
+ Map.entry("jcache", "camel-jcache"),
+ Map.entry("jcr", "camel-jcr"),
+ Map.entry("jdbc", "camel-jdbc"),
+ Map.entry("jetty", "camel-jetty"),
+ Map.entry("jgroups", "camel-jgroups"),
+ Map.entry("jgroups-raft", "camel-jgroups-raft"),
+ Map.entry("jira", "camel-jira"),
+ Map.entry("jms", "camel-jms"),
+ Map.entry("jmx", "camel-jmx"),
+ Map.entry("jolt", "camel-jolt"),
+ Map.entry("jooq", "camel-jooq"),
+ Map.entry("jpa", "camel-jpa"),
+ Map.entry("jslt", "camel-jslt"),
+ Map.entry("json-validator", "camel-json-validator"),
+ Map.entry("jsonata", "camel-jsonata"),
+ Map.entry("jt400", "camel-jt400"),
+ Map.entry("jte", "camel-jte"),
+ Map.entry("kafka", "camel-kafka"),
+ Map.entry("kamelet", "camel-kamelet"),
+ Map.entry("keycloak", "camel-keycloak"),
+ Map.entry("knative", "camel-knative"),
+ Map.entry("kserve", "camel-kserve"),
+ Map.entry("kubernetes-config-maps", "camel-kubernetes"),
+ Map.entry("kubernetes-cronjob", "camel-kubernetes"),
+ Map.entry("kubernetes-custom-resources", "camel-kubernetes"),
+ Map.entry("kubernetes-deployments", "camel-kubernetes"),
+ Map.entry("kubernetes-events", "camel-kubernetes"),
+ Map.entry("kubernetes-hpa", "camel-kubernetes"),
+ Map.entry("kubernetes-job", "camel-kubernetes"),
+ Map.entry("kubernetes-namespaces", "camel-kubernetes"),
+ Map.entry("kubernetes-nodes", "camel-kubernetes"),
+ Map.entry("kubernetes-persistent-volumes", "camel-kubernetes"),
+ Map.entry("kubernetes-persistent-volumes-claims",
"camel-kubernetes"),
+ Map.entry("kubernetes-pods", "camel-kubernetes"),
+ Map.entry("kubernetes-replication-controllers",
"camel-kubernetes"),
+ Map.entry("kubernetes-resources-quota", "camel-kubernetes"),
+ Map.entry("kubernetes-secrets", "camel-kubernetes"),
+ Map.entry("kubernetes-service-accounts", "camel-kubernetes"),
+ Map.entry("kubernetes-services", "camel-kubernetes"),
+ Map.entry("kudu", "camel-kudu"),
+ Map.entry("langchain4j-agent", "camel-langchain4j-agent"),
+ Map.entry("langchain4j-chat", "camel-langchain4j-chat"),
+ Map.entry("langchain4j-embeddings",
"camel-langchain4j-embeddings"),
+ Map.entry("langchain4j-embeddingstore",
"camel-langchain4j-embeddingstore"),
+ Map.entry("langchain4j-ingest", "camel-langchain4j-ingest"),
+ Map.entry("langchain4j-web-search",
"camel-langchain4j-web-search"),
+ Map.entry("language", "camel-language"),
+ Map.entry("ldap", "camel-ldap"),
+ Map.entry("ldif", "camel-ldif"),
+ Map.entry("log", "camel-log"),
+ Map.entry("lpr", "camel-printer"),
+ Map.entry("lucene", "camel-lucene"),
+ Map.entry("lumberjack", "camel-lumberjack"),
+ Map.entry("mapstruct", "camel-mapstruct"),
+ Map.entry("master", "camel-master"),
+ Map.entry("metrics", "camel-metrics"),
+ Map.entry("micrometer", "camel-micrometer"),
+ Map.entry("milo-browse", "camel-milo"),
+ Map.entry("milo-client", "camel-milo"),
+ Map.entry("milo-server", "camel-milo"),
+ Map.entry("milvus", "camel-milvus"),
+ Map.entry("mina", "camel-mina"),
+ Map.entry("mina-sftp", "camel-mina-sftp"),
+ Map.entry("minio", "camel-minio"),
+ Map.entry("mllp", "camel-mllp"),
+ Map.entry("mock", "camel-mock"),
+ Map.entry("mongodb", "camel-mongodb"),
+ Map.entry("mongodb-gridfs", "camel-mongodb-gridfs"),
+ Map.entry("mustache", "camel-mustache"),
+ Map.entry("mvel", "camel-mvel"),
+ Map.entry("mybatis", "camel-mybatis"),
+ Map.entry("mybatis-bean", "camel-mybatis"),
+ Map.entry("nats", "camel-nats"),
+ Map.entry("neo4j", "camel-neo4j"),
+ Map.entry("netty", "camel-netty"),
+ Map.entry("netty-http", "camel-netty-http"),
+ Map.entry("oaipmh", "camel-oaipmh"),
+ Map.entry("olingo2", "camel-olingo2"),
+ Map.entry("olingo4", "camel-olingo4"),
+ Map.entry("once", "camel-once"),
+ Map.entry("opa", "camel-opa"),
+ Map.entry("openai", "camel-openai"),
+ Map.entry("opensearch", "camel-opensearch"),
+ Map.entry("openshift-build-configs", "camel-kubernetes"),
+ Map.entry("openshift-builds", "camel-kubernetes"),
+ Map.entry("openshift-deploymentconfigs", "camel-kubernetes"),
+ Map.entry("openstack-cinder", "camel-openstack"),
+ Map.entry("openstack-glance", "camel-openstack"),
+ Map.entry("openstack-keystone", "camel-openstack"),
+ Map.entry("openstack-neutron", "camel-openstack"),
+ Map.entry("openstack-nova", "camel-openstack"),
+ Map.entry("openstack-swift", "camel-openstack"),
+ Map.entry("opentelemetry-metrics", "camel-opentelemetry-metrics"),
+ Map.entry("optaplanner", "camel-optaplanner"),
+ Map.entry("paho", "camel-paho"),
+ Map.entry("paho-mqtt5", "camel-paho-mqtt5"),
+ Map.entry("pdf", "camel-pdf"),
+ Map.entry("pg-replication-slot", "camel-pg-replication-slot"),
+ Map.entry("pgevent", "camel-pgevent"),
+ Map.entry("pgvector", "camel-pgvector"),
+ Map.entry("pinecone", "camel-pinecone"),
+ Map.entry("platform-http", "camel-platform-http"),
+ Map.entry("plc4x", "camel-plc4x"),
+ Map.entry("pop3", "camel-mail"),
+ Map.entry("pop3s", "camel-mail"),
+ Map.entry("pqc", "camel-pqc"),
+ Map.entry("pubnub", "camel-pubnub"),
+ Map.entry("pulsar", "camel-pulsar"),
+ Map.entry("qdrant", "camel-qdrant"),
+ Map.entry("quartz", "camel-quartz"),
+ Map.entry("quickfix", "camel-quickfix"),
+ Map.entry("reactive-streams", "camel-reactive-streams"),
+ Map.entry("ref", "camel-ref"),
+ Map.entry("rest", "camel-rest"),
+ Map.entry("rest-api", "camel-rest"),
+ Map.entry("rest-openapi", "camel-rest-openapi"),
+ Map.entry("rest-postman", "camel-rest-postman"),
+ Map.entry("robotframework", "camel-robotframework"),
+ Map.entry("rocketmq", "camel-rocketmq"),
+ Map.entry("rss", "camel-rss"),
+ Map.entry("saga", "camel-saga"),
+ Map.entry("salesforce", "camel-salesforce"),
+ Map.entry("sap-netweaver", "camel-sap-netweaver"),
+ Map.entry("scheduler", "camel-scheduler"),
+ Map.entry("schematron", "camel-schematron"),
+ Map.entry("scp", "camel-jsch"),
+ Map.entry("seda", "camel-seda"),
+ Map.entry("servicenow", "camel-servicenow"),
+ Map.entry("servlet", "camel-servlet"),
+ Map.entry("sftp", "camel-ftp"),
+ Map.entry("shell", "camel-shell"),
+ Map.entry("sjms", "camel-sjms"),
+ Map.entry("sjms2", "camel-sjms2"),
+ Map.entry("slack", "camel-slack"),
+ Map.entry("smb", "camel-smb"),
+ Map.entry("smooks", "camel-smooks"),
+ Map.entry("smpp", "camel-smpp"),
+ Map.entry("smpps", "camel-smpp"),
+ Map.entry("smtp", "camel-mail"),
+ Map.entry("smtps", "camel-mail"),
+ Map.entry("snmp", "camel-snmp"),
+ Map.entry("solr", "camel-solr"),
+ Map.entry("spiffe", "camel-spiffe"),
+ Map.entry("splunk-hec", "camel-splunk-hec"),
+ Map.entry("spring-ai-chat", "camel-spring-ai-chat"),
+ Map.entry("spring-ai-embeddings", "camel-spring-ai-embeddings"),
+ Map.entry("spring-ai-image", "camel-spring-ai-image"),
+ Map.entry("spring-ai-vector-store",
"camel-spring-ai-vector-store"),
+ Map.entry("spring-batch", "camel-spring-batch"),
+ Map.entry("spring-event", "camel-spring"),
+ Map.entry("spring-jdbc", "camel-spring-jdbc"),
+ Map.entry("spring-ldap", "camel-spring-ldap"),
+ Map.entry("spring-rabbitmq", "camel-spring-rabbitmq"),
+ Map.entry("spring-redis", "camel-spring-redis"),
+ Map.entry("spring-ws", "camel-spring-ws"),
+ Map.entry("sql", "camel-sql"),
+ Map.entry("sql-stored", "camel-sql"),
+ Map.entry("ssh", "camel-ssh"),
+ Map.entry("state-store", "camel-state-store"),
+ Map.entry("stax", "camel-stax"),
+ Map.entry("stitch", "camel-stitch"),
+ Map.entry("stream", "camel-stream"),
+ Map.entry("string-template", "camel-stringtemplate"),
+ Map.entry("stripe", "camel-stripe"),
+ Map.entry("stub", "camel-stub"),
+ Map.entry("tahu-edge", "camel-tahu"),
+ Map.entry("tahu-host", "camel-tahu"),
+ Map.entry("telegram", "camel-telegram"),
+ Map.entry("tensorflow-serving", "camel-tensorflow-serving"),
+ Map.entry("thrift", "camel-thrift"),
+ Map.entry("thymeleaf", "camel-thymeleaf"),
+ Map.entry("tika", "camel-tika"),
+ Map.entry("timer", "camel-timer"),
+ Map.entry("twilio", "camel-twilio"),
+ Map.entry("twitter-directmessage", "camel-twitter"),
+ Map.entry("twitter-search", "camel-twitter"),
+ Map.entry("twitter-timeline", "camel-twitter"),
+ Map.entry("undertow", "camel-undertow"),
+ Map.entry("validator", "camel-validator"),
+ Map.entry("velocity", "camel-velocity"),
+ Map.entry("vertx", "camel-vertx"),
+ Map.entry("vertx-http", "camel-vertx-http"),
+ Map.entry("vertx-websocket", "camel-vertx-websocket"),
+ Map.entry("wasm", "camel-wasm"),
+ Map.entry("weather", "camel-weather"),
+ Map.entry("weaviate", "camel-weaviate"),
+ Map.entry("web3j", "camel-web3j"),
+ Map.entry("webhook", "camel-webhook"),
+ Map.entry("whatsapp", "camel-whatsapp"),
+ Map.entry("wordpress", "camel-wordpress"),
+ Map.entry("workday", "camel-workday"),
+ Map.entry("xchange", "camel-xchange"),
+ Map.entry("xj", "camel-xj"),
+ Map.entry("xmlsecurity-sign", "camel-xmlsecurity"),
+ Map.entry("xmlsecurity-verify", "camel-xmlsecurity"),
+ Map.entry("xmpp", "camel-xmpp"),
+ Map.entry("xquery", "camel-saxon"),
+ Map.entry("xslt", "camel-xslt"),
+ Map.entry("xslt-saxon", "camel-xslt-saxon"),
+ Map.entry("zendesk", "camel-zendesk"),
+ Map.entry("zookeeper", "camel-zookeeper"),
+ Map.entry("zookeeper-master", "camel-zookeeper-master")
+ // COMPONENTS: END
+ );
+
+ // name -> artifactId
+ private static final Map<String, String> LANGUAGES = Map.ofEntries(
+ // Generated by camel build tools - do NOT edit this list!
+ // LANGUAGES: START
+ Map.entry("bean", "camel-bean"),
+ Map.entry("constant", "camel-core-languages"),
+ Map.entry("datasonnet", "camel-datasonnet"),
+ Map.entry("exchangeProperty", "camel-core-languages"),
+ Map.entry("file", "camel-core-languages"),
+ Map.entry("groovy", "camel-groovy"),
+ Map.entry("header", "camel-core-languages"),
+ Map.entry("hl7terser", "camel-hl7"),
+ Map.entry("jactl", "camel-jactl"),
+ Map.entry("java", "camel-joor"),
+ Map.entry("joor", "camel-joor"),
+ Map.entry("jq", "camel-jq"),
+ Map.entry("js", "camel-javascript"),
+ Map.entry("jsonpath", "camel-jsonpath"),
+ Map.entry("mvel", "camel-mvel"),
+ Map.entry("ognl", "camel-ognl"),
+ Map.entry("python", "camel-python"),
+ Map.entry("python3", "camel-python3"),
+ Map.entry("quickjs", "camel-quickjs"),
+ Map.entry("ref", "camel-core-languages"),
+ Map.entry("simple", "camel-core-languages"),
+ Map.entry("spel", "camel-spring"),
+ Map.entry("tokenize", "camel-core-languages"),
+ Map.entry("variable", "camel-core-languages"),
+ Map.entry("wasm", "camel-wasm"),
+ Map.entry("xpath", "camel-xpath"),
+ Map.entry("xquery", "camel-saxon"),
+ Map.entry("xtokenize", "camel-stax")
+ // LANGUAGES: END
+ );
+
+ // name -> artifactId
+ private static final Map<String, String> DATAFORMATS = Map.ofEntries(
+ // Generated by camel build tools - do NOT edit this list!
+ // DATAFORMATS: START
+ Map.entry("asn1", "camel-asn1"),
+ Map.entry("avro", "camel-avro"),
+ Map.entry("avroJackson", "camel-jackson-avro"),
+ Map.entry("barcode", "camel-barcode"),
+ Map.entry("base64", "camel-base64"),
+ Map.entry("beanio", "camel-beanio"),
+ Map.entry("bindyCsv", "camel-bindy"),
+ Map.entry("bindyFixed", "camel-bindy"),
+ Map.entry("bindyKvp", "camel-bindy"),
+ Map.entry("cbor", "camel-cbor"),
+ Map.entry("crypto", "camel-crypto"),
+ Map.entry("csv", "camel-csv"),
+ Map.entry("dfdl", "camel-dfdl"),
+ Map.entry("fastjson", "camel-fastjson"),
+ Map.entry("fhirJson", "camel-fhir"),
+ Map.entry("fhirXml", "camel-fhir"),
+ Map.entry("flatpack", "camel-flatpack"),
+ Map.entry("fory", "camel-fory"),
+ Map.entry("grok", "camel-grok"),
+ Map.entry("groovyJson", "camel-groovy"),
+ Map.entry("groovyXml", "camel-groovy"),
+ Map.entry("gson", "camel-gson"),
+ Map.entry("gzipDeflater", "camel-zip-deflater"),
+ Map.entry("hl7", "camel-hl7"),
+ Map.entry("ical", "camel-ical"),
+ Map.entry("iso8583", "camel-iso8583"),
+ Map.entry("jackson", "camel-jackson"),
+ Map.entry("jacksonXml", "camel-jacksonxml"),
+ Map.entry("jaxb", "camel-jaxb"),
+ Map.entry("jsonApi", "camel-jsonapi"),
+ Map.entry("jsonb", "camel-jsonb"),
+ Map.entry("lzf", "camel-lzf"),
+ Map.entry("mimeMultipart", "camel-mail"),
+ Map.entry("ocsf", "camel-ocsf"),
+ Map.entry("parquetAvro", "camel-parquet-avro"),
+ Map.entry("pgp", "camel-crypto-pgp"),
+ Map.entry("pqc", "camel-pqc"),
+ Map.entry("protobuf", "camel-protobuf"),
+ Map.entry("protobufJackson", "camel-jackson-protobuf"),
+ Map.entry("rss", "camel-rss"),
+ Map.entry("smooks", "camel-smooks"),
+ Map.entry("snakeYaml", "camel-snakeyaml"),
+ Map.entry("soap", "camel-soap"),
+ Map.entry("swiftMt", "camel-swift"),
+ Map.entry("swiftMx", "camel-swift"),
+ Map.entry("syslog", "camel-syslog"),
+ Map.entry("tarFile", "camel-tarfile"),
+ Map.entry("thrift", "camel-thrift"),
+ Map.entry("toon", "camel-toon"),
+ Map.entry("ubl", "camel-ubl"),
+ Map.entry("univocityCsv", "camel-univocity-parsers"),
+ Map.entry("univocityFixed", "camel-univocity-parsers"),
+ Map.entry("univocityTsv", "camel-univocity-parsers"),
+ Map.entry("xmlSecurity", "camel-xmlsecurity"),
+ Map.entry("zipDeflater", "camel-zip-deflater"),
+ Map.entry("zipFile", "camel-zipfile")
+ // DATAFORMATS: END
+ );
+
+ // simple class name -> javaType|interfaceType|artifactId
+ private static final Map<String, String> BEANS = Map.ofEntries(
+ // Generated by camel build tools - do NOT edit this list!
+ // BEANS: START
+ Map.entry("AcceptAllHeaderFilterStrategy",
+
"org.apache.camel.support.AcceptAllHeaderFilterStrategy|org.apache.camel.spi.HeaderFilterStrategy|camel-support"),
+ Map.entry("CaffeineAggregationRepository",
+
"org.apache.camel.component.caffeine.processor.aggregate.CaffeineAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-caffeine"),
+ Map.entry("CaffeineIdempotentRepository",
+
"org.apache.camel.component.caffeine.processor.idempotent.CaffeineIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-caffeine"),
+ Map.entry("CaffeineKeyValueRepository",
+
"org.apache.camel.component.caffeine.processor.CaffeineKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-caffeine"),
+ Map.entry("CassandraAggregationRepository",
+
"org.apache.camel.processor.aggregate.cassandra.CassandraAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-cassandraql"),
+ Map.entry("CassandraIdempotentRepository",
+
"org.apache.camel.processor.idempotent.cassandra.CassandraIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-cassandraql"),
+ Map.entry("CassandraKeyValueRepository",
+
"org.apache.camel.processor.keyvalue.cassandra.CassandraKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-cassandraql"),
+ Map.entry("ConsulClusterService",
+
"org.apache.camel.component.consul.cluster.ConsulClusterService|org.apache.camel.cluster.CamelClusterService|camel-consul"),
+ Map.entry("CronScheduledRoutePolicy",
+
"org.apache.camel.routepolicy.quartz.CronScheduledRoutePolicy|org.apache.camel.spi.RoutePolicy|camel-quartz"),
+ Map.entry("DefaultHeaderFilterStrategy",
+
"org.apache.camel.support.DefaultHeaderFilterStrategy|org.apache.camel.spi.HeaderFilterStrategy|camel-support"),
+ Map.entry("DurationRoutePolicy",
+
"org.apache.camel.impl.engine.DurationRoutePolicy|org.apache.camel.spi.RoutePolicy|camel-base-engine"),
+ Map.entry("DurationRoutePolicyFactory",
+
"org.apache.camel.impl.engine.DurationRoutePolicyFactory|org.apache.camel.spi.RoutePolicyFactory|camel-base-engine"),
+ Map.entry("EhcacheAggregationRepository",
+
"org.apache.camel.component.ehcache.processor.aggregate.EhcacheAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-ehcache"),
+ Map.entry("EhcacheIdempotentRepository",
+
"org.apache.camel.component.ehcache.processor.idempotent.EhcacheIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-ehcache"),
+ Map.entry("EhcacheKeyValueRepository",
+
"org.apache.camel.component.ehcache.processor.EhcacheKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-ehcache"),
+ Map.entry("ElasticsearchBulkRequestAggregationStrategy",
+
"org.apache.camel.component.es.aggregation.ElasticsearchBulkRequestAggregationStrategy|org.apache.camel.AggregationStrategy|camel-elasticsearch"),
+ Map.entry("FileIdempotentRepository",
+
"org.apache.camel.support.processor.idempotent.FileIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-support"),
+ Map.entry("FileLockClusterService",
+
"org.apache.camel.component.file.cluster.FileLockClusterService|org.apache.camel.cluster.CamelClusterService|camel-file"),
+ Map.entry("GroupedBodyAggregationStrategy",
+
"org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy|org.apache.camel.AggregationStrategy|camel-core-processor"),
+ Map.entry("GroupedExchangeAggregationStrategy",
+
"org.apache.camel.processor.aggregate.GroupedExchangeAggregationStrategy|org.apache.camel.AggregationStrategy|camel-core-processor"),
+ Map.entry("GroupedMessageAggregationStrategy",
+
"org.apache.camel.processor.aggregate.GroupedMessageAggregationStrategy|org.apache.camel.AggregationStrategy|camel-core-processor"),
+ Map.entry("HazelcastAggregationRepository",
+
"org.apache.camel.processor.aggregate.hazelcast.HazelcastAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-hazelcast"),
+ Map.entry("HazelcastIdempotentRepository",
+
"org.apache.camel.processor.idempotent.hazelcast.HazelcastIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-hazelcast"),
+ Map.entry("HazelcastKeyValueRepository",
+
"org.apache.camel.component.hazelcast.HazelcastKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-hazelcast"),
+ Map.entry("InfinispanEmbeddedAggregationRepository",
+
"org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-infinispan-embedded"),
+ Map.entry("InfinispanEmbeddedClusterService",
+
"org.apache.camel.component.infinispan.embedded.cluster.InfinispanEmbeddedClusterService|org.apache.camel.cluster.CamelClusterService|camel-infinispan-embedded"),
+ Map.entry("InfinispanEmbeddedIdempotentRepository",
+
"org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-infinispan-embedded"),
+ Map.entry("InfinispanRemoteAggregationRepository",
+
"org.apache.camel.component.infinispan.remote.InfinispanRemoteAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-infinispan"),
+ Map.entry("InfinispanRemoteClusterService",
+
"org.apache.camel.component.infinispan.remote.cluster.InfinispanRemoteClusterService|org.apache.camel.cluster.CamelClusterService|camel-infinispan"),
+ Map.entry("InfinispanRemoteIdempotentRepository",
+
"org.apache.camel.component.infinispan.remote.InfinispanRemoteIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-infinispan"),
+ Map.entry("InfinispanRemoteKeyValueRepository",
+
"org.apache.camel.component.infinispan.remote.InfinispanRemoteKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-infinispan"),
+ Map.entry("JCacheAggregationRepository",
+
"org.apache.camel.component.jcache.processor.aggregate.JCacheAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-jcache"),
+ Map.entry("JCacheIdempotentRepository",
+
"org.apache.camel.component.jcache.processor.idempotent.JCacheIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-jcache"),
+ Map.entry("JCacheKeyValueRepository",
+
"org.apache.camel.component.jcache.processor.JCacheKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-jcache"),
+ Map.entry("JGroupsRaftClusterService",
+
"org.apache.camel.component.jgroups.raft.cluster.JGroupsRaftClusterService|org.apache.camel.cluster.CamelClusterService|camel-jgroups-raft"),
+ Map.entry("JdbcAggregationRepository",
+
"org.apache.camel.processor.aggregate.jdbc.JdbcAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-sql"),
+ Map.entry("JdbcKeyValueRepository",
+
"org.apache.camel.processor.keyvalue.jdbc.JdbcKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-sql"),
+ Map.entry("JdbcMessageIdRepository",
+
"org.apache.camel.processor.idempotent.jdbc.JdbcMessageIdRepository|org.apache.camel.spi.IdempotentRepository|camel-sql"),
+ Map.entry("JpaKeyValueRepository",
+
"org.apache.camel.processor.keyvalue.jpa.JpaKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-jpa"),
+ Map.entry("KafkaIdempotentRepository",
+
"org.apache.camel.processor.idempotent.kafka.KafkaIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-kafka"),
+ Map.entry("KafkaKeyValueRepository",
+
"org.apache.camel.processor.keyvalue.kafka.KafkaKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-kafka"),
+ Map.entry("KeyValueAggregationRepository",
+
"org.apache.camel.support.KeyValueAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-support"),
+ Map.entry("KeyValueIdempotentRepository",
+
"org.apache.camel.support.KeyValueIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-support"),
+ Map.entry("KubernetesClusterService",
+
"org.apache.camel.component.kubernetes.cluster.KubernetesClusterService|org.apache.camel.cluster.CamelClusterService|camel-kubernetes"),
+ Map.entry("LoggingHttpActivityListener",
+
"org.apache.camel.component.http.LoggingHttpActivityListener|org.apache.camel.component.http.HttpActivityListener|camel-http"),
+ Map.entry("MemoryAggregationRepository",
+
"org.apache.camel.processor.aggregate.MemoryAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-core-processor"),
+ Map.entry("MemoryIdempotentRepository",
+
"org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-support"),
+ Map.entry("MemoryKeyValueRepository",
+
"org.apache.camel.support.MemoryKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-support"),
+ Map.entry("MongoDbIdempotentRepository",
+
"org.apache.camel.component.mongodb.processor.idempotent.MongoDbIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-mongodb"),
+ Map.entry("OpensearchBulkRequestAggregationStrategy",
+
"org.apache.camel.component.opensearch.aggregation.OpensearchBulkRequestAggregationStrategy|org.apache.camel.AggregationStrategy|camel-opensearch"),
+ Map.entry("RedisAggregationRepository",
+
"org.apache.camel.component.redis.processor.aggregate.RedisAggregationRepository|org.apache.camel.spi.AggregationRepository|camel-redis"),
+ Map.entry("RedisKeyValueRepository",
+
"org.apache.camel.component.redis.RedisKeyValueRepository|org.apache.camel.spi.KeyValueRepository|camel-redis"),
+ Map.entry("SimpleScheduledRoutePolicy",
+
"org.apache.camel.routepolicy.quartz.SimpleScheduledRoutePolicy|org.apache.camel.spi.RoutePolicy|camel-quartz"),
+ Map.entry("SpringCacheIdempotentRepository",
+
"org.apache.camel.spring.processor.idempotent.SpringCacheIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-spring"),
+ Map.entry("SpringRedisIdempotentRepository",
+
"org.apache.camel.component.redis.processor.idempotent.SpringRedisIdempotentRepository|org.apache.camel.spi.IdempotentRepository|camel-spring-redis"),
+ Map.entry("StringAggregationStrategy",
+
"org.apache.camel.processor.aggregate.StringAggregationStrategy|org.apache.camel.AggregationStrategy|camel-core-processor"),
+ Map.entry("TarAggregationStrategy",
+
"org.apache.camel.processor.aggregate.tarfile.TarAggregationStrategy|org.apache.camel.AggregationStrategy|camel-tarfile"),
+ Map.entry("ThrottlingExceptionRoutePolicy",
+
"org.apache.camel.throttling.ThrottlingExceptionRoutePolicy|org.apache.camel.spi.RoutePolicy|camel-support"),
+ Map.entry("ThrottlingInflightRoutePolicy",
+
"org.apache.camel.throttling.ThrottlingInflightRoutePolicy|org.apache.camel.spi.RoutePolicy|camel-support"),
+ Map.entry("UseLatestAggregationStrategy",
+
"org.apache.camel.processor.aggregate.UseLatestAggregationStrategy|org.apache.camel.AggregationStrategy|camel-core-processor"),
+ Map.entry("UseOriginalAggregationStrategy",
+
"org.apache.camel.processor.aggregate.UseOriginalAggregationStrategy|org.apache.camel.AggregationStrategy|camel-core-processor"),
+ Map.entry("XsltAggregationStrategy",
+
"org.apache.camel.component.xslt.XsltAggregationStrategy|org.apache.camel.AggregationStrategy|camel-xslt"),
+ Map.entry("XsltSaxonAggregationStrategy",
+
"org.apache.camel.component.xslt.saxon.XsltSaxonAggregationStrategy|org.apache.camel.AggregationStrategy|camel-xslt-saxon"),
+ Map.entry("ZipAggregationStrategy",
+
"org.apache.camel.processor.aggregate.zipfile.ZipAggregationStrategy|org.apache.camel.AggregationStrategy|camel-zipfile"),
+ Map.entry("ZooKeeperClusterService",
+
"org.apache.camel.component.zookeeper.cluster.ZooKeeperClusterService|org.apache.camel.cluster.CamelClusterService|camel-zookeeper")
+ // BEANS: END
+ );
+
+ private ArtifactUtils() {
+ }
+
+ /** The artifactId of the component with the given scheme, or null if it
is not a built-in Camel component. */
+ public static String componentArtifact(String scheme) {
+ return scheme != null ? COMPONENTS.get(scheme) : null;
+ }
+
+ /** The artifactId of the language with the given name, or null if it is
not a built-in Camel language. */
+ public static String languageArtifact(String name) {
+ return name != null ? LANGUAGES.get(name) : null;
+ }
+
+ /** The artifactId of the data format with the given name, or null if it
is not a built-in Camel data format. */
+ public static String dataFormatArtifact(String name) {
+ return name != null ? DATAFORMATS.get(name) : null;
+ }
+
+ /** The artifactId of the built-in bean with the given simple class name,
or null if there is none. */
+ public static String beanArtifact(String name) {
+ return beanField(name, 2);
+ }
+
+ /** The fully qualified class name of the built-in bean with the given
simple class name, or null. */
+ public static String beanJavaType(String name) {
+ return beanField(name, 0);
+ }
+
+ /** The interface the built-in bean with the given simple class name
implements, or null. */
+ public static String beanInterface(String name) {
+ return beanField(name, 1);
+ }
+
+ private static String beanField(String name, int index) {
+ String value = name != null ? BEANS.get(name) : null;
+ if (value == null) {
+ return null;
+ }
+ String[] parts = value.split("\\|", -1);
+ return parts[index].isEmpty() ? null : parts[index];
+ }
+
+ /** The schemes of all built-in components, including alternative schemes.
*/
+ public static Set<String> componentNames() {
+ return COMPONENTS.keySet();
+ }
+
+ /** The names of all built-in languages. */
+ public static Set<String> languageNames() {
+ return LANGUAGES.keySet();
+ }
+
+ /** The names of all built-in data formats. */
+ public static Set<String> dataFormatNames() {
+ return DATAFORMATS.keySet();
+ }
+
+ /** The simple class names of all built-in beans. */
+ public static Set<String> beanNames() {
+ return BEANS.keySet();
+ }
+
+ /**
+ * What to add to the classpath for the artifact: the plain artifactId,
which is what the runtime knows. The Spring
+ * Boot starter (artifactId-starter) and Quarkus extension
(camel-quarkus-name) are a convention for the tooling
+ * that can check they exist, as camel-jbang export does; not every
artifact has one. A core artifact is noted as
+ * part of camel-core.
+ */
+ public static String dependencyHint(String artifactId) {
+ if (artifactId == null || artifactId.isBlank()) {
+ return "";
+ }
+ if (isCoreArtifact(artifactId)) {
+ return "add " + artifactId + " (part of camel-core) to the
classpath";
+ }
+ return "add " + artifactId + " to the classpath";
+ }
+
+ private static boolean isCoreArtifact(String artifactId) {
+ return artifactId.startsWith("camel-core-") ||
artifactId.equals("camel-core") || artifactId.equals("camel-base")
+ || artifactId.equals("camel-base-engine") ||
artifactId.equals("camel-support")
+ || artifactId.equals("camel-api") ||
artifactId.equals("camel-util");
+ }
+
+ /**
+ * The hint for a component scheme that could not be resolved: the
artifact to add when the scheme is a built-in
+ * component, or else the built-in scheme that was likely meant.
+ *
+ * @return the hint in parentheses with a leading space, or an empty
string if there is nothing to say
+ */
+ public static String componentHint(String scheme) {
+ return hint(scheme, "component", COMPONENTS);
+ }
+
+ /**
+ * The hint for a language name that could not be resolved: the artifact
to add when the name is a built-in
+ * language, or else the built-in language that was likely meant.
+ *
+ * @return the hint in parentheses with a leading space, or an empty
string if there is nothing to say
+ */
+ public static String languageHint(String name) {
+ return hint(name, "language", LANGUAGES);
+ }
+
+ /**
+ * The hint for a data format name that could not be resolved: the
artifact to add when the name is a built-in data
+ * format, or else the built-in data format that was likely meant.
+ *
+ * @return the hint in parentheses with a leading space, or an empty
string if there is nothing to say
+ */
+ public static String dataFormatHint(String name) {
+ return hint(name, "data format", DATAFORMATS);
+ }
+
+ private static String hint(String name, String kind, Map<String, String>
table) {
+ if (name == null || name.isBlank()) {
+ return "";
+ }
+ String artifact = table.get(name);
+ if (artifact != null) {
+ return " (the " + name + " " + kind + " is in " + artifact + "; "
+ dependencyHint(artifact) + ")";
+ }
+ String best = closest(name, table.keySet());
+ if (best != null) {
+ return " (not a built-in Camel " + kind + "; did you mean '" +
best + "'?)";
+ }
+ return " (not a built-in Camel " + kind + ")";
+ }
+
+ /**
+ * The candidate closest to the name, case-insensitively, when it is
within a few edits (one for names up to three
+ * characters, two, or a third of the name's length for longer names). Of
candidates at the same distance the one
+ * sharing the longer prefix with the name wins, as typos are seldom in
the first letters (htps is https, not ftps);
+ * when that still ties there is no answer, as a wrong guess is worse than
none.
+ *
+ * @return the closest candidate, or null if none is close enough or the
closest is ambiguous
+ */
+ public static String closest(String name, Collection<String> candidates) {
+ if (name == null || name.isBlank() || candidates == null) {
+ return null;
+ }
+ String best = null;
+ int bestDistance = Integer.MAX_VALUE;
+ int bestPrefix = -1;
+ boolean tie = false;
+ int threshold = name.length() <= 3 ? 1 : Math.max(2, name.length() /
3);
+ String n = name.toLowerCase(Locale.ROOT);
+ for (String candidate : candidates) {
+ if (candidate == null) {
+ continue;
+ }
+ String c = candidate.toLowerCase(Locale.ROOT);
+ int d = distance(n, c);
+ if (d > threshold || d > bestDistance) {
+ continue;
+ }
+ int prefix = commonPrefix(n, c);
+ if (d < bestDistance || prefix > bestPrefix) {
+ best = candidate;
+ bestDistance = d;
+ bestPrefix = prefix;
+ tie = false;
+ } else if (prefix == bestPrefix) {
+ tie = true;
+ }
+ }
+ return tie ? null : best;
+ }
+
+ private static int commonPrefix(String a, String b) {
+ int max = Math.min(a.length(), b.length());
+ int i = 0;
+ while (i < max && a.charAt(i) == b.charAt(i)) {
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * The Damerau-Levenshtein distance (optimal string alignment):
insertions, deletions, substitutions and
+ * transpositions of adjacent characters each count as one edit.
+ */
+ static int distance(String a, String b) {
+ int n = a.length();
+ int m = b.length();
+ if (n == 0) {
+ return m;
+ }
+ if (m == 0) {
+ return n;
+ }
+ int[][] d = new int[n + 1][m + 1];
+ for (int i = 0; i <= n; i++) {
+ d[i][0] = i;
+ }
+ for (int j = 0; j <= m; j++) {
+ d[0][j] = j;
+ }
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= m; j++) {
+ int cost = a.charAt(i - 1) == b.charAt(j - 1) ? 0 : 1;
+ d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1),
d[i - 1][j - 1] + cost);
+ if (i > 1 && j > 1 && a.charAt(i - 1) == b.charAt(j - 2) &&
a.charAt(i - 2) == b.charAt(j - 1)) {
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
+ }
+ }
+ }
+ return d[n][m];
+ }
+
+}
diff --git
a/core/camel-util/src/test/java/org/apache/camel/util/ArtifactUtilsTest.java
b/core/camel-util/src/test/java/org/apache/camel/util/ArtifactUtilsTest.java
new file mode 100644
index 000000000000..2228c1c5d56f
--- /dev/null
+++ b/core/camel-util/src/test/java/org/apache/camel/util/ArtifactUtilsTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.util;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ArtifactUtilsTest {
+
+ @Test
+ public void testComponentArtifact() {
+
assertThat(ArtifactUtils.componentArtifact("kafka")).isEqualTo("camel-kafka");
+ // the artifact is not always camel-<scheme>
+
assertThat(ArtifactUtils.componentArtifact("aws2-ddbstream")).isEqualTo("camel-aws2-ddb");
+
assertThat(ArtifactUtils.componentArtifact("class")).isEqualTo("camel-bean");
+ // alternative schemes are listed too
+
assertThat(ArtifactUtils.componentArtifact("coaps+tcp")).isEqualTo("camel-coap");
+ assertThat(ArtifactUtils.componentArtifact("cheese")).isNull();
+ assertThat(ArtifactUtils.componentArtifact(null)).isNull();
+
assertThat(ArtifactUtils.componentNames()).hasSizeGreaterThan(300).contains("kafka",
"direct", "coaps+tcp");
+ }
+
+ @Test
+ public void testLanguageArtifact() {
+
assertThat(ArtifactUtils.languageArtifact("simple")).isEqualTo("camel-core-languages");
+
assertThat(ArtifactUtils.languageArtifact("xquery")).isEqualTo("camel-saxon");
+
assertThat(ArtifactUtils.languageArtifact("jsonpath")).isEqualTo("camel-jsonpath");
+ assertThat(ArtifactUtils.languageArtifact("cheese")).isNull();
+
assertThat(ArtifactUtils.languageNames()).hasSizeGreaterThan(20).contains("simple",
"xquery", "bean");
+ }
+
+ @Test
+ public void testDataFormatArtifact() {
+
assertThat(ArtifactUtils.dataFormatArtifact("jaxb")).isEqualTo("camel-jaxb");
+
assertThat(ArtifactUtils.dataFormatArtifact("jackson")).isEqualTo("camel-jackson");
+ assertThat(ArtifactUtils.dataFormatArtifact("cheese")).isNull();
+
assertThat(ArtifactUtils.dataFormatNames()).hasSizeGreaterThan(40).contains("jaxb",
"csv");
+ }
+
+ @Test
+ public void testBeans() {
+ assertThat(ArtifactUtils.beanJavaType("UseLatestAggregationStrategy"))
+
.isEqualTo("org.apache.camel.processor.aggregate.UseLatestAggregationStrategy");
+ assertThat(ArtifactUtils.beanInterface("UseLatestAggregationStrategy"))
+ .isEqualTo("org.apache.camel.AggregationStrategy");
+
assertThat(ArtifactUtils.beanArtifact("UseLatestAggregationStrategy")).isEqualTo("camel-core-processor");
+
assertThat(ArtifactUtils.beanArtifact("ZipAggregationStrategy")).isEqualTo("camel-zipfile");
+ assertThat(ArtifactUtils.beanArtifact("Cheese")).isNull();
+ assertThat(ArtifactUtils.beanJavaType(null)).isNull();
+
assertThat(ArtifactUtils.beanNames()).hasSizeGreaterThan(50).contains("MemoryIdempotentRepository");
+ }
+
+ @Test
+ public void testDependencyHint() {
+ assertThat(ArtifactUtils.dependencyHint("camel-saxon")).isEqualTo("add
camel-saxon to the classpath");
+ // core artifacts are noted as part of camel-core
+ assertThat(ArtifactUtils.dependencyHint("camel-core-languages"))
+ .isEqualTo("add camel-core-languages (part of camel-core) to
the classpath");
+ assertThat(ArtifactUtils.dependencyHint("camel-support"))
+ .isEqualTo("add camel-support (part of camel-core) to the
classpath");
+ // camel-base64 is a data format, not a core artifact
+
assertThat(ArtifactUtils.dependencyHint("camel-base64")).isEqualTo("add
camel-base64 to the classpath");
+ assertThat(ArtifactUtils.dependencyHint(null)).isEmpty();
+ assertThat(ArtifactUtils.dependencyHint(" ")).isEmpty();
+ }
+
+ @Test
+ public void testHints() {
+ assertThat(ArtifactUtils.componentHint("kafka"))
+ .isEqualTo(" (the kafka component is in camel-kafka; add
camel-kafka to the classpath)");
+ assertThat(ArtifactUtils.componentHint("kafak")).isEqualTo(" (not a
built-in Camel component; did you mean 'kafka'?)");
+ assertThat(ArtifactUtils.componentHint("Kafka")).isEqualTo(" (not a
built-in Camel component; did you mean 'kafka'?)");
+ assertThat(ArtifactUtils.componentHint("cheese")).isEqualTo(" (not a
built-in Camel component)");
+ assertThat(ArtifactUtils.componentHint("")).isEmpty();
+ assertThat(ArtifactUtils.componentHint(null)).isEmpty();
+
+ assertThat(ArtifactUtils.languageHint("xquery")).startsWith(" (the
xquery language is in camel-saxon; add camel-saxon");
+ assertThat(ArtifactUtils.languageHint("simpel")).isEqualTo(" (not a
built-in Camel language; did you mean 'simple'?)");
+ assertThat(ArtifactUtils.languageHint("simple")).startsWith(" (the
simple language is in camel-core-languages;");
+
+ assertThat(ArtifactUtils.dataFormatHint("jaxb")).startsWith(" (the
jaxb data format is in camel-jaxb; add camel-jaxb");
+ assertThat(ArtifactUtils.dataFormatHint("jakson"))
+ .isEqualTo(" (not a built-in Camel data format; did you mean
'jackson'?)");
+ }
+
+ @Test
+ public void testClosest() {
+ List<String> names = List.of("kafka", "kamelet", "file", "ftp",
"sftp", "xj", "xslt");
+ assertThat(ArtifactUtils.closest("kafak", names)).isEqualTo("kafka");
+ assertThat(ArtifactUtils.closest("KAFKA", names)).isEqualTo("kafka");
+ assertThat(ArtifactUtils.closest("files", names)).isEqualTo("file");
+ // short names allow one edit only, so xxx is nothing
+ assertThat(ArtifactUtils.closest("xxx", names)).isNull();
+ assertThat(ArtifactUtils.closest("ftps", names)).isEqualTo("ftp");
+ assertThat(ArtifactUtils.closest("cheese", names)).isNull();
+ assertThat(ArtifactUtils.closest("", names)).isNull();
+ assertThat(ArtifactUtils.closest("kafka", null)).isNull();
+ }
+
+ @Test
+ public void testClosestTie() {
+ List<String> names = List.of("ftp", "ftps", "http", "https", "jms",
"jmx");
+ // same distance to ftps and https: the longer shared prefix wins, as
typos are seldom in the first letters
+ assertThat(ArtifactUtils.closest("htps", names)).isEqualTo("https");
+ assertThat(ArtifactUtils.closest("htp", names)).isEqualTo("http");
+ // same distance and same prefix to jms and jmx: no guess rather than
a wrong one
+ assertThat(ArtifactUtils.closest("jm", names)).isNull();
+ // the real table, which has all of these
+ assertThat(ArtifactUtils.componentHint("htps")).isEqualTo(" (not a
built-in Camel component; did you mean 'https'?)");
+ // null candidates are skipped
+ assertThat(ArtifactUtils.closest("kafak", Arrays.asList(null,
"kafka"))).isEqualTo("kafka");
+ }
+
+ @Test
+ public void testDistance() {
+ assertThat(ArtifactUtils.distance("kafka", "kafka")).isZero();
+ assertThat(ArtifactUtils.distance("kafak", "kafka")).isEqualTo(1);
+ assertThat(ArtifactUtils.distance("", "abc")).isEqualTo(3);
+ assertThat(ArtifactUtils.distance("abc", "")).isEqualTo(3);
+ assertThat(ArtifactUtils.distance("simple", "sample")).isEqualTo(1);
+ }
+}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
index cffcaf708cfd..8931b52b2e4b 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
@@ -39,7 +39,7 @@ public final class ExpressionDeserializers extends
YamlDeserializerSupport {
Node val = setDeserializationContext(nt.getValueNode(), dc);
ExpressionDefinition answer = constructExpressionType(key, val);
if (answer == null) {
- throw new
org.apache.camel.dsl.yaml.common.exception.InvalidExpressionException(node,
"Unknown expression with id: " + key + ("bean".equals(key) ? " (the bean
language is written as method: {ref: myBean, method: process})" : ""));
+ throw new
org.apache.camel.dsl.yaml.common.exception.InvalidExpressionException(node,
"Unknown expression with id: " + key + ("bean".equals(key) ? " (the bean
language is written as method: {ref: myBean, method: process})" :
org.apache.camel.util.ArtifactUtils.languageHint(key)));
}
return answer;
}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
index 633e2f2e4ed3..3c01b90d1e7e 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
@@ -185,7 +185,7 @@ public class GenerateYamlDeserializersMojo extends
GenerateYamlSupportMojo {
.addStatement("ExpressionDefinition answer =
constructExpressionType(key, val)")
.beginControlFlow("if (answer == null)")
.addStatement(
- "throw new
org.apache.camel.dsl.yaml.common.exception.InvalidExpressionException(node,
\"Unknown expression with id: \" + key + (\"bean\".equals(key) ? \" (the bean
language is written as method: {ref: myBean, method: process})\" : \"\"))")
+ "throw new
org.apache.camel.dsl.yaml.common.exception.InvalidExpressionException(node,
\"Unknown expression with id: \" + key + (\"bean\".equals(key) ? \" (the bean
language is written as method: {ref: myBean, method: process})\" :
org.apache.camel.util.ArtifactUtils.languageHint(key)))")
.endControlFlow()
.addStatement("return answer")
.build())
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ExpressionTest.groovy
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ExpressionTest.groovy
index 278968204b4a..0ea5d4eb91db 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ExpressionTest.groovy
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ExpressionTest.groovy
@@ -171,4 +171,24 @@ class ExpressionTest extends YamlTestSupport {
context.routeDefinitions.size() == 1
}
+ // CAMEL-24752: an unknown expression id says which built-in language was
likely meant
+ def "Error: explicit not existing says did you mean"() {
+ when:
+ loadRoutesNoValidate('''
+ - from:
+ uri: "direct:start"
+ steps:
+ - setBody:
+ expression:
+ simpel: "${body}"
+ ''')
+ then:
+ def e = thrown(Exception)
+ def messages = []
+ for (Throwable t = e; t != null; t = t.cause) {
+ messages << t.message
+ }
+ messages.any { it != null && it.contains("Unknown expression with id:
simpel (not a built-in Camel language; did you mean 'simple'?)") }
+ }
+
}
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateArtifactHelper.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateArtifactHelper.java
new file mode 100644
index 000000000000..2599553bea29
--- /dev/null
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateArtifactHelper.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+import java.util.TreeMap;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.tooling.util.Strings;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProjectHelper;
+import org.codehaus.plexus.build.BuildContext;
+
+import static org.apache.camel.tooling.util.PackageHelper.findCamelDirectory;
+
+/**
+ * Updates ArtifactUtils.java in camel-util with which artifact ships each
component, language, data format and built-in
+ * bean, from the catalog, so runtime errors can say what to add to the
classpath.
+ */
+@Mojo(name = "update-artifact-helper", threadSafe = true)
+public class UpdateArtifactHelper extends AbstractGeneratorMojo {
+
+ private static final String JAVA_FILE =
"src/main/java/org/apache/camel/util/ArtifactUtils.java";
+
+ @Parameter(defaultValue =
"${project.basedir}/src/generated/resources/org/apache/camel/catalog/")
+ protected File jsonDir;
+
+ @Parameter(defaultValue = "${project.basedir}/")
+ protected File baseDir;
+
+ @Inject
+ public UpdateArtifactHelper(MavenProjectHelper projectHelper, BuildContext
buildContext) {
+ super(projectHelper, buildContext);
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ File camelDir = findCamelDirectory(baseDir, "core/camel-util");
+ if (camelDir == null) {
+ getLog().debug("No core/camel-util folder found, skipping
execution");
+ return;
+ }
+ List<Path> jsonFiles;
+ try (Stream<Path> stream =
PackageHelper.findJsonFiles(jsonDir.toPath())) {
+ jsonFiles = stream.toList();
+ }
+
+ Map<String, String> components = new TreeMap<>();
+ Map<String, String> languages = new TreeMap<>();
+ Map<String, String> dataformats = new TreeMap<>();
+ Map<String, String> beans = new TreeMap<>();
+
+ for (Path file : jsonFiles) {
+ final String name = PackageHelper.asName(file);
+ try {
+ Object jo =
Jsoner.deserialize(PackageHelper.loadText(file.toFile()));
+ if (!(jo instanceof JsonObject obj)) {
+ continue;
+ }
+ JsonObject component = obj.getMap("component");
+ JsonObject language = obj.getMap("language");
+ JsonObject dataformat = obj.getMap("dataformat");
+ JsonObject bean = obj.getMap("bean");
+ if (component != null) {
+ // every alternative scheme has its own json file, so the
scheme is the key
+ put(components, component.getString("scheme"),
component.getString("artifactId"));
+ } else if (language != null) {
+ put(languages, language.getString("name"),
language.getString("artifactId"));
+ } else if (dataformat != null) {
+ put(dataformats, dataformat.getString("name"),
dataformat.getString("artifactId"));
+ } else if (bean != null) {
+ String javaType = bean.getString("javaType");
+ String interfaceType =
bean.getStringOrDefault("interfaceType", "");
+ String artifactId = bean.getString("artifactId");
+ if (javaType != null && artifactId != null) {
+ put(beans, bean.getString("name"), javaType + "|" +
interfaceType + "|" + artifactId);
+ }
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException("Error loading json: " +
name, e);
+ }
+ }
+
+ getLog().info("There are " + components.size() + " component schemes,
" + languages.size() + " languages, "
+ + dataformats.size() + " data formats and " +
beans.size() + " beans with a known artifact");
+
+ try {
+ boolean updated = update(camelDir, "COMPONENTS", components);
+ updated |= update(camelDir, "LANGUAGES", languages);
+ updated |= update(camelDir, "DATAFORMATS", dataformats);
+ updated |= update(camelDir, "BEANS", beans);
+ if (updated) {
+ getLog().info("Updated camel-util/" + JAVA_FILE + " file");
+ } else {
+ getLog().debug("No changes to camel-util/" + JAVA_FILE + "
file");
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException("Error updating
ArtifactUtils.java", e);
+ }
+ }
+
+ private static void put(Map<String, String> table, String key, String
artifactId) {
+ if (key != null && artifactId != null) {
+ table.put(key, artifactId);
+ }
+ }
+
+ /**
+ * Rewrites the Map.entry lines between the START and END tokens of the
given table, in the layout the formatter
+ * keeps (so a rebuild does not change the file again).
+ */
+ private static boolean update(File camelDir, String token, Map<String,
String> table) throws Exception {
+ File java = new File(camelDir, JAVA_FILE);
+ String text = PackageHelper.loadText(java);
+ String startToken = "// " + token + ": START";
+ String endToken = "// " + token + ": END";
+ String spaces12 = " ";
+ String spaces4 = " ";
+
+ StringJoiner sb = new StringJoiner(",\n");
+ for (Map.Entry<String, String> entry : table.entrySet()) {
+ String line = spaces12 + "Map.entry(\"" + entry.getKey() + "\",
\"" + entry.getValue() + "\")";
+ if (line.length() > 120) {
+ // the formatter wraps a long entry after the key
+ line = spaces12 + "Map.entry(\"" + entry.getKey() + "\",\n" +
spaces12 + spaces4 + spaces4
+ + "\"" + entry.getValue() + "\")";
+ }
+ sb.add(line);
+ }
+ String changed = sb.toString().trim();
+
+ String existing = Strings.between(text, startToken, endToken);
+ if (existing == null) {
+ return false;
+ }
+ if (existing.trim().equals(changed)) {
+ return false;
+ }
+ String before = Strings.before(text, startToken);
+ String after = Strings.after(text, endToken);
+ text = before + startToken + "\n" + spaces12 + changed + "\n" +
spaces4 + endToken + after;
+ PackageHelper.writeText(java, text);
+ return true;
+ }
+}