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

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

commit 0e2d7e9100cb498e3563c397b9ba3506fe7de0c0
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Thu Sep 19 15:54:35 2024 +0200

    CAMEL-21241: properties component - Should capture details about resolved 
placeholders
---
 .../org/apache/camel/spi/PropertiesComponent.java  |  8 ++
 .../apache/camel/spi/PropertiesResolvedValue.java  | 24 ++++++
 .../properties/DefaultPropertiesLookup.java        |  9 ++-
 .../DefaultPropertiesLookupListener.java           | 62 +++++++++++++++
 .../properties/DefaultPropertiesParser.java        | 35 +++++---
 .../component/properties/PropertiesComponent.java  | 22 +++--
 .../camel/impl/console/PropertiesDevConsole.java   | 20 ++++-
 .../PropertiesComponentResolvedValueTest.java      | 93 ++++++++++++++++++++++
 .../camel/main/MainConfigurationDevConsole.java    | 18 +++++
 .../core/commands/process/ListProperties.java      | 26 +++++-
 10 files changed, 299 insertions(+), 18 deletions(-)

diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java
index ef725a8ec01..e078809d11e 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesComponent.java
@@ -84,6 +84,14 @@ public interface PropertiesComponent extends StaticService {
      */
     Optional<String> resolveProperty(String key);
 
+    /**
+     * Returns metadata about a property which has successfully been resolved.
+     *
+     * @param  key the name of the property
+     * @return     the property value and metadata if present
+     */
+    Optional<PropertiesResolvedValue> getResolvedValue(String key);
+
     /**
      * Loads the properties from the default locations and sources.
      *
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesResolvedValue.java
 
b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesResolvedValue.java
new file mode 100644
index 00000000000..2863b5065f7
--- /dev/null
+++ 
b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesResolvedValue.java
@@ -0,0 +1,24 @@
+/*
+ * 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.spi;
+
+/**
+ * Data about a {@link PropertiesComponent} property placeholder that has been 
resolved to a value by Camel.
+ */
+public record PropertiesResolvedValue(String name, String originalValue, 
String value, String defaultValue, String source) {
+
+}
diff --git 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
index 3f8ad715e0a..695edcee640 100644
--- 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
+++ 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
@@ -24,6 +24,8 @@ import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.LoadablePropertiesSource;
 import org.apache.camel.spi.PropertiesSource;
 import org.apache.camel.util.OrderedLocationProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Default {@link PropertiesLookup} which lookup properties from a {@link 
java.util.Properties} with all existing
@@ -31,6 +33,8 @@ import org.apache.camel.util.OrderedLocationProperties;
  */
 public class DefaultPropertiesLookup implements PropertiesLookup {
 
+    private static final Logger LOG = 
LoggerFactory.getLogger(DefaultPropertiesLookup.class);
+
     private final PropertiesComponent component;
 
     public DefaultPropertiesLookup(PropertiesComponent component) {
@@ -40,7 +44,9 @@ public class DefaultPropertiesLookup implements 
PropertiesLookup {
     @Override
     public String lookup(String name, String defaultValue) {
         try {
-            return doLookup(name, defaultValue);
+            String answer = doLookup(name, defaultValue);
+            LOG.trace("lookup(name: {} default: {}) -> {}", name, 
defaultValue, answer);
+            return answer;
         } catch (NoTypeConversionAvailableException e) {
             throw RuntimeCamelException.wrapRuntimeCamelException(e);
         }
@@ -119,6 +125,7 @@ public class DefaultPropertiesLookup implements 
PropertiesLookup {
     }
 
     private void onLookup(String name, String value, String defaultValue, 
String source) {
+        LOG.trace("Property (name: {} default: {}) resolved from source: {} -> 
{}", name, defaultValue, source, value);
         for (PropertiesLookupListener listener : 
component.getPropertiesLookupListeners()) {
             try {
                 listener.onLookup(name, value, defaultValue, source);
diff --git 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookupListener.java
 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookupListener.java
new file mode 100644
index 00000000000..d9af8fad85f
--- /dev/null
+++ 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookupListener.java
@@ -0,0 +1,62 @@
+/*
+ * 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.component.properties;
+
+import java.util.Map;
+
+import org.apache.camel.PropertiesLookupListener;
+import org.apache.camel.spi.PropertiesResolvedValue;
+import org.apache.camel.support.LRUCacheFactory;
+import org.apache.camel.support.service.ServiceSupport;
+
+/**
+ * A {@link PropertiesLookupListener} listener that captures the resolved 
properties for dev consoles, management and
+ * troubleshooting purposes.
+ */
+public class DefaultPropertiesLookupListener extends ServiceSupport implements 
PropertiesLookupListener {
+
+    private Map<String, PropertiesResolvedValue> properties;
+
+    @Override
+    public void onLookup(String name, String value, String defaultValue, 
String source) {
+        properties.put(name, new PropertiesResolvedValue(name, value, value, 
defaultValue, source));
+    }
+
+    void updateValue(String name, String newValue, String newSource) {
+        var p = properties.get(name);
+        if (p != null) {
+            String source = newSource != null ? newSource : p.source();
+            properties.put(name, new PropertiesResolvedValue(p.name(), 
p.originalValue(), newValue, p.defaultValue(), source));
+        }
+    }
+
+    public PropertiesResolvedValue getProperty(String key) {
+        return properties.get(key);
+    }
+
+    @Override
+    protected void doBuild() throws Exception {
+        // use a cache with max limit to avoid capturing endless property 
values
+        // if there are a lot of dynamic values
+        properties = LRUCacheFactory.newLRUCache(1000);
+    }
+
+    @Override
+    protected void doShutdown() throws Exception {
+        properties.clear();
+    }
+}
diff --git 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
index efcdac9b64e..43edd497bb1 100644
--- 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
+++ 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
@@ -118,7 +118,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
                 input = input.replace("?nested=false", "");
             }
             if (nested) {
-                return doParseNested(input, new HashSet<>());
+                return doParseNested(null, input, new HashSet<>());
             } else {
                 return doParse(input);
             }
@@ -137,7 +137,8 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
 
             StringBuilder answer = new StringBuilder(input.length());
             Property property;
-            while ((property = readProperty(input)) != null) {
+            String prevKey = null;
+            while ((property = readProperty(prevKey, input)) != null) {
                 String before = input.substring(0, property.getBeginIndex());
                 String after = input.substring(property.getEndIndex());
                 String parsed = property.getValue();
@@ -149,6 +150,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
                     return null;
                 }
                 input = after;
+                prevKey = property.getKey();
             }
             if (!input.isEmpty()) {
                 answer.append(input);
@@ -163,13 +165,13 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
          * @param  replacedPropertyKeys Already replaced property keys used 
for tracking circular references
          * @return                      Evaluated string
          */
-        private String doParseNested(String input, Set<String> 
replacedPropertyKeys) {
+        private String doParseNested(String prevKey, String input, Set<String> 
replacedPropertyKeys) {
             if (input == null) {
                 return null;
             }
             String answer = input;
             Property property;
-            while ((property = readProperty(answer)) != null) {
+            while ((property = readProperty(prevKey, answer)) != null) {
                 if (replacedPropertyKeys.contains(property.getKey())) {
                     // Check for circular references (skip optional)
                     boolean optional = 
property.getKey().startsWith(OPTIONAL_TOKEN);
@@ -181,6 +183,12 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
                     }
                 }
 
+                if (propertiesComponent != null) {
+                    // nested placeholder so update resolved property with new 
value
+                    String k = prevKey != null ? prevKey : property.getKey();
+                    propertiesComponent.updateResolvedValue(k, 
property.getValue(), null);
+                }
+
                 Set<String> newReplaced = new HashSet<>(replacedPropertyKeys);
                 newReplaced.add(property.getKey());
 
@@ -191,7 +199,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
                 }
                 String before = answer.substring(0, beginIndex);
                 String after = answer.substring(property.getEndIndex());
-                String parsed = doParseNested(property.getValue(), 
newReplaced);
+                String parsed = doParseNested(property.getKey(), 
property.getValue(), newReplaced);
                 if (parsed != null) {
                     answer = before + parsed + after;
                 } else {
@@ -213,7 +221,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
          * @param  input Input string
          * @return       A property in the given string or {@code null} if not 
found
          */
-        private Property readProperty(String input) {
+        private Property readProperty(String prevKey, String input) {
             // Find the index of the first valid suffix token
             int suffix = getSuffixIndex(input);
 
@@ -232,7 +240,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
             }
 
             String key = input.substring(prefix + PREFIX_TOKEN.length(), 
suffix);
-            String value = getPropertyValue(key, input);
+            String value = getPropertyValue(prevKey, key, input);
             return new Property(prefix, suffix + SUFFIX_TOKEN.length(), key, 
value);
         }
 
@@ -308,7 +316,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
          * @param  input Input string (used for exception message if value not 
found)
          * @return       Value of the property with the given key
          */
-        private String getPropertyValue(String key, String input) {
+        private String getPropertyValue(String prevKey, String key, String 
input) {
             if (key == null) {
                 return null;
             }
@@ -326,7 +334,7 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
                     String remainder = StringHelper.after(key, ":");
                     boolean remainderOptional = 
remainder.startsWith(OPTIONAL_TOKEN);
                     if (function.lookupFirst(remainder)) {
-                        String value = getPropertyValue(remainder, input);
+                        String value = getPropertyValue(prevKey, remainder, 
input);
                         if (value == null && (remainderOptional || 
function.optional(remainder))) {
                             return null;
                         }
@@ -366,6 +374,8 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
                             log.debug("Property with key [{}] applied by 
function [{}] -> {}", key, function.getName(),
                                     value);
                         }
+                        String k = prevKey != null ? prevKey : key;
+                        propertiesComponent.updateResolvedValue(k, value, 
function.getName());
                         return value;
                     }
                 }
@@ -382,6 +392,13 @@ public class DefaultPropertiesParser implements 
PropertiesParser {
             if (value == null && defaultValue != null) {
                 log.debug("Property with key [{}] not found, using default 
value: {}", key, defaultValue);
                 value = defaultValue;
+                for (PropertiesLookupListener listener : 
propertiesComponent.getPropertiesLookupListeners()) {
+                    try {
+                        listener.onLookup(key, value, defaultValue, null);
+                    } catch (Exception e) {
+                        // ignore
+                    }
+                }
             }
 
             if (value == null) {
diff --git 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
index 0cfa3746e72..698103884de 100644
--- 
a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
+++ 
b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesComponent.java
@@ -36,6 +36,7 @@ import org.apache.camel.spi.Configurer;
 import org.apache.camel.spi.FactoryFinder;
 import org.apache.camel.spi.LoadablePropertiesSource;
 import org.apache.camel.spi.PropertiesFunction;
+import org.apache.camel.spi.PropertiesResolvedValue;
 import org.apache.camel.spi.PropertiesSource;
 import org.apache.camel.spi.PropertiesSourceFactory;
 import org.apache.camel.spi.annotations.JdkService;
@@ -111,6 +112,7 @@ public class PropertiesComponent extends ServiceSupport
     private final PropertiesLookup propertiesLookup = new 
DefaultPropertiesLookup(this);
     private final List<PropertiesLookupListener> propertiesLookupListeners = 
new ArrayList<>();
     private final PropertiesSourceFactory propertiesSourceFactory = new 
DefaultPropertiesSourceFactory(this);
+    private final DefaultPropertiesLookupListener 
defaultPropertiesLookupListener = new DefaultPropertiesLookupListener();
     private final List<PropertiesSource> sources = new ArrayList<>();
     private List<PropertiesLocation> locations = new ArrayList<>();
     private String location;
@@ -128,6 +130,7 @@ public class PropertiesComponent extends ServiceSupport
     private boolean autoDiscoverPropertiesSources = true;
 
     public PropertiesComponent() {
+        addPropertiesLookupListener(defaultPropertiesLookupListener);
         // include out of the box functions
         addPropertiesFunction(new EnvPropertiesFunction());
         addPropertiesFunction(new SysPropertiesFunction());
@@ -187,6 +190,15 @@ public class PropertiesComponent extends ServiceSupport
         }
     }
 
+    @Override
+    public Optional<PropertiesResolvedValue> getResolvedValue(String key) {
+        return 
Optional.ofNullable(defaultPropertiesLookupListener.getProperty(key));
+    }
+
+    public void updateResolvedValue(String key, String newValue, String 
newSource) {
+        defaultPropertiesLookupListener.updateValue(key, newValue, newSource);
+    }
+
     @Override
     public Properties loadProperties() {
         // this method may be replaced by loadProperties(k -> true) but the 
underlying sources
@@ -784,27 +796,27 @@ public class PropertiesComponent extends ServiceSupport
         }
 
         sources.sort(OrderedComparator.get());
-        ServiceHelper.initService(sources, propertiesFunctionResolver);
+        ServiceHelper.initService(sources, propertiesFunctionResolver, 
defaultPropertiesLookupListener);
     }
 
     @Override
     protected void doBuild() throws Exception {
-        ServiceHelper.buildService(sources, propertiesFunctionResolver);
+        ServiceHelper.buildService(sources, propertiesFunctionResolver, 
defaultPropertiesLookupListener);
     }
 
     @Override
     protected void doStart() throws Exception {
-        ServiceHelper.startService(sources, propertiesFunctionResolver);
+        ServiceHelper.startService(sources, propertiesFunctionResolver, 
defaultPropertiesLookupListener);
     }
 
     @Override
     protected void doStop() throws Exception {
-        ServiceHelper.stopService(sources, propertiesFunctionResolver);
+        ServiceHelper.stopService(sources, propertiesFunctionResolver, 
defaultPropertiesLookupListener);
     }
 
     @Override
     protected void doShutdown() throws Exception {
-        ServiceHelper.stopAndShutdownServices(sources, 
propertiesFunctionResolver);
+        ServiceHelper.stopAndShutdownServices(sources, 
propertiesFunctionResolver, defaultPropertiesLookupListener);
     }
 
     private void addPropertiesLocationsAsPropertiesSource(PropertiesLocation 
location, int order) {
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
index 9b0884b3e67..2ebe943ee55 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
@@ -82,10 +82,28 @@ public class PropertiesDevConsole extends 
AbstractDevConsole {
             String k = entry.getKey().toString();
             Object v = entry.getValue();
             String loc = olp != null ? olp.getLocation(k) : null;
-
+            String originalValue = null;
+            String defaultValue = null;
+            String source = null;
+            var m = pc.getResolvedValue(k);
+            if (m.isPresent()) {
+                originalValue = m.get().originalValue();
+                defaultValue = m.get().defaultValue();
+                source = m.get().source();
+                v = m.get().value();
+            }
             JsonObject jo = new JsonObject();
             jo.put("key", k);
             jo.put("value", v);
+            if (originalValue != null) {
+                jo.put("originalValue", originalValue);
+            }
+            if (defaultValue != null) {
+                jo.put("defaultValue", defaultValue);
+            }
+            if (source != null) {
+                jo.put("source", source);
+            }
             if (loc != null) {
                 jo.put("location", loc);
                 jo.put("internal", isInternal(loc));
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentResolvedValueTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentResolvedValueTest.java
new file mode 100644
index 00000000000..c7768d33412
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/properties/PropertiesComponentResolvedValueTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.component.properties;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class PropertiesComponentResolvedValueTest extends ContextTestSupport {
+
+    @Test
+    public void testResolved() {
+        org.apache.camel.spi.PropertiesComponent pc = 
context.getPropertiesComponent();
+
+        Assertions.assertTrue(pc.getResolvedValue("unknown").isEmpty());
+        Assertions.assertTrue(pc.getResolvedValue("greeting").isPresent());
+        Assertions.assertTrue(pc.getResolvedValue("cool.end").isPresent());
+        Assertions.assertTrue(pc.getResolvedValue("place").isPresent());
+        Assertions.assertTrue(pc.getResolvedValue("myserver").isPresent());
+
+        // added initial via code
+        var p = pc.getResolvedValue("greeting").get();
+        Assertions.assertEquals("greeting", p.name());
+        Assertions.assertEquals("Hello World", p.originalValue());
+        Assertions.assertEquals("Hello World", p.value());
+        Assertions.assertEquals("Hi", p.defaultValue());
+        Assertions.assertEquals("InitialProperties", p.source());
+
+        // from properties file
+        p = pc.getResolvedValue("cool.end").get();
+        Assertions.assertEquals("cool.end", p.name());
+        Assertions.assertEquals("mock:result", p.originalValue());
+        Assertions.assertEquals("mock:result", p.value());
+        Assertions.assertNull(p.defaultValue());
+        
Assertions.assertEquals("classpath:org/apache/camel/component/properties/myproperties.properties",
 p.source());
+
+        // no source but using default value
+        p = pc.getResolvedValue("place").get();
+        Assertions.assertEquals("place", p.name());
+        Assertions.assertEquals("Paris", p.originalValue());
+        Assertions.assertEquals("Paris", p.value());
+        Assertions.assertEquals("Paris", p.defaultValue());
+        Assertions.assertNull(p.source());
+
+        // nested
+        p = pc.getResolvedValue("myserver").get();
+        Assertions.assertEquals("myserver", p.name());
+        Assertions.assertEquals("127.0.0.1", p.value());
+        Assertions.assertEquals("{{env:MY_SERVER:127.0.0.1}}", 
p.originalValue());
+        Assertions.assertNull(p.defaultValue());
+        Assertions.assertEquals("env", p.source());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .setBody(constant("{{greeting:Hi}}"))
+                        .setHeader("bar", constant("{{?place:Paris}}"))
+                        .setHeader("server", constant("{{myserver}}"))
+                        .to("{{cool.end}}");
+            }
+        };
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        
context.getPropertiesComponent().setLocation("classpath:org/apache/camel/component/properties/myproperties.properties");
+        context.getPropertiesComponent().addInitialProperty("greeting", "Hello 
World");
+        context.getPropertiesComponent().addInitialProperty("myserver", 
"{{env:MY_SERVER:127.0.0.1}}");
+        return context;
+    }
+
+}
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationDevConsole.java
 
b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationDevConsole.java
index e1a7dce07d7..aa88d984716 100644
--- 
a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationDevConsole.java
+++ 
b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationDevConsole.java
@@ -18,6 +18,7 @@ package org.apache.camel.main;
 
 import java.util.Map;
 
+import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.annotations.DevConsole;
 import org.apache.camel.support.console.AbstractDevConsole;
 import org.apache.camel.util.OrderedLocationProperties;
@@ -68,6 +69,8 @@ public class MainConfigurationDevConsole extends 
AbstractDevConsole {
 
     @Override
     protected Map<String, Object> doCallJson(Map<String, Object> options) {
+        PropertiesComponent pc = getCamelContext().getPropertiesComponent();
+
         JsonObject root = new JsonObject();
         if (!startupConfiguration.isEmpty()) {
             JsonArray arr = new JsonArray();
@@ -75,10 +78,25 @@ public class MainConfigurationDevConsole extends 
AbstractDevConsole {
                 String k = entry.getKey().toString();
                 Object v = entry.getValue();
                 String loc = startupConfiguration.getLocation(k);
+                Object defaultValue = startupConfiguration.getDefaultValue(k);
 
                 JsonObject jo = new JsonObject();
                 jo.put("key", k);
                 jo.put("value", v);
+                if (defaultValue != null) {
+                    jo.put("defaultValue", defaultValue);
+                }
+                // enrich if present
+                pc.getResolvedValue(k).ifPresent(r -> {
+                    String ov = r.originalValue();
+                    if (ov != null) {
+                        jo.put("originalValue", ov);
+                    }
+                    String src = r.source();
+                    if (src != null) {
+                        jo.put("source", src);
+                    }
+                });
                 if (loc != null) {
                     jo.put("location", loc);
                     jo.put("internal", isInternal(loc));
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProperties.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProperties.java
index 3da48a218bf..02bb13ceb90 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProperties.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListProperties.java
@@ -28,6 +28,7 @@ import com.github.freva.asciitable.OverflowBehaviour;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.util.SensitiveUtils;
+import org.apache.camel.util.StringHelper;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import picocli.CommandLine;
@@ -58,6 +59,9 @@ public class ListProperties extends ProcessWatchCommand {
     @CommandLine.Option(names = { "--startup" }, description = "List only 
startup configuration")
     boolean startup;
 
+    @CommandLine.Option(names = { "--verbose" }, description = "Whether to 
include more details")
+    boolean verbose;
+
     @CommandLine.Option(names = { "--internal" }, description = "Whether to 
include internal configuration")
     boolean internal;
 
@@ -109,7 +113,13 @@ public class ListProperties extends ProcessWatchCommand {
                                 value = "xxxxxx";
                             }
                             row.value = value;
+                            value = jo.getString("originalValue");
+                            if (mask && 
SensitiveUtils.containsSensitive(row.key)) {
+                                value = "xxxxxx";
+                            }
+                            row.originalValue = value;
                             row.internalLoc = 
jo.getBooleanOrDefault("internal", false);
+                            row.source = jo.getString("source");
                             row.loc = 
sanitizeLocation(jo.getString("location"));
                             boolean accept = internal || !row.internalLoc;
                             if (accept) {
@@ -132,12 +142,22 @@ public class ListProperties extends ProcessWatchCommand {
                     new 
Column().header("KEY").dataAlign(HorizontalAlign.LEFT).maxWidth(50, 
OverflowBehaviour.ELLIPSIS_RIGHT)
                             .with(r -> r.key),
                     new 
Column().header("VALUE").dataAlign(HorizontalAlign.LEFT).maxWidth(80, 
OverflowBehaviour.NEWLINE)
-                            .with(r -> "" + r.value))));
+                            .with(r -> "" + r.value),
+                    new 
Column().header("FUNCTION").visible(verbose).dataAlign(HorizontalAlign.LEFT)
+                            .maxWidth(50, OverflowBehaviour.ELLIPSIS_RIGHT)
+                            .with(this::getFunction),
+                    new Column().header("ORIGINAL 
VALUE").visible(verbose).dataAlign(HorizontalAlign.LEFT)
+                            .maxWidth(80, OverflowBehaviour.NEWLINE)
+                            .with(r -> "" + r.originalValue))));
         }
 
         return 0;
     }
 
+    protected String getFunction(Row r) {
+        return StringHelper.before(r.source, ":", r.source);
+    }
+
     protected int sortRow(Row o1, Row o2) {
         String s = sort;
         int negate = 1;
@@ -162,6 +182,8 @@ public class ListProperties extends ProcessWatchCommand {
         String name;
         String key;
         Object value;
+        Object originalValue;
+        String source;
         String loc;
         boolean internalLoc;
 
@@ -182,7 +204,7 @@ public class ListProperties extends ProcessWatchCommand {
             loc = "camel-main";
         } else if ("SYS".equals(loc)) {
             loc = "JVM System Property";
-        } else if ("ENV".equals(loc)) {
+        } else if ("ENV".equals(loc) || "env".equals(loc)) {
             loc = "OS Environment Variable";
         } else if ("arguments".equals(loc) || "CLI".equals(loc)) {
             loc = "Command Line";

Reply via email to