This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch feature/CAMEL-24658-catalog-term-lookup in repository https://gitbox.apache.org/repos/asf/camel.git
commit 44de46832b6d4961d0d10242db8c1efda1b5aa23 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Sep 8 19:17:19 2026 +0200 CAMEL-24658: camel-catalog - find components by protocol or product name (s3 -> aws2-s3, mqtt -> paho-mqtt5) Adds suggestComponentNames, suggestDataFormatNames and suggestLanguageNames to CamelCatalog. They rank matches by exact scheme, declared aliases, alternative schemes, title words, scheme words and finally substring, and return a component that shares one implementation under several schemes (imap, pop3, smtp) only once. The lookup is a default method working on the existing JSON, so it also serves older catalogs loaded via CatalogLoader. Component aliases reuse @Metadata(aliases) on the endpoint class, which the endpoint schema generator now copies into the component model. The mail component declares mail/email and ActiveMQ declares amq. The Camel TUI drops its hardcoded scheme alias map in favour of the catalog and tui_catalog_doc returns suggestions when a name is not found. The camel catalog commands include the suggestions in their did-you-mean output. Co-Authored-By: Claude Fable 5.1 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../apache/camel/catalog/components/activemq.json | 1 + .../apache/camel/catalog/components/activemq6.json | 1 + .../org/apache/camel/catalog/components/imap.json | 1 + .../org/apache/camel/catalog/components/imaps.json | 1 + .../org/apache/camel/catalog/components/pop3.json | 1 + .../org/apache/camel/catalog/components/pop3s.json | 1 + .../org/apache/camel/catalog/components/smtp.json | 1 + .../org/apache/camel/catalog/components/smtps.json | 1 + .../org/apache/camel/catalog/CamelCatalog.java | 45 +++++ .../apache/camel/catalog/CatalogTermMatcher.java | 199 +++++++++++++++++++++ .../org/apache/camel/catalog/CamelCatalogTest.java | 59 ++++++ .../apache/camel/component/activemq/activemq.json | 1 + .../camel/component/activemq/ActiveMQEndpoint.java | 2 + .../camel/component/activemq6/activemq6.json | 1 + .../component/activemq6/ActiveMQEndpoint.java | 2 + .../org/apache/camel/component/mail/imap.json | 1 + .../org/apache/camel/component/mail/imaps.json | 1 + .../org/apache/camel/component/mail/pop3.json | 1 + .../org/apache/camel/component/mail/pop3s.json | 1 + .../org/apache/camel/component/mail/smtp.json | 1 + .../org/apache/camel/component/mail/smtps.json | 1 + .../apache/camel/component/mail/MailEndpoint.java | 2 + .../modules/ROOT/pages/camel-catalog.adoc | 34 ++++ .../core/commands/catalog/CatalogBaseCommand.java | 14 +- .../core/commands/catalog/CatalogComponent.java | 5 + .../core/commands/catalog/CatalogDataFormat.java | 5 + .../core/commands/catalog/CatalogLanguage.java | 5 + .../commands/catalog/CatalogComponentTest.java | 11 ++ .../jbang/core/commands/tui/TuiToolRegistry.java | 69 +++---- .../tui/TuiToolRegistrySuggestComponentsTest.java | 46 ----- .../packaging/EndpointSchemaGeneratorMojo.java | 6 + 31 files changed, 429 insertions(+), 91 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json index a09d44a724dd..d6d6c9c9d608 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq.json @@ -9,6 +9,7 @@ "label": "messaging", "javaType": "org.apache.camel.component.activemq.ActiveMQComponent", "supportLevel": "Stable", + "aliases": [ "amq" ], "groupId": "org.apache.camel", "artifactId": "camel-activemq", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json index 04ddf3b3a40b..b1e04a0ba8b4 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/activemq6.json @@ -9,6 +9,7 @@ "label": "messaging", "javaType": "org.apache.camel.component.activemq6.ActiveMQComponent", "supportLevel": "Stable", + "aliases": [ "amq" ], "groupId": "org.apache.camel", "artifactId": "camel-activemq6", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json index 6c49e15393b5..621ba167453a 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imap.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json index 2d59fa225fa5..be87ed8c11ae 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/imaps.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json index 6827a27d73bf..e868fcf6abdd 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json index 582c2818dc8a..d2e95187a967 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pop3s.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json index c0cd0d423a4e..33dff5f867f5 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtp.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json index c5c1721f6c6a..2b2188d35521 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smtps.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java index d71a426840bc..c0ba6aa5638f 100644 --- a/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java +++ b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CamelCatalog.java @@ -266,6 +266,51 @@ public interface CamelCatalog { */ List<String> findOtherNames(String filter); + /** + * Finds component names that match a term such as a protocol or product name, best match first: the exact scheme, + * then declared aliases, alternative schemes, words of the title, words of the scheme, and finally schemes + * containing the term. Components that share one implementation under several schemes (such as imap, pop3 and smtp) + * are returned once, by the scheme that matched or otherwise their primary scheme. + * <p> + * For example <tt>mqtt</tt> finds <tt>paho-mqtt5</tt>, <tt>s3</tt> finds <tt>aws2-s3</tt>, and <tt>amq</tt> finds + * <tt>activemq</tt>. + * + * @param term the protocol, product, or component name to look for + * @param max the maximum number of names to return, or 0 for no limit + * @return the matching component names, or an empty list if none match + * @since 4.23 + */ + default List<String> suggestComponentNames(String term, int max) { + return CatalogTermMatcher.suggestComponentNames(this, term, max); + } + + /** + * Finds data format names that match a term such as a format or library name (for example <tt>yaml</tt> finds + * <tt>snakeYaml</tt>), best match first: the exact name, then declared aliases, words of the title, words of the + * name, and finally names containing the term. + * + * @param term the format, library, or data format name to look for + * @param max the maximum number of names to return, or 0 for no limit + * @return the matching data format names, or an empty list if none match + * @since 4.23 + */ + default List<String> suggestDataFormatNames(String term, int max) { + return CatalogTermMatcher.suggestNames(findDataFormatNames(), this::dataFormatModel, term, max); + } + + /** + * Finds language names that match a term, best match first: the exact name, then declared aliases, words of the + * title, words of the name, and finally names containing the term. + * + * @param term the language name to look for + * @param max the maximum number of names to return, or 0 for no limit + * @return the matching language names, or an empty list if none match + * @since 4.23 + */ + default List<String> suggestLanguageNames(String term, int max) { + return CatalogTermMatcher.suggestNames(findLanguageNames(), this::languageModel, term, max); + } + /** * Returns the component information as JSON format. * diff --git a/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CatalogTermMatcher.java b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CatalogTermMatcher.java new file mode 100644 index 000000000000..70093f5e1fa9 --- /dev/null +++ b/catalog/camel-catalog/src/main/java/org/apache/camel/catalog/CatalogTermMatcher.java @@ -0,0 +1,199 @@ +/* + * 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.catalog; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +import org.apache.camel.tooling.model.BaseModel; +import org.apache.camel.tooling.model.ComponentModel; + +/** + * Ranks catalog entries against a search term such as a protocol or product name (mqtt, s3, rabbitmq) for the suggest + * methods on {@link CamelCatalog}. + */ +final class CatalogTermMatcher { + + private static final int NO_MATCH = -1; + /** Shorter terms are too ambiguous to match as a substring of a name. */ + private static final int MIN_SUBSTRING_LENGTH = 3; + + private record Match(String name, int rank, String group) { + } + + private CatalogTermMatcher() { + } + + /** + * Component names matching the term, best match first. Components that share one implementation under several + * schemes (imap, pop3, smtp) are returned once: by the scheme that matched, or otherwise their primary scheme. + */ + static List<String> suggestComponentNames(CamelCatalog catalog, String term, int max) { + String key = normalize(term); + if (key.isEmpty()) { + return List.of(); + } + List<Match> matches = new ArrayList<>(); + for (String name : catalog.findComponentNames()) { + ComponentModel model = name == null || name.isBlank() ? null : catalog.componentModel(name); + if (model == null) { + continue; + } + int rank = rankComponent(model, key); + if (rank != NO_MATCH) { + String group = model.getAlternativeSchemes(); + matches.add(new Match(name, rank, group == null || group.isBlank() ? null : group)); + } + } + Map<String, Match> bestPerGroup = new HashMap<>(); + for (Match match : matches) { + if (match.group() != null) { + bestPerGroup.merge(match.group(), match, CatalogTermMatcher::better); + } + } + matches.removeIf(m -> m.group() != null && bestPerGroup.get(m.group()) != m); + return names(matches, max); + } + + /** + * Data format or language names matching the term, best match first. + */ + static List<String> suggestNames( + List<String> names, Function<String, ? extends BaseModel<?>> modelLoader, String term, int max) { + String key = normalize(term); + if (key.isEmpty()) { + return List.of(); + } + List<Match> matches = new ArrayList<>(); + for (String name : names) { + BaseModel<?> model = name == null || name.isBlank() ? null : modelLoader.apply(name); + if (model == null) { + continue; + } + int rank = rankNamed(model, key); + if (rank != NO_MATCH) { + matches.add(new Match(name, rank, null)); + } + } + return names(matches, max); + } + + private static int rankComponent(ComponentModel model, String key) { + String scheme = model.getScheme(); + if (key.equals(normalize(scheme))) { + return 0; + } + if (matchesAlias(model, key)) { + return 1; + } + if (model.getAlternativeSchemes() != null && containsNormalized(model.getAlternativeSchemes().split(","), key)) { + return 2; + } + if (matchesTitle(model, key)) { + return 3; + } + if (containsNormalized(words(scheme), key)) { + return 4; + } + if (key.length() >= MIN_SUBSTRING_LENGTH && normalize(scheme).contains(key)) { + return 5; + } + return NO_MATCH; + } + + private static int rankNamed(BaseModel<?> model, String key) { + String name = model.getName(); + if (key.equals(normalize(name))) { + return 0; + } + if (matchesAlias(model, key)) { + return 1; + } + if (matchesTitle(model, key)) { + return 2; + } + if (containsNormalized(camelCaseWords(name), key)) { + return 3; + } + if (key.length() >= MIN_SUBSTRING_LENGTH && normalize(name).contains(key)) { + return 4; + } + return NO_MATCH; + } + + private static boolean matchesAlias(BaseModel<?> model, String key) { + List<String> aliases = model.getAliases(); + return aliases != null && containsNormalized(aliases.toArray(new String[0]), key); + } + + private static boolean matchesTitle(BaseModel<?> model, String key) { + String title = model.getTitle(); + return title != null && (key.equals(normalize(title)) || containsNormalized(words(title), key)); + } + + private static Match better(Match a, Match b) { + if (a.rank() != b.rank()) { + return a.rank() < b.rank() ? a : b; + } + // same rank: prefer the scheme listed first, which is the primary one + List<String> schemes = Arrays.asList(a.group().split(",")); + return schemes.indexOf(b.name()) < schemes.indexOf(a.name()) ? b : a; + } + + private static List<String> names(List<Match> matches, int max) { + matches.sort(Comparator.comparingInt(Match::rank)); + List<String> answer = new ArrayList<>(matches.size()); + for (Match match : matches) { + if (max > 0 && answer.size() >= max) { + break; + } + // a catalog can list a name twice when a component was added on top of the built-in ones + if (!answer.contains(match.name())) { + answer.add(match.name()); + } + } + return answer; + } + + private static boolean containsNormalized(String[] candidates, String key) { + for (String candidate : candidates) { + if (key.equals(normalize(candidate))) { + return true; + } + } + return false; + } + + private static String[] words(String text) { + return text == null ? new String[0] : text.split("[^A-Za-z0-9]+"); + } + + private static String[] camelCaseWords(String text) { + return text == null ? new String[0] : words(text.replaceAll("([a-z0-9])([A-Z])", "$1 $2")); + } + + private static String normalize(String text) { + return text == null ? "" : text.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", ""); + } + +} diff --git a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java index cc847e65b3c4..b4ea441b8927 100644 --- a/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java +++ b/catalog/camel-catalog/src/test/java/org/apache/camel/catalog/CamelCatalogTest.java @@ -182,6 +182,65 @@ public class CamelCatalogTest { assertTrue(names.contains("saga")); } + @Test + public void testComponentAliases() { + // aliases declared via @Metadata on the endpoint class end up in the component model + ComponentModel mail = catalog.componentModel("imap"); + assertNotNull(mail); + assertTrue(mail.getAliases().contains("mail")); + assertTrue(mail.getAliases().contains("email")); + assertTrue(catalog.componentModel("activemq").getAliases().contains("amq")); + } + + @Test + public void testSuggestComponentNames() { + // exact scheme first, then words of the title or scheme + assertEquals(List.of("aws2-s3", "aws2-s3-vectors"), catalog.suggestComponentNames("s3", 0)); + assertEquals(List.of("aws2-sqs"), catalog.suggestComponentNames("sqs", 0)); + assertEquals(List.of("paho-mqtt5"), catalog.suggestComponentNames("mqtt", 0)); + assertEquals(List.of("spring-rabbitmq"), catalog.suggestComponentNames("rabbitmq", 0)); + assertEquals(List.of("google-pubsub"), catalog.suggestComponentNames("pubsub", 0)); + assertEquals(List.of("azure-servicebus"), catalog.suggestComponentNames("servicebus", 0)); + assertEquals(List.of("azure-servicebus"), catalog.suggestComponentNames("Service-Bus", 0)); + assertEquals(List.of("kafka", "aws2-msk"), catalog.suggestComponentNames("kafka", 0)); + // aliases rank right after the exact scheme, substring matches last + assertEquals(List.of("activemq", "activemq6", "amqp"), catalog.suggestComponentNames("amq", 0)); + assertEquals(List.of("paho-mqtt5"), catalog.suggestComponentNames("paho-mqtt", 0)); + assertTrue(catalog.suggestComponentNames("xyzq", 0).isEmpty()); + assertTrue(catalog.suggestComponentNames("", 5).isEmpty()); + assertTrue(catalog.suggestComponentNames(null, 5).isEmpty()); + // max caps the result + List<String> aws = catalog.suggestComponentNames("aws", 0); + assertTrue(aws.size() > 5, "aws should match many components, was: " + aws); + assertEquals(aws.subList(0, 5), catalog.suggestComponentNames("aws", 5)); + } + + @Test + public void testSuggestComponentNamesDedupesAlternativeSchemes() { + // one implementation under several schemes is suggested once, by the scheme that matched + assertEquals(List.of("smtp"), catalog.suggestComponentNames("smtp", 0)); + assertEquals(List.of("https"), catalog.suggestComponentNames("https", 0)); + // or by its primary scheme when the match came from an alias + List<String> mail = catalog.suggestComponentNames("mail", 0); + assertEquals("imap", mail.get(0), "mail should suggest the mail component first, was: " + mail); + assertFalse(mail.contains("smtp"), "mail should not repeat the mail component, was: " + mail); + assertTrue(mail.contains("google-mail"), "mail should also suggest google-mail, was: " + mail); + } + + @Test + public void testSuggestDataFormatAndLanguageNames() { + assertEquals(List.of("snakeYaml"), catalog.suggestDataFormatNames("yaml", 0)); + List<String> json = catalog.suggestDataFormatNames("json", 0); + assertTrue(json.contains("jackson"), "json should suggest jackson, was: " + json); + assertTrue(json.contains("gson"), "json should suggest gson, was: " + json); + assertEquals("jackson", catalog.suggestDataFormatNames("jackson", 0).get(0)); + + assertEquals("simple", catalog.suggestLanguageNames("simple", 0).get(0)); + List<String> path = catalog.suggestLanguageNames("path", 0); + assertTrue(path.contains("xpath"), "path should suggest xpath, was: " + path); + assertTrue(path.contains("jsonpath"), "path should suggest jsonpath, was: " + path); + } + @Test public void testEipModelAliases() { // verify aliases are present in the EIP model diff --git a/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json b/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json index a09d44a724dd..d6d6c9c9d608 100644 --- a/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json +++ b/components/camel-activemq/src/generated/resources/META-INF/org/apache/camel/component/activemq/activemq.json @@ -9,6 +9,7 @@ "label": "messaging", "javaType": "org.apache.camel.component.activemq.ActiveMQComponent", "supportLevel": "Stable", + "aliases": [ "amq" ], "groupId": "org.apache.camel", "artifactId": "camel-activemq", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java index be92147a9dc4..c2c8432f5ff0 100644 --- a/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java +++ b/components/camel-activemq/src/main/java/org/apache/camel/component/activemq/ActiveMQEndpoint.java @@ -25,12 +25,14 @@ import org.apache.camel.component.jms.JmsComponent; import org.apache.camel.component.jms.JmsConfiguration; import org.apache.camel.component.jms.JmsEndpoint; import org.apache.camel.spi.EndpointServiceLocation; +import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; /** * Send messages to (or consume from) Apache ActiveMQ 5.x. This component extends the Camel JMS component. */ +@Metadata(aliases = { "amq" }) @UriEndpoint(firstVersion = "1.0.0", extendsScheme = "jms", scheme = "activemq", title = "ActiveMQ 5.x", syntax = "activemq:destinationType:destinationName", category = { Category.MESSAGING }) diff --git a/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json b/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json index 04ddf3b3a40b..b1e04a0ba8b4 100644 --- a/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json +++ b/components/camel-activemq6/src/generated/resources/META-INF/org/apache/camel/component/activemq6/activemq6.json @@ -9,6 +9,7 @@ "label": "messaging", "javaType": "org.apache.camel.component.activemq6.ActiveMQComponent", "supportLevel": "Stable", + "aliases": [ "amq" ], "groupId": "org.apache.camel", "artifactId": "camel-activemq6", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java index 877e2fb8a4bf..cd3bed8b2bba 100644 --- a/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java +++ b/components/camel-activemq6/src/main/java/org/apache/camel/component/activemq6/ActiveMQEndpoint.java @@ -25,12 +25,14 @@ import org.apache.camel.component.jms.JmsComponent; import org.apache.camel.component.jms.JmsConfiguration; import org.apache.camel.component.jms.JmsEndpoint; import org.apache.camel.spi.EndpointServiceLocation; +import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; /** * Send messages to (or consume from) Apache ActiveMQ 6.x. This component extends the Camel JMS component. */ +@Metadata(aliases = { "amq" }) @UriEndpoint(firstVersion = "4.7.0", extendsScheme = "jms", scheme = "activemq6", title = "ActiveMQ 6.x", syntax = "activemq6:destinationType:destinationName", category = { Category.MESSAGING }) diff --git a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json index 6c49e15393b5..621ba167453a 100644 --- a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json +++ b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imap.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json index 2d59fa225fa5..be87ed8c11ae 100644 --- a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json +++ b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/imaps.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json index 6827a27d73bf..e868fcf6abdd 100644 --- a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json +++ b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json index 582c2818dc8a..d2e95187a967 100644 --- a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json +++ b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/pop3s.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json index c0cd0d423a4e..33dff5f867f5 100644 --- a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json +++ b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtp.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json index c5c1721f6c6a..2b2188d35521 100644 --- a/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json +++ b/components/camel-mail/src/generated/resources/META-INF/org/apache/camel/component/mail/smtps.json @@ -9,6 +9,7 @@ "label": "mail", "javaType": "org.apache.camel.component.mail.MailComponent", "supportLevel": "Stable", + "aliases": [ "mail", "email" ], "groupId": "org.apache.camel", "artifactId": "camel-mail", "version": "4.23.0-SNAPSHOT", diff --git a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java index fddb518f54a8..7280476da01a 100644 --- a/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java +++ b/components/camel-mail/src/main/java/org/apache/camel/component/mail/MailEndpoint.java @@ -30,6 +30,7 @@ import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategyAware; import org.apache.camel.spi.IdempotentRepository; +import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.support.ScheduledPollEndpoint; @@ -42,6 +43,7 @@ import static org.apache.camel.component.mail.MailConstants.MAIL_HANDLE_DUPLICAT /** * Send and receive emails using imap, pop3 and smtp protocols. */ +@Metadata(aliases = { "mail", "email" }) @UriEndpoint(firstVersion = "1.0.0", scheme = "imap,imaps,pop3,pop3s,smtp,smtps", title = "IMAP,IMAPS,POP3,POP3S,SMTP,SMTPS", syntax = "imap:host:port", alternativeSyntax = "imap:username:password@host:port", category = { Category.MAIL }, headersClass = MailConstants.class) diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc index ec08bff1087c..7f63d0149d28 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc @@ -88,6 +88,40 @@ nesting level in the YAML DSL, with types, descriptions, required flags, default and enum choices. It can be used by third-party tooling (editors, IDE plugins, visual designers) to provide context-aware code completion and validation for YAML DSL routes. +=== Finding components by protocol or product name + +People, and AI agents driving Camel through tooling, often refer to a component by the protocol or +product it connects to rather than by its Camel scheme: _mqtt_ (the component is `paho-mqtt5`), +_s3_ (`aws2-s3`), _rabbitmq_ (`spring-rabbitmq`), _pubsub_ (`google-pubsub`). The catalog can look +components, data formats and languages up by such a term, ranking the best matches first: +the exact scheme or name, then declared aliases, alternative schemes, words of the title, words of the +scheme or name, and finally names containing the term. A component that shares one implementation under +several schemes (such as `imap`, `pop3` and `smtp`) is returned once. + +[source,java] +---- +CamelCatalog catalog = new DefaultCamelCatalog(); + +catalog.suggestComponentNames("mqtt", 5); // [paho-mqtt5] +catalog.suggestComponentNames("s3", 5); // [aws2-s3, aws2-s3-vectors] +catalog.suggestComponentNames("amq", 5); // [activemq, activemq6, amqp] +catalog.suggestComponentNames("aws", 5); // at most five of the many AWS components +catalog.suggestDataFormatNames("yaml", 5); // [snakeYaml] +catalog.suggestLanguageNames("path", 5); // [jsonpath, xpath] +---- + +Component developers can declare the shorthands a title cannot provide with the `aliases` attribute of +`@Metadata` on the endpoint class, as the mail component does for _mail_ and _email_: + +[source,java] +---- +@Metadata(aliases = { "mail", "email" }) +@UriEndpoint(firstVersion = "1.0.0", scheme = "imap,imaps,pop3,pop3s,smtp,smtps", ...) +public class MailEndpoint extends ScheduledPollEndpoint { +---- + +Aliases are descriptive names for discovery only; they cannot be used as the scheme in an endpoint URI. + === Documentation in the Catalog The catalog includes the AsciiDoc documentation pages from all modules. This makes the full diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java index b8ac84ee7605..f872d2d5cfac 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java @@ -96,6 +96,13 @@ public abstract class CatalogBaseCommand extends CamelCommand { abstract List<Row> collectRows(); + /** + * Names the catalog suggests for a filter that matched nothing, such as a protocol or product name (mqtt, s3). + */ + List<String> suggestNames(String term) { + return List.of(); + } + String getGAV(ArtifactModel<?> model) { return model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion(); } @@ -186,7 +193,12 @@ public abstract class CatalogBaseCommand extends CamelCommand { } else if (filterName != null) { // suggest similar names when filter returns no results List<String> allNames = collectRows().stream().map(r -> r.name).collect(Collectors.toList()); - List<String> suggestions = SuggestSimilarHelper.didYouMean(allNames, filterName); + List<String> suggestions = new ArrayList<>(suggestNames(filterName)); + for (String similar : SuggestSimilarHelper.didYouMean(allNames, filterName)) { + if (!suggestions.contains(similar)) { + suggestions.add(similar); + } + } if (!suggestions.isEmpty()) { printer().println("No results for filter: " + filterName + ". Did you mean? " + String.join(", ", suggestions)); } else { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java index 154e96b3f38e..99c766667b6f 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponent.java @@ -57,4 +57,9 @@ public class CatalogComponent extends CatalogBaseCommand { return rows; } + @Override + List<String> suggestNames(String term) { + return catalog.suggestComponentNames(term, 5); + } + } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java index 14c06a37723b..c785dbb0aa49 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDataFormat.java @@ -57,4 +57,9 @@ public class CatalogDataFormat extends CatalogBaseCommand { return rows; } + @Override + List<String> suggestNames(String term) { + return catalog.suggestDataFormatNames(term, 5); + } + } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java index 3d641290c184..a1e8a672b17a 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogLanguage.java @@ -58,4 +58,9 @@ public class CatalogLanguage extends CatalogBaseCommand { return rows; } + @Override + List<String> suggestNames(String term) { + return catalog.suggestLanguageNames(term, 5); + } + } diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java index e5dd1ed0a6c9..fd0b15bd4a0e 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogComponentTest.java @@ -70,6 +70,17 @@ class CatalogComponentTest extends CamelCommandBaseTestSupport { "JSON output should contain a row with name kafka, was: " + printer.getOutput()); } + @Test + void shouldSuggestComponentByProtocolOrProductName() throws Exception { + CatalogComponent command = createCommand(); + command.filterName = "servicebus"; + int exit = command.doCall(); + assertEquals(0, exit); + String out = printer.getOutput(); + assertTrue(out.contains("Did you mean? azure-servicebus"), + "a product name should suggest the component using it, was: " + out); + } + @Test void shouldSuggestSimilarWhenFilterHasNoMatch() throws Exception { CatalogComponent command = createCommand(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java index 147a52da3dce..ac293802f848 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistry.java @@ -1487,17 +1487,16 @@ class TuiToolRegistry { return null; } String scheme = endpoint.substring(0, colon).toLowerCase(); - List<String> names; + List<String> similar; try { CamelCatalog catalog = CatalogLoader.loadCatalog(null, facade.getSelectedCamelVersion(), true); - names = catalog.findComponentNames(); + if (catalog.findComponentNames().contains(scheme)) { + return null; + } + similar = catalog.suggestComponentNames(scheme, 5); } catch (Exception e) { return null; } - if (names.contains(scheme)) { - return null; - } - List<String> similar = suggestComponents(scheme, names); if (similar.isEmpty()) { return "Camel has no component named '" + scheme + "'. Use tui_catalog_doc to find the right component, " + "then send again with its scheme."; @@ -1507,40 +1506,6 @@ class TuiToolRegistry { + endpoint.substring(colon) + "'."; } - /** Well-known names people use for a protocol that differ from the Camel component name. */ - private static final Map<String, List<String>> SCHEME_ALIASES = Map.of( - "mqtt", List.of("paho-mqtt5", "paho"), - "mqtt5", List.of("paho-mqtt5"), - "rabbitmq", List.of("spring-rabbitmq"), - "amq", List.of("activemq", "jms"), - "rest", List.of("rest", "platform-http", "http"), - "s3", List.of("aws2-s3"), - "sqs", List.of("aws2-sqs"), - "sns", List.of("aws2-sns"), - "pubsub", List.of("google-pubsub"), - "servicebus", List.of("azure-servicebus")); - - /** - * Catalog component names that resemble the scheme: known aliases first, then names containing the scheme (or - * contained in it), at most five. - */ - static List<String> suggestComponents(String scheme, List<String> componentNames) { - List<String> result = new ArrayList<>(); - for (String alias : SCHEME_ALIASES.getOrDefault(scheme, List.of())) { - if (componentNames.contains(alias) && !result.contains(alias)) { - result.add(alias); - } - } - if (scheme.length() >= 3) { - for (String name : componentNames) { - if ((name.contains(scheme) || scheme.contains(name)) && !name.equals(scheme) && !result.contains(name)) { - result.add(name); - } - } - } - return result.size() > 5 ? result.subList(0, 5) : result; - } - private String callExecuteSql(Map<String, Object> args) { String query = (String) args.get("query"); if (query == null || query.isBlank()) { @@ -1818,7 +1783,7 @@ class TuiToolRegistry { return buildComponentDocJson(cm, lowerFilter, includeOptions, doc); } if (kind != null) { - return "{\"error\": \"Component not found: " + name + "\"}"; + return notFound("Component", name, catalog.suggestComponentNames(name, 5)); } } if (kind == null || "dataformat".equals(kind)) { @@ -1828,7 +1793,7 @@ class TuiToolRegistry { return buildDataFormatDocJson(dm, lowerFilter, includeOptions, doc); } if (kind != null) { - return "{\"error\": \"Data format not found: " + name + "\"}"; + return notFound("Data format", name, catalog.suggestDataFormatNames(name, 5)); } } if (kind == null || "language".equals(kind)) { @@ -1838,7 +1803,7 @@ class TuiToolRegistry { return buildLanguageDocJson(lm, lowerFilter, includeOptions, doc); } if (kind != null) { - return "{\"error\": \"Language not found: " + name + "\"}"; + return notFound("Language", name, catalog.suggestLanguageNames(name, 5)); } } if (kind == null || "eip".equals(kind)) { @@ -1851,7 +1816,23 @@ class TuiToolRegistry { return "{\"error\": \"EIP not found: " + name + "\"}"; } } - return "{\"error\": \"Artifact not found: " + name + "\"}"; + List<String> suggestions = new ArrayList<>(catalog.suggestComponentNames(name, 5)); + suggestions.addAll(catalog.suggestDataFormatNames(name, 3)); + suggestions.addAll(catalog.suggestLanguageNames(name, 3)); + return notFound("Artifact", name, suggestions); + } + + /** + * Error for a catalog lookup that found nothing, with the names the catalog suggests for the term (a protocol or + * product name such as mqtt or s3) so the next call can use one of them. + */ + private static String notFound(String kind, String name, List<String> suggestions) { + JsonObject error = new JsonObject(); + error.put("error", kind + " not found: " + name); + if (!suggestions.isEmpty()) { + error.put("suggestions", new JsonArray(suggestions)); + } + return error.toJson(); } @SuppressWarnings("unchecked") diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistrySuggestComponentsTest.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistrySuggestComponentsTest.java deleted file mode 100644 index 95e48a617124..000000000000 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiToolRegistrySuggestComponentsTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.dsl.jbang.core.commands.tui; - -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class TuiToolRegistrySuggestComponentsTest { - - private static final List<String> NAMES = List.of("activemq", "aws2-s3", "aws2-sqs", "http", "jms", "kafka", - "paho", "paho-mqtt5", "platform-http", "rest", "spring-rabbitmq", "timer"); - - @Test - void knownAliasesComeFirst() { - assertEquals(List.of("paho-mqtt5", "paho"), TuiToolRegistry.suggestComponents("mqtt", NAMES)); - assertEquals(List.of("spring-rabbitmq"), TuiToolRegistry.suggestComponents("rabbitmq", NAMES)); - assertEquals(List.of("aws2-s3"), TuiToolRegistry.suggestComponents("s3", NAMES)); - } - - @Test - void substringMatchesFillInWhenThereIsNoAlias() { - assertEquals(List.of("http"), TuiToolRegistry.suggestComponents("https", NAMES)); - assertEquals(List.of("paho", "paho-mqtt5"), TuiToolRegistry.suggestComponents("paho-mqtt", NAMES)); - assertTrue(TuiToolRegistry.suggestComponents("xyz", NAMES).isEmpty()); - // very short schemes would match too much, so they only get alias hits - assertTrue(TuiToolRegistry.suggestComponents("mq", NAMES).isEmpty()); - } -} diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java index 6be3dfa1237a..7fde90d74c5f 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java @@ -859,6 +859,12 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo { model.setFirstVersion(firstVersion); } + // aliases (protocol or product names people use for the component) come from @Metadata on the endpoint class + Metadata endpointMetadata = endpointClassElement.getAnnotation(Metadata.class); + if (endpointMetadata != null && endpointMetadata.aliases().length > 0) { + model.setAliases(new ArrayList<>(Arrays.asList(endpointMetadata.aliases()))); + } + model.setDescription(project.getDescription()); model.setGroupId(project.getGroupId()); model.setArtifactId(project.getArtifactId());
