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";