This is an automated email from the ASF dual-hosted git repository. squakez pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 781cea14e3caede73c65c4db614a4abeb142e331 Author: Pasquale Congiusti <[email protected]> AuthorDate: Sat Jun 6 09:53:49 2026 +0200 feat: enable Camel custom components * Removed the limitation which was forcing the presence of the dependency in the catalog * Added a unit test providing a custom component * Added documentation to explain how to enable custom component. Closes #6644 --- .../ROOT/pages/configuration/dependencies.adoc | 42 +++++++++++++++++- e2e/common/misc/custom_component_test.go | 51 ++++++++++++++++++++++ e2e/common/misc/files/custom-echo-component.yaml | 11 +++++ pkg/builder/quarkus.go | 8 +++- pkg/util/source/inspector.go | 5 ++- 5 files changed, 113 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/configuration/dependencies.adoc b/docs/modules/ROOT/pages/configuration/dependencies.adoc index 386b20af8..c02c00fa4 100644 --- a/docs/modules/ROOT/pages/configuration/dependencies.adoc +++ b/docs/modules/ROOT/pages/configuration/dependencies.adoc @@ -46,7 +46,7 @@ You can explicitly add dependency using the `-d` flag (short name of the flag `- kamel run -d mvn:com.google.guava:guava:26.0-jre -d camel:http Integration.java ``` -With that command you will add a dependency of Guava and the Camel HTTP component. This feature can also be disabled if needed (although we discourage you from doing it) by disabling the _dependencies_ trait (`-t dependencies.enabled=false`). +With that command you will add a dependency of Guava and the Camel HTTP component. [[dependencies-kind]] == Kind of dependencies @@ -95,6 +95,46 @@ azure:user/repo/version The `version` can be omitted when you are willing to use the `main` branch. Otherwise it will represent the branch or tag used in the project repo. +[[custom-components]] +== Custom components + +When you are running advanced Integrations you may find useful the possibility to create a Camel custom component. A custom component is like any other embedded component, but, it is not available out of the box in the catalog, hence, you will need to provide the dependency explicitly. For example, given the following route configured to run an `echo:test` endpoint: + +```yaml +- route: + from: + uri: timer:yaml + parameters: + period: "1000" + steps: + - setBody: + simple: Hello Camel from ${routeId} + - to: + uri: echo:test + - log: ${body} +``` +you will need to include the custom component dependency (which, must be developed as expected by Camel framework) and a few more build time properties (only for Quarkus runtime). You can see the example we've provided in the https://github.com/squakez/camel-echo[`camel-echo` component Github repository]. What's important is that the dependency is published and your Maven repository can reach it. In the example below we're using the Jitpack dependency: + +```bash +kamel run custom-echo-component-route.yaml \ + -d github:squakez/camel-echo \ + -t builder.properties=quarkus.index-dependency.camel-echo.group-id=com.github.squakez \ + -t builder.properties=quarkus.index-dependency.camel-echo.artifact-id=camel-echo \ + -t builder.properties=quarkus.camel.service.discovery.include-patterns=META-INF/services/org/apache/camel/echo +``` + +This is required to instruct the compiler about the Jandex index. You can replace the `custom-echo` with the name of your dependency and you will need to provide the same SPI also provided in your custom component dependency. + +NOTE: if you're running Camel Main or Spring Boot runtimes (for example, via Git repository build) you won't need to provide any build time property because those runtimes leverage the existing SPI provided in the dependency. + +The above approach is the simplest one you can use and it will work if your custom component has not any particular Quarkus requirement or you don't need to run in Quarkus native mode. If that is the case or you want to avoid proving the builder properties, then, you will have to write a Quarkus extension and provide just the runtime extension dependency to your application. Take as example the https://github.com/squakez/camel-echo-quarkus-extension[`camel-echo-quarkus-extenstion` Github [...] + +```bash +kamel run custom-echo-component-route.yaml -d mvn:com.example.camel:camel-quarkus-echo:1.0.0 +``` + +During the compilation, the runtime dependency will instruct the compiler how to properly use the Camel custom component it refers. + [[dependencies-dynamic]] == Dynamic URIs diff --git a/e2e/common/misc/custom_component_test.go b/e2e/common/misc/custom_component_test.go new file mode 100644 index 000000000..6810f520d --- /dev/null +++ b/e2e/common/misc/custom_component_test.go @@ -0,0 +1,51 @@ +//go:build integration +// +build integration + +// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "integration" + +/* +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 common + +import ( + "context" + "testing" + + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + + . "github.com/apache/camel-k/v2/e2e/support" + v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1" +) + +func TestCustomComponent(t *testing.T) { + t.Parallel() + WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) { + name := RandomizedSuffixName("customcomp") + g.Expect(KamelRun(t, ctx, ns, + "files/custom-echo-component.yaml", + "-d", "github:squakez/camel-echo", + "-t", "builder.properties=quarkus.index-dependency.camel-echo.group-id=com.github.squakez", + "-t", "builder.properties=quarkus.index-dependency.camel-echo.artifact-id=camel-echo", + "-t", "builder.properties=quarkus.camel.service.discovery.include-patterns=META-INF/services/org/apache/camel/echo", + "--name", name).Execute()).To(Succeed()) + g.Eventually(IntegrationConditionStatus(t, ctx, ns, name, v1.IntegrationConditionReady), TestTimeoutMedium).Should(Equal(corev1.ConditionTrue)) + g.Eventually(IntegrationLogs(t, ctx, ns, name)).Should(ContainSubstring("echo: Hello Camel")) + }) +} diff --git a/e2e/common/misc/files/custom-echo-component.yaml b/e2e/common/misc/files/custom-echo-component.yaml new file mode 100644 index 000000000..b539d0d06 --- /dev/null +++ b/e2e/common/misc/files/custom-echo-component.yaml @@ -0,0 +1,11 @@ +- route: + from: + uri: timer:yaml + parameters: + period: "1000" + steps: + - setBody: + simple: Hello Camel from ${routeId} + - to: + uri: echo:test + - log: ${body} diff --git a/pkg/builder/quarkus.go b/pkg/builder/quarkus.go index 707319c5d..69a747f7d 100644 --- a/pkg/builder/quarkus.go +++ b/pkg/builder/quarkus.go @@ -263,7 +263,13 @@ func computeApplicationProperties(appPropertiesPath string, applicationPropertie applicationProperties["quarkus.camel.routes-discovery.enabled"] = boolean.FalseString // required for to resolve data type transformers at runtime with service discovery // the different Camel runtimes use different resource paths for the service lookup - applicationProperties["quarkus.camel.service.discovery.include-patterns"] = "META-INF/services/org/apache/camel/datatype/converter/*,META-INF/services/org/apache/camel/datatype/transformer/*,META-INF/services/org/apache/camel/transformer/*" + serviceDiscoveryVar := "quarkus.camel.service.discovery.include-patterns" + serviceDiscoveryProp := "META-INF/services/org/apache/camel/datatype/converter/*,META-INF/services/org/apache/camel/datatype/transformer/*,META-INF/services/org/apache/camel/transformer/*" + if applicationProperties[serviceDiscoveryVar] == "" { + applicationProperties[serviceDiscoveryVar] = serviceDiscoveryProp + } else { + applicationProperties[serviceDiscoveryVar] = applicationProperties[serviceDiscoveryVar] + "," + serviceDiscoveryProp + } // Workaround to prevent JS runtime errors, see https://github.com/apache/camel-quarkus/issues/5678 applicationProperties["quarkus.class-loading.parent-first-artifacts"] = "org.graalvm.regex:regex" defer f.Close() diff --git a/pkg/util/source/inspector.go b/pkg/util/source/inspector.go index 04797a1da..654e6e986 100644 --- a/pkg/util/source/inspector.go +++ b/pkg/util/source/inspector.go @@ -367,8 +367,9 @@ func (i *baseInspector) addDependencies(uri string, meta *Metadata, consumer boo candidateComp, scheme := i.catalog.DecodeComponent(uri) if candidateComp == nil || scheme == nil { - return fmt.Errorf("component not found for uri %q in camel catalog runtime version %s", - uri, i.catalog.GetRuntimeVersion()) + // just ignore as it could be a custom component which has to be + // provided by the final user + return nil } meta.AddDependency(candidateComp.GetDependencyID())
