This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-3.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 4c29f2058d400ee431d34f72384f8ef0a9fd0eee
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri May 5 10:25:17 2023 +0200

    CAMEL-19320: camel-jbang - Reload on demand
---
 .../camel/impl/engine/DefaultRoutesLoader.java     |   6 +-
 .../camel/impl/console/ContextDevConsole.java      |  25 ++---
 .../apache/camel/main/MainPropertiesReload.java    |   5 +-
 .../support/DefaultContextReloadStrategy.java      |   5 +
 .../support/FileWatcherResourceReloadStrategy.java |  25 +++--
 .../camel/support/RouteOnDemandReloadStrategy.java | 112 +++++++++++++++++++++
 .../camel/support/RouteWatcherReloadStrategy.java  |  54 +++++++---
 .../camel/cli/connector/LocalCliConnector.java     |  26 ++++-
 .../apache/camel/dsl/jbang/core/commands/Run.java  |   1 +
 9 files changed, 217 insertions(+), 42 deletions(-)

diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
index 5407c04f4ac..4ae4e65ea92 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
@@ -192,8 +192,11 @@ public class DefaultRoutesLoader extends ServiceSupport 
implements RoutesLoader,
     @Override
     public Set<String> updateRoutes(Collection<Resource> resources) throws 
Exception {
         Set<String> answer = new LinkedHashSet<>();
-        Collection<RoutesBuilder> builders = findRoutesBuilders(resources);
+        if (resources == null || resources.isEmpty()) {
+            return answer;
+        }
 
+        Collection<RoutesBuilder> builders = findRoutesBuilders(resources);
         for (RoutesBuilder builder : builders) {
             // update any existing route configurations first
             if (builder instanceof RouteConfigurationsBuilder) {
@@ -201,7 +204,6 @@ public class DefaultRoutesLoader extends ServiceSupport 
implements RoutesLoader,
                 rcb.updateRouteConfigurationsToCamelContext(getCamelContext());
             }
         }
-
         for (RoutesBuilder builder : builders) {
             // update any existing routes
             Set<String> ids = 
builder.updateRoutesToCamelContext(getCamelContext());
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
index 86c722336b6..514fbc20958 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
@@ -19,6 +19,7 @@ package org.apache.camel.impl.console;
 import java.util.Date;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.api.management.ManagedCamelContext;
@@ -53,13 +54,13 @@ public class ContextDevConsole extends AbstractDevConsole {
             ManagedCamelContextMBean mb = mcc.getManagedCamelContext();
             if (mb != null) {
                 int reloaded = 0;
-                ResourceReloadStrategy rrs = 
getCamelContext().hasService(ResourceReloadStrategy.class);
-                if (rrs != null) {
-                    reloaded += rrs.getReloadCounter();
+                Set<ResourceReloadStrategy> rrs = 
getCamelContext().hasServices(ResourceReloadStrategy.class);
+                for (ResourceReloadStrategy r : rrs) {
+                    reloaded += r.getReloadCounter();
                 }
-                ContextReloadStrategy crs = 
getCamelContext().hasService(ContextReloadStrategy.class);
-                if (crs != null) {
-                    reloaded += crs.getReloadCounter();
+                Set<ContextReloadStrategy> crs = 
getCamelContext().hasServices(ContextReloadStrategy.class);
+                for (ContextReloadStrategy r : crs) {
+                    reloaded += r.getReloadCounter();
                 }
                 String load1 = getLoad1(mb);
                 String load5 = getLoad5(mb);
@@ -123,13 +124,13 @@ public class ContextDevConsole extends AbstractDevConsole 
{
                 JsonObject stats = new JsonObject();
 
                 int reloaded = 0;
-                ResourceReloadStrategy rrs = 
getCamelContext().hasService(ResourceReloadStrategy.class);
-                if (rrs != null) {
-                    reloaded += rrs.getReloadCounter();
+                Set<ResourceReloadStrategy> rrs = 
getCamelContext().hasServices(ResourceReloadStrategy.class);
+                for (ResourceReloadStrategy r : rrs) {
+                    reloaded += r.getReloadCounter();
                 }
-                ContextReloadStrategy crs = 
getCamelContext().hasService(ContextReloadStrategy.class);
-                if (crs != null) {
-                    reloaded += crs.getReloadCounter();
+                Set<ContextReloadStrategy> crs = 
getCamelContext().hasServices(ContextReloadStrategy.class);
+                for (ContextReloadStrategy r : crs) {
+                    reloaded += r.getReloadCounter();
                 }
                 String load1 = getLoad1(mb);
                 String load5 = getLoad5(mb);
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/MainPropertiesReload.java 
b/core/camel-main/src/main/java/org/apache/camel/main/MainPropertiesReload.java
index 34ddef39139..ce2855d31a1 100644
--- 
a/core/camel-main/src/main/java/org/apache/camel/main/MainPropertiesReload.java
+++ 
b/core/camel-main/src/main/java/org/apache/camel/main/MainPropertiesReload.java
@@ -20,6 +20,8 @@ import java.util.Properties;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
+import org.apache.camel.NonManagedService;
+import org.apache.camel.StaticService;
 import org.apache.camel.spi.PropertiesReload;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.OrderedLocationProperties;
@@ -27,7 +29,8 @@ import org.apache.camel.util.OrderedLocationProperties;
 /**
  * Reloading of application.properties when using Camel Main in reload mode, 
such as when using camel-jbang.
  */
-public class MainPropertiesReload extends ServiceSupport implements 
PropertiesReload, CamelContextAware {
+public class MainPropertiesReload extends ServiceSupport
+        implements StaticService, NonManagedService, PropertiesReload, 
CamelContextAware {
 
     private final BaseMainSupport main;
     private CamelContext camelContext;
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
index 4df77bc0ed2..83bf9147a35 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/DefaultContextReloadStrategy.java
@@ -48,6 +48,11 @@ public class DefaultContextReloadStrategy extends 
ServiceSupport implements Cont
         this.camelContext = camelContext;
     }
 
+    @ManagedOperation(description = "Trigger on-demand reloading")
+    public void onReload() {
+        onReload("JMX Management");
+    }
+
     @Override
     public void onReload(Object source) {
         LOG.info("Reloading CamelContext ({}) triggered by: {}", 
camelContext.getName(), source);
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
index 1e62cd72720..54a865c84b7 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/FileWatcherResourceReloadStrategy.java
@@ -61,14 +61,15 @@ public class FileWatcherResourceReloadStrategy extends 
ResourceReloadStrategySup
 
     private static final Logger LOG = 
LoggerFactory.getLogger(FileWatcherResourceReloadStrategy.class);
 
-    private String folder;
-    private boolean isRecursive;
-    private WatchService watcher;
-    private ExecutorService executorService;
-    private WatchFileChangesTask task;
-    private Map<WatchKey, Path> folderKeys;
-    private long pollTimeout = 2000;
-    private FileFilter fileFilter;
+    WatchService watcher;
+    ExecutorService executorService;
+    WatchFileChangesTask task;
+    Map<WatchKey, Path> folderKeys;
+    FileFilter fileFilter;
+    String folder;
+    boolean isRecursive;
+    boolean scheduler = true;
+    long pollTimeout = 2000;
 
     public FileWatcherResourceReloadStrategy() {
         setRecursive(false);
@@ -96,6 +97,10 @@ public class FileWatcherResourceReloadStrategy extends 
ResourceReloadStrategySup
         this.isRecursive = isRecursive;
     }
 
+    public void setScheduler(boolean scheduler) {
+        this.scheduler = scheduler;
+    }
+
     /**
      * Sets the poll timeout in millis. The default value is 2000.
      */
@@ -137,6 +142,10 @@ public class FileWatcherResourceReloadStrategy extends 
ResourceReloadStrategySup
             // no folder configured
             return;
         }
+        if (!scheduler) {
+            // do not start scheduler so exit start phase
+            return;
+        }
 
         File dir = new File(folder);
         if (dir.exists() && dir.isDirectory()) {
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
new file mode 100644
index 00000000000..6012c9e564a
--- /dev/null
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteOnDemandReloadStrategy.java
@@ -0,0 +1,112 @@
+/*
+ * 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.support;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.api.management.ManagedOperation;
+import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.util.FileUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Strategy for triggering on-demand reloading of Camel routes in a running 
Camel application. The strategy is triggered
+ * on-demand and reload all files from a directory (and subdirectories).
+ */
+@ManagedResource(description = "Managed RouteOnDemandReloadStrategy")
+public class RouteOnDemandReloadStrategy extends RouteWatcherReloadStrategy {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(RouteOnDemandReloadStrategy.class);
+
+    public RouteOnDemandReloadStrategy() {
+        setScheduler(false);
+    }
+
+    public RouteOnDemandReloadStrategy(String directory) {
+        super(directory);
+        setScheduler(false);
+    }
+
+    public RouteOnDemandReloadStrategy(String directory, boolean recursive) {
+        super(directory, recursive);
+        setScheduler(false);
+    }
+
+    /**
+     * Triggers on-demand reloading
+     */
+    @ManagedOperation(description = "Trigger on-demand reloading")
+    public void onReload() {
+        onReload("JMX Management");
+    }
+
+    /**
+     * Triggers on-demand reloading
+     */
+    public void onReload(Object source) {
+        try {
+            doOnReload(source);
+            incSucceededCounter();
+        } catch (Exception e) {
+            incFailedCounter();
+            LOG.warn("Error reloading routes due " + e.getMessage()
+                     + ". This exception is ignored.",
+                    e);
+        }
+    }
+
+    protected void doOnReload(Object source) throws Exception {
+        List<Resource> properties = new ArrayList<>();
+        List<Resource> routes = new ArrayList<>();
+
+        File dir = new File(getFolder());
+        for (Path path : ResourceHelper.findInFileSystem(dir.toPath(), 
getPattern())) {
+            Resource res = ResourceHelper.resolveResource(getCamelContext(), 
"file:" + path.toString());
+            String ext = FileUtil.onlyExt(path.getFileName().toString());
+            if ("properties".equals(ext)) {
+                properties.add(res);
+            } else {
+                routes.add(res);
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("On-demand reload scanned {} files (properties: {}, 
routes: {})",
+                    properties.size() + routes.size(), properties.size(), 
routes.size());
+        }
+
+        // reload properties first
+        boolean reloaded = false;
+        for (Resource res : properties) {
+            reloaded |= onPropertiesReload(res, false);
+        }
+        boolean removeEverything = routes.isEmpty();
+        if (reloaded || !routes.isEmpty()) {
+            // trigger routes to also reload if properties was reloaded
+            onRouteReload(routes, removeEverything);
+        } else {
+            // rare situation where all routes are deleted
+            onRouteReload(null, removeEverything);
+        }
+    }
+
+}
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
index a3ac3215fae..f63af35d3ec 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
@@ -147,9 +147,9 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
             // attach listener that triggers the route update
             setResourceReload((name, resource) -> {
                 if (name.endsWith(".properties")) {
-                    onPropertiesReload(resource);
+                    onPropertiesReload(resource, true);
                 } else {
-                    onRouteReload(resource);
+                    onRouteReload(List.of(resource), true);
                 }
             });
         }
@@ -162,7 +162,7 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
         return "Live route reloading enabled (directory: " + dir + ")";
     }
 
-    protected void onPropertiesReload(Resource resource) throws Exception {
+    protected boolean onPropertiesReload(Resource resource, boolean 
reloadRoutes) throws Exception {
         LOG.info("Reloading properties: {}. (Only Camel routes and components 
can be updated with changes)",
                 resource.getLocation());
 
@@ -183,19 +183,21 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
             pc.keepOnlyChangeProperties(changed);
         }
 
-        if (changed == null || !changed.isEmpty()) {
-            boolean reloaded = pc.reloadProperties(resource.getLocation());
+        boolean reloaded = false;
+        if (changed != null && !changed.isEmpty()) {
+            reloaded = pc.reloadProperties(resource.getLocation());
             if (reloaded) {
-                if (pr != null) {
-                    pr.onReload(resource.getLocation(), changed);
-                }
+                pr.onReload(resource.getLocation(), changed);
                 // trigger all routes to be reloaded
-                onRouteReload(null);
+                if (reloadRoutes) {
+                    onRouteReload(null, true);
+                }
             }
         }
+        return reloaded;
     }
 
-    protected void onRouteReload(Resource resource) {
+    protected void onRouteReload(Collection<Resource> resources, boolean 
removeEverything) {
         // remember all existing resources
         List<Resource> sources = new ArrayList<>();
 
@@ -204,7 +206,7 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
             // to the last working set
             previousSources.forEach(rs -> {
                 // remember all the sources of the current routes (except the 
updated)
-                if (rs != null && !equalResourceLocation(resource, rs)) {
+                if (rs != null && !equalResourceLocation(resources, rs)) {
                     sources.add(rs);
                 }
             });
@@ -216,7 +218,7 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
                 // remember all the sources of the current routes (except the 
updated)
                 getCamelContext().getRoutes().forEach(r -> {
                     Resource rs = r.getSourceResource();
-                    if (rs != null && !equalResourceLocation(resource, rs)) {
+                    if (rs != null && !equalResourceLocation(resources, rs)) {
                         sources.add(rs);
                     }
                 });
@@ -227,8 +229,12 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
                 getCamelContext().getEndpointRegistry().clear();
             }
 
-            if (resource != null && 
Files.exists(Paths.get(resource.getURI()))) {
-                sources.add(resource);
+            if (resources != null) {
+                for (Resource resource : resources) {
+                    if (Files.exists(Paths.get(resource.getURI()))) {
+                        sources.add(resource);
+                    }
+                }
             }
 
             Collection<Resource> extras
@@ -246,6 +252,11 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
             previousSources.clear();
             previousSources.addAll(sources);
 
+            // special situation where we remove all routes
+            if (removeEverything) {
+                sources.clear();
+            }
+
             // reload those other routes that was stopped and removed as we 
want to keep running those
             Set<String> ids
                     = 
getCamelContext().adapt(ExtendedCamelContext.class).getRoutesLoader().updateRoutes(sources);
@@ -312,6 +323,21 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
         }
     }
 
+    /**
+     * Whether the target is loading any of the given sources
+     */
+    private static boolean equalResourceLocation(Collection<Resource> sources, 
Resource target) {
+        if (sources == null || target == null || sources.isEmpty()) {
+            return false;
+        }
+        for (Resource source : sources) {
+            if (equalResourceLocation(source, target)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Whether the two resources are loading the same resource
      */
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index 6d7dab139fc..5bda4c0790d 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -46,8 +46,11 @@ import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
 import org.apache.camel.spi.ContextReloadStrategy;
+import org.apache.camel.spi.ResourceReloadStrategy;
 import org.apache.camel.support.DefaultContextReloadStrategy;
 import org.apache.camel.support.PatternHelper;
+import org.apache.camel.support.RouteOnDemandReloadStrategy;
+import org.apache.camel.support.RouteWatcherReloadStrategy;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.FileUtil;
@@ -249,12 +252,25 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
             } else if ("gc".equals(action)) {
                 System.gc();
             } else if ("reload".equals(action)) {
-                ContextReloadStrategy reloader = 
camelContext.hasService(ContextReloadStrategy.class);
-                if (reloader == null) {
-                    reloader = new DefaultContextReloadStrategy();
-                    camelContext.addService(reloader);
+                Optional<String> dir = 
camelContext.getPropertiesComponent().resolveProperty("camel.jbang.sourceDir");
+                if (dir.isPresent()) {
+                    // if using source-dir then reload this specific directory
+                    RouteOnDemandReloadStrategy reloader = 
camelContext.hasService(RouteOnDemandReloadStrategy.class);
+                    if (reloader == null) {
+                        reloader = new RouteOnDemandReloadStrategy(dir.get(), 
true);
+                        reloader.setPattern("*");
+                        camelContext.addService(reloader);
+                    }
+                    reloader.onReload("Camel CLI");
+                } else {
+                    // general camel reloading
+                    ContextReloadStrategy reloader = 
camelContext.hasService(ContextReloadStrategy.class);
+                    if (reloader == null) {
+                        reloader = new DefaultContextReloadStrategy();
+                        camelContext.addService(reloader);
+                    }
+                    reloader.onReload("Camel CLI");
                 }
-                reloader.onReload("Camel CLI");
             } else if ("reset-stats".equals(action)) {
                 ManagedCamelContext mcc = 
camelContext.getExtension(ManagedCamelContext.class);
                 if (mcc != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index d444c3a0c66..1019a170aa6 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -538,6 +538,7 @@ public class Run extends CamelCommand {
         if (dev && (sourceDir != null || sjReload.length() > 0)) {
             main.addInitialProperty("camel.main.routesReloadEnabled", "true");
             if (sourceDir != null) {
+                main.addInitialProperty("camel.jbang.sourceDir", sourceDir);
                 main.addInitialProperty("camel.main.routesReloadDirectory", 
sourceDir);
                 main.addInitialProperty("camel.main.routesReloadPattern", "*");
                 
main.addInitialProperty("camel.main.routesReloadDirectoryRecursive", "true");

Reply via email to