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/10/20 08:26:44 UTC

[camel] branch main updated: CAMEL-18624: camel-jbang - Should load custom type converters when adding new JARs

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 8bdf3833361 CAMEL-18624: camel-jbang - Should load custom type converters when adding new JARs
8bdf3833361 is described below

commit 8bdf383336129c9cc3df8d691bea12c78fac181d
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Oct 20 10:26:02 2022 +0200

    CAMEL-18624: camel-jbang - Should load custom type converters when adding new JARs
---
 .../java/org/apache/camel/main/KameletMain.java    |   8 +-
 .../main/download/ArtifactDownloadListener.java    |  34 +++++++
 .../camel/main/download/DependencyDownloader.java  |   5 +
 .../main/download/MavenDependencyDownloader.java   |  11 +++
 .../TypeConverterLoaderDownloadListener.java       | 105 +++++++++++++++++++++
 5 files changed, 162 insertions(+), 1 deletion(-)

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 0a20be32185..204ea84733b 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
@@ -43,6 +43,7 @@ import org.apache.camel.main.download.DownloadListener;
 import org.apache.camel.main.download.KameletMainInjector;
 import org.apache.camel.main.download.KnownDependenciesResolver;
 import org.apache.camel.main.download.MavenDependencyDownloader;
+import org.apache.camel.main.download.TypeConverterLoaderDownloadListener;
 import org.apache.camel.main.http.VertxHttpServer;
 import org.apache.camel.main.injection.AnnotationDependencyInjection;
 import org.apache.camel.main.util.ExtraFilesClassLoader;
