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 dd1b5087c99 CAMEL-21641: Java DSL: RouteBuilder to support inlined
component conf… (#16888)
dd1b5087c99 is described below
commit dd1b5087c99dbbfa5dd30fc044bfe25d72fa9797
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Jan 23 07:28:57 2025 +0100
CAMEL-21641: Java DSL: RouteBuilder to support inlined component conf…
(#16888)
* CAMEL-21641: Java DSL: RouteBuilder to support inlined component
configurers using lambda style for low-code usage in Java to be in a single
soure file, that supports jbang exporting (stub)
---
.../org/apache/camel/builder/RouteBuilder.java | 99 ++++++++++++++++++++++
.../component/log/LogComponentCustomizeTest.java | 53 ++++++++++++
.../log/LogComponentCustomizeThreeTest.java | 74 ++++++++++++++++
.../log/LogComponentCustomizeTwoTest.java | 53 ++++++++++++
.../camel/util/function/ThrowingFunction.java | 2 +-
.../{ThrowingFunction.java => VoidFunction.java} | 15 ++--
6 files changed, 286 insertions(+), 10 deletions(-)
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
index e20d5b77289..0841e8465cc 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
+import org.apache.camel.Component;
import org.apache.camel.Endpoint;
import org.apache.camel.ErrorHandlerFactory;
import org.apache.camel.Ordered;
@@ -51,6 +52,8 @@ import
org.apache.camel.model.errorhandler.RefErrorHandlerDefinition;
import org.apache.camel.model.rest.RestConfigurationDefinition;
import org.apache.camel.model.rest.RestDefinition;
import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.Language;
import org.apache.camel.spi.OnCamelContextEvent;
import org.apache.camel.spi.PropertiesComponent;
import org.apache.camel.spi.Resource;
@@ -62,6 +65,7 @@ import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.function.ThrowingBiConsumer;
import org.apache.camel.util.function.ThrowingConsumer;
+import org.apache.camel.util.function.VoidFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -204,6 +208,101 @@ public abstract class RouteBuilder extends BuilderSupport
implements RoutesBuild
// noop
}
+ /**
+ * To customize a given component / data format / language / service using
Java lambda style. This is only intended
+ * for low-code integrations where you have a minimal set of code and
files, to make it easy and quick to
+ * customize/configure components directly in a single {@link
RouteBuilder} class.
+ *
+ * @param type the type such as a component FQN class name
+ * @param code callback for customizing the component instance (if present)
+ */
+ public <T> void customize(Class<T> type, VoidFunction<T> code) throws
Exception {
+ ObjectHelper.notNull(type, "type", this);
+ ObjectHelper.notNull(code, "code", this);
+
+ // custom may be stored directly in registry so lookup first here
+ T obj = getContext().getRegistry().findSingleByType(type);
+ // try component / dataformat / language
+ if (obj == null && Component.class.isAssignableFrom(type)) {
+ org.apache.camel.spi.annotations.Component ann
+ =
type.getAnnotation(org.apache.camel.spi.annotations.Component.class);
+ if (ann != null) {
+ String name = ann.value();
+ // just grab first scheme name if the component has scheme
alias (eg http,https)
+ if (name.contains(",")) {
+ name = StringHelper.before(name, ",");
+ }
+ // do not auto-start component as we need to configure it first
+ obj = (T) getCamelContext().getComponent(name, true, false);
+ }
+ if (obj != null &&
!type.getName().equals("org.apache.camel.component.stub.StubComponent")
+ &&
"org.apache.camel.component.stub.StubComponent".equals(obj.getClass().getName()))
{
+ // if we run in stub mode then we can't apply the
configuration as we stubbed the actual component
+ obj = null;
+ }
+ }
+ if (obj == null && DataFormat.class.isAssignableFrom(type)) {
+ // it's maybe a dataformat
+ org.apache.camel.spi.annotations.Dataformat ann
+ =
type.getAnnotation(org.apache.camel.spi.annotations.Dataformat.class);
+ if (ann != null) {
+ String name = ann.value();
+ obj = (T) getCamelContext().resolveDataFormat(name);
+ }
+ }
+ if (obj == null && Language.class.isAssignableFrom(type)) {
+ // it's maybe a dataformat
+ org.apache.camel.spi.annotations.Language ann
+ =
type.getAnnotation(org.apache.camel.spi.annotations.Language.class);
+ if (ann != null) {
+ String name = ann.value();
+ obj = (T) getCamelContext().resolveLanguage(name);
+ }
+ }
+ // it may be a special service
+ if (obj == null) {
+ obj = getContext().hasService(type);
+ }
+ if (obj != null) {
+ code.apply(obj);
+ }
+ }
+
+ /**
+ * To customize a given component / data format / language / service using
Java lambda style. This is only intended
+ * for low-code integrations where you have a minimal set of code and
files, to make it easy and quick to
+ * customize/configure components directly in a single {@link
RouteBuilder} class.
+ *
+ * @param name the name of the component / service
+ * @param type the type such as a component FQN class name
+ * @param code callback for customizing the component instance (if present)
+ */
+ public <T> void customize(String name, Class<T> type, VoidFunction<T>
code) throws Exception {
+ ObjectHelper.notNull(name, "name", this);
+ ObjectHelper.notNull(type, "type", this);
+ ObjectHelper.notNull(code, "code", this);
+
+ T obj = getContext().getRegistry().lookupByNameAndType(name, type);
+ if (obj == null && Component.class.isAssignableFrom(type)) {
+ // do not auto-start component as we need to configure it first
+ obj = (T) getCamelContext().getComponent(name, true, false);
+ if (obj != null && !"stub".equals(name)
+ &&
"org.apache.camel.component.stub.StubComponent".equals(obj.getClass().getName()))
{
+ // if we run in stub mode then we can't apply the
configuration as we stubbed the actual component
+ obj = null;
+ }
+ }
+ if (obj == null && DataFormat.class.isAssignableFrom(type)) {
+ obj = (T) getCamelContext().resolveDataFormat(name);
+ }
+ if (obj == null && Language.class.isAssignableFrom(type)) {
+ obj = (T) getCamelContext().resolveLanguage(name);
+ }
+ if (obj != null) {
+ code.apply(obj);
+ }
+ }
+
/**
* Binds the bean to the repository (if possible).
*
diff --git
a/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeTest.java
b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeTest.java
new file mode 100644
index 00000000000..1d0bda20951
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.component.log;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class LogComponentCustomizeTest extends ContextTestSupport {
+
+ private final LogCustomFormatterTest.TestExchangeFormatter formatter = new
LogCustomFormatterTest.TestExchangeFormatter();
+
+ @Test
+ public void testCustomize() throws Exception {
+ Assertions.assertEquals(0, formatter.getCounter());
+
+ template.sendBody("direct:start", "Hello World");
+
+ Assertions.assertEquals(1, formatter.getCounter());
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ // customize the log component using java lambda style (using
the default name)
+ customize(LogComponent.class, l -> {
+ l.setExchangeFormatter(formatter);
+ });
+
+ from("direct:start")
+ .to("log:foo");
+ }
+ };
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeThreeTest.java
b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeThreeTest.java
new file mode 100644
index 00000000000..18b8361ae65
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeThreeTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.component.log;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Handler;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.support.service.ServiceSupport;
+import org.junit.jupiter.api.Test;
+
+public class LogComponentCustomizeThreeTest extends ContextTestSupport {
+
+ private final MyService myService = new MyService();
+
+ @Test
+ public void testCustomize() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ context.addService(myService);
+
+ customize(MyService.class, s -> {
+ s.setPrefix("Hello");
+ });
+
+ from("direct:start")
+ .bean(myService)
+ .to("mock:result");
+ }
+ };
+ }
+
+ private static final class MyService extends ServiceSupport {
+
+ private String prefix;
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Handler
+ public String hello(String body) {
+ return prefix + " " + body;
+ }
+ }
+}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeTwoTest.java
b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeTwoTest.java
new file mode 100644
index 00000000000..e89495bf787
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/component/log/LogComponentCustomizeTwoTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.component.log;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class LogComponentCustomizeTwoTest extends ContextTestSupport {
+
+ private final LogCustomFormatterTest.TestExchangeFormatter formatter = new
LogCustomFormatterTest.TestExchangeFormatter();
+
+ @Test
+ public void testCustomize() throws Exception {
+ Assertions.assertEquals(0, formatter.getCounter());
+
+ template.sendBody("direct:start", "Hello World");
+
+ Assertions.assertEquals(1, formatter.getCounter());
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ // customize the log component using java lambda style
+ customize("log", LogComponent.class, l -> {
+ l.setExchangeFormatter(formatter);
+ });
+
+ from("direct:start")
+ .to("log:foo");
+ }
+ };
+ }
+}
diff --git
a/core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
b/core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
index eaf005ad08d..debb4cdede1 100644
---
a/core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
+++
b/core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
@@ -17,7 +17,7 @@
package org.apache.camel.util.function;
/**
- * Represents a function that accepts a single arguments, produces a result
and may thrown an exception.
+ * Represents a function that accepts a single arguments, produces a result
and may throw an exception.
*
* @param <I> the type of the input of the function
* @param <R> the type of the result of the function
diff --git
a/core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
b/core/camel-util/src/main/java/org/apache/camel/util/function/VoidFunction.java
similarity index 65%
copy from
core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
copy to
core/camel-util/src/main/java/org/apache/camel/util/function/VoidFunction.java
index eaf005ad08d..c41b591c850 100644
---
a/core/camel-util/src/main/java/org/apache/camel/util/function/ThrowingFunction.java
+++
b/core/camel-util/src/main/java/org/apache/camel/util/function/VoidFunction.java
@@ -17,22 +17,19 @@
package org.apache.camel.util.function;
/**
- * Represents a function that accepts a single arguments, produces a result
and may thrown an exception.
+ * Represents a void function that accepts a single arguments.
*
* @param <I> the type of the input of the function
- * @param <R> the type of the result of the function
- * @param <T> the type of the exception the accept method may throw
*
* @see java.util.function.Function
*/
@FunctionalInterface
-public interface ThrowingFunction<I, R, T extends Throwable> {
+public interface VoidFunction<I> {
+
/**
- * Applies this function to the given argument, potentially throwing an
exception.
+ * Applies this function to the given argument.
*
- * @param in the function argument
- * @return the function result
- * @throws T the exception that may be thrown
+ * @param in the function argument
*/
- R apply(I in) throws T;
+ void apply(I in) throws Exception;
}