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 c9f60a3b559 CAMEL-21542: camel-jbang - Detect known beans from Java
imports (#16546)
c9f60a3b559 is described below
commit c9f60a3b559dc29934913c471b482b567187512f
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Dec 12 19:54:42 2024 +0100
CAMEL-21542: camel-jbang - Detect known beans from Java imports (#16546)
* CAMEL-21542: camel-jbang - Detect known beans from Java imports
---
.../org/apache/camel/spi/CompilePostProcessor.java | 4 +-
...PostProcessor.java => CompilePreProcessor.java} | 21 +++---
.../dsl/support/RouteBuilderLoaderSupport.java | 32 +++++++--
.../org/apache/camel/dsl/java/joor/Helper.java | 17 +++++
.../dsl/java/joor/JavaRoutesBuilderLoader.java | 7 ++
.../org/apache/camel/dsl/java/joor/DummyRoute.java | 3 +
.../java/joor/{DummyRoute.java => HelperTest.java} | 22 ++++--
.../camel/dsl/jbang/core/commands/ExportTest.java | 25 +++++++
.../src/test/resources/MyKafkaRepo.java} | 18 +++--
.../java/org/apache/camel/main/KameletMain.java | 4 ++
.../main/download/JavaKnownImportsDownloader.java | 78 ++++++++++++++++++++++
.../apache/camel/tooling/model/ArtifactModel.java | 4 ++
12 files changed, 205 insertions(+), 30 deletions(-)
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
b/core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
index 0f58bc2a75b..0024c25466a 100644
---
a/core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
+++
b/core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
@@ -19,11 +19,13 @@ package org.apache.camel.spi;
import org.apache.camel.CamelContext;
/**
- * Allows to plugin custom post processors that are processed after the DSL
has loaded the source and compiled into a
+ * Allows to plugin custom post-processors that are processed after the DSL
has loaded the source and compiled into a
* Java object.
* <p/>
* This is used to detect and handle {@link org.apache.camel.BindToRegistry}
and {@link org.apache.camel.Converter}
* classes.
+ *
+ * @see CompilePreProcessor
*/
public interface CompilePostProcessor {
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
b/core/camel-api/src/main/java/org/apache/camel/spi/CompilePreProcessor.java
similarity index 60%
copy from
core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
copy to
core/camel-api/src/main/java/org/apache/camel/spi/CompilePreProcessor.java
index 0f58bc2a75b..6214573a8d2 100644
---
a/core/camel-api/src/main/java/org/apache/camel/spi/CompilePostProcessor.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/CompilePreProcessor.java
@@ -19,27 +19,24 @@ package org.apache.camel.spi;
import org.apache.camel.CamelContext;
/**
- * Allows to plugin custom post processors that are processed after the DSL
has loaded the source and compiled into a
+ * Allows to plugin custom pre-processors that are processed before the DSL
has loaded the source and compiled into a
* Java object.
* <p/>
- * This is used to detect and handle {@link org.apache.camel.BindToRegistry}
and {@link org.apache.camel.Converter}
- * classes.
+ * This is used among others to detect imported classes that may need to be
downloaded into classloader to allow to
+ * compile the class.
+ *
+ * @see CompilePostProcessor
*/
-public interface CompilePostProcessor {
+public interface CompilePreProcessor {
/**
- * Invoked after the class has been compiled
+ * Invoked before the class has been compiled
*
* @param camelContext the camel context
* @param name the name of the resource/class
- * @param clazz the class
- * @param byteCode byte code that was compiled from the source as the
class (only supported on some DSLs)
- * @param instance the object created as instance of the class (if
any)
+ * @param code the source code of the class
* @throws Exception is thrown if error during post-processing
*/
- void postCompile(
- CamelContext camelContext, String name,
- Class<?> clazz, byte[] byteCode, Object instance)
- throws Exception;
+ void preCompile(CamelContext camelContext, String name, String code)
throws Exception;
}
diff --git
a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
index 2c3a8b237e6..1689b8d78f7 100644
---
a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
+++
b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/RouteBuilderLoaderSupport.java
@@ -32,6 +32,7 @@ import org.apache.camel.api.management.ManagedOperation;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.builder.RouteBuilderLifecycleStrategy;
import org.apache.camel.spi.CompilePostProcessor;
+import org.apache.camel.spi.CompilePreProcessor;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.RoutesBuilderLoader;
import org.apache.camel.spi.StartupStepRecorder;
@@ -42,6 +43,7 @@ import org.apache.camel.support.RoutesBuilderLoaderSupport;
*/
public abstract class RouteBuilderLoaderSupport extends
RoutesBuilderLoaderSupport {
private final String extension;
+ private final List<CompilePreProcessor> compilePreProcessors = new
ArrayList<>();
private final List<CompilePostProcessor> compilePostProcessors = new
ArrayList<>();
private StartupStepRecorder recorder;
private SourceLoader sourceLoader = new DefaultSourceLoader();
@@ -62,6 +64,21 @@ public abstract class RouteBuilderLoaderSupport extends
RoutesBuilderLoaderSuppo
return super.isSupportedExtension(extension);
}
+ /**
+ * Gets the registered {@link CompilePreProcessor}.
+ */
+ public List<CompilePreProcessor> getCompilePreProcessors() {
+ return compilePreProcessors;
+ }
+
+ /**
+ * Add a custom {@link CompilePreProcessor} to handle specific
pre-processing before compiling the source into a
+ * Java object.
+ */
+ public void addCompilePreProcessor(CompilePreProcessor preProcessor) {
+ this.compilePreProcessors.add(preProcessor);
+ }
+
/**
* Gets the registered {@link CompilePostProcessor}.
*/
@@ -91,11 +108,18 @@ public abstract class RouteBuilderLoaderSupport extends
RoutesBuilderLoaderSuppo
super.doStart();
if (getCamelContext() != null) {
- // discover optional compile post-processors to be used
- Set<CompilePostProcessor> pres =
getCamelContext().getRegistry().findByType(CompilePostProcessor.class);
+ // discover optional compile pre-processors to be used
+ Set<CompilePreProcessor> pres =
getCamelContext().getRegistry().findByType(CompilePreProcessor.class);
if (pres != null && !pres.isEmpty()) {
- for (CompilePostProcessor pre : pres) {
- addCompilePostProcessor(pre);
+ for (CompilePreProcessor pre : pres) {
+ addCompilePreProcessor(pre);
+ }
+ }
+ // discover optional compile post-processors to be used
+ Set<CompilePostProcessor> posts =
getCamelContext().getRegistry().findByType(CompilePostProcessor.class);
+ if (posts != null && !posts.isEmpty()) {
+ for (CompilePostProcessor post : posts) {
+ addCompilePostProcessor(post);
}
}
// discover a special source loader to be used
diff --git
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/Helper.java
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/Helper.java
index 70ac21efe2d..ced703d4677 100644
---
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/Helper.java
+++
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/Helper.java
@@ -16,6 +16,8 @@
*/
package org.apache.camel.dsl.java.joor;
+import java.util.ArrayList;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -31,6 +33,9 @@ public final class Helper {
private static final Pattern PACKAGE_PATTERN = Pattern.compile(
"^\\s*package\\s+([a-zA-Z][.\\w]*)\\s*;.*$", Pattern.MULTILINE);
+ private static final Pattern IMPORT_PATTERN = Pattern.compile(
+ "^import\\s+([a-zA-Z][.\\w]*)\\s*;", Pattern.MULTILINE);
+
private Helper() {
}
@@ -52,4 +57,16 @@ public final class Helper {
? matcher.group(1) + "." + name
: name;
}
+
+ public static List<String> determineImports(String content) {
+ List<String> answer = new ArrayList<>();
+ final Matcher matcher = IMPORT_PATTERN.matcher(content);
+ while (matcher.find()) {
+ String imp = matcher.group(1);
+ imp = imp.trim();
+ answer.add(imp);
+ }
+ return answer;
+ }
+
}
diff --git
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
index d805e559cd7..bed3da758b0 100644
---
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
+++
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
@@ -42,6 +42,7 @@ import org.apache.camel.language.joor.CompilationUnit;
import org.apache.camel.language.joor.JavaJoorClassLoader;
import org.apache.camel.language.joor.MultiCompile;
import org.apache.camel.spi.CompilePostProcessor;
+import org.apache.camel.spi.CompilePreProcessor;
import org.apache.camel.spi.CompileStrategy;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.ResourceAware;
@@ -189,6 +190,12 @@ public class JavaRoutesBuilderLoader extends
ExtendedRouteBuilderLoaderSupport {
}
String content = IOHelper.loadText(is);
String name = determineName(resource, content);
+
+ // allow any pre-processing
+ for (CompilePreProcessor pre : getCompilePreProcessors()) {
+ pre.preCompile(getCamelContext(), name, content);
+ }
+
unit.addClass(name, content);
// ensure class gets recompiled
classLoader.removeClass(name);
diff --git
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
b/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
index eda67fb303a..95fd1a3f8e8 100644
---
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
+++
b/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
@@ -16,10 +16,13 @@
*/
package org.apache.camel.dsl.java.joor;
+import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
public class DummyRoute extends RouteBuilder {
+ private CamelContext camelContext;
+
@Override
public void configure() throws Exception {
from("direct:dummy")
diff --git
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
b/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/HelperTest.java
similarity index 54%
copy from
dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
copy to
dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/HelperTest.java
index eda67fb303a..c5ff4da9e60 100644
---
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
+++
b/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/HelperTest.java
@@ -16,13 +16,23 @@
*/
package org.apache.camel.dsl.java.joor;
-import org.apache.camel.builder.RouteBuilder;
+import java.io.FileInputStream;
+import java.util.Collections;
+import java.util.List;
-public class DummyRoute extends RouteBuilder {
+import org.apache.camel.util.IOHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
- @Override
- public void configure() throws Exception {
- from("direct:dummy")
- .to("mock:end");
+public class HelperTest {
+
+ @Test
+ public void testImports() throws Exception {
+ List<String> list = Helper.determineImports(
+ IOHelper.loadText(new
FileInputStream("src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java")));
+ Collections.sort(list);
+ Assertions.assertEquals(2, list.size());
+ Assertions.assertEquals("org.apache.camel.CamelContext", list.get(0));
+ Assertions.assertEquals("org.apache.camel.builder.RouteBuilder",
list.get(1));
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
index 0bacb326e45..bd50898cd9f 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ExportTest.java
@@ -418,6 +418,31 @@ class ExportTest {
}
}
+ @ParameterizedTest
+ @MethodSource("runtimeProvider")
+ public void shouldExportKafka(RuntimeType rt) throws Exception {
+ Export command = createCommand(rt, new String[] {
"src/test/resources/MyKafkaRepo.java" },
+ "--gav=examples:route:1.0.0", "--dir=" + workingDir,
"--quiet");
+ int exit = command.doCall();
+
+ Assertions.assertEquals(0, exit);
+ Model model = readMavenModel();
+ Assertions.assertEquals("examples", model.getGroupId());
+ Assertions.assertEquals("route", model.getArtifactId());
+ Assertions.assertEquals("1.0.0", model.getVersion());
+
+ if (rt == RuntimeType.main) {
+ Assertions.assertTrue(containsDependency(model.getDependencies(),
"org.apache.camel", "camel-kafka", null));
+ } else if (rt == RuntimeType.springBoot) {
+ Assertions.assertTrue(
+ containsDependency(model.getDependencies(),
"org.apache.camel.springboot", "camel-kafka-starter",
+ null));
+ } else if (rt == RuntimeType.quarkus) {
+ Assertions.assertTrue(
+ containsDependency(model.getDependencies(),
"org.apache.camel.quarkus", "camel-quarkus-kafka", null));
+ }
+ }
+
@ParameterizedTest
@MethodSource("runtimeProvider")
public void shouldExportSecretBean(RuntimeType rt) throws Exception {
diff --git
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/MyKafkaRepo.java
similarity index 66%
copy from
dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
copy to dsl/camel-jbang/camel-jbang-core/src/test/resources/MyKafkaRepo.java
index eda67fb303a..3a2792d5013 100644
---
a/dsl/camel-java-joor-dsl/src/test/java/org/apache/camel/dsl/java/joor/DummyRoute.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/MyKafkaRepo.java
@@ -14,15 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.dsl.java.joor;
-import org.apache.camel.builder.RouteBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
-public class DummyRoute extends RouteBuilder {
+import org.apache.camel.processor.idempotent.kafka.KafkaIdempotentRepository;
- @Override
- public void configure() throws Exception {
- from("direct:dummy")
- .to("mock:end");
+@Component
+public class MyKafkaRepo {
+
+ @Bean
+ public KafkaIdempotentRepository createRepo() {
+ KafkaIdempotentRepository repo = new
KafkaIdempotentRepository("myrepo", "localhost:9091");
+ return repo;
}
}
+
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index 3b24e69b4c3..326fe34a9b2 100644
---
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -61,6 +61,7 @@ import org.apache.camel.main.download.DownloadListener;
import org.apache.camel.main.download.DownloadModelineParser;
import org.apache.camel.main.download.ExportPropertiesParser;
import org.apache.camel.main.download.ExportTypeConverter;
+import org.apache.camel.main.download.JavaKnownImportsDownloader;
import org.apache.camel.main.download.KameletAutowiredLifecycleStrategy;
import org.apache.camel.main.download.KameletMainInjector;
import org.apache.camel.main.download.KnownDependenciesResolver;
@@ -495,6 +496,9 @@ public class KameletMain extends MainCommandLineSupport {
boolean lazyBean =
"true".equals(getInitialProperties().get(getInstanceType() + ".lazyBean"));
new AnnotationDependencyInjection(answer, lazyBean);
+ // add support for automatic downloaded needed JARs from java imports
+ new JavaKnownImportsDownloader(answer);
+
if (!silent) {
// silent should not include cli-connector
// setup cli-connector if not already done
diff --git
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/JavaKnownImportsDownloader.java
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/JavaKnownImportsDownloader.java
new file mode 100644
index 00000000000..914f0472eae
--- /dev/null
+++
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/JavaKnownImportsDownloader.java
@@ -0,0 +1,78 @@
+/*
+ * 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.main.download;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.spi.CompilePreProcessor;
+import org.apache.camel.tooling.model.PojoBeanModel;
+
+/**
+ * Java DSL that can detect known classes from the java imports and download
JARs
+ */
+public class JavaKnownImportsDownloader implements CompilePreProcessor {
+
+ private static final Pattern IMPORT_PATTERN = Pattern.compile(
+ "^import\\s+([a-zA-Z][.\\w]*)\\s*;", Pattern.MULTILINE);
+
+ private final CamelCatalog catalog = new DefaultCamelCatalog();
+ private final DependencyDownloader downloader;
+
+ public JavaKnownImportsDownloader(CamelContext camelContext) {
+ this.downloader = camelContext.hasService(DependencyDownloader.class);
+ camelContext.getRegistry().bind("JavaJoorKnownImportsDownloader",
this);
+ }
+
+ @Override
+ public void preCompile(CamelContext camelContext, String name, String
code) throws Exception {
+ List<String> imports = determineImports(code);
+ for (String imp : imports) {
+ // is this a known bean then we can determine the dependency
+ for (String n : catalog.findBeansNames()) {
+ PojoBeanModel m = catalog.pojoBeanModel(n);
+ if (m != null && imp.equals(m.getJavaType())) {
+ downloadLoader(m.getGroupId(), m.getArtifactId(),
m.getVersion());
+ break;
+ }
+ }
+ }
+ }
+
+ private void downloadLoader(String groupId, String artifactId, String
version) {
+ if (!downloader.alreadyOnClasspath(groupId, artifactId, version)) {
+ downloader.downloadDependency(groupId, artifactId, version);
+ }
+ }
+
+ private static List<String> determineImports(String content) {
+ List<String> answer = new ArrayList<>();
+ final Matcher matcher = IMPORT_PATTERN.matcher(content);
+ while (matcher.find()) {
+ String imp = matcher.group(1);
+ imp = imp.trim();
+ answer.add(imp);
+ }
+ return answer;
+ }
+
+}
diff --git
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ArtifactModel.java
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ArtifactModel.java
index 39bff116009..b7a6c3a2ec7 100644
---
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ArtifactModel.java
+++
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ArtifactModel.java
@@ -51,4 +51,8 @@ public abstract class ArtifactModel<O extends
BaseOptionModel> extends BaseModel
this.version = version;
}
+ public String toGav() {
+ return groupId + ":" + artifactId + ":" + version;
+ }
+
}