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 624eddac5efa CAMEL-26320: camel-main - a properties file that changed
in dev mode applies its camel.resilience4j and camel.faulttolerance keys
(#28509)
624eddac5efa is described below
commit 624eddac5efa2d927dda4a5a8b9da7b01ae3417d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 26 17:22:41 2026 +0100
CAMEL-26320: camel-main - a properties file that changed in dev mode
applies its camel.resilience4j and camel.faulttolerance keys (#28509)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
.../org/apache/camel/main/BaseMainSupport.java | 38 ++++++++
.../MainPropertiesReloadCircuitBreakerTest.java | 100 +++++++++++++++++++++
2 files changed, 138 insertions(+)
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 7fc74449180a..5f7f6b771fa4 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -3155,6 +3155,8 @@ public abstract class BaseMainSupport extends BaseService
{
OrderedLocationProperties autoConfiguredProperties = new
OrderedLocationProperties();
// the beans first, as the components that are configured next may
refer to them (CAMEL-25767)
bindReloadedBeans(camelContext, reloadedProperties, changed,
autoConfiguredProperties);
+ // the defaults of the circuit breakers, which the routes created
again after the reload pick up
+ reloadCircuitBreakerDefaults(camelContext, reloadedProperties,
autoConfiguredProperties);
// a component of the reload is created anew, so it gets all its
options, not only those that changed
addOtherOptionsOfReloadedComponents(camelContext, reloadedProperties);
doAutoConfigurationFromProperties(camelContext, reloadedProperties,
properties, true, autoConfiguredProperties);
@@ -3165,6 +3167,42 @@ public abstract class BaseMainSupport extends
BaseService {
}
}
+ /**
+ * The camel.resilience4j.* and camel.faulttolerance.* keys of re-loaded
properties, applied to the default
+ * configurations of the circuit breakers as when the application starts.
A properties file that changed in dev mode
+ * creates the routes again after this, and a circuit breaker reads the
defaults when its route is created, so
+ * settings such as slidingWindowSize and failureRateThreshold can be
tried out without a restart.
+ */
+ private void reloadCircuitBreakerDefaults(
+ CamelContext camelContext, OrderedLocationProperties
reloadedProperties,
+ OrderedLocationProperties autoConfiguredProperties)
+ throws Exception {
+ OrderedLocationProperties resilience4jProperties = new
OrderedLocationProperties();
+ OrderedLocationProperties faultToleranceProperties = new
OrderedLocationProperties();
+ // stringPropertyNames is a live view of the keys, which are removed
below
+ for (String key : new
ArrayList<>(reloadedProperties.stringPropertyNames())) {
+ String loc = reloadedProperties.getLocation(key);
+ String value = reloadedProperties.getProperty(key);
+ if (startsWithIgnoreCase(key, "camel.resilience4j.")) {
+ String option = key.substring(19);
+ validateOptionAndValue(key, option, value);
+ resilience4jProperties.put(loc, optionKey(option), value);
+ reloadedProperties.remove(key);
+ } else if (startsWithIgnoreCase(key, "camel.faulttolerance.")) {
+ String option = key.substring(21);
+ validateOptionAndValue(key, option, value);
+ faultToleranceProperties.put(loc, optionKey(option), value);
+ reloadedProperties.remove(key);
+ }
+ }
+ if (!resilience4jProperties.isEmpty() ||
!faultToleranceProperties.isEmpty()) {
+ LOG.debug("Auto-configuring the circuit breakers from reloaded
properties: {}",
+ resilience4jProperties.size() +
faultToleranceProperties.size());
+
MainSupportModelConfigurer.configureModelCamelContext(camelContext,
mainConfigurationProperties,
+ autoConfiguredProperties, resilience4jProperties,
faultToleranceProperties);
+ }
+ }
+
/**
* Adds the other current options of the components that the reloaded
properties configure, as a reload creates
* these components anew: without them, a component would lose the options
that did not change. The options of the
diff --git
a/core/camel-main/src/test/java/org/apache/camel/main/MainPropertiesReloadCircuitBreakerTest.java
b/core/camel-main/src/test/java/org/apache/camel/main/MainPropertiesReloadCircuitBreakerTest.java
new file mode 100644
index 000000000000..c70e136bbc6d
--- /dev/null
+++
b/core/camel-main/src/test/java/org/apache/camel/main/MainPropertiesReloadCircuitBreakerTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import java.util.Properties;
+
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.Resilience4jConfigurationDefinition;
+import org.apache.camel.util.OrderedLocationProperties;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A properties file that changed in dev mode applies its camel.resilience4j.*
and camel.faulttolerance.* keys to the
+ * default configurations of the circuit breakers, as when the application
starts. The routes are created again after
+ * the reload, and a circuit breaker reads the defaults when its route is
created, so the new values take effect without
+ * a restart.
+ */
+public class MainPropertiesReloadCircuitBreakerTest {
+
+ @Test
+ public void theCircuitBreakerKeysOfAChangedPropertiesFileAreApplied()
throws Exception {
+ Properties startup =
properties("camel.resilience4j.sliding-window-size", "100",
+ "camel.resilience4j.failure-rate-threshold", "50");
+ Main main = main(startup);
+ try {
+ main.start();
+ ModelCamelContext model = (ModelCamelContext)
main.getCamelContext();
+
assertThat(model.getResilience4jConfiguration(null).getSlidingWindowSize()).isEqualTo("100");
+
+ // application.properties saved in dev mode, with the keys written
in either form
+ reload(main).onChanged("file:application.properties", properties(
+ "camel.resilience4j.slidingWindowSize", "4",
+ "camel.resilience4j.minimum-number-of-calls", "4",
+ "camel.faulttolerance.requestVolumeThreshold", "6"));
+
+ Resilience4jConfigurationDefinition resilience4j =
model.getResilience4jConfiguration(null);
+ assertThat(resilience4j.getSlidingWindowSize()).isEqualTo("4");
+ assertThat(resilience4j.getMinimumNumberOfCalls()).isEqualTo("4");
+ // a key that did not change keeps its value (a float in the main
configuration, so "50.0")
+
assertThat(Float.parseFloat(resilience4j.getFailureRateThreshold())).isEqualTo(50f);
+ // a configuration that the application did not have before is
created
+
assertThat(model.getFaultToleranceConfiguration(null).getRequestVolumeThreshold()).isEqualTo("6");
+ } finally {
+ main.stop();
+ }
+ }
+
+ @Test
+ public void aReloadWithoutCircuitBreakerKeysLeavesTheDefaultsAsTheyAre()
throws Exception {
+ Main main = main(properties("camel.resilience4j.sliding-window-size",
"100"));
+ try {
+ main.start();
+ ModelCamelContext model = (ModelCamelContext)
main.getCamelContext();
+
+ reload(main).onChanged("file:application.properties",
properties("greeting", "hello"));
+
+
assertThat(model.getResilience4jConfiguration(null).getSlidingWindowSize()).isEqualTo("100");
+ assertThat(model.getFaultToleranceConfiguration(null)).isNull();
+ } finally {
+ main.stop();
+ }
+ }
+
+ private static Main main(Properties properties) {
+ Main main = new Main();
+ main.setDefaultPropertyPlaceholderLocation("false");
+ main.setOverrideProperties(properties);
+ return main;
+ }
+
+ private static MainPropertiesReload reload(Main main) {
+ MainPropertiesReload reload = new MainPropertiesReload(main);
+ reload.setCamelContext(main.getCamelContext());
+ return reload;
+ }
+
+ private static OrderedLocationProperties properties(String...
keysAndValues) {
+ OrderedLocationProperties answer = new OrderedLocationProperties();
+ for (int i = 0; i < keysAndValues.length; i += 2) {
+ answer.put("file:application.properties", keysAndValues[i],
keysAndValues[i + 1]);
+ }
+ return answer;
+ }
+}