You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/06/01 14:23:44 UTC

[camel] branch main updated: CAMEL-18151: camel-jbang - Export command for quarkus

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 5af4888658d CAMEL-18151: camel-jbang - Export command for quarkus
5af4888658d is described below

commit 5af4888658d30365b16c096f05cc5e3ee5e863b5
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Jun 1 16:23:20 2022 +0200

    CAMEL-18151: camel-jbang - Export command for quarkus
---
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   3 +-
 .../dsl/jbang/core/commands/ExportQuarkus.java     | 324 +++++++++++++++++++++
 .../src/main/resources/templates/quarkus-main.tmpl |  13 +
 .../src/main/resources/templates/quarkus-pom.tmpl  | 142 +++++++++
 4 files changed, 481 insertions(+), 1 deletion(-)

diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 84976957b85..40f3c67aadf 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -46,7 +46,8 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("manifests", new CommandLine(new Manifest(main)))
                         .addSubcommand("image", new CommandLine(new Image(main))))
                 .addSubcommand("export", new CommandLine(new Export(main))
-                        .addSubcommand("spring-boot", new CommandLine(new ExportSpringBoot(main))))
+                        .addSubcommand("spring-boot", new CommandLine(new ExportSpringBoot(main)))
+                        .addSubcommand("quarkus", new CommandLine(new ExportQuarkus(main))))
                 .addSubcommand("deploy", new CommandLine(new Deploy(main)))
                 .addSubcommand("undeploy", new CommandLine(new Undeploy(main)));
         /* // TODO: do not show commands that are deprecated and to be either removed or reworked
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java
new file mode 100644
index 00000000000..d331b19f6d8
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java
@@ -0,0 +1,324 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.apache.camel.main.MavenGav;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.OrderedProperties;
+import org.apache.camel.util.StringHelper;
+import org.apache.commons.io.FileUtils;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "quarkus", description = "Export as Quarkus project")
+class ExportQuarkus extends CamelCommand {
+
+    private static final String BUILD_DIR = ".camel-jbang/work";
+
+    private static final String[] SETTINGS_PROP_SOURCE_KEYS = new String[] {
+            "camel.main.routesIncludePattern",
+            "camel.component.properties.location",
+            "camel.component.kamelet.location",
+            "camel.jbang.classpathFiles"
+    };
+
+    @CommandLine.Option(names = { "--gav" }, description = "The Maven group:artifact:version", required = true)
+    private String gav;
+
+    @CommandLine.Option(names = { "--main-classname" },
+                        description = "The class name of the Quarkus application class",
+                        defaultValue = "CamelApplication")
+    private String mainClassname;
+
+    @CommandLine.Option(names = { "--java-version" }, description = "Java version (11 or 17)",
+                        defaultValue = "11")
+    private String javaVersion;
+
+    @CommandLine.Option(names = { "--quarkus-version" }, description = "Quarkus version",
+                        defaultValue = "2.9.2.Final")
+    private String quarkusVersion;
+
+    @CommandLine.Option(names = { "--kamelets-version" }, description = "Apache Camel Kamelets version",
+                        defaultValue = "0.8.1")
+    private String kameletsVersion;
+
+    @CommandLine.Option(names = { "-dir", "--directory" }, description = "Directory where the project will be exported",
+                        defaultValue = ".")
+    private String exportDir;
+
+    @CommandLine.Option(names = { "--fresh" }, description = "Make sure we use fresh (i.e. non-cached) resources")
+    private boolean fresh;
+
+    public ExportQuarkus(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        String[] ids = gav.split(":");
+        if (ids.length != 3) {
+            System.out.println("--gav must be in syntax: groupId:artifactId:version");
+            return 1;
+        }
+
+        File profile = new File(getProfile() + ".properties");
+
+        // the settings file has information what to export
+        File settings = new File(Run.WORK_DIR + "/" + Run.RUN_SETTINGS_FILE);
+        if (fresh || !settings.exists()) {
+            // allow to automatic build
+            System.out.println("Generating fresh run data");
+            int silent = runSilently();
+            if (silent != 0) {
+                return silent;
+            }
+        } else {
+            System.out.println("Reusing existing run data");
+        }
+
+        System.out.println("Exporting as Quarkus project to: " + exportDir);
+
+        // use a temporary work dir
+        File buildDir = new File(BUILD_DIR);
+        FileUtil.removeDir(buildDir);
+        buildDir.mkdirs();
+
+        // copy source files
+        String packageName = ids[0] + "." + ids[1];
+        File srcJavaDir = new File(BUILD_DIR, "src/main/java/" + packageName.replace('.', '/'));
+        srcJavaDir.mkdirs();
+        File srcResourcesDir = new File(BUILD_DIR, "src/main/resources");
+        srcResourcesDir.mkdirs();
+        File srcCamelResourcesDir = new File(BUILD_DIR, "src/main/resources/camel");
+        srcCamelResourcesDir.mkdirs();
+        copySourceFiles(settings, srcJavaDir, srcResourcesDir, srcCamelResourcesDir, packageName);
+        // copy from settings to profile
+        copySettingsAndProfile(settings, profile, srcResourcesDir);
+        // create main class
+        createMainClassSource(srcJavaDir, packageName, mainClassname);
+        // gather dependencies
+        Set<String> deps = resolveDependencies(settings);
+        // create pom
+        createPom(new File(BUILD_DIR, "pom.xml"), deps);
+
+        if (exportDir.equals(".")) {
+            // we export to current dir so prepare for this by cleaning up existing files
+            File target = new File(exportDir);
+            for (File f : target.listFiles()) {
+                if (!f.isHidden() && f.isDirectory()) {
+                    FileUtil.removeDir(f);
+                } else if (!f.isHidden() && f.isFile()) {
+                    f.delete();
+                }
+            }
+        }
+        // copy to export dir and remove work dir
+        FileUtils.copyDirectory(new File(BUILD_DIR), new File(exportDir));
+        FileUtil.removeDir(new File(BUILD_DIR));
+
+        return 0;
+    }
+
+    private void createPom(File pom, Set<String> deps) throws Exception {
+        String[] ids = gav.split(":");
+
+        InputStream is = ExportQuarkus.class.getClassLoader().getResourceAsStream("templates/quarkus-pom.tmpl");
+        String context = IOHelper.loadText(is);
+        IOHelper.close(is);
+
+        context = context.replaceFirst("\\{\\{ \\.GroupId }}", ids[0]);
+        context = context.replaceFirst("\\{\\{ \\.ArtifactId }}", ids[1]);
+        context = context.replaceFirst("\\{\\{ \\.Version }}", ids[2]);
+        context = context.replaceAll("\\{\\{ \\.QuarkusVersion }}", quarkusVersion);
+        context = context.replaceFirst("\\{\\{ \\.JavaVersion }}", javaVersion);
+
+        StringBuilder sb = new StringBuilder();
+        for (String dep : deps) {
+            MavenGav gav = MavenGav.parseGav(null, dep);
+            String gid = gav.getGroupId();
+            String aid = gav.getArtifactId();
+            String v = gav.getVersion();
+            // transform to camel-quarkus extension GAV
+            if ("org.apache.camel".equals(gid)) {
+                gid = "org.apache.camel.quarkus";
+                aid = aid.replace("camel-", "camel-quarkus-");
+                v = null;
+            }
+            sb.append("        <dependency>\n");
+            sb.append("            <groupId>").append(gid).append("</groupId>\n");
+            sb.append("            <artifactId>").append(aid).append("</artifactId>\n");
+            if (v != null) {
+                sb.append("            <version>").append(v).append("</version>\n");
+            }
+            sb.append("        </dependency>\n");
+        }
+        context = context.replaceFirst("\\{\\{ \\.CamelDependencies }}", sb.toString());
+
+        IOHelper.writeText(context, new FileOutputStream(pom, false));
+    }
+
+    private Set<String> resolveDependencies(File settings) throws Exception {
+        Set<String> answer = new TreeSet<>();
+        List<String> lines = Files.readAllLines(settings.toPath());
+        for (String line : lines) {
+            if (line.startsWith("dependency=")) {
+                String v = StringHelper.after(line, "dependency=");
+                // skip endpointdsl as its already included, and  core-languages and java-joor as we let quarkus compile
+                boolean skip = v == null || v.contains("org.apache.camel:camel-core-languages")
+                        || v.contains("org.apache.camel:camel-java-joor-dsl")
+                        || v.contains("camel-endpointdsl");
+                if (!skip) {
+                    answer.add(v);
+                }
+                if (v != null && v.contains("org.apache.camel:camel-kamelet")) {
+                    // include kamelet catalog if we use kamelets
+                    answer.add("org.apache.camel.kamelets:camel-kamelets:" + kameletsVersion);
+                }
+            }
+        }
+        return answer;
+    }
+
+    private void createMainClassSource(File srcJavaDir, String packageName, String mainClassname) throws Exception {
+        InputStream is = ExportQuarkus.class.getClassLoader().getResourceAsStream("templates/quarkus-main.tmpl");
+        String context = IOHelper.loadText(is);
+        IOHelper.close(is);
+
+        context = context.replaceFirst("\\{\\{ \\.PackageName }}", packageName);
+        context = context.replaceAll("\\{\\{ \\.MainClassname }}", mainClassname);
+        IOHelper.writeText(context, new FileOutputStream(srcJavaDir + "/" + mainClassname + ".java", false));
+    }
+
+    private Integer runSilently() throws Exception {
+        Run run = new Run(getMain());
+        Integer code = run.runSilent();
+        return code;
+    }
+
+    private void copySourceFiles(
+            File settings, File srcJavaDir, File srcResourcesDir, File srcCamelResourcesDir, String packageName)
+            throws Exception {
+        // read the settings file and find the files to copy
+        OrderedProperties prop = new OrderedProperties();
+        prop.load(new FileInputStream(settings));
+
+        for (String k : SETTINGS_PROP_SOURCE_KEYS) {
+            String files = prop.getProperty(k);
+            if (files != null) {
+                for (String f : files.split(",")) {
+                    String scheme = getScheme(f);
+                    if (scheme != null) {
+                        f = f.substring(scheme.length() + 1);
+                    }
+                    String ext = FileUtil.onlyExt(f, true);
+                    boolean java = "java".equals(ext);
+                    boolean camel = "camel.main.routesIncludePattern".equals(k) || "camel.component.kamelet.location".equals(k);
+                    File target = java ? srcJavaDir : camel ? srcCamelResourcesDir : srcResourcesDir;
+                    File source = new File(f);
+                    File out = new File(target, source.getName());
+                    safeCopy(source, out, true);
+                    if (java) {
+                        // need to append package name in java source file
+                        List<String> lines = Files.readAllLines(out.toPath());
+                        lines.add(0, "");
+                        lines.add(0, "package " + packageName + ";");
+                        FileOutputStream fos = new FileOutputStream(out);
+                        for (String line : lines) {
+                            fos.write(line.getBytes(StandardCharsets.UTF_8));
+                            fos.write("\n".getBytes(StandardCharsets.UTF_8));
+                        }
+                        IOHelper.close(fos);
+                    }
+                }
+            }
+        }
+    }
+
+    private void copySettingsAndProfile(File settings, File profile, File targetDir) throws Exception {
+        OrderedProperties prop = new OrderedProperties();
+        prop.load(new FileInputStream(settings));
+        OrderedProperties prop2 = new OrderedProperties();
+        if (profile.exists()) {
+            prop2.load(new FileInputStream(profile));
+        }
+
+        for (Map.Entry<Object, Object> entry : prop.entrySet()) {
+            String key = entry.getKey().toString();
+            boolean skip = "camel.main.routesCompileDirectory".equals(key) || "camel.main.routesReloadEnabled".equals(key);
+            if (!skip && key.startsWith("camel.main")) {
+                prop2.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        FileOutputStream fos = new FileOutputStream(new File(targetDir, profile.getName()), false);
+        for (Map.Entry<Object, Object> entry : prop2.entrySet()) {
+            String k = entry.getKey().toString();
+            String v = entry.getValue().toString();
+            // files are now loaded in classpath
+            v = v.replaceAll("file:", "classpath:");
+            if ("camel.main.routesIncludePattern".equals(k)) {
+                // camel.main.routesIncludePattern should remove all .java as we use quarkus to load them
+                // camel.main.routesIncludePattern should remove all file: classpath: as we copy them to src/main/resources/camel where camel auto-load from
+                v = Arrays.stream(v.split(","))
+                        .filter(n -> !n.endsWith(".java") && !n.startsWith("file:") && !n.startsWith("classpath:"))
+                        .collect(Collectors.joining(","));
+            }
+            if (!v.isBlank()) {
+                String line = k + "=" + v;
+                fos.write(line.getBytes(StandardCharsets.UTF_8));
+                fos.write("\n".getBytes(StandardCharsets.UTF_8));
+            }
+        }
+        IOHelper.close(fos);
+    }
+
+    private static void safeCopy(File source, File target, boolean override) throws Exception {
+        if (!source.exists()) {
+            return;
+        }
+
+        if (!target.exists()) {
+            Files.copy(source.toPath(), target.toPath());
+        } else if (override) {
+            Files.copy(source.toPath(), target.toPath(),
+                    StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
+    private static String getScheme(String name) {
+        int pos = name.indexOf(":");
+        if (pos != -1) {
+            return name.substring(0, pos);
+        }
+        return null;
+    }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-main.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-main.tmpl
new file mode 100644
index 00000000000..2d678a67867
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-main.tmpl
@@ -0,0 +1,13 @@
+package {{ .PackageName }};
+
+import io.quarkus.runtime.annotations.QuarkusMain;
+import io.quarkus.runtime.Quarkus;
+
+@QuarkusMain
+public class {{ .MainClassname }} {
+
+    public static void main(String[] args) {
+        Quarkus.run(args);
+    }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-pom.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-pom.tmpl
new file mode 100644
index 00000000000..dc780acc37b
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/quarkus-pom.tmpl
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>{{ .GroupId }}</groupId>
+    <artifactId>{{ .ArtifactId }}</artifactId>
+    <version>{{ .Version }}</version>
+
+    <properties>
+        <compiler-plugin.version>3.8.1</compiler-plugin.version>
+        <failsafe.useModulePath>false</failsafe.useModulePath>
+        <maven.compiler.release>{{ .JavaVersion }}</maven.compiler.release>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
+        <quarkus.platform.version>{{ .QuarkusVersion }}</quarkus.platform.version>
+        <surefire-plugin.version>3.0.0-M5</surefire-plugin.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-camel-bom</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-platform-http</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-microprofile-health</artifactId>
+        </dependency>
+{{ .CamelDependencies }}
+
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build</goal>
+                            <goal>generate-code</goal>
+                            <goal>generate-code-tests</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${compiler-plugin.version}</version>
+                <configuration>
+                    <compilerArgs>
+                        <arg>-parameters</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire-plugin.version}</version>
+                <configuration>
+                    <systemPropertyVariables>
+                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        <maven.home>${maven.home}</maven.home>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <version>${surefire-plugin.version}</version>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        <native.image.path>
+                                            ${project.build.directory}/${project.build.finalName}-runner
+                                        </native.image.path>
+                                        <java.util.logging.manager>org.jboss.logmanager.LogManager
+                                        </java.util.logging.manager>
+                                        <maven.home>${maven.home}</maven.home>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <quarkus.package.type>native</quarkus.package.type>
+            </properties>
+        </profile>
+    </profiles>
+</project>