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 05ae5dc77c4 CAMEL-18412: camel-jbang - Add support for hawtio 05ae5dc77c4 is described below commit 05ae5dc77c4d6aeb1b8627247298a9cb81210c9b Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Aug 19 17:31:54 2022 +0200 CAMEL-18412: camel-jbang - Add support for hawtio --- .../modules/ROOT/pages/camel-jbang.adoc | 51 +++++++++ dsl/camel-jbang/camel-jbang-core/pom.xml | 15 +++ .../dsl/jbang/core/commands/CamelCommand.java | 2 +- .../dsl/jbang/core/commands/CamelJBangMain.java | 4 + .../core/commands/jolokia/AvailablePortFinder.java | 113 +++++++++++++++++++ .../dsl/jbang/core/commands/jolokia/Hawtio.java | 124 +++++++++++++++++++++ .../dsl/jbang/core/commands/jolokia/Jolokia.java | 74 ++++++++++++ .../camel/main/download/DependencyDownloader.java | 10 ++ .../download/DependencyDownloaderClassLoader.java | 5 + .../camel/main/download/DownloadThreadPool.java | 11 +- .../main/download/MavenDependencyDownloader.java | 45 +++++--- 11 files changed, 433 insertions(+), 21 deletions(-) diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc index bb8f1f4c136..9fd0731765d 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc @@ -561,6 +561,57 @@ Stopping running Camel integration (pid: 42171) Stopping running Camel integration (pid: 44805) ---- +=== Using Jolokia and Hawtio + +The https://hawt.io/[Hawtio] web console allows inspecting running Camel applications, such +as all the JMX management information, and not but least to visualize the Camel routes +with live performance metrics. Hawtio is a handy tool for many years, and we have made it +easy to use Hawtio with Camel JBang. + +To let Hawtio able to inspect the Camel integrations, then at first you need to attach +the Jolokia JVM Agent in the running integration, this can be done as follows: + +[source,bash] +---- +camel ps +37928 camel run foo.yaml (age: 20h18m) +44805 camel run dude.java (age: 58s) +---- + +With the PID you can then attach Jolokia: + +[source,bash] +---- +camel jolokia 37928 +Started Jolokia for PID 37928 +http://127.0.0.1:8778/jolokia/ +---- + +Then you can launch https://hawt.io/[Hawtio] using Camel JBang: + +[source,bash] +---- +camel hawtio +---- + +This will automatically download and start Hawtio, and open in web browser. + +TIP: See `camel hawtio --help` for options. + +And when Hawtio launches in the web browser, click the _Discover_ tab which should +list all the local available Jolokia Agents (yes you can use `camel jolokia PID` to connect +to multiple different Camel integrations and from this list select which to load). + +Click the green _lightning_ icon to connect to running Camel integration (of choice). + +You can uninstall the Jolokia JVM Agent in a running Camel application when no longer needed: + +[source,bash] +---- +camel jolokia 37928 --stop +Stopped Jolokia for PID 37928 +---- + === Scripting from terminal using pipes You can also execute a Camel JBang file as a script that can be used for terminal scripting with pipes and filters. diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml b/dsl/camel-jbang/camel-jbang-core/pom.xml index fdca84698c7..84de066a0fe 100644 --- a/dsl/camel-jbang/camel-jbang-core/pom.xml +++ b/dsl/camel-jbang/camel-jbang-core/pom.xml @@ -58,6 +58,7 @@ <artifactId>camel-resourceresolver-github</artifactId> </dependency> + <!-- cli --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> @@ -67,6 +68,20 @@ <artifactId>picocli</artifactId> </dependency> + <!-- jolokia --> + <dependency> + <groupId>org.jolokia</groupId> + <artifactId>jolokia-jvm</artifactId> + <version>1.7.1</version> + </dependency> + <!-- servlet for launching hawtio --> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <version>${javax-servlet-api-version}</version> + <scope>optional</scope> + </dependency> + <!-- logging --> <dependency> <groupId>org.slf4j</groupId> diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java index 029eadf5f2c..caa1c6f2ee9 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java @@ -20,7 +20,7 @@ import java.util.concurrent.Callable; import picocli.CommandLine; -abstract class CamelCommand implements Callable<Integer> { +public abstract class CamelCommand implements Callable<Integer> { public static final String PID_DIR = "${sys:user.home}/.camel"; 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 3b7911d76cc..428e701c176 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 @@ -20,6 +20,8 @@ import java.util.concurrent.Callable; import org.apache.camel.catalog.CamelCatalog; import org.apache.camel.catalog.DefaultCamelCatalog; +import org.apache.camel.dsl.jbang.core.commands.jolokia.Hawtio; +import org.apache.camel.dsl.jbang.core.commands.jolokia.Jolokia; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -35,6 +37,8 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("stop", new CommandLine(new StopProcess(main))) .addSubcommand("init", new CommandLine(new Init(main))) .addSubcommand("bind", new CommandLine(new Bind(main))) + .addSubcommand("jolokia", new CommandLine(new Jolokia(main))) + .addSubcommand("hawtio", new CommandLine(new Hawtio(main))) .addSubcommand("pipe", new CommandLine(new Pipe(main))) .addSubcommand("generate", new CommandLine(new CodeGenerator(main)) .addSubcommand("rest", new CommandLine(new CodeRestGenerator(main)))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/AvailablePortFinder.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/AvailablePortFinder.java new file mode 100644 index 00000000000..252cde0061b --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/AvailablePortFinder.java @@ -0,0 +1,113 @@ +/* + * 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.jolokia; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class AvailablePortFinder { + + private static final AvailablePortFinder INSTANCE = new AvailablePortFinder(); + + private class Port implements AutoCloseable { + final int port; + + public Port(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + public void release() { + AvailablePortFinder.this.release(this); + } + + public String toString() { + return Integer.toString(port); + } + + @Override + public void close() { + release(); + } + } + + private final Map<Integer, Port> portMapping = new ConcurrentHashMap<>(); + + synchronized void release(Port port) { + INSTANCE.portMapping.remove(port.getPort(), port); + } + + /** + * Gets the next available port in the given range. + * + * @param fromPort port number start range. + * @param toPort port number end range. + * + * @throws IllegalStateException if there are no ports available + * @return the available port + */ + public static int getNextAvailable(int fromPort, int toPort) { + try (Port port = INSTANCE.findPort(fromPort, toPort)) { + return port.getPort(); + } + } + + synchronized Port findPort(int fromPort, int toPort) { + for (int i = fromPort; i <= toPort; i++) { + try { + final int port = probePort(i); + Port p = new Port(port); + Port prv = INSTANCE.portMapping.putIfAbsent(port, p); + if (prv == null) { + return p; + } + } catch (IllegalStateException e) { + // do nothing, let's try the next port + } + } + throw new IllegalStateException("Cannot find free port"); + } + + /** + * Probe a port to see if it is free + * + * @param port an integer port number to be tested. If port is 0, then the next available port is + * returned. + * @throws IllegalStateException if the port is not free or, in case of port 0, if there are no ports available at + * all. + * @return the port number itself if the port is free or, in case of port 0, the first + * available port number. + */ + private static int probePort(int port) { + try (ServerSocket ss = new ServerSocket()) { + ss.setReuseAddress(true); + ss.bind(new InetSocketAddress((InetAddress) null, port), 1); + int probedPort = ss.getLocalPort(); + return probedPort; + } catch (IOException e) { + throw new IllegalStateException("Cannot find free port", e); + } + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/Hawtio.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/Hawtio.java new file mode 100644 index 00000000000..844820e80a9 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/Hawtio.java @@ -0,0 +1,124 @@ +/* + * 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.jolokia; + +import java.awt.*; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.concurrent.CountDownLatch; + +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.main.download.DependencyDownloaderClassLoader; +import org.apache.camel.main.download.MavenArtifact; +import org.apache.camel.main.download.MavenDependencyDownloader; +import org.apache.camel.support.ObjectHelper; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "hawtio", description = "Launch Hawtio web console") +public class Hawtio extends CamelCommand { + + @CommandLine.Option(names = { "--version" }, + description = "Version of the Hawtio web console", defaultValue = "2.15.0") + private String version = "2.15.0"; + + @CommandLine.Option(names = { "--port" }, + description = "Port number to use for Hawtio web console", defaultValue = "8080") + private int port = 8080; + + @CommandLine.Option(names = { "--openUrl" }, + description = "To automatic open hawtio web console in the web browser", defaultValue = "true") + private boolean openUrl = true; + + private final CountDownLatch shutdownLatch = new CountDownLatch(1); + + public Hawtio(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + ClassLoader cl = createClassLoader(); + + MavenDependencyDownloader downloader = new MavenDependencyDownloader(); + downloader.setClassLoader(cl); + downloader.start(); + // download hawtio embedded mode + downloader.downloadDependency("io.hawt", "hawtio-embedded", version); + // download war that has the web-console + MavenArtifact ma = downloader.downloadArtifact("io.hawt", "hawtio-war:war", version); + if (ma == null) { + System.err.println("Cannot download io.hawt:hawtio-war:war:" + version); + return 1; + } + + String war = ma.getFile().getAbsolutePath(); + + // invoke hawtio main app that launches hawtio + try { + // turn off hawito auth + System.setProperty("hawtio.authenticationEnabled", "false"); + + // use CL from camel context that now has the downloaded JAR + Thread.currentThread().setContextClassLoader(cl); + Class<?> clazz = cl.loadClass("io.hawt.embedded.Main"); + Object hawt = clazz.getDeclaredConstructor().newInstance(); + Method m = clazz.getMethod("setWar", String.class); + ObjectHelper.invokeMethod(m, hawt, war); + m = clazz.getMethod("run"); + ObjectHelper.invokeMethod(m, hawt); + + if (openUrl) { + // open web browser + String url = "http://localhost:" + port + "/hawtio"; + System.setProperty("hawtio.url", url); + if (openUrl && Desktop.isDesktopSupported()) { + try { + Desktop.getDesktop().browse(new URI(url)); + } catch (Exception e) { + System.err.println(String.format("Failed to open browser session, to access hawtio visit \"%s\"", url)); + } + } + } + + // keep JVM running + installHangupInterceptor(); + shutdownLatch.await(); + + } catch (Throwable e) { + System.err.println("Cannot launch hawtio due to: " + e.getMessage()); + e.printStackTrace(); + return 1; + } finally { + downloader.stop(); + } + + return 0; + } + + private ClassLoader createClassLoader() { + ClassLoader parentCL = Hawtio.class.getClassLoader(); + return new DependencyDownloaderClassLoader(parentCL); + } + + private void installHangupInterceptor() { + Thread task = new Thread(shutdownLatch::countDown); + Runtime.getRuntime().addShutdownHook(task); + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/Jolokia.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/Jolokia.java new file mode 100644 index 00000000000..08c13f919c0 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/jolokia/Jolokia.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.dsl.jbang.core.commands.jolokia; + +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.jolokia.jvmagent.client.command.CommandDispatcher; +import org.jolokia.jvmagent.client.util.OptionsAndArgs; +import org.jolokia.jvmagent.client.util.PlatformUtils; +import org.jolokia.jvmagent.client.util.VirtualMachineHandlerOperations; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "jolokia", description = "Attach Jolokia JVM Agent to a running Camel integration") +public class Jolokia extends CamelCommand { + + @CommandLine.Parameters(description = "PID of running Camel integration", arity = "1") + private long pid; + + @CommandLine.Option(names = { "--stop" }, + description = "Stops the Jolokia JVM Agent in the running Camel integration") + private boolean stop; + + public Jolokia(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + int exitCode; + + try { + OptionsAndArgs options; + if (stop) { + options = new OptionsAndArgs(null, "stop", "" + pid); + } else { + // find a new free port to use when starting a new connection + long port = AvailablePortFinder.getNextAvailable(8778, 10000); + options = new OptionsAndArgs(null, "--port", "" + port, "start", "" + pid); + } + VirtualMachineHandlerOperations vmHandler = PlatformUtils.createVMAccess(options); + CommandDispatcher dispatcher = new CommandDispatcher(options); + + Object vm = options.needsVm() ? vmHandler.attachVirtualMachine() : null; + try { + exitCode = dispatcher.dispatchCommand(vm, vmHandler); + } finally { + if (vm != null) { + vmHandler.detachAgent(vm); + } + } + } catch (Exception e) { + System.err.println("Cannot execute jolokia command due " + e.getMessage()); + exitCode = 1; + } + + return exitCode; + } + +} 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 5e0ccde36e4..f8edee21777 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 @@ -24,6 +24,16 @@ import org.apache.camel.StaticService; */ public interface DependencyDownloader extends CamelContextAware, StaticService { + /** + * Classloader able to load from downloaded dependencies. + */ + ClassLoader getClassLoader(); + + /** + * Sets the classloader to use that will be able to load from downloaded dependencies + */ + void setClassLoader(ClassLoader classLoader); + /** * Adds a listener to capture download activity */ diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderClassLoader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderClassLoader.java index b01ab78a4eb..1655fcfbe5f 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderClassLoader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloaderClassLoader.java @@ -40,6 +40,11 @@ public class DependencyDownloaderClassLoader extends URLClassLoader { } } + @Override + public Class<?> loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } + public List<String> getDownloaded() { return Arrays.stream(getURLs()).map(URL::getFile).collect(Collectors.toList()); } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java index 07dd293c646..7ff523c0a26 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java @@ -17,6 +17,7 @@ package org.apache.camel.main.download; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -83,13 +84,19 @@ class DownloadThreadPool extends ServiceSupport implements CamelContextAware { @Override protected void doBuild() throws Exception { - executorService = camelContext.getExecutorServiceManager().newCachedThreadPool(this, "MavenDownload"); + if (camelContext != null) { + executorService = camelContext.getExecutorServiceManager().newCachedThreadPool(this, "MavenDownload"); + } else { + executorService = Executors.newCachedThreadPool(); + } } @Override protected void doShutdown() throws Exception { - if (executorService != null) { + if (executorService != null && camelContext != null) { camelContext.getExecutorServiceManager().shutdown(executorService); + } else if (executorService != null) { + executorService.shutdown(); } } } 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 853cb5e1db4..46c5c4f03fe 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 @@ -45,6 +45,7 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende private String[] bootClasspath; private DownloadThreadPool threadPool; + private ClassLoader classLoader; private CamelContext camelContext; private final Set<DownloadListener> downloadListeners = new LinkedHashSet<>(); private String repos; @@ -60,6 +61,14 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende this.camelContext = camelContext; } + public ClassLoader getClassLoader() { + return classLoader; + } + + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + @Override public void addDownloadListener(DownloadListener downloadListener) { CamelContextAware.trySetCamelContext(downloadListener, getCamelContext()); @@ -153,15 +162,15 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende = MavenDependencyResolver.resolveDependenciesViaAether(deps, mavenRepos, false, fresh, transitively); LOG.debug("Resolved {} -> [{}]", gav, artifacts); - DependencyDownloaderClassLoader classLoader - = (DependencyDownloaderClassLoader) camelContext.getApplicationContextClassLoader(); - for (MavenArtifact a : artifacts) { File file = a.getFile(); // only add to classpath if not already present (do not trigger listener) if (!alreadyOnClasspath(a.getGav().getGroupId(), a.getGav().getArtifactId(), a.getGav().getVersion(), false)) { - classLoader.addFile(file); + if (classLoader instanceof DependencyDownloaderClassLoader) { + DependencyDownloaderClassLoader ddc = (DependencyDownloaderClassLoader) classLoader; + ddc.addFile(file); + } LOG.trace("Added classpath: {}", a.getGav()); } } @@ -234,22 +243,19 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende } } - if (camelContext.getApplicationContextClassLoader() != null) { - ClassLoader cl = camelContext.getApplicationContextClassLoader(); - if (cl instanceof URLClassLoader) { - URLClassLoader ucl = (URLClassLoader) cl; - for (URL u : ucl.getURLs()) { - String s = u.toString(); - if (s.contains(target)) { - // trigger listener - if (listener) { - for (DownloadListener dl : downloadListeners) { - dl.onDownloadDependency(groupId, artifactId, version); - } + if (classLoader instanceof URLClassLoader) { + URLClassLoader ucl = (URLClassLoader) classLoader; + for (URL u : ucl.getURLs()) { + String s = u.toString(); + if (s.contains(target)) { + // trigger listener + if (listener) { + for (DownloadListener dl : downloadListeners) { + dl.onDownloadDependency(groupId, artifactId, version); } - // already on classpath - return true; } + // already on classpath + return true; } } } @@ -258,6 +264,9 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende @Override protected void doBuild() throws Exception { + if (classLoader == null && camelContext != null) { + classLoader = camelContext.getApplicationContextClassLoader(); + } threadPool = new DownloadThreadPool(); threadPool.setCamelContext(camelContext); ServiceHelper.buildService(threadPool);