@@ -299,9 +300,13 @@ public class KameletMain extends MainCommandLineSupport {
         DefaultCamelContext answer = new DefaultCamelContext(false);
         answer.setLogJvmUptime(true);
         if (download) {
-            answer.setApplicationContextClassLoader(createApplicationContextClassLoader());
+            ClassLoader dynamicCL = createApplicationContextClassLoader();
+            answer.setApplicationContextClassLoader(dynamicCL);
+            answer.getPackageScanClassResolver().addClassLoader(dynamicCL);
+            answer.getPackageScanResourceResolver().addClassLoader(dynamicCL);
 
             MavenDependencyDownloader downloader = new MavenDependencyDownloader();
+            downloader.setClassLoader(dynamicCL);
             downloader.setCamelContext(answer);
             downloader.setRepos(repos);
             downloader.setFresh(fresh);
@@ -311,6 +316,7 @@ public class KameletMain extends MainCommandLineSupport {
                 downloader.addDownloadListener(downloadListener);
             }
             downloader.addDownloadListener(new AutoConfigureDownloadListener());
+            downloader.addArtifactDownloadListener(new TypeConverterLoaderDownloadListener());
 
             // register as extension
             try {
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ArtifactDownloadListener.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ArtifactDownloadListener.java
new file mode 100644
index 00000000000..619bccbc0b2
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/ArtifactDownloadListener.java
@@ -0,0 +1,34 @@
+/*
+ * 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.io.File;
+
+/**
+ * Listener for downloading a maven file (can be downloaded from a local cache)
+ */
+@FunctionalInterface
+public interface ArtifactDownloadListener {
+
+    /**
+     * After the artifact has been downloaded
+     *
+     * @param file  the downloaded artifact as a file
+     */
+    void onDownloadedFile(File file);
+
+}
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java
index a7cc346329c..9296d653da2 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java
@@ -39,6 +39,11 @@ public interface DependencyDownloader extends CamelContextAware, StaticService {
      */
     void addDownloadListener(DownloadListener downloadListener);
 
+    /**
+     * Adds a listener to capture download activity
+     */
+    void addArtifactDownloadListener(ArtifactDownloadListener downloadListener);
+
     String getRepos();
 
     /**
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java
index c60c2b95793..53b48ffba5a 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java
@@ -220,6 +220,7 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende
     private ClassLoader classLoader;
     private CamelContext camelContext;
     private final Set<DownloadListener> downloadListeners = new LinkedHashSet<>();
+    private final Set<ArtifactDownloadListener> artifactDownloadListeners = new LinkedHashSet<>();
 
     // repository URLs set from "camel.jbang.repos" property or --repos option.
     private String repos;
@@ -258,6 +259,12 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende
         downloadListeners.add(downloadListener);
     }
 
+    @Override
+    public void addArtifactDownloadListener(ArtifactDownloadListener downloadListener) {
+        CamelContextAware.trySetCamelContext(downloadListener, getCamelContext());
+        artifactDownloadListeners.add(downloadListener);
+    }
+
     @Override
     public String getRepos() {
         return repos;
@@ -367,6 +374,10 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende
                         DependencyDownloaderClassLoader ddc = (DependencyDownloaderClassLoader) classLoader;
                         ddc.addFile(file);
                     }
+                    // trigger listener after downloaded and added to classloader
+                    for (ArtifactDownloadListener listener : artifactDownloadListeners) {
+                        listener.onDownloadedFile(file);
+                    }
                     LOG.trace("Added classpath: {}", a.getGav());
                 }
             }
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/TypeConverterLoaderDownloadListener.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/TypeConverterLoaderDownloadListener.java
new file mode 100644
index 00000000000..a713f562fc4
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/TypeConverterLoaderDownloadListener.java
@@ -0,0 +1,105 @@
+/*
+ * 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.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.impl.converter.BaseTypeConverterRegistry;
+import org.apache.camel.spi.TypeConverterLoader;
+import org.apache.camel.util.IOHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TypeConverterLoaderDownloadListener implements ArtifactDownloadListener, CamelContextAware {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TypeConverterLoaderDownloadListener.class);
+
+    private CamelContext camelContext;
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public void onDownloadedFile(File file) {
+        try {
+            loadTypeConverters(file);
+        } catch (Exception e) {
+            // ignore
+        }
+    }
+
+    protected void loadTypeConverters(File file) throws Exception {
+        // use isolated classloader to load the service file as we only want to check this file
+        // (and not what is already in the existing classloader)
+        DependencyDownloaderClassLoader cl = new DependencyDownloaderClassLoader(null);
+        cl.addFile(file);
+
+        // load names for custom type converters from the downloaded JAR
+        Collection<String> loaders = new ArrayList<>();
+        findTypeConverterLoaderClasses(loaders,
+                cl.getResourceAsStream(BaseTypeConverterRegistry.META_INF_SERVICES_TYPE_CONVERTER_LOADER));
+        findTypeConverterLoaderClasses(loaders,
+                cl.getResourceAsStream(BaseTypeConverterRegistry.META_INF_SERVICES_FALLBACK_TYPE_CONVERTER));
+        loadTypeConverters(loaders);
+    }
+
+    protected void findTypeConverterLoaderClasses(Collection<String> loaders, InputStream is) throws IOException {
+        if (is != null) {
+            BufferedReader reader = IOHelper.buffered(new InputStreamReader(is, StandardCharsets.UTF_8));
+            String line;
+            do {
+                line = reader.readLine();
+                if (line != null && !line.startsWith("#") && !line.isEmpty()) {
+                    loaders.add(line);
+                }
+            } while (line != null);
+            IOHelper.close(reader);
+        }
+    }
+
+    protected void loadTypeConverters(Collection<String> loaders) throws ClassNotFoundException {
+        for (String name : loaders) {
+            LOG.debug("Resolving TypeConverterLoader: {}", name);
+            Class<?> clazz = getCamelContext().getClassResolver().resolveMandatoryClass(name);
+            Object obj = getCamelContext().getInjector().newInstance(clazz, false);
+            CamelContextAware.trySetCamelContext(obj, getCamelContext());
+            if (obj instanceof TypeConverterLoader) {
+                TypeConverterLoader loader = (TypeConverterLoader) obj;
+                CamelContextAware.trySetCamelContext(loader, getCamelContext());
+                LOG.debug("TypeConverterLoader: {} loading converters", name);
+                loader.load(getCamelContext().getTypeConverterRegistry());
+            }
+        }
+    }
+
+}