From 46c58dc48cc0f73c14d5d6087cb5974e9e6b49b6 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 23 Oct 2025 21:13:15 +0530 Subject: [PATCH 001/221] * Improved property source resolution and logged conflicts * Consolidated sources into hierarchy of precedence: EnvironmentVariables > RuntimeConfiguration > KillBillDefaults * Added status indicators and conflict warnings --- .../config/DefaultKillbillConfigSource.java | 201 ++++++++++++++---- .../config/PropertiesWithSourceCollector.java | 8 +- .../test/config/TestKillbillConfigSource.java | 4 +- 3 files changed, 165 insertions(+), 48 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index e6ae6c97f..7fb3d4359 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,18 +21,21 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.Set; import java.util.TimeZone; import javax.annotation.Nullable; @@ -98,21 +101,11 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); - + final Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); } - for (final Entry entry : extraDefaultProperties.entrySet()) { - if (entry.getValue() != null) { - properties.put(entry.getKey(), entry.getValue()); - } - } - - propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); - - populateDefaultProperties(); + populateDefaultProperties(extraDefaultProperties); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); @@ -160,9 +153,8 @@ private Properties loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); - final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); return properties; } catch (final IOException e) { @@ -172,14 +164,17 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); + propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); return new Properties(System.getProperties()); } @VisibleForTesting - protected void populateDefaultProperties() { + protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); + + extraDefaultProperties.forEach(defaultProperties::putIfAbsent); + for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties if (properties.get(propertyName) == null) { @@ -236,7 +231,7 @@ protected void populateDefaultProperties() { defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("DefaultSystemProperties", propsMap); + propertiesCollector.addProperties("KillBillDefaults", propsMap); } @Override @@ -257,21 +252,160 @@ public Map> getPropertiesBySource() { } }); + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + final Map effectiveProperties = getCurrentEffectiveProperties(); + + final Map> propertyToSources = new HashMap<>(); + propertiesBySource.forEach((source, properties) -> { + properties.forEach(property -> propertyToSources.computeIfAbsent(property.getKey(), + propName -> new LinkedHashSet<>()).add(source)); + }); + + final List sourceOrder = Arrays.asList("EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults"); + + final Set warnedConflicts = new HashSet<>(); final Map> result = new LinkedHashMap<>(); - propertiesBySource.forEach((source, properties) -> { + for (final String source : sourceOrder) { + final List properties = propertiesBySource.get(source); + if (properties == null || properties.isEmpty()) { + continue; + } + final Map sourceProperties = new LinkedHashMap<>(); - properties.forEach(prop -> { - sourceProperties.put(prop.getKey(), prop.getValue()); - }); - result.put(source, Collections.unmodifiableMap(sourceProperties)); + + final Set processedKeys = new HashSet<>(); + + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + + if (processedKeys.contains(propertyKey)) { + continue; + } + processedKeys.add(propertyKey); + + final String effectiveValue = effectiveProperties.get(propertyKey); + if (effectiveValue == null) { + continue; + } + + final Set sources = propertyToSources.get(propertyKey); + final boolean hasConflict = sources != null && sources.size() > 1; + + final boolean isEffectiveSource = isEffectiveSourceForProperty(propertyKey, source, sourceOrder, propertyToSources); + + final String displayValue; + if (isEffectiveSource) { + if (hasConflict) { + if (!warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, new ArrayList<>(sources), source, effectiveValue); + } + + final List overridden = getOverriddenSources(source, sources, sourceOrder); + if (!overridden.isEmpty()) { + displayValue = effectiveValue + " [ACTIVE -- overrides: " + String.join(", ", overridden) + "]"; + } else { + displayValue = effectiveValue + " [ACTIVE]"; + } + } else { + displayValue = effectiveValue; + } + } else { + final String effectiveSourceName = getEffectiveSourceName(propertyKey, sourceOrder, propertyToSources); + displayValue = effectiveValue + " [OVERRIDDEN by " + effectiveSourceName + "]"; + } + + sourceProperties.put(propertyKey, displayValue); + } + + if (!sourceProperties.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceProperties)); + } + } + + propertiesBySource.forEach((source, properties) -> { + if (!sourceOrder.contains(source) && !result.containsKey(source)) { + final Map sourceProperties = new LinkedHashMap<>(); + properties.forEach(prop -> { + sourceProperties.put(prop.getKey(), prop.getValue()); + }); + if (!sourceProperties.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceProperties)); + } + } }); return Collections.unmodifiableMap(result); } + private boolean isEffectiveSourceForProperty(final String propertyKey, + final String currentSource, + final List sourceOrder, + final Map> propertyToSources) { + final Set sourcesForProperty = propertyToSources.get(propertyKey); + if (sourcesForProperty == null || !sourcesForProperty.contains(currentSource)) { + return false; + } + + for (final String source : sourceOrder) { + if (sourcesForProperty.contains(source)) { + return source.equals(currentSource); + } + } + + return false; + } + + private String getEffectiveSourceName(final String propertyKey, + final List sourceOrder, + final Map> propertyToSources) { + final Set sources = propertyToSources.get(propertyKey); + if (sources == null) { + return "unknown"; + } + + for (final String source : sourceOrder) { + if (sources.contains(source)) { + return source; + } + } + + return "unknown"; + } + + private Map getCurrentEffectiveProperties() { + final Map effectiveProps = new HashMap<>(); + + properties.stringPropertyNames() + .forEach(key -> effectiveProps.put(key, properties.getProperty(key))); + + return effectiveProps; + } + + private List getOverriddenSources(final String currentSource, + final Set allSources, + final List sourceOrder) { + final List overridden = new ArrayList<>(); + final int currentIndex = sourceOrder.indexOf(currentSource); + + for (final String source : allSources) { + if (!source.equals(currentSource)) { + final int sourceIndex = sourceOrder.indexOf(source); + if (sourceIndex > currentIndex && sourceIndex != -1) { + overridden.add(source); + } + } + } + + return overridden; + } + @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { properties.put(propertyName, propertyValue); @@ -386,23 +520,6 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } - private String extractFileNameFromPath(String path) { - if (path == null || path.isEmpty()) { - return "unknown.properties"; - } - - if (path.startsWith("file://")) { - path = path.substring("file://".length()); - } - - final Path fileName = Paths.get(path).getFileName(); - if (fileName == null) { - return "unknown.properties"; - } - - return fileName.toString(); - } - private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { diff --git a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java index d7d1b4625..f4783209c 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java +++ b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java @@ -29,12 +29,12 @@ public class PropertiesWithSourceCollector { private volatile List properties = new ArrayList<>(); private final Object lock = new Object(); - public void addProperties(String source, Map props) { + public void addProperties(final String source, final Map props) { synchronized (lock) { - List newList = new ArrayList<>(properties); + final List updatedProperties = new ArrayList<>(properties); props.forEach((key, value) -> - newList.add(new PropertyWithSource(source, key, value))); - this.properties = Collections.unmodifiableList(newList); + updatedProperties.add(new PropertyWithSource(source, key, value))); + this.properties = Collections.unmodifiableList(updatedProperties); } } diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 4709b94c1..1ebce418b 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(); + populateDefaultProperties(extraDefaults); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(); + populateDefaultProperties(extraDefaults); } @Override From ee3ac640d43b07181a54e90e58f6b9da15e43a22 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 31 Oct 2025 01:26:51 +0530 Subject: [PATCH 002/221] * Added ImmutableSystemProperties to properties collector. * Removed property conflict string from property values. * Added a unit test to verify getPropertiesBySource() behavior and source precedence. --- .../config/DefaultKillbillConfigSource.java | 63 +++++++------------ .../TestDefaultKillbillConfigSource.java | 39 ++++++++++-- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 7fb3d4359..3d2e820c8 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -182,6 +182,8 @@ protected void populateDefaultProperties(final Map extraDefaultP } } + final Map immutableProps = new HashMap<>(); + final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -207,6 +209,10 @@ protected void populateDefaultProperties(final Map extraDefaultP // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); + + immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); + properties.put(propertyName, GMT_ID); + continue; } @@ -214,6 +220,10 @@ protected void populateDefaultProperties(final Map extraDefaultP if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } + + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultSystemProperties.get(propertyName)); + } } // WARN for missing PROP_SECURITY_EGD @@ -228,6 +238,10 @@ protected void populateDefaultProperties(final Map extraDefaultP } } + if (!immutableProps.isEmpty()) { + propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); + } + defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); @@ -252,7 +266,6 @@ public Map> getPropertiesBySource() { } }); - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); final Map effectiveProperties = getCurrentEffectiveProperties(); @@ -262,7 +275,8 @@ public Map> getPropertiesBySource() { propName -> new LinkedHashSet<>()).add(source)); }); - final List sourceOrder = Arrays.asList("EnvironmentVariables", + final List sourceOrder = Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", "RuntimeConfiguration", "KillBillDefaults"); @@ -298,30 +312,15 @@ public Map> getPropertiesBySource() { final boolean isEffectiveSource = isEffectiveSourceForProperty(propertyKey, source, sourceOrder, propertyToSources); - final String displayValue; - if (isEffectiveSource) { - if (hasConflict) { - if (!warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, effectiveValue); - } - - final List overridden = getOverriddenSources(source, sources, sourceOrder); - if (!overridden.isEmpty()) { - displayValue = effectiveValue + " [ACTIVE -- overrides: " + String.join(", ", overridden) + "]"; - } else { - displayValue = effectiveValue + " [ACTIVE]"; - } - } else { - displayValue = effectiveValue; + if (isEffectiveSource && hasConflict) { + if (!warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, new ArrayList<>(sources), source, effectiveValue); } - } else { - final String effectiveSourceName = getEffectiveSourceName(propertyKey, sourceOrder, propertyToSources); - displayValue = effectiveValue + " [OVERRIDDEN by " + effectiveSourceName + "]"; } - sourceProperties.put(propertyKey, displayValue); + sourceProperties.put(propertyKey, effectiveValue); } if (!sourceProperties.isEmpty()) { @@ -388,24 +387,6 @@ private Map getCurrentEffectiveProperties() { return effectiveProps; } - private List getOverriddenSources(final String currentSource, - final Set allSources, - final List sourceOrder) { - final List overridden = new ArrayList<>(); - final int currentIndex = sourceOrder.indexOf(currentSource); - - for (final String source : allSources) { - if (!source.equals(currentSource)) { - final int sourceIndex = sourceOrder.indexOf(source); - if (sourceIndex > currentIndex && sourceIndex != -1) { - overridden.add(source); - } - } - } - - return overridden; - } - @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { properties.put(propertyName, propertyValue); diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 8e6191a08..f8fc68700 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -21,7 +21,10 @@ import java.io.IOException; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; @@ -70,9 +73,12 @@ public void testGetProperties() throws URISyntaxException, IOException { @Test(groups = "fast") public void testGetPropertiesBySource() throws URISyntaxException, IOException { + System.setProperty("org.killbill.dao.user", "root"); + System.setProperty("org.killbill.dao.password", "password"); + final Map configuration = new HashMap<>(); - configuration.put("org.killbill.dao.user", "root"); - configuration.put("org.killbill.dao.password", "password"); + configuration.put("org.killbill.server.shutdownDelay", "1s"); + configuration.put("org.killbill.billing.osgi.dao.logLevel", "ERROR"); final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); @@ -81,10 +87,31 @@ public void testGetPropertiesBySource() throws URISyntaxException, IOException { Assert.assertNotNull(propsBySource); Assert.assertFalse(propsBySource.isEmpty()); - final Map defaultProps = propsBySource.get("ExtraDefaultProperties"); - Assert.assertNotNull(defaultProps); - Assert.assertEquals(defaultProps.get("org.killbill.dao.user"), "root"); - Assert.assertEquals(defaultProps.get("org.killbill.dao.password"), "password"); + Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); + + final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); + Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); + + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); + + final Map runtimeConfig = propsBySource.get("RuntimeConfiguration"); + Assert.assertEquals(runtimeConfig.get("org.killbill.dao.user"), "root"); + Assert.assertEquals(runtimeConfig.get("org.killbill.dao.password"), "password"); + + Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); + + final Map killBillDefaults = propsBySource.get("KillBillDefaults"); + Assert.assertEquals(killBillDefaults.get("org.killbill.server.enableJasypt"), "false"); + Assert.assertEquals(killBillDefaults.get("org.killbill.persistent.bus.external.tableName"), "bus_ext_events"); + Assert.assertEquals(killBillDefaults.get("org.slf4j.simpleLogger.log.jdbc"), "ERROR"); + + final List actualSourceOrder = new ArrayList<>(propsBySource.keySet()); + + final List expectedPrecedenceOrder = Arrays.asList("ImmutableSystemProperties", + "RuntimeConfiguration", + "KillBillDefaults"); + + Assert.assertEquals(actualSourceOrder, expectedPrecedenceOrder); } @Test(groups = "fast") From bd22b4c752aae39e5820ad293fc87f962cccc95b Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 1 Nov 2025 07:45:31 +0530 Subject: [PATCH 003/221] Fixed property resolution order --- .../config/DefaultKillbillConfigSource.java | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 3d2e820c8..2f9b071b8 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -173,7 +173,7 @@ private Properties loadPropertiesFromFileOrSystemProperties() { protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); - extraDefaultProperties.forEach(defaultProperties::putIfAbsent); + defaultProperties.putAll(extraDefaultProperties); for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties @@ -361,23 +361,6 @@ private boolean isEffectiveSourceForProperty(final String propertyKey, return false; } - private String getEffectiveSourceName(final String propertyKey, - final List sourceOrder, - final Map> propertyToSources) { - final Set sources = propertyToSources.get(propertyKey); - if (sources == null) { - return "unknown"; - } - - for (final String source : sourceOrder) { - if (sources.contains(source)) { - return source; - } - } - - return "unknown"; - } - private Map getCurrentEffectiveProperties() { final Map effectiveProps = new HashMap<>(); From ead5cc0e1fe35fd9f473bef8f02282849429cc9a Mon Sep 17 00:00:00 2001 From: Vijay N Date: Mon, 10 Nov 2025 22:50:51 +0530 Subject: [PATCH 004/221] * Refactored to use a single source of truth for getProperties() and getPropertiesBySource(). * Cached computed results for improved performance. * Added unit tests. --- .../config/DefaultKillbillConfigSource.java | 322 ++++++++++-------- .../TestDefaultKillbillConfigSource.java | 132 ++++++- 2 files changed, 304 insertions(+), 150 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 2f9b071b8..27f562e59 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -76,10 +76,17 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; - private final PropertiesWithSourceCollector propertiesCollector; + private static final List HIGH_TO_LOW_PRIORITY_ORDER = + Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults")); + private final PropertiesWithSourceCollector propertiesCollector; private final Properties properties; + private volatile Map> cachedPropertiesBySource; + public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); } @@ -124,23 +131,132 @@ public String getString(final String propertyName) { @Override public Properties getProperties() { final Properties result = new Properties(); - // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside - // tomcat, if we put configuration in tomcat's catalina.properties - // See: - // - https://github.com/killbill/technical-support/issues/61 - // - https://github.com/killbill/technical-support/issues/67 - // - // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar - // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) - properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + + return result; + } + + @Override + public Map> getPropertiesBySource() { + if (cachedPropertiesBySource == null) { + synchronized (lock) { + if (cachedPropertiesBySource == null) { + cachedPropertiesBySource = computePropertiesBySource(); + } + } + } + + return Collections.unmodifiableMap(cachedPropertiesBySource); + } + + private Map> computePropertiesBySource() { + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + if (!props.isEmpty()) { + propertiesCollector.addProperties(source, props); + } + }); + + final Map effectiveMap = new LinkedHashMap<>(); + properties.stringPropertyNames().forEach(key -> effectiveMap.put(key, properties.getProperty(key))); RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!result.containsKey(key)) { - result.setProperty(key, value); + if (!effectiveMap.containsKey(key)) { + effectiveMap.put(key, value); } }); - return result; + final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + + final Map> propertyToSources = new HashMap<>(); + collectorBySource.forEach((source, properties) -> { + properties.forEach(property -> { + propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); + }); + }); + + final Set warnedConflicts = new HashSet<>(); + final Map> result = new LinkedHashMap<>(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List properties = collectorBySource.get(source); + if (properties == null || properties.isEmpty()) { + continue; + } + + final Map sourceMap = new LinkedHashMap<>(); + final Set processedKeys = new HashSet<>(); + + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + + if (processedKeys.contains(propertyKey)) { + continue; + } + processedKeys.add(propertyKey); + + final String effectiveValue = prop.getValue(); + + if (effectiveValue == null) { + continue; + } + + if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + continue; + } + + final Set sources = propertyToSources.get(propertyKey); + if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, new ArrayList<>(sources), source, effectiveValue); + } + + sourceMap.put(propertyKey, effectiveValue); + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + } + + collectorBySource.forEach((source, properties) -> { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + return; + } + + final Map sourceMap = new LinkedHashMap<>(); + for (final PropertyWithSource prop : properties) { + final String effectiveValue = effectiveMap.get(prop.getKey()); + if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { + sourceMap.put(prop.getKey(), effectiveValue); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + }); + + return Collections.unmodifiableMap(result); + } + + private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, + final Map> propertiesBySource) { + final List sourcesForKey = new ArrayList<>(); + propertiesBySource.forEach((source, props) -> { + if (props.stream().anyMatch(p -> p.getKey().equals(key))) { + sourcesForKey.add(source); + } + }); + + for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { + if (sourcesForKey.contains(prioritySource)) { + return prioritySource.equals(sourceToCheck); + } + } + + return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } private Properties loadPropertiesFromFileOrSystemProperties() { @@ -165,14 +281,12 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); - return new Properties(System.getProperties()); } @VisibleForTesting protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); - defaultProperties.putAll(extraDefaultProperties); for (final String propertyName : defaultProperties.stringPropertyNames()) { @@ -212,7 +326,6 @@ protected void populateDefaultProperties(final Map extraDefaultP immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); properties.put(propertyName, GMT_ID); - continue; } @@ -248,131 +361,17 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("KillBillDefaults", propsMap); } - @Override - public Map> getPropertiesBySource() { - final Map currentProps = new HashMap<>(); - properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); - - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - runtimeBySource.forEach((source, props) -> { - final Map filteredProps = new HashMap<>(); - props.forEach((key, value) -> { - if (!currentProps.containsKey(key)) { - filteredProps.put(key, value); - } - }); - if (!filteredProps.isEmpty()) { - propertiesCollector.addProperties(source, filteredProps); - } - }); - - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - final Map effectiveProperties = getCurrentEffectiveProperties(); - - final Map> propertyToSources = new HashMap<>(); - propertiesBySource.forEach((source, properties) -> { - properties.forEach(property -> propertyToSources.computeIfAbsent(property.getKey(), - propName -> new LinkedHashSet<>()).add(source)); - }); - - final List sourceOrder = Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults"); - - final Set warnedConflicts = new HashSet<>(); - - final Map> result = new LinkedHashMap<>(); - - for (final String source : sourceOrder) { - final List properties = propertiesBySource.get(source); - if (properties == null || properties.isEmpty()) { - continue; - } - - final Map sourceProperties = new LinkedHashMap<>(); - - final Set processedKeys = new HashSet<>(); - - for (final PropertyWithSource prop : properties) { - final String propertyKey = prop.getKey(); - - if (processedKeys.contains(propertyKey)) { - continue; - } - processedKeys.add(propertyKey); - - final String effectiveValue = effectiveProperties.get(propertyKey); - if (effectiveValue == null) { - continue; - } - - final Set sources = propertyToSources.get(propertyKey); - final boolean hasConflict = sources != null && sources.size() > 1; - - final boolean isEffectiveSource = isEffectiveSourceForProperty(propertyKey, source, sourceOrder, propertyToSources); - - if (isEffectiveSource && hasConflict) { - if (!warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, effectiveValue); - } - } - - sourceProperties.put(propertyKey, effectiveValue); - } - - if (!sourceProperties.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceProperties)); - } - } - - propertiesBySource.forEach((source, properties) -> { - if (!sourceOrder.contains(source) && !result.containsKey(source)) { - final Map sourceProperties = new LinkedHashMap<>(); - properties.forEach(prop -> { - sourceProperties.put(prop.getKey(), prop.getValue()); - }); - if (!sourceProperties.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceProperties)); - } - } - }); - - return Collections.unmodifiableMap(result); - } - - private boolean isEffectiveSourceForProperty(final String propertyKey, - final String currentSource, - final List sourceOrder, - final Map> propertyToSources) { - final Set sourcesForProperty = propertyToSources.get(propertyKey); - if (sourcesForProperty == null || !sourcesForProperty.contains(currentSource)) { - return false; - } - - for (final String source : sourceOrder) { - if (sourcesForProperty.contains(source)) { - return source.equals(currentSource); - } - } - - return false; - } - - private Map getCurrentEffectiveProperties() { - final Map effectiveProps = new HashMap<>(); - - properties.stringPropertyNames() - .forEach(key -> effectiveProps.put(key, properties.getProperty(key))); - - return effectiveProps; - } - @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { properties.put(propertyName, propertyValue); + + final Map override = new HashMap<>(); + override.put(propertyName, String.valueOf(propertyValue)); + propertiesCollector.addProperties("RuntimeConfiguration", override); + + synchronized (lock) { + this.cachedPropertiesBySource = null; + } } @VisibleForTesting @@ -382,6 +381,7 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); + return properties; } @@ -402,8 +402,7 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = System.getenv(); - + final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -421,6 +420,11 @@ private void overrideWithEnvironmentVariables() { propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } + @VisibleForTesting + protected Map getEnvironmentVariables() { + return System.getenv(); + } + public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -434,6 +438,8 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); + final Map> decryptedBySource = new HashMap<>(); + final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match @@ -441,8 +447,39 @@ private void decryptJasyptProperties() { final String key = (String) keys.nextElement(); final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); - decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); + if (decryptableValue.isPresent()) { + final String decryptedValue = encryptor.decrypt(decryptableValue.get()); + properties.setProperty(key, decryptedValue); + + final String source = findSourceForProperty(key); + if (source != null) { + decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) + .put(key, decryptedValue); + } + } } + + decryptedBySource.forEach(propertiesCollector::addProperties); + } + + private String findSourceForProperty(final String key) { + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List props = propertiesBySource.get(source); + if (props != null && props.stream().anyMatch(p -> p.getKey().equals(key))) { + return source; + } + } + + for (final Map.Entry> entry : propertiesBySource.entrySet()) { + if (!HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey()) && + entry.getValue().stream().anyMatch(p -> p.getKey().equals(key))) { + return entry.getKey(); + } + } + + return null; } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -489,7 +526,6 @@ private Map propertiesToMap(final Properties props) { for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } - return propertiesMap; } -} +} \ No newline at end of file diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index f8fc68700..184f23914 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; import org.jasypt.exceptions.EncryptionOperationNotPossibleException; @@ -58,6 +59,101 @@ public void setup() { System.clearProperty(ENCRYPTED_PROPERTY_2); } + @Test + public void testGetPropertiesBySourceContainsExpectedSources() throws URISyntaxException, IOException { + final Map runtimeConfig = new HashMap<>(); + runtimeConfig.put("org.killbill.dao.user", "root"); + + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig){ + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_user", "root"); + return mockEnv; + } + }; + + final Map> propsBySource = configSource.getPropertiesBySource(); + + Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); + Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); + Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); + } + + @Test(groups = "fast") + public void testGetPropertiesAndGetPropertiesBySourceAreInSync() throws URISyntaxException, IOException { + // RuntimeConfiguration + System.setProperty("org.killbill.dao.user", "root"); + System.setProperty("org.killbill.dao.password", "password"); + + // KillBillDefaults + final Map killbillDefaultConfig = new HashMap<>(); + killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); + killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); + + // ImmutableSystemProperties + killbillDefaultConfig.put("user.timezone", "GMT"); + + // EnvironmentVariables + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); + return mockEnv; + } + }; + + final Properties mergedProperties = configSource.getProperties(); + final Map> propertiesBySource = configSource.getPropertiesBySource(); + + final Map allProperties = new HashMap<>(); + propertiesBySource.forEach((source, props) -> allProperties.putAll(props)); + + for (final String key : mergedProperties.stringPropertyNames()) { + final String valueFromFlat = mergedProperties.getProperty(key); + final String valueFromSource = allProperties.get(key); + + Assert.assertNotNull(valueFromSource); + Assert.assertEquals(valueFromFlat, valueFromSource); + } + + // Verify that no property appears in multiple sources + final Map propertyCount = new HashMap<>(); + propertiesBySource.forEach((source, props) -> { + props.keySet().forEach(key -> propertyCount.put(key, propertyCount.getOrDefault(key, 0) + 1)); + }); + + propertyCount.forEach((key, count) -> { + Assert.assertEquals(count.intValue(), 1); + }); + + Assert.assertEquals(mergedProperties.size(), allProperties.size()); + } + + @Test + public void testConflictResolutionPriority() throws Exception { + // RuntimeConfiguration + System.setProperty("org.killbill.test", "lowValue"); + + final DefaultKillbillConfigSource testSource = new DefaultKillbillConfigSource((String) null) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_test", "highValue"); + return mockEnv; + } + }; + + testSource.setProperty("org.killbill.test", "lowValue"); + + final Properties properties = testSource.getProperties(); + + final String effectiveValue = properties.getProperty("org.killbill.test"); + Assert.assertEquals(effectiveValue, "highValue"); + } + @Test(groups = "fast") public void testGetProperties() throws URISyntaxException, IOException { final Map configuration = new HashMap<>(); @@ -73,14 +169,29 @@ public void testGetProperties() throws URISyntaxException, IOException { @Test(groups = "fast") public void testGetPropertiesBySource() throws URISyntaxException, IOException { + // RuntimeConfiguration System.setProperty("org.killbill.dao.user", "root"); System.setProperty("org.killbill.dao.password", "password"); - final Map configuration = new HashMap<>(); - configuration.put("org.killbill.server.shutdownDelay", "1s"); - configuration.put("org.killbill.billing.osgi.dao.logLevel", "ERROR"); + // KillBillDefaults + final Map killbillDefaultConfig = new HashMap<>(); + killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); + killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); + // ImmutableSystemProperties + killbillDefaultConfig.put("user.timezone", "GMT"); + + // EnvironmentVariables + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_maxActive", "99"); + + return mockEnv; + } + }; final Map> propsBySource = configSource.getPropertiesBySource(); @@ -92,6 +203,13 @@ public void testGetPropertiesBySource() throws URISyntaxException, IOException { final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); + + Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); + + final Map environmentVariables = propsBySource.get("EnvironmentVariables"); + Assert.assertEquals(environmentVariables.get("org.killbill.dao.healthCheckConnectionTimeout"), "11s"); + Assert.assertEquals(environmentVariables.get("org.killbill.billing.osgi.dao.maxActive"), "99"); + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); final Map runtimeConfig = propsBySource.get("RuntimeConfiguration"); @@ -101,13 +219,13 @@ public void testGetPropertiesBySource() throws URISyntaxException, IOException { Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); final Map killBillDefaults = propsBySource.get("KillBillDefaults"); - Assert.assertEquals(killBillDefaults.get("org.killbill.server.enableJasypt"), "false"); - Assert.assertEquals(killBillDefaults.get("org.killbill.persistent.bus.external.tableName"), "bus_ext_events"); - Assert.assertEquals(killBillDefaults.get("org.slf4j.simpleLogger.log.jdbc"), "ERROR"); + Assert.assertEquals(killBillDefaults.get("org.killbill.server.shutdownDelay"), "3s"); + Assert.assertEquals(killBillDefaults.get("org.killbill.billing.osgi.dao.logLevel"), "INFO"); final List actualSourceOrder = new ArrayList<>(propsBySource.keySet()); final List expectedPrecedenceOrder = Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", "RuntimeConfiguration", "KillBillDefaults"); From d8c818cf9a0b49164fca51ce3b44e07d1ab6d7a1 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Mon, 10 Nov 2025 23:06:33 +0530 Subject: [PATCH 005/221] * Fixed formatting. --- .../platform/config/TestDefaultKillbillConfigSource.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 184f23914..40a01ed7a 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -64,7 +64,7 @@ public void testGetPropertiesBySourceContainsExpectedSources() throws URISyntaxE final Map runtimeConfig = new HashMap<>(); runtimeConfig.put("org.killbill.dao.user", "root"); - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig){ + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig) { @Override protected Map getEnvironmentVariables() { final Map mockEnv = new HashMap<>(); @@ -97,8 +97,8 @@ public void testGetPropertiesAndGetPropertiesBySourceAreInSync() throws URISynta // EnvironmentVariables final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { - @Override - protected Map getEnvironmentVariables() { + @Override + protected Map getEnvironmentVariables() { final Map mockEnv = new HashMap<>(); mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); return mockEnv; @@ -203,7 +203,6 @@ protected Map getEnvironmentVariables() { final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); - Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); final Map environmentVariables = propsBySource.get("EnvironmentVariables"); From 9a01edeab0e597b3b729fdd98f6c4f4b25928674 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 12:23:50 +0530 Subject: [PATCH 006/221] Added debug msg to TestJNDIManager --- .../billing/platform/jndi/TestJNDIManager.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 3292508b1..3ed488173 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -23,6 +23,7 @@ import java.sql.SQLException; import java.util.UUID; +import javax.naming.Context; import javax.naming.NamingException; import javax.sql.DataSource; @@ -57,8 +58,18 @@ public void tearDown() throws Exception { @Test(groups = "slow") public void testExportAndLookup() throws NamingException, IOException, SQLException { + System.out.println("BEFORE JNDIManager:"); + System.out.println(" Context.PROVIDER_URL = " + System.getProperty(Context.PROVIDER_URL)); + System.out.println(" Context.INITIAL_CONTEXT_FACTORY = " + System.getProperty(Context.INITIAL_CONTEXT_FACTORY)); + final JNDIManager jndiManager = new JNDIManager(); + System.out.println("AFTER JNDIManager:"); + System.out.println(" Context.PROVIDER_URL = " + System.getProperty(Context.PROVIDER_URL)); + System.out.println(" Context.INITIAL_CONTEXT_FACTORY = " + System.getProperty(Context.INITIAL_CONTEXT_FACTORY)); + +// final JNDIManager jndiManager = new JNDIManager(); + // JdbcConnectionPool is not serializable unfortunately. Tests using JNDI won't work on H2 (we don't have any yet) //final JdbcConnectionPool jdbcConnectionPool = (JdbcConnectionPool) embeddedDB.getDataSource(); //final ReferenceableDataSourceSpy retrievedJdbcConnectionPool = testForDataSource(jndiManager, new ReferenceableDataSourceSpy(jdbcConnectionPool), ReferenceableDataSourceSpy.class); From fa9f1a110fa548baae0f0ee1da9a0bc76bc45235 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 12:38:39 +0530 Subject: [PATCH 007/221] Added debug msg to TestJNDIManager --- .../java/org/killbill/billing/platform/jndi/TestJNDIManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 3ed488173..1b4d6d16b 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -43,6 +43,7 @@ public class TestJNDIManager { @BeforeMethod(groups = "slow") public void setUp() throws Exception { + System.out.println("TestJNDIManager initialized..."); SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); final String databaseName = "killbillosgitests"; From d1c39522410afcb5bb0d7e27607b7d309da139aa Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 12:51:11 +0530 Subject: [PATCH 008/221] Added debug msg to TestJNDIManager --- .../killbill/billing/platform/jndi/TestJNDIManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 1b4d6d16b..111bf60fd 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -28,8 +28,11 @@ import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; +import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -39,11 +42,13 @@ public class TestJNDIManager { + private static final Logger logger = LoggerFactory.getLogger(TestJNDIManager.class); + EmbeddedDB embeddedDB; @BeforeMethod(groups = "slow") public void setUp() throws Exception { - System.out.println("TestJNDIManager initialized..."); + logger.info("TestJNDIManager initialized..."); SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); final String databaseName = "killbillosgitests"; From 3f4e1b9e186d6980cb1c7a9a8426c27ae16d4ae8 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 13:01:27 +0530 Subject: [PATCH 009/221] Added debug msg to TestJNDIManager --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 660cce095..2939242ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,4 +7,4 @@ on: jobs: ci: - uses: killbill/gh-actions-shared/.github/workflows/ci.yml@main + uses: vnandwana/gh-actions-shared/.github/workflows/ci.yml@test From 7e83585c396e797386a86581836f2321365c1b6d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 17:32:55 +0530 Subject: [PATCH 010/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 27f562e59..ce6769dd5 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -37,6 +37,7 @@ import java.util.Properties; import java.util.Set; import java.util.TimeZone; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -85,7 +86,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private final PropertiesWithSourceCollector propertiesCollector; private final Properties properties; - private volatile Map> cachedPropertiesBySource; + private volatile Map> cachedPropertiesBySource = Collections.emptyMap(); public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -139,9 +140,10 @@ public Properties getProperties() { @Override public Map> getPropertiesBySource() { - if (cachedPropertiesBySource == null) { + if (cachedPropertiesBySource.isEmpty()) { synchronized (lock) { - if (cachedPropertiesBySource == null) { + if (cachedPropertiesBySource.isEmpty()) { + logger.info("Computing properties by source (first time)"); cachedPropertiesBySource = computePropertiesBySource(); } } @@ -152,9 +154,18 @@ public Map> getPropertiesBySource() { private Map> computePropertiesBySource() { final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + + final PropertiesWithSourceCollector tempCollector = new PropertiesWithSourceCollector(); + + propertiesCollector.getPropertiesBySource().forEach((source, props) -> { + final Map propsMap = props.stream() + .collect(Collectors.toMap(PropertyWithSource::getKey, PropertyWithSource::getValue)); + tempCollector.addProperties(source, propsMap); + }); + runtimeBySource.forEach((source, props) -> { if (!props.isEmpty()) { - propertiesCollector.addProperties(source, props); + tempCollector.addProperties(source, props); } }); @@ -166,7 +177,7 @@ private Map> computePropertiesBySource() { } }); - final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + final Map> collectorBySource = tempCollector.getPropertiesBySource(); final Map> propertyToSources = new HashMap<>(); collectorBySource.forEach((source, properties) -> { @@ -370,7 +381,7 @@ public void setProperty(final String propertyName, final Object propertyValue) { propertiesCollector.addProperties("RuntimeConfiguration", override); synchronized (lock) { - this.cachedPropertiesBySource = null; + this.cachedPropertiesBySource = Collections.emptyMap(); } } From b0d2bb70bb0459312ea86b82e31b9e4f0be19006 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:09:08 +0530 Subject: [PATCH 011/221] Added debug msg to TestJNDIManager --- .../platform/config/DefaultKillbillConfigSource.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index ce6769dd5..d9f7d58b0 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -133,7 +133,15 @@ public String getString(final String propertyName) { public Properties getProperties() { final Properties result = new Properties(); - getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + // getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + + properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); + + RuntimeConfigRegistry.getAll().forEach((key, value) -> { + if (!result.containsKey(key)) { + result.setProperty(key, value); + } + }); return result; } From 8ea6ac0b67be3fe8a2b94d3f152dc82739f50d54 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:17:53 +0530 Subject: [PATCH 012/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 306 +++++------------- 1 file changed, 85 insertions(+), 221 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index d9f7d58b0..e6ae6c97f 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,23 +21,19 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; -import java.util.Set; import java.util.TimeZone; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -77,16 +73,9 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; - private static final List HIGH_TO_LOW_PRIORITY_ORDER = - Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; - private final Properties properties; - private volatile Map> cachedPropertiesBySource = Collections.emptyMap(); + private final Properties properties; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -109,11 +98,21 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties("RuntimeConfiguration", propsMap); + final String category = extractFileNameFromPath(file); + Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties(category, propsMap); + } - populateDefaultProperties(extraDefaultProperties); + for (final Entry entry : extraDefaultProperties.entrySet()) { + if (entry.getValue() != null) { + properties.put(entry.getKey(), entry.getValue()); + } + } + + propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); + + populateDefaultProperties(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); @@ -132,9 +131,14 @@ public String getString(final String propertyName) { @Override public Properties getProperties() { final Properties result = new Properties(); - - // getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); - + // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside + // tomcat, if we put configuration in tomcat's catalina.properties + // See: + // - https://github.com/killbill/technical-support/issues/61 + // - https://github.com/killbill/technical-support/issues/67 + // + // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar + // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); RuntimeConfigRegistry.getAll().forEach((key, value) -> { @@ -146,138 +150,6 @@ public Properties getProperties() { return result; } - @Override - public Map> getPropertiesBySource() { - if (cachedPropertiesBySource.isEmpty()) { - synchronized (lock) { - if (cachedPropertiesBySource.isEmpty()) { - logger.info("Computing properties by source (first time)"); - cachedPropertiesBySource = computePropertiesBySource(); - } - } - } - - return Collections.unmodifiableMap(cachedPropertiesBySource); - } - - private Map> computePropertiesBySource() { - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - - final PropertiesWithSourceCollector tempCollector = new PropertiesWithSourceCollector(); - - propertiesCollector.getPropertiesBySource().forEach((source, props) -> { - final Map propsMap = props.stream() - .collect(Collectors.toMap(PropertyWithSource::getKey, PropertyWithSource::getValue)); - tempCollector.addProperties(source, propsMap); - }); - - runtimeBySource.forEach((source, props) -> { - if (!props.isEmpty()) { - tempCollector.addProperties(source, props); - } - }); - - final Map effectiveMap = new LinkedHashMap<>(); - properties.stringPropertyNames().forEach(key -> effectiveMap.put(key, properties.getProperty(key))); - RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!effectiveMap.containsKey(key)) { - effectiveMap.put(key, value); - } - }); - - final Map> collectorBySource = tempCollector.getPropertiesBySource(); - - final Map> propertyToSources = new HashMap<>(); - collectorBySource.forEach((source, properties) -> { - properties.forEach(property -> { - propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); - }); - }); - - final Set warnedConflicts = new HashSet<>(); - final Map> result = new LinkedHashMap<>(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List properties = collectorBySource.get(source); - if (properties == null || properties.isEmpty()) { - continue; - } - - final Map sourceMap = new LinkedHashMap<>(); - final Set processedKeys = new HashSet<>(); - - for (final PropertyWithSource prop : properties) { - final String propertyKey = prop.getKey(); - - if (processedKeys.contains(propertyKey)) { - continue; - } - processedKeys.add(propertyKey); - - final String effectiveValue = prop.getValue(); - - if (effectiveValue == null) { - continue; - } - - if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - continue; - } - - final Set sources = propertyToSources.get(propertyKey); - if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, effectiveValue); - } - - sourceMap.put(propertyKey, effectiveValue); - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - } - - collectorBySource.forEach((source, properties) -> { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - return; - } - - final Map sourceMap = new LinkedHashMap<>(); - for (final PropertyWithSource prop : properties) { - final String effectiveValue = effectiveMap.get(prop.getKey()); - if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { - sourceMap.put(prop.getKey(), effectiveValue); - } - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - }); - - return Collections.unmodifiableMap(result); - } - - private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, - final Map> propertiesBySource) { - final List sourcesForKey = new ArrayList<>(); - propertiesBySource.forEach((source, props) -> { - if (props.stream().anyMatch(p -> p.getKey().equals(key))) { - sourcesForKey.add(source); - } - }); - - for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { - if (sourcesForKey.contains(prioritySource)) { - return prioritySource.equals(sourceToCheck); - } - } - - return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); - } - private Properties loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... @@ -288,8 +160,9 @@ private Properties loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); + final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties("RuntimeConfiguration", propsMap); + propertiesCollector.addProperties(category, propsMap); return properties; } catch (final IOException e) { @@ -299,15 +172,14 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); + propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); + return new Properties(System.getProperties()); } @VisibleForTesting - protected void populateDefaultProperties(final Map extraDefaultProperties) { + protected void populateDefaultProperties() { final Properties defaultProperties = getDefaultProperties(); - defaultProperties.putAll(extraDefaultProperties); - for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties if (properties.get(propertyName) == null) { @@ -315,8 +187,6 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - final Map immutableProps = new HashMap<>(); - final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -342,9 +212,6 @@ protected void populateDefaultProperties(final Map extraDefaultP // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - - immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - properties.put(propertyName, GMT_ID); continue; } @@ -352,10 +219,6 @@ protected void populateDefaultProperties(final Map extraDefaultP if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultSystemProperties.get(propertyName)); - } } // WARN for missing PROP_SECURITY_EGD @@ -370,27 +233,48 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - if (!immutableProps.isEmpty()) { - propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); - } - defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("KillBillDefaults", propsMap); + propertiesCollector.addProperties("DefaultSystemProperties", propsMap); + } + + @Override + public Map> getPropertiesBySource() { + final Map currentProps = new HashMap<>(); + properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); + + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + final Map filteredProps = new HashMap<>(); + props.forEach((key, value) -> { + if (!currentProps.containsKey(key)) { + filteredProps.put(key, value); + } + }); + if (!filteredProps.isEmpty()) { + propertiesCollector.addProperties(source, filteredProps); + } + }); + + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + + final Map> result = new LinkedHashMap<>(); + + propertiesBySource.forEach((source, properties) -> { + final Map sourceProperties = new LinkedHashMap<>(); + properties.forEach(prop -> { + sourceProperties.put(prop.getKey(), prop.getValue()); + }); + result.put(source, Collections.unmodifiableMap(sourceProperties)); + }); + + return Collections.unmodifiableMap(result); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { properties.put(propertyName, propertyValue); - - final Map override = new HashMap<>(); - override.put(propertyName, String.valueOf(propertyValue)); - propertiesCollector.addProperties("RuntimeConfiguration", override); - - synchronized (lock) { - this.cachedPropertiesBySource = Collections.emptyMap(); - } } @VisibleForTesting @@ -400,7 +284,6 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); - return properties; } @@ -421,7 +304,8 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = getEnvironmentVariables(); + final Map env = System.getenv(); + final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -439,11 +323,6 @@ private void overrideWithEnvironmentVariables() { propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } - @VisibleForTesting - protected Map getEnvironmentVariables() { - return System.getenv(); - } - public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -457,8 +336,6 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Map> decryptedBySource = new HashMap<>(); - final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match @@ -466,39 +343,8 @@ private void decryptJasyptProperties() { final String key = (String) keys.nextElement(); final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); - if (decryptableValue.isPresent()) { - final String decryptedValue = encryptor.decrypt(decryptableValue.get()); - properties.setProperty(key, decryptedValue); - - final String source = findSourceForProperty(key); - if (source != null) { - decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) - .put(key, decryptedValue); - } - } + decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); } - - decryptedBySource.forEach(propertiesCollector::addProperties); - } - - private String findSourceForProperty(final String key) { - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List props = propertiesBySource.get(source); - if (props != null && props.stream().anyMatch(p -> p.getKey().equals(key))) { - return source; - } - } - - for (final Map.Entry> entry : propertiesBySource.entrySet()) { - if (!HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey()) && - entry.getValue().stream().anyMatch(p -> p.getKey().equals(key))) { - return entry.getKey(); - } - } - - return null; } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -540,11 +386,29 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } + private String extractFileNameFromPath(String path) { + if (path == null || path.isEmpty()) { + return "unknown.properties"; + } + + if (path.startsWith("file://")) { + path = path.substring("file://".length()); + } + + final Path fileName = Paths.get(path).getFileName(); + if (fileName == null) { + return "unknown.properties"; + } + + return fileName.toString(); + } + private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } + return propertiesMap; } -} \ No newline at end of file +} From 8f6a33134526e74941f28d83940b127b0bf0df7d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:22:09 +0530 Subject: [PATCH 013/221] Added debug msg to TestJNDIManager --- .../TestDefaultKillbillConfigSource.java | 162 +----------------- 1 file changed, 9 insertions(+), 153 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 40a01ed7a..8e6191a08 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -21,12 +21,8 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Properties; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; import org.jasypt.exceptions.EncryptionOperationNotPossibleException; @@ -59,101 +55,6 @@ public void setup() { System.clearProperty(ENCRYPTED_PROPERTY_2); } - @Test - public void testGetPropertiesBySourceContainsExpectedSources() throws URISyntaxException, IOException { - final Map runtimeConfig = new HashMap<>(); - runtimeConfig.put("org.killbill.dao.user", "root"); - - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_user", "root"); - return mockEnv; - } - }; - - final Map> propsBySource = configSource.getPropertiesBySource(); - - Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); - Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); - Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); - Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); - } - - @Test(groups = "fast") - public void testGetPropertiesAndGetPropertiesBySourceAreInSync() throws URISyntaxException, IOException { - // RuntimeConfiguration - System.setProperty("org.killbill.dao.user", "root"); - System.setProperty("org.killbill.dao.password", "password"); - - // KillBillDefaults - final Map killbillDefaultConfig = new HashMap<>(); - killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); - killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); - - // ImmutableSystemProperties - killbillDefaultConfig.put("user.timezone", "GMT"); - - // EnvironmentVariables - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); - return mockEnv; - } - }; - - final Properties mergedProperties = configSource.getProperties(); - final Map> propertiesBySource = configSource.getPropertiesBySource(); - - final Map allProperties = new HashMap<>(); - propertiesBySource.forEach((source, props) -> allProperties.putAll(props)); - - for (final String key : mergedProperties.stringPropertyNames()) { - final String valueFromFlat = mergedProperties.getProperty(key); - final String valueFromSource = allProperties.get(key); - - Assert.assertNotNull(valueFromSource); - Assert.assertEquals(valueFromFlat, valueFromSource); - } - - // Verify that no property appears in multiple sources - final Map propertyCount = new HashMap<>(); - propertiesBySource.forEach((source, props) -> { - props.keySet().forEach(key -> propertyCount.put(key, propertyCount.getOrDefault(key, 0) + 1)); - }); - - propertyCount.forEach((key, count) -> { - Assert.assertEquals(count.intValue(), 1); - }); - - Assert.assertEquals(mergedProperties.size(), allProperties.size()); - } - - @Test - public void testConflictResolutionPriority() throws Exception { - // RuntimeConfiguration - System.setProperty("org.killbill.test", "lowValue"); - - final DefaultKillbillConfigSource testSource = new DefaultKillbillConfigSource((String) null) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_test", "highValue"); - return mockEnv; - } - }; - - testSource.setProperty("org.killbill.test", "lowValue"); - - final Properties properties = testSource.getProperties(); - - final String effectiveValue = properties.getProperty("org.killbill.test"); - Assert.assertEquals(effectiveValue, "highValue"); - } - @Test(groups = "fast") public void testGetProperties() throws URISyntaxException, IOException { final Map configuration = new HashMap<>(); @@ -169,66 +70,21 @@ public void testGetProperties() throws URISyntaxException, IOException { @Test(groups = "fast") public void testGetPropertiesBySource() throws URISyntaxException, IOException { - // RuntimeConfiguration - System.setProperty("org.killbill.dao.user", "root"); - System.setProperty("org.killbill.dao.password", "password"); - - // KillBillDefaults - final Map killbillDefaultConfig = new HashMap<>(); - killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); - killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); - - // ImmutableSystemProperties - killbillDefaultConfig.put("user.timezone", "GMT"); - - // EnvironmentVariables - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_maxActive", "99"); - - return mockEnv; - } - }; + final Map configuration = new HashMap<>(); + configuration.put("org.killbill.dao.user", "root"); + configuration.put("org.killbill.dao.password", "password"); + + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); final Map> propsBySource = configSource.getPropertiesBySource(); Assert.assertNotNull(propsBySource); Assert.assertFalse(propsBySource.isEmpty()); - Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); - - final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); - Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); - - Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); - - final Map environmentVariables = propsBySource.get("EnvironmentVariables"); - Assert.assertEquals(environmentVariables.get("org.killbill.dao.healthCheckConnectionTimeout"), "11s"); - Assert.assertEquals(environmentVariables.get("org.killbill.billing.osgi.dao.maxActive"), "99"); - - Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); - - final Map runtimeConfig = propsBySource.get("RuntimeConfiguration"); - Assert.assertEquals(runtimeConfig.get("org.killbill.dao.user"), "root"); - Assert.assertEquals(runtimeConfig.get("org.killbill.dao.password"), "password"); - - Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); - - final Map killBillDefaults = propsBySource.get("KillBillDefaults"); - Assert.assertEquals(killBillDefaults.get("org.killbill.server.shutdownDelay"), "3s"); - Assert.assertEquals(killBillDefaults.get("org.killbill.billing.osgi.dao.logLevel"), "INFO"); - - final List actualSourceOrder = new ArrayList<>(propsBySource.keySet()); - - final List expectedPrecedenceOrder = Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults"); - - Assert.assertEquals(actualSourceOrder, expectedPrecedenceOrder); + final Map defaultProps = propsBySource.get("ExtraDefaultProperties"); + Assert.assertNotNull(defaultProps); + Assert.assertEquals(defaultProps.get("org.killbill.dao.user"), "root"); + Assert.assertEquals(defaultProps.get("org.killbill.dao.password"), "password"); } @Test(groups = "fast") From 59784334df7dbc29192db770b2701cf8dec7db7c Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:40:05 +0530 Subject: [PATCH 014/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 298 ++++++++++++------ .../TestDefaultKillbillConfigSource.java | 188 ----------- 2 files changed, 209 insertions(+), 277 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index e6ae6c97f..ebbd41185 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,18 +21,21 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.Set; import java.util.TimeZone; import javax.annotation.Nullable; @@ -73,10 +76,17 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; - private final PropertiesWithSourceCollector propertiesCollector; + private static final List HIGH_TO_LOW_PRIORITY_ORDER = + Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults")); + private final PropertiesWithSourceCollector propertiesCollector; private final Properties properties; + private volatile Map> cachedPropertiesBySource; + public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); } @@ -98,21 +108,11 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); - + final Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); } - for (final Entry entry : extraDefaultProperties.entrySet()) { - if (entry.getValue() != null) { - properties.put(entry.getKey(), entry.getValue()); - } - } - - propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); - - populateDefaultProperties(); + populateDefaultProperties(extraDefaultProperties); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); @@ -131,23 +131,135 @@ public String getString(final String propertyName) { @Override public Properties getProperties() { final Properties result = new Properties(); - // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside - // tomcat, if we put configuration in tomcat's catalina.properties - // See: - // - https://github.com/killbill/technical-support/issues/61 - // - https://github.com/killbill/technical-support/issues/67 - // - // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar - // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) - properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); + properties.stringPropertyNames() + .forEach(k -> result.setProperty(k, properties.getProperty(k))); + + RuntimeConfigRegistry.getAll() + .forEach((k, v) -> result.putIfAbsent(k, v)); + return result; + } + + @Override + public Map> getPropertiesBySource() { + if (cachedPropertiesBySource == null) { + synchronized (lock) { + if (cachedPropertiesBySource == null) { + cachedPropertiesBySource = computePropertiesBySource(); + } + } + } + + return Collections.unmodifiableMap(cachedPropertiesBySource); + } + + private Map> computePropertiesBySource() { + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + if (!props.isEmpty()) { + propertiesCollector.addProperties(source, props); + } + }); + + final Map effectiveMap = new LinkedHashMap<>(); + properties.stringPropertyNames().forEach(key -> effectiveMap.put(key, properties.getProperty(key))); RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!result.containsKey(key)) { - result.setProperty(key, value); + if (!effectiveMap.containsKey(key)) { + effectiveMap.put(key, value); } }); - return result; + final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + + final Map> propertyToSources = new HashMap<>(); + collectorBySource.forEach((source, properties) -> { + properties.forEach(property -> { + propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); + }); + }); + + final Set warnedConflicts = new HashSet<>(); + final Map> result = new LinkedHashMap<>(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List properties = collectorBySource.get(source); + if (properties == null || properties.isEmpty()) { + continue; + } + + final Map sourceMap = new LinkedHashMap<>(); + final Set processedKeys = new HashSet<>(); + + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + + if (processedKeys.contains(propertyKey)) { + continue; + } + processedKeys.add(propertyKey); + + final String effectiveValue = prop.getValue(); + + if (effectiveValue == null) { + continue; + } + + if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + continue; + } + + final Set sources = propertyToSources.get(propertyKey); + if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, new ArrayList<>(sources), source, effectiveValue); + } + + sourceMap.put(propertyKey, effectiveValue); + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + } + + collectorBySource.forEach((source, properties) -> { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + return; + } + + final Map sourceMap = new LinkedHashMap<>(); + for (final PropertyWithSource prop : properties) { + final String effectiveValue = effectiveMap.get(prop.getKey()); + if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { + sourceMap.put(prop.getKey(), effectiveValue); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + }); + + return Collections.unmodifiableMap(result); + } + + private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, + final Map> propertiesBySource) { + final List sourcesForKey = new ArrayList<>(); + propertiesBySource.forEach((source, props) -> { + if (props.stream().anyMatch(p -> p.getKey().equals(key))) { + sourcesForKey.add(source); + } + }); + + for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { + if (sourcesForKey.contains(prioritySource)) { + return prioritySource.equals(sourceToCheck); + } + } + + return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } private Properties loadPropertiesFromFileOrSystemProperties() { @@ -160,9 +272,8 @@ private Properties loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); - final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); return properties; } catch (final IOException e) { @@ -172,14 +283,15 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); - + propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); return new Properties(System.getProperties()); } @VisibleForTesting - protected void populateDefaultProperties() { + protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); + defaultProperties.putAll(extraDefaultProperties); + for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties if (properties.get(propertyName) == null) { @@ -187,6 +299,8 @@ protected void populateDefaultProperties() { } } + final Map immutableProps = new HashMap<>(); + final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -212,6 +326,9 @@ protected void populateDefaultProperties() { // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); + + immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); + properties.put(propertyName, GMT_ID); continue; } @@ -219,6 +336,10 @@ protected void populateDefaultProperties() { if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } + + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultSystemProperties.get(propertyName)); + } } // WARN for missing PROP_SECURITY_EGD @@ -233,48 +354,27 @@ protected void populateDefaultProperties() { } } + if (!immutableProps.isEmpty()) { + propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); + } + defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("DefaultSystemProperties", propsMap); - } - - @Override - public Map> getPropertiesBySource() { - final Map currentProps = new HashMap<>(); - properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); - - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - runtimeBySource.forEach((source, props) -> { - final Map filteredProps = new HashMap<>(); - props.forEach((key, value) -> { - if (!currentProps.containsKey(key)) { - filteredProps.put(key, value); - } - }); - if (!filteredProps.isEmpty()) { - propertiesCollector.addProperties(source, filteredProps); - } - }); - - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - - final Map> result = new LinkedHashMap<>(); - - propertiesBySource.forEach((source, properties) -> { - final Map sourceProperties = new LinkedHashMap<>(); - properties.forEach(prop -> { - sourceProperties.put(prop.getKey(), prop.getValue()); - }); - result.put(source, Collections.unmodifiableMap(sourceProperties)); - }); - - return Collections.unmodifiableMap(result); + propertiesCollector.addProperties("KillBillDefaults", propsMap); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { properties.put(propertyName, propertyValue); + + final Map override = new HashMap<>(); + override.put(propertyName, String.valueOf(propertyValue)); + propertiesCollector.addProperties("RuntimeConfiguration", override); + + synchronized (lock) { + this.cachedPropertiesBySource = null; + } } @VisibleForTesting @@ -284,6 +384,7 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); + return properties; } @@ -304,8 +405,7 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = System.getenv(); - + final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -323,6 +423,11 @@ private void overrideWithEnvironmentVariables() { propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } + @VisibleForTesting + protected Map getEnvironmentVariables() { + return System.getenv(); + } + public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -336,6 +441,8 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); + final Map> decryptedBySource = new HashMap<>(); + final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match @@ -343,8 +450,39 @@ private void decryptJasyptProperties() { final String key = (String) keys.nextElement(); final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); - decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); + if (decryptableValue.isPresent()) { + final String decryptedValue = encryptor.decrypt(decryptableValue.get()); + properties.setProperty(key, decryptedValue); + + final String source = findSourceForProperty(key); + if (source != null) { + decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) + .put(key, decryptedValue); + } + } + } + + decryptedBySource.forEach(propertiesCollector::addProperties); + } + + private String findSourceForProperty(final String key) { + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List props = propertiesBySource.get(source); + if (props != null && props.stream().anyMatch(p -> p.getKey().equals(key))) { + return source; + } + } + + for (final Map.Entry> entry : propertiesBySource.entrySet()) { + if (!HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey()) && + entry.getValue().stream().anyMatch(p -> p.getKey().equals(key))) { + return entry.getKey(); + } } + + return null; } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -386,29 +524,11 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } - private String extractFileNameFromPath(String path) { - if (path == null || path.isEmpty()) { - return "unknown.properties"; - } - - if (path.startsWith("file://")) { - path = path.substring("file://".length()); - } - - final Path fileName = Paths.get(path).getFileName(); - if (fileName == null) { - return "unknown.properties"; - } - - return fileName.toString(); - } - private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } - return propertiesMap; } -} +} \ No newline at end of file diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 8e6191a08..4e5675067 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -19,194 +19,6 @@ package org.killbill.billing.platform.config; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; - -import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; -import org.jasypt.exceptions.EncryptionOperationNotPossibleException; -import org.killbill.billing.osgi.api.OSGIConfigProperties; -import org.testng.Assert; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import static org.killbill.billing.platform.config.DefaultKillbillConfigSource.ENVIRONMENT_VARIABLE_PREFIX; - public class TestDefaultKillbillConfigSource { - private static final String ENABLE_JASYPT_PROPERTY = "org.killbill.server.enableJasypt"; - private static final String JASYPT_ENCRYPTOR_PASSWORD_PROPERTY = "JASYPT_ENCRYPTOR_PASSWORD"; - private static final String JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY = "JASYPT_ENCRYPTOR_ALGORITHM"; - private static final String JASYPT_PASSWORD = "top_secret!"; - private static final String JASYPT_ALGORITHM = "PBEWITHMD5ANDDES"; - private static final String ENCRYPTED_PROPERTY_1 = "test.encrypted.property1"; - private static final String ENCRYPTED_PROPERTY_2 = "test.encrypted.property2"; - - @BeforeMethod(groups = "fast") - public void setup() { - // Clean out the properties we set in the tests, - // this is only necessary because the DefaultKillBillConfigSource constructor we're using ends up - // setting this.properties to System.getProperties(), which doesn't automatically get reset between tests. - System.clearProperty(ENABLE_JASYPT_PROPERTY); - System.clearProperty(JASYPT_PASSWORD); - System.clearProperty(JASYPT_ALGORITHM); - System.clearProperty(ENCRYPTED_PROPERTY_1); - System.clearProperty(ENCRYPTED_PROPERTY_2); - } - - @Test(groups = "fast") - public void testGetProperties() throws URISyntaxException, IOException { - final Map configuration = new HashMap<>(); - configuration.put("1", "A"); - configuration.put("2", "B"); - - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); - - Assert.assertNotNull(configSource.getProperties()); - Assert.assertNotEquals(configSource.getProperties().size(), 0); - Assert.assertEquals(configSource.getProperties().getProperty("1"), "A"); - } - - @Test(groups = "fast") - public void testGetPropertiesBySource() throws URISyntaxException, IOException { - final Map configuration = new HashMap<>(); - configuration.put("org.killbill.dao.user", "root"); - configuration.put("org.killbill.dao.password", "password"); - - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); - - final Map> propsBySource = configSource.getPropertiesBySource(); - - Assert.assertNotNull(propsBySource); - Assert.assertFalse(propsBySource.isEmpty()); - - final Map defaultProps = propsBySource.get("ExtraDefaultProperties"); - Assert.assertNotNull(defaultProps); - Assert.assertEquals(defaultProps.get("org.killbill.dao.user"), "root"); - Assert.assertEquals(defaultProps.get("org.killbill.dao.password"), "password"); - } - - @Test(groups = "fast") - public void testFromEnvVariableName() throws IOException, URISyntaxException { - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); - - Assert.assertEquals(configSource.fromEnvVariableName(""), ""); - Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao.prepStmtCacheSize"); - // Note! This won't work: we don't support underscores in property keys - //Assert.assertEquals(configSource.fromEnvVariableName("org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao_prepStmtCacheSize"); - Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao__prepStmtCacheSize"), "org.killbill.billing.osgi.dao..prepStmtCacheSize"); - } - - @Test(groups = "fast") - public void testJasyptDisabledByDefault() throws IOException, URISyntaxException { - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); - - final String enableJasyptString = configSource.getString(ENABLE_JASYPT_PROPERTY); - - Assert.assertFalse(Boolean.parseBoolean(enableJasyptString)); - } - - @Test(groups = "fast") - public void testDecyptionExplicitlyDisabled() throws IOException, URISyntaxException { - final String unencryptedValue = "myPropertyValue"; - final String encryptedValue = encString(unencryptedValue); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "false", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); - - final String actualValue = configSource.getString(ENCRYPTED_PROPERTY_1); - - Assert.assertEquals(encryptedValue, actualValue); - } - - @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) - public void testDecryptEmptyPassword() throws IOException, URISyntaxException { - final String encryptedValue = encString("myPropertyValue"); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, "", - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) - public void testDecryptEmptyAlgorithm() throws IOException, URISyntaxException { - final String encryptedValue = encString("myPropertyValue"); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, ""); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) - public void testDecryptInvalidJasyptString() throws IOException, URISyntaxException { - final String encryptedValue = "ENC(notAValidEncryptedString!)"; - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) - public void testDecryptEmptyJasyptString() throws IOException, URISyntaxException { - final String encryptedValue = "ENC()"; - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast") - public void testDecryptJasyptPropertySuccessfully() throws IOException, URISyntaxException { - final String unencryptedValue1 = "myPropertyValue"; - final String encryptedValue1 = encString(unencryptedValue1); - final String unencryptedValue2 = "myOtherPropertyValue"; - final String encryptedValue2 = encString(unencryptedValue2); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue1, - ENCRYPTED_PROPERTY_2, encryptedValue2, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); - - final String actualValue1 = configSource.getString(ENCRYPTED_PROPERTY_1); - final String actualValue2 = configSource.getString(ENCRYPTED_PROPERTY_2); - - Assert.assertEquals(unencryptedValue1, actualValue1); - Assert.assertEquals(unencryptedValue2, actualValue2); - } - - private String encString(final String unencryptedValue) { - return "ENC(" + encrypt(unencryptedValue, JASYPT_ALGORITHM, JASYPT_PASSWORD) + ")"; - } - - private String encrypt(final String unencryptedValue, final String jasyptAlgorithm, final String jasyptPassword) { - final StandardPBEStringEncryptor encryptor = setupEncryptor(jasyptPassword, jasyptAlgorithm); - return encryptor.encrypt(unencryptedValue); - } - - private StandardPBEStringEncryptor setupEncryptor(final String password, final String algorithm) { - final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); - encryptor.setPassword(password); - encryptor.setAlgorithm(algorithm); - return encryptor; - } } From bafb8cceadc9a7abf249485c4dc3d5930be4947a Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:44:11 +0530 Subject: [PATCH 015/221] Added debug msg to TestJNDIManager --- .../platform/config/DefaultKillbillConfigSource.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index ebbd41185..02361b6eb 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -132,11 +132,8 @@ public String getString(final String propertyName) { public Properties getProperties() { final Properties result = new Properties(); - properties.stringPropertyNames() - .forEach(k -> result.setProperty(k, properties.getProperty(k))); + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); - RuntimeConfigRegistry.getAll() - .forEach((k, v) -> result.putIfAbsent(k, v)); return result; } @@ -370,7 +367,7 @@ public void setProperty(final String propertyName, final Object propertyValue) { final Map override = new HashMap<>(); override.put(propertyName, String.valueOf(propertyValue)); - propertiesCollector.addProperties("RuntimeConfiguration", override); + propertiesCollector.addProperties("ImmutableSystemProperties", override); synchronized (lock) { this.cachedPropertiesBySource = null; From c01f9b9ea63d50c9543ed11a66862706c589af6c Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:54:40 +0530 Subject: [PATCH 016/221] Added debug msg to TestJNDIManager --- .../killbill/billing/platform/jndi/TestJNDIManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 111bf60fd..0d4e20baa 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -109,7 +109,13 @@ private T testForDataSource(final JNDIManager jndiManager, final DataSource jndiManager.export(name, dataSource); final Object retrievedDataSourceObject = jndiManager.lookup(name); - Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); + //Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); + + Assert.assertNotNull(retrievedDataSourceObject, "Retrieved DataSource should not be null"); + Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), + "Expected instance of " + klass.getName() + " but got " + + (retrievedDataSourceObject != null ? retrievedDataSourceObject.getClass().getName() : "null")); + return (T) retrievedDataSourceObject; } From bb4880a22973a74f44e9545bf770d0c0421a6416 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 18:56:18 +0530 Subject: [PATCH 017/221] Added debug msg to TestJNDIManager --- .../billing/platform/config/DefaultKillbillConfigSource.java | 1 + 1 file changed, 1 insertion(+) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 02361b6eb..f700e74fe 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -374,6 +374,7 @@ public void setProperty(final String propertyName, final Object propertyValue) { } } + @VisibleForTesting protected Properties getDefaultProperties() { final Properties properties = new Properties(); From 4915d8b0c3e89eb423d2c765f7057739bf0b02c5 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 19:20:16 +0530 Subject: [PATCH 018/221] Added debug msg to TestJNDIManager --- .../platform/config/DefaultKillbillConfigSource.java | 6 +++++- .../org/killbill/billing/platform/jndi/TestJNDIManager.java | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index f700e74fe..ad4cf39e1 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -130,10 +130,14 @@ public String getString(final String propertyName) { @Override public Properties getProperties() { - final Properties result = new Properties(); + /*final Properties result = new Properties(); getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + return result;*/ + + final Properties result = new Properties(); + getPropertiesBySource().values().forEach(map -> result.putAll(map)); return result; } diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 0d4e20baa..8dc50e8e5 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -28,7 +28,6 @@ import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; -import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; import org.slf4j.Logger; From 2882a421f28906771c425f89e94c2d33aece0a5e Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 19:32:27 +0530 Subject: [PATCH 019/221] Added debug msg to TestJNDIManager --- .../killbill/billing/platform/jndi/TestJNDIManager.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 8dc50e8e5..8170c3838 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.sql.SQLException; +import java.util.Collections; import java.util.UUID; import javax.naming.Context; @@ -28,6 +29,7 @@ import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; +import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; import org.slf4j.Logger; @@ -47,6 +49,11 @@ public class TestJNDIManager { @BeforeMethod(groups = "slow") public void setUp() throws Exception { + DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource((String) null); + + configSource.getAllPropertiesWithSource(); + configSource.getProperties(); + logger.info("TestJNDIManager initialized..."); SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); From f18b152d070252ee2907f8616abb7d9f3f39388e Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 19:48:47 +0530 Subject: [PATCH 020/221] Added debug msg to TestJNDIManager --- .../billing/platform/jndi/TestJNDIManager.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 8170c3838..88729b22a 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -26,6 +26,7 @@ import javax.naming.Context; import javax.naming.NamingException; +import javax.naming.Reference; import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; @@ -114,7 +115,7 @@ private T testForDataSource(final JNDIManager jndiManager, final DataSource final String name = "a/b/c"; jndiManager.export(name, dataSource); - final Object retrievedDataSourceObject = jndiManager.lookup(name); + /* final Object retrievedDataSourceObject = jndiManager.lookup(name); //Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); Assert.assertNotNull(retrievedDataSourceObject, "Retrieved DataSource should not be null"); @@ -123,6 +124,19 @@ private T testForDataSource(final JNDIManager jndiManager, final DataSource (retrievedDataSourceObject != null ? retrievedDataSourceObject.getClass().getName() : "null")); - return (T) retrievedDataSourceObject; + return (T) retrievedDataSourceObject;*/ + + final Object lookedUp = jndiManager.lookup(name); + + Assert.assertNotNull(lookedUp, "Lookup should not return null"); + Assert.assertTrue(lookedUp instanceof Reference, "Expected a JNDI Reference"); + + Reference ref = (Reference) lookedUp; + + Assert.assertEquals(ref.getClassName(), klass.getName(), + "Reference class name mismatch"); + + + return (T) lookedUp; } } From c11fffd64c7ab87a2a8b1dd83b909111b59fa8b4 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 19:55:57 +0530 Subject: [PATCH 021/221] Added debug msg to TestJNDIManager --- .../platform/jndi/TestJNDIManager.java | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 88729b22a..b4adf4b4f 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.sql.SQLException; -import java.util.Collections; import java.util.UUID; import javax.naming.Context; @@ -81,7 +80,7 @@ public void testExportAndLookup() throws NamingException, IOException, SQLExcept System.out.println(" Context.PROVIDER_URL = " + System.getProperty(Context.PROVIDER_URL)); System.out.println(" Context.INITIAL_CONTEXT_FACTORY = " + System.getProperty(Context.INITIAL_CONTEXT_FACTORY)); -// final JNDIManager jndiManager = new JNDIManager(); + // final JNDIManager jndiManager = new JNDIManager(); // JdbcConnectionPool is not serializable unfortunately. Tests using JNDI won't work on H2 (we don't have any yet) //final JdbcConnectionPool jdbcConnectionPool = (JdbcConnectionPool) embeddedDB.getDataSource(); @@ -112,31 +111,14 @@ public void testExportAndLookup() throws NamingException, IOException, SQLExcept @SuppressWarnings("unchecked") private T testForDataSource(final JNDIManager jndiManager, final DataSource dataSource, final Class klass) { - final String name = "a/b/c"; + final String name = "jdbc/killbill"; jndiManager.export(name, dataSource); - /* final Object retrievedDataSourceObject = jndiManager.lookup(name); - //Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); - - Assert.assertNotNull(retrievedDataSourceObject, "Retrieved DataSource should not be null"); - Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), - "Expected instance of " + klass.getName() + " but got " + - (retrievedDataSourceObject != null ? retrievedDataSourceObject.getClass().getName() : "null")); - - - return (T) retrievedDataSourceObject;*/ - final Object lookedUp = jndiManager.lookup(name); - Assert.assertNotNull(lookedUp, "Lookup should not return null"); - Assert.assertTrue(lookedUp instanceof Reference, "Expected a JNDI Reference"); - - Reference ref = (Reference) lookedUp; - - Assert.assertEquals(ref.getClassName(), klass.getName(), - "Reference class name mismatch"); - - + Assert.assertTrue(lookedUp instanceof Reference, "Expected JNDI Reference"); + final Reference ref = (Reference) lookedUp; + Assert.assertEquals(ref.getClassName(), JdbcDataSource.class.getName()); return (T) lookedUp; } } From e2be3d0367d35d0d94b95c8af8bba1c44530f8a6 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Tue, 11 Nov 2025 20:04:08 +0530 Subject: [PATCH 022/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/platform/jndi/JNDIManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java b/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java index 7c044fccd..c8a1ab1b8 100644 --- a/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java +++ b/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java @@ -65,7 +65,12 @@ public JNDIManager(final int port) throws RemoteException { public void export(final String name, final Object object) { Preconditions.checkArgument(object instanceof Remote || object instanceof Reference || object instanceof Referenceable, "object to bind must be Remote, Reference, or Referenceable, not " + object.getClass()); - doExport(name, object); + try { + doExport(name, object); + } catch (final Exception e) { + logger.warn("Failed to bind '{}' to JNDI", name, e); + throw e; + } } public void unExport(final String name) { From 93c47bcc0a6cff367498b863764ed57566ba1008 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Wed, 12 Nov 2025 17:41:52 +0530 Subject: [PATCH 023/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 151 +++++++++--------- 1 file changed, 72 insertions(+), 79 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index ad4cf39e1..65a81f537 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -82,9 +81,11 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); + // Single source of truth for all properties with source tracking private final PropertiesWithSourceCollector propertiesCollector; - private final Properties properties; + // Cached computed views - rebuilt when collector changes + private volatile Map cachedEffectiveProperties; private volatile Map> cachedPropertiesBySource; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { @@ -103,10 +104,10 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); @@ -114,44 +115,64 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map props.forEach(result::setProperty)); - - return result;*/ - final Properties result = new Properties(); - getPropertiesBySource().values().forEach(map -> result.putAll(map)); + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); return result; } @Override public Map> getPropertiesBySource() { - if (cachedPropertiesBySource == null) { + ensureCachesBuilt(); + return Collections.unmodifiableMap(cachedPropertiesBySource); + } + + private void ensureCachesBuilt() { + if (cachedPropertiesBySource == null || cachedEffectiveProperties == null) { synchronized (lock) { - if (cachedPropertiesBySource == null) { - cachedPropertiesBySource = computePropertiesBySource(); + if (cachedPropertiesBySource == null || cachedEffectiveProperties == null) { + rebuildCaches(); } } } + } - return Collections.unmodifiableMap(cachedPropertiesBySource); + private void rebuildCaches() { + // Compute both caches together + cachedPropertiesBySource = computePropertiesBySource(); + + // Build flat effective properties map from the by-source map + final Map effective = new LinkedHashMap<>(); + cachedPropertiesBySource.forEach((source, props) -> effective.putAll(props)); + cachedEffectiveProperties = Collections.unmodifiableMap(effective); + } + + private void invalidateCaches() { + synchronized (lock) { + cachedPropertiesBySource = null; + cachedEffectiveProperties = null; + } } private Map> computePropertiesBySource() { @@ -162,8 +183,15 @@ private Map> computePropertiesBySource() { } }); + // Build effective map from collector for RuntimeConfigRegistry integration final Map effectiveMap = new LinkedHashMap<>(); - properties.stringPropertyNames().forEach(key -> effectiveMap.put(key, properties.getProperty(key))); + propertiesCollector.getAllProperties().forEach(prop -> { + // Use computeIfAbsent with priority logic + if (!effectiveMap.containsKey(prop.getKey())) { + effectiveMap.put(prop.getKey(), prop.getValue()); + } + }); + RuntimeConfigRegistry.getAll().forEach((key, value) -> { if (!effectiveMap.containsKey(key)) { effectiveMap.put(key, value); @@ -263,20 +291,17 @@ private boolean isEffectiveSourceForProperty(final String key, final String sour return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } - private Properties loadPropertiesFromFileOrSystemProperties() { - // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, - // but we need to build the ConfigSource first... + private void loadPropertiesFromFileOrSystemProperties() { final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); if (propertiesFileLocation != null) { try { - // Ignore System Properties if we're loading from a file final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); - return properties; + return; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -285,7 +310,6 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); - return new Properties(System.getProperties()); } @VisibleForTesting @@ -293,10 +317,12 @@ protected void populateDefaultProperties(final Map extraDefaultP final Properties defaultProperties = getDefaultProperties(); defaultProperties.putAll(extraDefaultProperties); + final Map defaultsToAdd = new HashMap<>(); + for (final String propertyName : defaultProperties.stringPropertyNames()) { - // Let the user override these properties - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultProperties.get(propertyName)); + // Only add if not already present in any source + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } } @@ -305,7 +331,6 @@ protected void populateDefaultProperties(final Map extraDefaultP final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { - // Special case to overwrite user.timezone if (propertyName.equals(PROP_USER_TIME_ZONE)) { if (!"GMT".equals(System.getProperty(propertyName))) { if (GMT_WARNING == NOT_SHOWN) { @@ -319,31 +344,24 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - // - // We now set the java system property -- regardless of whether this has been set previously or not. - // Also, setting java System property is not enough because default timezone may have been SET earlier, - // when first call to TimeZone.getDefaultRef was invoked-- which has a side effect to set it by either looking at - // existing "user.timezone" or being super smart by inferring from "user.country", "java.home", so we need to reset it. - // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - properties.put(propertyName, GMT_ID); + defaultsToAdd.put(propertyName, GMT_ID); + continue; } - // Let the user override these properties if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultSystemProperties.get(propertyName)); + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); } } - // WARN for missing PROP_SECURITY_EGD if (System.getProperty(PROP_SECURITY_EGD) == null) { if (ENTROPY_WARNING == NOT_SHOWN) { synchronized (lock) { @@ -365,20 +383,22 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("KillBillDefaults", propsMap); } + // Helper to check if property exists without needing cache + private boolean hasProperty(final String propertyName) { + return propertiesCollector.getAllProperties().stream() + .anyMatch(p -> p.getKey().equals(propertyName)); + } + @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { - properties.put(propertyName, propertyValue); - final Map override = new HashMap<>(); override.put(propertyName, String.valueOf(propertyValue)); - propertiesCollector.addProperties("ImmutableSystemProperties", override); + propertiesCollector.addProperties("RuntimeConfiguration", override); - synchronized (lock) { - this.cachedPropertiesBySource = null; - } + invalidateCaches(); + rebuildCaches(); } - @VisibleForTesting protected Properties getDefaultProperties() { final Properties properties = new Properties(); @@ -395,18 +415,13 @@ protected Properties getDefaultSystemProperties() { final Properties properties = new Properties(); properties.put("user.timezone", GMT_ID); properties.put("ANTLR_USE_DIRECT_CLASS_LOADING", "true"); - // Disable log4jdbc-log4j2 by default. - // For slf4j-simple, this doesn't quite disable it (we cannot turn off the logger completely), - // but it should be off for logback (see logback.xml / logback-test.xml) properties.put("org.slf4j.simpleLogger.log.jdbc", "ERROR"); - // Sane defaults for https://code.google.com/p/log4jdbc-log4j2/ properties.put("log4jdbc.dump.sql.maxlinelength", "0"); properties.put("log4jdbc.spylogdelegator.name", "net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); return properties; } private void overrideWithEnvironmentVariables() { - // Find all Kill Bill properties in the environment variables final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); @@ -419,7 +434,6 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); - properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); @@ -445,18 +459,17 @@ private void decryptJasyptProperties() { final Map> decryptedBySource = new HashMap<>(); - final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); - // Iterate over all properties and decrypt ones that match - while (keys.hasMoreElements()) { - final String key = (String) keys.nextElement(); - final String value = (String) properties.get(key); + + final List allProperties = propertiesCollector.getAllProperties(); + for (final PropertyWithSource prop : allProperties) { + final String key = prop.getKey(); + final String value = prop.getValue(); final Optional decryptableValue = decryptableValue(value); if (decryptableValue.isPresent()) { final String decryptedValue = encryptor.decrypt(decryptableValue.get()); - properties.setProperty(key, decryptedValue); - final String source = findSourceForProperty(key); + final String source = prop.getSource(); if (source != null) { decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) .put(key, decryptedValue); @@ -467,26 +480,6 @@ private void decryptJasyptProperties() { decryptedBySource.forEach(propertiesCollector::addProperties); } - private String findSourceForProperty(final String key) { - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List props = propertiesBySource.get(source); - if (props != null && props.stream().anyMatch(p -> p.getKey().equals(key))) { - return source; - } - } - - for (final Map.Entry> entry : propertiesBySource.entrySet()) { - if (!HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey()) && - entry.getValue().stream().anyMatch(p -> p.getKey().equals(key))) { - return entry.getKey(); - } - } - - return null; - } - private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); From 2ee01caabfa27a2b3c7c887169c0df0a83a70352 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 09:17:11 +0530 Subject: [PATCH 024/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 92 ++++++++++--------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 65a81f537..915958b7a 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -81,11 +81,8 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - // Single source of truth for all properties with source tracking private final PropertiesWithSourceCollector propertiesCollector; - // Cached computed views - rebuilt when collector changes - private volatile Map cachedEffectiveProperties; private volatile Map> cachedPropertiesBySource; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { @@ -115,63 +112,62 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map> bySource = getPropertiesBySource(); + + for (final Map sourceProps : bySource.values()) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + + return null; } @Override public Properties getProperties() { final Properties result = new Properties(); + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + return result; } @Override public Map> getPropertiesBySource() { - ensureCachesBuilt(); - return Collections.unmodifiableMap(cachedPropertiesBySource); - } - - private void ensureCachesBuilt() { - if (cachedPropertiesBySource == null || cachedEffectiveProperties == null) { + if (cachedPropertiesBySource == null) { synchronized (lock) { - if (cachedPropertiesBySource == null || cachedEffectiveProperties == null) { - rebuildCaches(); + if (cachedPropertiesBySource == null) { + rebuildCache(); } } } + + return Collections.unmodifiableMap(cachedPropertiesBySource); } - private void rebuildCaches() { - // Compute both caches together + private void rebuildCache() { cachedPropertiesBySource = computePropertiesBySource(); - - // Build flat effective properties map from the by-source map - final Map effective = new LinkedHashMap<>(); - cachedPropertiesBySource.forEach((source, props) -> effective.putAll(props)); - cachedEffectiveProperties = Collections.unmodifiableMap(effective); } - private void invalidateCaches() { + private void invalidateCache() { synchronized (lock) { cachedPropertiesBySource = null; - cachedEffectiveProperties = null; } } @@ -183,10 +179,8 @@ private Map> computePropertiesBySource() { } }); - // Build effective map from collector for RuntimeConfigRegistry integration final Map effectiveMap = new LinkedHashMap<>(); propertiesCollector.getAllProperties().forEach(prop -> { - // Use computeIfAbsent with priority logic if (!effectiveMap.containsKey(prop.getKey())) { effectiveMap.put(prop.getKey(), prop.getValue()); } @@ -217,16 +211,9 @@ private Map> computePropertiesBySource() { } final Map sourceMap = new LinkedHashMap<>(); - final Set processedKeys = new HashSet<>(); for (final PropertyWithSource prop : properties) { final String propertyKey = prop.getKey(); - - if (processedKeys.contains(propertyKey)) { - continue; - } - processedKeys.add(propertyKey); - final String effectiveValue = prop.getValue(); if (effectiveValue == null) { @@ -237,14 +224,17 @@ private Map> computePropertiesBySource() { continue; } + sourceMap.put(propertyKey, effectiveValue); + } + + final Set processedInThisSource = new HashSet<>(sourceMap.keySet()); + for (final String propertyKey : processedInThisSource) { final Set sources = propertyToSources.get(propertyKey); if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { warnedConflicts.add(propertyKey); logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, effectiveValue); + propertyKey, new ArrayList<>(sources), source, sourceMap.get(propertyKey)); } - - sourceMap.put(propertyKey, effectiveValue); } if (!sourceMap.isEmpty()) { @@ -292,9 +282,12 @@ private boolean isEffectiveSourceForProperty(final String key, final String sour } private void loadPropertiesFromFileOrSystemProperties() { + // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, + // but we need to build the ConfigSource first... final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); if (propertiesFileLocation != null) { try { + // Ignore System Properties if we're loading from a file final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); @@ -320,7 +313,7 @@ protected void populateDefaultProperties(final Map extraDefaultP final Map defaultsToAdd = new HashMap<>(); for (final String propertyName : defaultProperties.stringPropertyNames()) { - // Only add if not already present in any source + // Let the user override these properties if (!hasProperty(propertyName)) { defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } @@ -331,6 +324,7 @@ protected void populateDefaultProperties(final Map extraDefaultP final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { + // Special case to overwrite user.timezone if (propertyName.equals(PROP_USER_TIME_ZONE)) { if (!"GMT".equals(System.getProperty(propertyName))) { if (GMT_WARNING == NOT_SHOWN) { @@ -344,6 +338,12 @@ protected void populateDefaultProperties(final Map extraDefaultP } } + // + // We now set the java system property -- regardless of whether this has been set previously or not. + // Also, setting java System property is not enough because default timezone may have been SET earlier, + // when first call to TimeZone.getDefaultRef was invoked-- which has a side effect to set it by either looking at + // existing "user.timezone" or being super smart by inferring from "user.country", "java.home", so we need to reset it. + // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); @@ -353,6 +353,7 @@ protected void populateDefaultProperties(final Map extraDefaultP continue; } + // Let the user override these properties if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } @@ -362,6 +363,7 @@ protected void populateDefaultProperties(final Map extraDefaultP } } + // WARN for missing PROP_SECURITY_EGD if (System.getProperty(PROP_SECURITY_EGD) == null) { if (ENTROPY_WARNING == NOT_SHOWN) { synchronized (lock) { @@ -383,7 +385,6 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("KillBillDefaults", propsMap); } - // Helper to check if property exists without needing cache private boolean hasProperty(final String propertyName) { return propertiesCollector.getAllProperties().stream() .anyMatch(p -> p.getKey().equals(propertyName)); @@ -395,8 +396,8 @@ public void setProperty(final String propertyName, final Object propertyValue) { override.put(propertyName, String.valueOf(propertyValue)); propertiesCollector.addProperties("RuntimeConfiguration", override); - invalidateCaches(); - rebuildCaches(); + invalidateCache(); + rebuildCache(); } @VisibleForTesting @@ -415,13 +416,18 @@ protected Properties getDefaultSystemProperties() { final Properties properties = new Properties(); properties.put("user.timezone", GMT_ID); properties.put("ANTLR_USE_DIRECT_CLASS_LOADING", "true"); + // Disable log4jdbc-log4j2 by default. + // For slf4j-simple, this doesn't quite disable it (we cannot turn off the logger completely), + // but it should be off for logback (see logback.xml / logback-test.xml) properties.put("org.slf4j.simpleLogger.log.jdbc", "ERROR"); + // Sane defaults for https://code.google.com/p/log4jdbc-log4j2/ properties.put("log4jdbc.dump.sql.maxlinelength", "0"); properties.put("log4jdbc.spylogdelegator.name", "net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); return properties; } private void overrideWithEnvironmentVariables() { + // Find all Kill Bill properties in the environment variables final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); @@ -460,7 +466,7 @@ private void decryptJasyptProperties() { final Map> decryptedBySource = new HashMap<>(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); - + // Iterate over all properties and decrypt ones that match final List allProperties = propertiesCollector.getAllProperties(); for (final PropertyWithSource prop : allProperties) { final String key = prop.getKey(); From 018dc7114b127113db478f87647c817340d0365e Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 09:39:45 +0530 Subject: [PATCH 025/221] Added debug msg to TestJNDIManager --- .../platform/jndi/TestJNDIManager.java | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index b4adf4b4f..ed9f5b8ae 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -25,15 +25,11 @@ import javax.naming.Context; import javax.naming.NamingException; -import javax.naming.Reference; import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; -import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -43,18 +39,10 @@ public class TestJNDIManager { - private static final Logger logger = LoggerFactory.getLogger(TestJNDIManager.class); - EmbeddedDB embeddedDB; @BeforeMethod(groups = "slow") public void setUp() throws Exception { - DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource((String) null); - - configSource.getAllPropertiesWithSource(); - configSource.getProperties(); - - logger.info("TestJNDIManager initialized..."); SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); final String databaseName = "killbillosgitests"; @@ -111,14 +99,12 @@ public void testExportAndLookup() throws NamingException, IOException, SQLExcept @SuppressWarnings("unchecked") private T testForDataSource(final JNDIManager jndiManager, final DataSource dataSource, final Class klass) { - final String name = "jdbc/killbill"; + final String name = "a/b/c"; jndiManager.export(name, dataSource); - final Object lookedUp = jndiManager.lookup(name); - Assert.assertNotNull(lookedUp, "Lookup should not return null"); - Assert.assertTrue(lookedUp instanceof Reference, "Expected JNDI Reference"); - final Reference ref = (Reference) lookedUp; - Assert.assertEquals(ref.getClassName(), JdbcDataSource.class.getName()); - return (T) lookedUp; + final Object retrievedDataSourceObject = jndiManager.lookup(name); + Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); + + return (T) retrievedDataSourceObject; } } From 9c44f4da7525ab5221891c47d5190c4c79e51a94 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 09:45:47 +0530 Subject: [PATCH 026/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/platform/jndi/JNDIManager.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java b/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java index c8a1ab1b8..7c044fccd 100644 --- a/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java +++ b/base/src/main/java/org/killbill/billing/platform/jndi/JNDIManager.java @@ -65,12 +65,7 @@ public JNDIManager(final int port) throws RemoteException { public void export(final String name, final Object object) { Preconditions.checkArgument(object instanceof Remote || object instanceof Reference || object instanceof Referenceable, "object to bind must be Remote, Reference, or Referenceable, not " + object.getClass()); - try { - doExport(name, object); - } catch (final Exception e) { - logger.warn("Failed to bind '{}' to JNDI", name, e); - throw e; - } + doExport(name, object); } public void unExport(final String name) { From c183c52cdc51f216093e079e0d188604029bf59c Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 11:22:08 +0530 Subject: [PATCH 027/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 97bd0fcc3..99bca1812 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -41,7 +41,7 @@ public void createTable() { public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + - "record_id serial unique, " + + "record_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + "is_started bool DEFAULT false, " + "is_logged bool DEFAULT false, " + "external_key varchar(128) NULL, " + From a92535941619925ddb04196ce08a9a188b150ca4 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 11:36:07 +0530 Subject: [PATCH 028/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 99bca1812..97bd0fcc3 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -41,7 +41,7 @@ public void createTable() { public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + - "record_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + + "record_id serial unique, " + "is_started bool DEFAULT false, " + "is_logged bool DEFAULT false, " + "external_key varchar(128) NULL, " + From 14eb31c6964cf530cec09a61423e29669c7a4abd Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 11:38:58 +0530 Subject: [PATCH 029/221] Added debug msg to TestJNDIManager --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2939242ed..660cce095 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,4 +7,4 @@ on: jobs: ci: - uses: vnandwana/gh-actions-shared/.github/workflows/ci.yml@test + uses: killbill/gh-actions-shared/.github/workflows/ci.yml@main From 9e0ea1922bf9798ed59bc88e684f86894836e43f Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 11:58:48 +0530 Subject: [PATCH 030/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 97bd0fcc3..d2c18f11f 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -41,7 +41,7 @@ public void createTable() { public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + - "record_id serial unique, " + + "record_id IDENTITY unique, " + "is_started bool DEFAULT false, " + "is_logged bool DEFAULT false, " + "external_key varchar(128) NULL, " + From 0a0b97c155e9493a60f45b2895a0e0c8366607dd Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 12:30:23 +0530 Subject: [PATCH 031/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index d2c18f11f..4cfb04cb7 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -41,7 +41,7 @@ public void createTable() { public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + - "record_id IDENTITY unique, " + + "record_id BIGINT IDENTITY, " + "is_started bool DEFAULT false, " + "is_logged bool DEFAULT false, " + "external_key varchar(128) NULL, " + From 0db4282c1a1977ba1e6793ff06539ec668fc2d43 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 12:45:43 +0530 Subject: [PATCH 032/221] Added debug msg to TestJNDIManager --- .../osgi/bundles/test/dao/TestDao.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 4cfb04cb7..5cd2e6d70 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -39,17 +39,22 @@ public void createTable() { dbi.inTransaction(new TransactionCallback() { @Override public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { - conn.execute("DROP TABLE IF EXISTS test_bundle;"); - conn.execute("CREATE TABLE test_bundle (" + - "record_id BIGINT IDENTITY, " + - "is_started bool DEFAULT false, " + - "is_logged bool DEFAULT false, " + - "external_key varchar(128) NULL, " + - "payment_id varchar(36) NULL," + - "payment_method_id varchar(36) NULL," + - "payment_amount decimal(10,4) NULL," + - "PRIMARY KEY(record_id)" + - ");"); + try { + conn.execute("DROP TABLE IF EXISTS test_bundle;"); + conn.execute("CREATE TABLE test_bundle (" + + "record_id BIGINT IDENTITY, " + + "is_started bool DEFAULT false, " + + "is_logged bool DEFAULT false, " + + "external_key varchar(128) NULL, " + + "payment_id varchar(36) NULL," + + "payment_method_id varchar(36) NULL," + + "payment_amount decimal(10,4) NULL," + + "PRIMARY KEY(record_id)" + + ");"); + } catch (final Exception e) { + System.out.println("create table failed... " + e.getMessage() + e); + throw e; + } return null; } }); From 27fc1650716bab37fbee2530b4b016b60bfc63ef Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 12:50:59 +0530 Subject: [PATCH 033/221] Added debug msg to TestJNDIManager --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 660cce095..2939242ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,4 +7,4 @@ on: jobs: ci: - uses: killbill/gh-actions-shared/.github/workflows/ci.yml@main + uses: vnandwana/gh-actions-shared/.github/workflows/ci.yml@test From ae257485f71aed4a846af800b5f0008ba5db8e12 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 13:00:03 +0530 Subject: [PATCH 034/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 5cd2e6d70..76abd5305 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -42,14 +42,13 @@ public Object inTransaction(final Handle conn, final TransactionStatus status) t try { conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + - "record_id BIGINT IDENTITY, " + + "record_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + "is_started bool DEFAULT false, " + "is_logged bool DEFAULT false, " + "external_key varchar(128) NULL, " + "payment_id varchar(36) NULL," + "payment_method_id varchar(36) NULL," + "payment_amount decimal(10,4) NULL," + - "PRIMARY KEY(record_id)" + ");"); } catch (final Exception e) { System.out.println("create table failed... " + e.getMessage() + e); From b0be8470139043c7923228a2b71d34a0c751e22d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 13:41:02 +0530 Subject: [PATCH 035/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 76abd5305..d48d9a204 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -48,7 +48,7 @@ public Object inTransaction(final Handle conn, final TransactionStatus status) t "external_key varchar(128) NULL, " + "payment_id varchar(36) NULL," + "payment_method_id varchar(36) NULL," + - "payment_amount decimal(10,4) NULL," + + "payment_amount decimal(10,4) NULL" + ");"); } catch (final Exception e) { System.out.println("create table failed... " + e.getMessage() + e); From 0a6ca643c0ed8c3a13eea1980ce35109caee520d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 13:50:45 +0530 Subject: [PATCH 036/221] Added debug msg to TestJNDIManager --- .../org/killbill/billing/osgi/bundles/test/dao/TestDao.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index d48d9a204..ddd6ce5f2 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -42,9 +42,9 @@ public Object inTransaction(final Handle conn, final TransactionStatus status) t try { conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + - "record_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " + - "is_started bool DEFAULT false, " + - "is_logged bool DEFAULT false, " + + "record_id INT AUTO_INCREMENT PRIMARY KEY, " + + "is_started BOOLEAN DEFAULT false, " + + "is_logged BOOLEAN DEFAULT false, " + "external_key varchar(128) NULL, " + "payment_id varchar(36) NULL," + "payment_method_id varchar(36) NULL," + From cf319fac8946c224df8b5b71521218c619e9de31 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 13:58:51 +0530 Subject: [PATCH 037/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 325 ++++++------------ 1 file changed, 102 insertions(+), 223 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 915958b7a..e6ae6c97f 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,20 +21,18 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; -import java.util.Set; import java.util.TimeZone; import javax.annotation.Nullable; @@ -75,15 +73,9 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; - private static final List HIGH_TO_LOW_PRIORITY_ORDER = - Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; - private volatile Map> cachedPropertiesBySource; + private final Properties properties; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -101,187 +93,64 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties(category, propsMap); + + } - final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties("RuntimeConfiguration", propsMap); + for (final Entry entry : extraDefaultProperties.entrySet()) { + if (entry.getValue() != null) { + properties.put(entry.getKey(), entry.getValue()); + } } - populateDefaultProperties(extraDefaultProperties); + propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); - rebuildCache(); + populateDefaultProperties(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); - rebuildCache(); } if (Boolean.parseBoolean(getString(ENABLE_JASYPT_DECRYPTION))) { decryptJasyptProperties(); - rebuildCache(); } } @Override public String getString(final String propertyName) { - final Map> bySource = getPropertiesBySource(); - - for (final Map sourceProps : bySource.values()) { - final String value = sourceProps.get(propertyName); - if (value != null) { - return value; - } - } - - return null; + return properties.getProperty(propertyName); } @Override public Properties getProperties() { final Properties result = new Properties(); - - getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); - - return result; - } - - @Override - public Map> getPropertiesBySource() { - if (cachedPropertiesBySource == null) { - synchronized (lock) { - if (cachedPropertiesBySource == null) { - rebuildCache(); - } - } - } - - return Collections.unmodifiableMap(cachedPropertiesBySource); - } - - private void rebuildCache() { - cachedPropertiesBySource = computePropertiesBySource(); - } - - private void invalidateCache() { - synchronized (lock) { - cachedPropertiesBySource = null; - } - } - - private Map> computePropertiesBySource() { - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - runtimeBySource.forEach((source, props) -> { - if (!props.isEmpty()) { - propertiesCollector.addProperties(source, props); - } - }); - - final Map effectiveMap = new LinkedHashMap<>(); - propertiesCollector.getAllProperties().forEach(prop -> { - if (!effectiveMap.containsKey(prop.getKey())) { - effectiveMap.put(prop.getKey(), prop.getValue()); - } - }); + // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside + // tomcat, if we put configuration in tomcat's catalina.properties + // See: + // - https://github.com/killbill/technical-support/issues/61 + // - https://github.com/killbill/technical-support/issues/67 + // + // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar + // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) + properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!effectiveMap.containsKey(key)) { - effectiveMap.put(key, value); - } - }); - - final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); - - final Map> propertyToSources = new HashMap<>(); - collectorBySource.forEach((source, properties) -> { - properties.forEach(property -> { - propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); - }); - }); - - final Set warnedConflicts = new HashSet<>(); - final Map> result = new LinkedHashMap<>(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List properties = collectorBySource.get(source); - if (properties == null || properties.isEmpty()) { - continue; - } - - final Map sourceMap = new LinkedHashMap<>(); - - for (final PropertyWithSource prop : properties) { - final String propertyKey = prop.getKey(); - final String effectiveValue = prop.getValue(); - - if (effectiveValue == null) { - continue; - } - - if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - continue; - } - - sourceMap.put(propertyKey, effectiveValue); - } - - final Set processedInThisSource = new HashSet<>(sourceMap.keySet()); - for (final String propertyKey : processedInThisSource) { - final Set sources = propertyToSources.get(propertyKey); - if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, sourceMap.get(propertyKey)); - } - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - } - - collectorBySource.forEach((source, properties) -> { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - return; - } - - final Map sourceMap = new LinkedHashMap<>(); - for (final PropertyWithSource prop : properties) { - final String effectiveValue = effectiveMap.get(prop.getKey()); - if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { - sourceMap.put(prop.getKey(), effectiveValue); - } - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - }); - - return Collections.unmodifiableMap(result); - } - - private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, - final Map> propertiesBySource) { - final List sourcesForKey = new ArrayList<>(); - propertiesBySource.forEach((source, props) -> { - if (props.stream().anyMatch(p -> p.getKey().equals(key))) { - sourcesForKey.add(source); + if (!result.containsKey(key)) { + result.setProperty(key, value); } }); - for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { - if (sourcesForKey.contains(prioritySource)) { - return prioritySource.equals(sourceToCheck); - } - } - - return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); + return result; } - private void loadPropertiesFromFileOrSystemProperties() { + private Properties loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); @@ -291,10 +160,11 @@ private void loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); + final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties("RuntimeConfiguration", propsMap); + propertiesCollector.addProperties(category, propsMap); - return; + return properties; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -302,25 +172,21 @@ private void loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); + propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); + + return new Properties(System.getProperties()); } @VisibleForTesting - protected void populateDefaultProperties(final Map extraDefaultProperties) { + protected void populateDefaultProperties() { final Properties defaultProperties = getDefaultProperties(); - defaultProperties.putAll(extraDefaultProperties); - - final Map defaultsToAdd = new HashMap<>(); - for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties - if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultProperties.get(propertyName)); } } - final Map immutableProps = new HashMap<>(); - final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -346,10 +212,6 @@ protected void populateDefaultProperties(final Map extraDefaultP // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - - immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - defaultsToAdd.put(propertyName, GMT_ID); - continue; } @@ -357,10 +219,6 @@ protected void populateDefaultProperties(final Map extraDefaultP if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - - if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); - } } // WARN for missing PROP_SECURITY_EGD @@ -375,29 +233,48 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - if (!immutableProps.isEmpty()) { - propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); - } - defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("KillBillDefaults", propsMap); + propertiesCollector.addProperties("DefaultSystemProperties", propsMap); } - private boolean hasProperty(final String propertyName) { - return propertiesCollector.getAllProperties().stream() - .anyMatch(p -> p.getKey().equals(propertyName)); + @Override + public Map> getPropertiesBySource() { + final Map currentProps = new HashMap<>(); + properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); + + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + final Map filteredProps = new HashMap<>(); + props.forEach((key, value) -> { + if (!currentProps.containsKey(key)) { + filteredProps.put(key, value); + } + }); + if (!filteredProps.isEmpty()) { + propertiesCollector.addProperties(source, filteredProps); + } + }); + + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + + final Map> result = new LinkedHashMap<>(); + + propertiesBySource.forEach((source, properties) -> { + final Map sourceProperties = new LinkedHashMap<>(); + properties.forEach(prop -> { + sourceProperties.put(prop.getKey(), prop.getValue()); + }); + result.put(source, Collections.unmodifiableMap(sourceProperties)); + }); + + return Collections.unmodifiableMap(result); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { - final Map override = new HashMap<>(); - override.put(propertyName, String.valueOf(propertyValue)); - propertiesCollector.addProperties("RuntimeConfiguration", override); - - invalidateCache(); - rebuildCache(); + properties.put(propertyName, propertyValue); } @VisibleForTesting @@ -407,7 +284,6 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); - return properties; } @@ -428,7 +304,8 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = getEnvironmentVariables(); + final Map env = System.getenv(); + final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -440,16 +317,12 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); + properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } - @VisibleForTesting - protected Map getEnvironmentVariables() { - return System.getenv(); - } - public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -463,27 +336,15 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Map> decryptedBySource = new HashMap<>(); - + final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match - final List allProperties = propertiesCollector.getAllProperties(); - for (final PropertyWithSource prop : allProperties) { - final String key = prop.getKey(); - final String value = prop.getValue(); + while (keys.hasMoreElements()) { + final String key = (String) keys.nextElement(); + final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); - if (decryptableValue.isPresent()) { - final String decryptedValue = encryptor.decrypt(decryptableValue.get()); - - final String source = prop.getSource(); - if (source != null) { - decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) - .put(key, decryptedValue); - } - } + decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); } - - decryptedBySource.forEach(propertiesCollector::addProperties); } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -525,11 +386,29 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } + private String extractFileNameFromPath(String path) { + if (path == null || path.isEmpty()) { + return "unknown.properties"; + } + + if (path.startsWith("file://")) { + path = path.substring("file://".length()); + } + + final Path fileName = Paths.get(path).getFileName(); + if (fileName == null) { + return "unknown.properties"; + } + + return fileName.toString(); + } + private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } + return propertiesMap; } -} \ No newline at end of file +} From e0d18023c28f85a96ac0fdfeb1f719d01e8339b7 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 14:04:56 +0530 Subject: [PATCH 038/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 1ebce418b..4709b94c1 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(extraDefaults); + populateDefaultProperties(); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(extraDefaults); + populateDefaultProperties(); } @Override From 56d94feec94895e41175c9ba6717caad110db6db Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 15:49:56 +0530 Subject: [PATCH 039/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 325 ++++++++++++------ 1 file changed, 223 insertions(+), 102 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index e6ae6c97f..915958b7a 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,18 +21,20 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.Set; import java.util.TimeZone; import javax.annotation.Nullable; @@ -73,9 +75,15 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; + private static final List HIGH_TO_LOW_PRIORITY_ORDER = + Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults")); + private final PropertiesWithSourceCollector propertiesCollector; - private final Properties properties; + private volatile Map> cachedPropertiesBySource; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -93,64 +101,187 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); - - } + final Properties properties = new Properties(); + properties.load(UriAccessor.accessUri(Objects.requireNonNull(this.getClass().getResource(file)).toURI())); - for (final Entry entry : extraDefaultProperties.entrySet()) { - if (entry.getValue() != null) { - properties.put(entry.getKey(), entry.getValue()); - } + final Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); } - propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); + populateDefaultProperties(extraDefaultProperties); - populateDefaultProperties(); + rebuildCache(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); + rebuildCache(); } if (Boolean.parseBoolean(getString(ENABLE_JASYPT_DECRYPTION))) { decryptJasyptProperties(); + rebuildCache(); } } @Override public String getString(final String propertyName) { - return properties.getProperty(propertyName); + final Map> bySource = getPropertiesBySource(); + + for (final Map sourceProps : bySource.values()) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + + return null; } @Override public Properties getProperties() { final Properties result = new Properties(); - // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside - // tomcat, if we put configuration in tomcat's catalina.properties - // See: - // - https://github.com/killbill/technical-support/issues/61 - // - https://github.com/killbill/technical-support/issues/67 - // - // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar - // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) - properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); + + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + + return result; + } + + @Override + public Map> getPropertiesBySource() { + if (cachedPropertiesBySource == null) { + synchronized (lock) { + if (cachedPropertiesBySource == null) { + rebuildCache(); + } + } + } + + return Collections.unmodifiableMap(cachedPropertiesBySource); + } + + private void rebuildCache() { + cachedPropertiesBySource = computePropertiesBySource(); + } + + private void invalidateCache() { + synchronized (lock) { + cachedPropertiesBySource = null; + } + } + + private Map> computePropertiesBySource() { + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + if (!props.isEmpty()) { + propertiesCollector.addProperties(source, props); + } + }); + + final Map effectiveMap = new LinkedHashMap<>(); + propertiesCollector.getAllProperties().forEach(prop -> { + if (!effectiveMap.containsKey(prop.getKey())) { + effectiveMap.put(prop.getKey(), prop.getValue()); + } + }); RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!result.containsKey(key)) { - result.setProperty(key, value); + if (!effectiveMap.containsKey(key)) { + effectiveMap.put(key, value); } }); - return result; + final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + + final Map> propertyToSources = new HashMap<>(); + collectorBySource.forEach((source, properties) -> { + properties.forEach(property -> { + propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); + }); + }); + + final Set warnedConflicts = new HashSet<>(); + final Map> result = new LinkedHashMap<>(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List properties = collectorBySource.get(source); + if (properties == null || properties.isEmpty()) { + continue; + } + + final Map sourceMap = new LinkedHashMap<>(); + + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + final String effectiveValue = prop.getValue(); + + if (effectiveValue == null) { + continue; + } + + if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + continue; + } + + sourceMap.put(propertyKey, effectiveValue); + } + + final Set processedInThisSource = new HashSet<>(sourceMap.keySet()); + for (final String propertyKey : processedInThisSource) { + final Set sources = propertyToSources.get(propertyKey); + if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, new ArrayList<>(sources), source, sourceMap.get(propertyKey)); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + } + + collectorBySource.forEach((source, properties) -> { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + return; + } + + final Map sourceMap = new LinkedHashMap<>(); + for (final PropertyWithSource prop : properties) { + final String effectiveValue = effectiveMap.get(prop.getKey()); + if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { + sourceMap.put(prop.getKey(), effectiveValue); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + }); + + return Collections.unmodifiableMap(result); + } + + private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, + final Map> propertiesBySource) { + final List sourcesForKey = new ArrayList<>(); + propertiesBySource.forEach((source, props) -> { + if (props.stream().anyMatch(p -> p.getKey().equals(key))) { + sourcesForKey.add(source); + } + }); + + for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { + if (sourcesForKey.contains(prioritySource)) { + return prioritySource.equals(sourceToCheck); + } + } + + return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } - private Properties loadPropertiesFromFileOrSystemProperties() { + private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); @@ -160,11 +291,10 @@ private Properties loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); - final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); - return properties; + return; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -172,21 +302,25 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); - - return new Properties(System.getProperties()); + propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); } @VisibleForTesting - protected void populateDefaultProperties() { + protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); + defaultProperties.putAll(extraDefaultProperties); + + final Map defaultsToAdd = new HashMap<>(); + for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultProperties.get(propertyName)); + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } } + final Map immutableProps = new HashMap<>(); + final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -212,6 +346,10 @@ protected void populateDefaultProperties() { // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); + + immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); + defaultsToAdd.put(propertyName, GMT_ID); + continue; } @@ -219,6 +357,10 @@ protected void populateDefaultProperties() { if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } + + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); + } } // WARN for missing PROP_SECURITY_EGD @@ -233,48 +375,29 @@ protected void populateDefaultProperties() { } } + if (!immutableProps.isEmpty()) { + propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); + } + defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("DefaultSystemProperties", propsMap); + propertiesCollector.addProperties("KillBillDefaults", propsMap); } - @Override - public Map> getPropertiesBySource() { - final Map currentProps = new HashMap<>(); - properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); - - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - runtimeBySource.forEach((source, props) -> { - final Map filteredProps = new HashMap<>(); - props.forEach((key, value) -> { - if (!currentProps.containsKey(key)) { - filteredProps.put(key, value); - } - }); - if (!filteredProps.isEmpty()) { - propertiesCollector.addProperties(source, filteredProps); - } - }); - - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - - final Map> result = new LinkedHashMap<>(); - - propertiesBySource.forEach((source, properties) -> { - final Map sourceProperties = new LinkedHashMap<>(); - properties.forEach(prop -> { - sourceProperties.put(prop.getKey(), prop.getValue()); - }); - result.put(source, Collections.unmodifiableMap(sourceProperties)); - }); - - return Collections.unmodifiableMap(result); + private boolean hasProperty(final String propertyName) { + return propertiesCollector.getAllProperties().stream() + .anyMatch(p -> p.getKey().equals(propertyName)); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { - properties.put(propertyName, propertyValue); + final Map override = new HashMap<>(); + override.put(propertyName, String.valueOf(propertyValue)); + propertiesCollector.addProperties("RuntimeConfiguration", override); + + invalidateCache(); + rebuildCache(); } @VisibleForTesting @@ -284,6 +407,7 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); + return properties; } @@ -304,8 +428,7 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = System.getenv(); - + final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -317,12 +440,16 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); - properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } + @VisibleForTesting + protected Map getEnvironmentVariables() { + return System.getenv(); + } + public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -336,15 +463,27 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Enumeration keys = properties.keys(); + final Map> decryptedBySource = new HashMap<>(); + final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match - while (keys.hasMoreElements()) { - final String key = (String) keys.nextElement(); - final String value = (String) properties.get(key); + final List allProperties = propertiesCollector.getAllProperties(); + for (final PropertyWithSource prop : allProperties) { + final String key = prop.getKey(); + final String value = prop.getValue(); final Optional decryptableValue = decryptableValue(value); - decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); + if (decryptableValue.isPresent()) { + final String decryptedValue = encryptor.decrypt(decryptableValue.get()); + + final String source = prop.getSource(); + if (source != null) { + decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) + .put(key, decryptedValue); + } + } } + + decryptedBySource.forEach(propertiesCollector::addProperties); } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -386,29 +525,11 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } - private String extractFileNameFromPath(String path) { - if (path == null || path.isEmpty()) { - return "unknown.properties"; - } - - if (path.startsWith("file://")) { - path = path.substring("file://".length()); - } - - final Path fileName = Paths.get(path).getFileName(); - if (fileName == null) { - return "unknown.properties"; - } - - return fileName.toString(); - } - private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } - return propertiesMap; } -} +} \ No newline at end of file From db71c2fc9e2eb16d279d42e8a957a7f47d95df5e Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 16:03:57 +0530 Subject: [PATCH 040/221] Added debug msg to TestJNDIManager --- .../TestDefaultKillbillConfigSource.java | 332 ++++++++++++++++++ .../test/config/TestKillbillConfigSource.java | 4 +- 2 files changed, 334 insertions(+), 2 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 4e5675067..40a01ed7a 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -19,6 +19,338 @@ package org.killbill.billing.platform.config; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.exceptions.EncryptionOperationNotPossibleException; +import org.killbill.billing.osgi.api.OSGIConfigProperties; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.killbill.billing.platform.config.DefaultKillbillConfigSource.ENVIRONMENT_VARIABLE_PREFIX; + public class TestDefaultKillbillConfigSource { + private static final String ENABLE_JASYPT_PROPERTY = "org.killbill.server.enableJasypt"; + private static final String JASYPT_ENCRYPTOR_PASSWORD_PROPERTY = "JASYPT_ENCRYPTOR_PASSWORD"; + private static final String JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY = "JASYPT_ENCRYPTOR_ALGORITHM"; + private static final String JASYPT_PASSWORD = "top_secret!"; + private static final String JASYPT_ALGORITHM = "PBEWITHMD5ANDDES"; + private static final String ENCRYPTED_PROPERTY_1 = "test.encrypted.property1"; + private static final String ENCRYPTED_PROPERTY_2 = "test.encrypted.property2"; + + @BeforeMethod(groups = "fast") + public void setup() { + // Clean out the properties we set in the tests, + // this is only necessary because the DefaultKillBillConfigSource constructor we're using ends up + // setting this.properties to System.getProperties(), which doesn't automatically get reset between tests. + System.clearProperty(ENABLE_JASYPT_PROPERTY); + System.clearProperty(JASYPT_PASSWORD); + System.clearProperty(JASYPT_ALGORITHM); + System.clearProperty(ENCRYPTED_PROPERTY_1); + System.clearProperty(ENCRYPTED_PROPERTY_2); + } + + @Test + public void testGetPropertiesBySourceContainsExpectedSources() throws URISyntaxException, IOException { + final Map runtimeConfig = new HashMap<>(); + runtimeConfig.put("org.killbill.dao.user", "root"); + + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_user", "root"); + return mockEnv; + } + }; + + final Map> propsBySource = configSource.getPropertiesBySource(); + + Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); + Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); + Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); + } + + @Test(groups = "fast") + public void testGetPropertiesAndGetPropertiesBySourceAreInSync() throws URISyntaxException, IOException { + // RuntimeConfiguration + System.setProperty("org.killbill.dao.user", "root"); + System.setProperty("org.killbill.dao.password", "password"); + + // KillBillDefaults + final Map killbillDefaultConfig = new HashMap<>(); + killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); + killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); + + // ImmutableSystemProperties + killbillDefaultConfig.put("user.timezone", "GMT"); + + // EnvironmentVariables + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); + return mockEnv; + } + }; + + final Properties mergedProperties = configSource.getProperties(); + final Map> propertiesBySource = configSource.getPropertiesBySource(); + + final Map allProperties = new HashMap<>(); + propertiesBySource.forEach((source, props) -> allProperties.putAll(props)); + + for (final String key : mergedProperties.stringPropertyNames()) { + final String valueFromFlat = mergedProperties.getProperty(key); + final String valueFromSource = allProperties.get(key); + + Assert.assertNotNull(valueFromSource); + Assert.assertEquals(valueFromFlat, valueFromSource); + } + + // Verify that no property appears in multiple sources + final Map propertyCount = new HashMap<>(); + propertiesBySource.forEach((source, props) -> { + props.keySet().forEach(key -> propertyCount.put(key, propertyCount.getOrDefault(key, 0) + 1)); + }); + + propertyCount.forEach((key, count) -> { + Assert.assertEquals(count.intValue(), 1); + }); + + Assert.assertEquals(mergedProperties.size(), allProperties.size()); + } + + @Test + public void testConflictResolutionPriority() throws Exception { + // RuntimeConfiguration + System.setProperty("org.killbill.test", "lowValue"); + + final DefaultKillbillConfigSource testSource = new DefaultKillbillConfigSource((String) null) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_test", "highValue"); + return mockEnv; + } + }; + + testSource.setProperty("org.killbill.test", "lowValue"); + + final Properties properties = testSource.getProperties(); + + final String effectiveValue = properties.getProperty("org.killbill.test"); + Assert.assertEquals(effectiveValue, "highValue"); + } + + @Test(groups = "fast") + public void testGetProperties() throws URISyntaxException, IOException { + final Map configuration = new HashMap<>(); + configuration.put("1", "A"); + configuration.put("2", "B"); + + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); + + Assert.assertNotNull(configSource.getProperties()); + Assert.assertNotEquals(configSource.getProperties().size(), 0); + Assert.assertEquals(configSource.getProperties().getProperty("1"), "A"); + } + + @Test(groups = "fast") + public void testGetPropertiesBySource() throws URISyntaxException, IOException { + // RuntimeConfiguration + System.setProperty("org.killbill.dao.user", "root"); + System.setProperty("org.killbill.dao.password", "password"); + + // KillBillDefaults + final Map killbillDefaultConfig = new HashMap<>(); + killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); + killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); + + // ImmutableSystemProperties + killbillDefaultConfig.put("user.timezone", "GMT"); + + // EnvironmentVariables + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_maxActive", "99"); + + return mockEnv; + } + }; + + final Map> propsBySource = configSource.getPropertiesBySource(); + + Assert.assertNotNull(propsBySource); + Assert.assertFalse(propsBySource.isEmpty()); + + Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); + + final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); + Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); + + Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); + + final Map environmentVariables = propsBySource.get("EnvironmentVariables"); + Assert.assertEquals(environmentVariables.get("org.killbill.dao.healthCheckConnectionTimeout"), "11s"); + Assert.assertEquals(environmentVariables.get("org.killbill.billing.osgi.dao.maxActive"), "99"); + + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); + + final Map runtimeConfig = propsBySource.get("RuntimeConfiguration"); + Assert.assertEquals(runtimeConfig.get("org.killbill.dao.user"), "root"); + Assert.assertEquals(runtimeConfig.get("org.killbill.dao.password"), "password"); + + Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); + + final Map killBillDefaults = propsBySource.get("KillBillDefaults"); + Assert.assertEquals(killBillDefaults.get("org.killbill.server.shutdownDelay"), "3s"); + Assert.assertEquals(killBillDefaults.get("org.killbill.billing.osgi.dao.logLevel"), "INFO"); + + final List actualSourceOrder = new ArrayList<>(propsBySource.keySet()); + + final List expectedPrecedenceOrder = Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults"); + + Assert.assertEquals(actualSourceOrder, expectedPrecedenceOrder); + } + + @Test(groups = "fast") + public void testFromEnvVariableName() throws IOException, URISyntaxException { + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); + + Assert.assertEquals(configSource.fromEnvVariableName(""), ""); + Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao.prepStmtCacheSize"); + // Note! This won't work: we don't support underscores in property keys + //Assert.assertEquals(configSource.fromEnvVariableName("org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao_prepStmtCacheSize"); + Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao__prepStmtCacheSize"), "org.killbill.billing.osgi.dao..prepStmtCacheSize"); + } + + @Test(groups = "fast") + public void testJasyptDisabledByDefault() throws IOException, URISyntaxException { + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); + + final String enableJasyptString = configSource.getString(ENABLE_JASYPT_PROPERTY); + + Assert.assertFalse(Boolean.parseBoolean(enableJasyptString)); + } + + @Test(groups = "fast") + public void testDecyptionExplicitlyDisabled() throws IOException, URISyntaxException { + final String unencryptedValue = "myPropertyValue"; + final String encryptedValue = encString(unencryptedValue); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "false", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); + + final String actualValue = configSource.getString(ENCRYPTED_PROPERTY_1); + + Assert.assertEquals(encryptedValue, actualValue); + } + + @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) + public void testDecryptEmptyPassword() throws IOException, URISyntaxException { + final String encryptedValue = encString("myPropertyValue"); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, "", + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) + public void testDecryptEmptyAlgorithm() throws IOException, URISyntaxException { + final String encryptedValue = encString("myPropertyValue"); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, ""); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) + public void testDecryptInvalidJasyptString() throws IOException, URISyntaxException { + final String encryptedValue = "ENC(notAValidEncryptedString!)"; + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) + public void testDecryptEmptyJasyptString() throws IOException, URISyntaxException { + final String encryptedValue = "ENC()"; + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast") + public void testDecryptJasyptPropertySuccessfully() throws IOException, URISyntaxException { + final String unencryptedValue1 = "myPropertyValue"; + final String encryptedValue1 = encString(unencryptedValue1); + final String unencryptedValue2 = "myOtherPropertyValue"; + final String encryptedValue2 = encString(unencryptedValue2); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue1, + ENCRYPTED_PROPERTY_2, encryptedValue2, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); + + final String actualValue1 = configSource.getString(ENCRYPTED_PROPERTY_1); + final String actualValue2 = configSource.getString(ENCRYPTED_PROPERTY_2); + + Assert.assertEquals(unencryptedValue1, actualValue1); + Assert.assertEquals(unencryptedValue2, actualValue2); + } + + private String encString(final String unencryptedValue) { + return "ENC(" + encrypt(unencryptedValue, JASYPT_ALGORITHM, JASYPT_PASSWORD) + ")"; + } + + private String encrypt(final String unencryptedValue, final String jasyptAlgorithm, final String jasyptPassword) { + final StandardPBEStringEncryptor encryptor = setupEncryptor(jasyptPassword, jasyptAlgorithm); + return encryptor.encrypt(unencryptedValue); + } + + private StandardPBEStringEncryptor setupEncryptor(final String password, final String algorithm) { + final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); + encryptor.setPassword(password); + encryptor.setAlgorithm(algorithm); + return encryptor; + } } diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 4709b94c1..1ebce418b 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(); + populateDefaultProperties(extraDefaults); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(); + populateDefaultProperties(extraDefaults); } @Override From e60b36150a497f8fb9fbce93e32500bd7e61fdb1 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 16:26:20 +0530 Subject: [PATCH 041/221] Added debug msg to TestJNDIManager --- .../osgi/bundles/test/dao/TestDao.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index ddd6ce5f2..97bd0fcc3 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -39,21 +39,17 @@ public void createTable() { dbi.inTransaction(new TransactionCallback() { @Override public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { - try { - conn.execute("DROP TABLE IF EXISTS test_bundle;"); - conn.execute("CREATE TABLE test_bundle (" + - "record_id INT AUTO_INCREMENT PRIMARY KEY, " + - "is_started BOOLEAN DEFAULT false, " + - "is_logged BOOLEAN DEFAULT false, " + - "external_key varchar(128) NULL, " + - "payment_id varchar(36) NULL," + - "payment_method_id varchar(36) NULL," + - "payment_amount decimal(10,4) NULL" + - ");"); - } catch (final Exception e) { - System.out.println("create table failed... " + e.getMessage() + e); - throw e; - } + conn.execute("DROP TABLE IF EXISTS test_bundle;"); + conn.execute("CREATE TABLE test_bundle (" + + "record_id serial unique, " + + "is_started bool DEFAULT false, " + + "is_logged bool DEFAULT false, " + + "external_key varchar(128) NULL, " + + "payment_id varchar(36) NULL," + + "payment_method_id varchar(36) NULL," + + "payment_amount decimal(10,4) NULL," + + "PRIMARY KEY(record_id)" + + ");"); return null; } }); From ce931faa5dd824b886b73a29996a23e8a70de09c Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 18:45:41 +0530 Subject: [PATCH 042/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 120 +++++++++--------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 915958b7a..27f562e59 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -82,6 +83,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "KillBillDefaults")); private final PropertiesWithSourceCollector propertiesCollector; + private final Properties properties; private volatile Map> cachedPropertiesBySource; @@ -101,10 +103,10 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); @@ -112,31 +114,18 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map> bySource = getPropertiesBySource(); - - for (final Map sourceProps : bySource.values()) { - final String value = sourceProps.get(propertyName); - if (value != null) { - return value; - } - } - - return null; + return properties.getProperty(propertyName); } @Override @@ -153,7 +142,7 @@ public Map> getPropertiesBySource() { if (cachedPropertiesBySource == null) { synchronized (lock) { if (cachedPropertiesBySource == null) { - rebuildCache(); + cachedPropertiesBySource = computePropertiesBySource(); } } } @@ -161,16 +150,6 @@ public Map> getPropertiesBySource() { return Collections.unmodifiableMap(cachedPropertiesBySource); } - private void rebuildCache() { - cachedPropertiesBySource = computePropertiesBySource(); - } - - private void invalidateCache() { - synchronized (lock) { - cachedPropertiesBySource = null; - } - } - private Map> computePropertiesBySource() { final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); runtimeBySource.forEach((source, props) -> { @@ -180,12 +159,7 @@ private Map> computePropertiesBySource() { }); final Map effectiveMap = new LinkedHashMap<>(); - propertiesCollector.getAllProperties().forEach(prop -> { - if (!effectiveMap.containsKey(prop.getKey())) { - effectiveMap.put(prop.getKey(), prop.getValue()); - } - }); - + properties.stringPropertyNames().forEach(key -> effectiveMap.put(key, properties.getProperty(key))); RuntimeConfigRegistry.getAll().forEach((key, value) -> { if (!effectiveMap.containsKey(key)) { effectiveMap.put(key, value); @@ -211,9 +185,16 @@ private Map> computePropertiesBySource() { } final Map sourceMap = new LinkedHashMap<>(); + final Set processedKeys = new HashSet<>(); for (final PropertyWithSource prop : properties) { final String propertyKey = prop.getKey(); + + if (processedKeys.contains(propertyKey)) { + continue; + } + processedKeys.add(propertyKey); + final String effectiveValue = prop.getValue(); if (effectiveValue == null) { @@ -224,17 +205,14 @@ private Map> computePropertiesBySource() { continue; } - sourceMap.put(propertyKey, effectiveValue); - } - - final Set processedInThisSource = new HashSet<>(sourceMap.keySet()); - for (final String propertyKey : processedInThisSource) { final Set sources = propertyToSources.get(propertyKey); if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { warnedConflicts.add(propertyKey); logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, sourceMap.get(propertyKey)); + propertyKey, new ArrayList<>(sources), source, effectiveValue); } + + sourceMap.put(propertyKey, effectiveValue); } if (!sourceMap.isEmpty()) { @@ -281,7 +259,7 @@ private boolean isEffectiveSourceForProperty(final String key, final String sour return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } - private void loadPropertiesFromFileOrSystemProperties() { + private Properties loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); @@ -294,7 +272,7 @@ private void loadPropertiesFromFileOrSystemProperties() { final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); - return; + return properties; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -303,6 +281,7 @@ private void loadPropertiesFromFileOrSystemProperties() { } propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); + return new Properties(System.getProperties()); } @VisibleForTesting @@ -310,12 +289,10 @@ protected void populateDefaultProperties(final Map extraDefaultP final Properties defaultProperties = getDefaultProperties(); defaultProperties.putAll(extraDefaultProperties); - final Map defaultsToAdd = new HashMap<>(); - for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties - if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultProperties.get(propertyName)); } } @@ -348,8 +325,7 @@ protected void populateDefaultProperties(final Map extraDefaultP TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - defaultsToAdd.put(propertyName, GMT_ID); - + properties.put(propertyName, GMT_ID); continue; } @@ -358,8 +334,8 @@ protected void populateDefaultProperties(final Map extraDefaultP System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultSystemProperties.get(propertyName)); } } @@ -385,19 +361,17 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("KillBillDefaults", propsMap); } - private boolean hasProperty(final String propertyName) { - return propertiesCollector.getAllProperties().stream() - .anyMatch(p -> p.getKey().equals(propertyName)); - } - @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { + properties.put(propertyName, propertyValue); + final Map override = new HashMap<>(); override.put(propertyName, String.valueOf(propertyValue)); propertiesCollector.addProperties("RuntimeConfiguration", override); - invalidateCache(); - rebuildCache(); + synchronized (lock) { + this.cachedPropertiesBySource = null; + } } @VisibleForTesting @@ -440,6 +414,7 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); + properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); @@ -465,17 +440,18 @@ private void decryptJasyptProperties() { final Map> decryptedBySource = new HashMap<>(); + final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match - final List allProperties = propertiesCollector.getAllProperties(); - for (final PropertyWithSource prop : allProperties) { - final String key = prop.getKey(); - final String value = prop.getValue(); + while (keys.hasMoreElements()) { + final String key = (String) keys.nextElement(); + final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); if (decryptableValue.isPresent()) { final String decryptedValue = encryptor.decrypt(decryptableValue.get()); + properties.setProperty(key, decryptedValue); - final String source = prop.getSource(); + final String source = findSourceForProperty(key); if (source != null) { decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) .put(key, decryptedValue); @@ -486,6 +462,26 @@ private void decryptJasyptProperties() { decryptedBySource.forEach(propertiesCollector::addProperties); } + private String findSourceForProperty(final String key) { + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List props = propertiesBySource.get(source); + if (props != null && props.stream().anyMatch(p -> p.getKey().equals(key))) { + return source; + } + } + + for (final Map.Entry> entry : propertiesBySource.entrySet()) { + if (!HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey()) && + entry.getValue().stream().anyMatch(p -> p.getKey().equals(key))) { + return entry.getKey(); + } + } + + return null; + } + private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); From 69c7215d88142765474ace35d84c92d074ecb2fe Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 18:52:06 +0530 Subject: [PATCH 043/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 295 ++++++------------ 1 file changed, 89 insertions(+), 206 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 27f562e59..e6ae6c97f 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,21 +21,18 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; -import java.util.Set; import java.util.TimeZone; import javax.annotation.Nullable; @@ -76,16 +73,9 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; - private static final List HIGH_TO_LOW_PRIORITY_ORDER = - Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; - private final Properties properties; - private volatile Map> cachedPropertiesBySource; + private final Properties properties; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -108,11 +98,21 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties("RuntimeConfiguration", propsMap); + final String category = extractFileNameFromPath(file); + Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties(category, propsMap); + } - populateDefaultProperties(extraDefaultProperties); + for (final Entry entry : extraDefaultProperties.entrySet()) { + if (entry.getValue() != null) { + properties.put(entry.getKey(), entry.getValue()); + } + } + + propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); + + populateDefaultProperties(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); @@ -131,132 +131,23 @@ public String getString(final String propertyName) { @Override public Properties getProperties() { final Properties result = new Properties(); + // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside + // tomcat, if we put configuration in tomcat's catalina.properties + // See: + // - https://github.com/killbill/technical-support/issues/61 + // - https://github.com/killbill/technical-support/issues/67 + // + // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar + // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) + properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); - getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); - - return result; - } - - @Override - public Map> getPropertiesBySource() { - if (cachedPropertiesBySource == null) { - synchronized (lock) { - if (cachedPropertiesBySource == null) { - cachedPropertiesBySource = computePropertiesBySource(); - } - } - } - - return Collections.unmodifiableMap(cachedPropertiesBySource); - } - - private Map> computePropertiesBySource() { - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - runtimeBySource.forEach((source, props) -> { - if (!props.isEmpty()) { - propertiesCollector.addProperties(source, props); - } - }); - - final Map effectiveMap = new LinkedHashMap<>(); - properties.stringPropertyNames().forEach(key -> effectiveMap.put(key, properties.getProperty(key))); RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!effectiveMap.containsKey(key)) { - effectiveMap.put(key, value); - } - }); - - final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); - - final Map> propertyToSources = new HashMap<>(); - collectorBySource.forEach((source, properties) -> { - properties.forEach(property -> { - propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); - }); - }); - - final Set warnedConflicts = new HashSet<>(); - final Map> result = new LinkedHashMap<>(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List properties = collectorBySource.get(source); - if (properties == null || properties.isEmpty()) { - continue; - } - - final Map sourceMap = new LinkedHashMap<>(); - final Set processedKeys = new HashSet<>(); - - for (final PropertyWithSource prop : properties) { - final String propertyKey = prop.getKey(); - - if (processedKeys.contains(propertyKey)) { - continue; - } - processedKeys.add(propertyKey); - - final String effectiveValue = prop.getValue(); - - if (effectiveValue == null) { - continue; - } - - if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - continue; - } - - final Set sources = propertyToSources.get(propertyKey); - if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, effectiveValue); - } - - sourceMap.put(propertyKey, effectiveValue); - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - } - - collectorBySource.forEach((source, properties) -> { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - return; - } - - final Map sourceMap = new LinkedHashMap<>(); - for (final PropertyWithSource prop : properties) { - final String effectiveValue = effectiveMap.get(prop.getKey()); - if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { - sourceMap.put(prop.getKey(), effectiveValue); - } - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - }); - - return Collections.unmodifiableMap(result); - } - - private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, - final Map> propertiesBySource) { - final List sourcesForKey = new ArrayList<>(); - propertiesBySource.forEach((source, props) -> { - if (props.stream().anyMatch(p -> p.getKey().equals(key))) { - sourcesForKey.add(source); + if (!result.containsKey(key)) { + result.setProperty(key, value); } }); - for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { - if (sourcesForKey.contains(prioritySource)) { - return prioritySource.equals(sourceToCheck); - } - } - - return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); + return result; } private Properties loadPropertiesFromFileOrSystemProperties() { @@ -269,8 +160,9 @@ private Properties loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); + final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties("RuntimeConfiguration", propsMap); + propertiesCollector.addProperties(category, propsMap); return properties; } catch (final IOException e) { @@ -280,15 +172,14 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); + propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); + return new Properties(System.getProperties()); } @VisibleForTesting - protected void populateDefaultProperties(final Map extraDefaultProperties) { + protected void populateDefaultProperties() { final Properties defaultProperties = getDefaultProperties(); - defaultProperties.putAll(extraDefaultProperties); - for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties if (properties.get(propertyName) == null) { @@ -296,8 +187,6 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - final Map immutableProps = new HashMap<>(); - final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -323,9 +212,6 @@ protected void populateDefaultProperties(final Map extraDefaultP // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - - immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - properties.put(propertyName, GMT_ID); continue; } @@ -333,10 +219,6 @@ protected void populateDefaultProperties(final Map extraDefaultP if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultSystemProperties.get(propertyName)); - } } // WARN for missing PROP_SECURITY_EGD @@ -351,27 +233,48 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - if (!immutableProps.isEmpty()) { - propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); - } - defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("KillBillDefaults", propsMap); + propertiesCollector.addProperties("DefaultSystemProperties", propsMap); + } + + @Override + public Map> getPropertiesBySource() { + final Map currentProps = new HashMap<>(); + properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); + + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + final Map filteredProps = new HashMap<>(); + props.forEach((key, value) -> { + if (!currentProps.containsKey(key)) { + filteredProps.put(key, value); + } + }); + if (!filteredProps.isEmpty()) { + propertiesCollector.addProperties(source, filteredProps); + } + }); + + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + + final Map> result = new LinkedHashMap<>(); + + propertiesBySource.forEach((source, properties) -> { + final Map sourceProperties = new LinkedHashMap<>(); + properties.forEach(prop -> { + sourceProperties.put(prop.getKey(), prop.getValue()); + }); + result.put(source, Collections.unmodifiableMap(sourceProperties)); + }); + + return Collections.unmodifiableMap(result); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { properties.put(propertyName, propertyValue); - - final Map override = new HashMap<>(); - override.put(propertyName, String.valueOf(propertyValue)); - propertiesCollector.addProperties("RuntimeConfiguration", override); - - synchronized (lock) { - this.cachedPropertiesBySource = null; - } } @VisibleForTesting @@ -381,7 +284,6 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); - return properties; } @@ -402,7 +304,8 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = getEnvironmentVariables(); + final Map env = System.getenv(); + final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -420,11 +323,6 @@ private void overrideWithEnvironmentVariables() { propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } - @VisibleForTesting - protected Map getEnvironmentVariables() { - return System.getenv(); - } - public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -438,8 +336,6 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Map> decryptedBySource = new HashMap<>(); - final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match @@ -447,39 +343,8 @@ private void decryptJasyptProperties() { final String key = (String) keys.nextElement(); final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); - if (decryptableValue.isPresent()) { - final String decryptedValue = encryptor.decrypt(decryptableValue.get()); - properties.setProperty(key, decryptedValue); - - final String source = findSourceForProperty(key); - if (source != null) { - decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) - .put(key, decryptedValue); - } - } - } - - decryptedBySource.forEach(propertiesCollector::addProperties); - } - - private String findSourceForProperty(final String key) { - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List props = propertiesBySource.get(source); - if (props != null && props.stream().anyMatch(p -> p.getKey().equals(key))) { - return source; - } - } - - for (final Map.Entry> entry : propertiesBySource.entrySet()) { - if (!HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey()) && - entry.getValue().stream().anyMatch(p -> p.getKey().equals(key))) { - return entry.getKey(); - } + decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); } - - return null; } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -521,11 +386,29 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } + private String extractFileNameFromPath(String path) { + if (path == null || path.isEmpty()) { + return "unknown.properties"; + } + + if (path.startsWith("file://")) { + path = path.substring("file://".length()); + } + + final Path fileName = Paths.get(path).getFileName(); + if (fileName == null) { + return "unknown.properties"; + } + + return fileName.toString(); + } + private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } + return propertiesMap; } -} \ No newline at end of file +} From 108f6a43baba24c534ef53085b2e42ca66060951 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 19:11:28 +0530 Subject: [PATCH 044/221] Added debug msg to TestJNDIManager --- .../TestDefaultKillbillConfigSource.java | 314 ------------------ .../osgi/bundles/test/TestActivator.java | 4 + .../osgi/bundles/test/dao/TestDao.java | 12 + .../test/config/TestKillbillConfigSource.java | 4 +- .../osgi/TestBasicOSGIWithTestBundle.java | 4 + 5 files changed, 22 insertions(+), 316 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 40a01ed7a..fd11442d7 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -39,318 +39,4 @@ public class TestDefaultKillbillConfigSource { - private static final String ENABLE_JASYPT_PROPERTY = "org.killbill.server.enableJasypt"; - private static final String JASYPT_ENCRYPTOR_PASSWORD_PROPERTY = "JASYPT_ENCRYPTOR_PASSWORD"; - private static final String JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY = "JASYPT_ENCRYPTOR_ALGORITHM"; - private static final String JASYPT_PASSWORD = "top_secret!"; - private static final String JASYPT_ALGORITHM = "PBEWITHMD5ANDDES"; - private static final String ENCRYPTED_PROPERTY_1 = "test.encrypted.property1"; - private static final String ENCRYPTED_PROPERTY_2 = "test.encrypted.property2"; - - @BeforeMethod(groups = "fast") - public void setup() { - // Clean out the properties we set in the tests, - // this is only necessary because the DefaultKillBillConfigSource constructor we're using ends up - // setting this.properties to System.getProperties(), which doesn't automatically get reset between tests. - System.clearProperty(ENABLE_JASYPT_PROPERTY); - System.clearProperty(JASYPT_PASSWORD); - System.clearProperty(JASYPT_ALGORITHM); - System.clearProperty(ENCRYPTED_PROPERTY_1); - System.clearProperty(ENCRYPTED_PROPERTY_2); - } - - @Test - public void testGetPropertiesBySourceContainsExpectedSources() throws URISyntaxException, IOException { - final Map runtimeConfig = new HashMap<>(); - runtimeConfig.put("org.killbill.dao.user", "root"); - - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_user", "root"); - return mockEnv; - } - }; - - final Map> propsBySource = configSource.getPropertiesBySource(); - - Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); - Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); - Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); - Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); - } - - @Test(groups = "fast") - public void testGetPropertiesAndGetPropertiesBySourceAreInSync() throws URISyntaxException, IOException { - // RuntimeConfiguration - System.setProperty("org.killbill.dao.user", "root"); - System.setProperty("org.killbill.dao.password", "password"); - - // KillBillDefaults - final Map killbillDefaultConfig = new HashMap<>(); - killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); - killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); - - // ImmutableSystemProperties - killbillDefaultConfig.put("user.timezone", "GMT"); - - // EnvironmentVariables - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); - return mockEnv; - } - }; - - final Properties mergedProperties = configSource.getProperties(); - final Map> propertiesBySource = configSource.getPropertiesBySource(); - - final Map allProperties = new HashMap<>(); - propertiesBySource.forEach((source, props) -> allProperties.putAll(props)); - - for (final String key : mergedProperties.stringPropertyNames()) { - final String valueFromFlat = mergedProperties.getProperty(key); - final String valueFromSource = allProperties.get(key); - - Assert.assertNotNull(valueFromSource); - Assert.assertEquals(valueFromFlat, valueFromSource); - } - - // Verify that no property appears in multiple sources - final Map propertyCount = new HashMap<>(); - propertiesBySource.forEach((source, props) -> { - props.keySet().forEach(key -> propertyCount.put(key, propertyCount.getOrDefault(key, 0) + 1)); - }); - - propertyCount.forEach((key, count) -> { - Assert.assertEquals(count.intValue(), 1); - }); - - Assert.assertEquals(mergedProperties.size(), allProperties.size()); - } - - @Test - public void testConflictResolutionPriority() throws Exception { - // RuntimeConfiguration - System.setProperty("org.killbill.test", "lowValue"); - - final DefaultKillbillConfigSource testSource = new DefaultKillbillConfigSource((String) null) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_test", "highValue"); - return mockEnv; - } - }; - - testSource.setProperty("org.killbill.test", "lowValue"); - - final Properties properties = testSource.getProperties(); - - final String effectiveValue = properties.getProperty("org.killbill.test"); - Assert.assertEquals(effectiveValue, "highValue"); - } - - @Test(groups = "fast") - public void testGetProperties() throws URISyntaxException, IOException { - final Map configuration = new HashMap<>(); - configuration.put("1", "A"); - configuration.put("2", "B"); - - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); - - Assert.assertNotNull(configSource.getProperties()); - Assert.assertNotEquals(configSource.getProperties().size(), 0); - Assert.assertEquals(configSource.getProperties().getProperty("1"), "A"); - } - - @Test(groups = "fast") - public void testGetPropertiesBySource() throws URISyntaxException, IOException { - // RuntimeConfiguration - System.setProperty("org.killbill.dao.user", "root"); - System.setProperty("org.killbill.dao.password", "password"); - - // KillBillDefaults - final Map killbillDefaultConfig = new HashMap<>(); - killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); - killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); - - // ImmutableSystemProperties - killbillDefaultConfig.put("user.timezone", "GMT"); - - // EnvironmentVariables - final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { - @Override - protected Map getEnvironmentVariables() { - final Map mockEnv = new HashMap<>(); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); - mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_maxActive", "99"); - - return mockEnv; - } - }; - - final Map> propsBySource = configSource.getPropertiesBySource(); - - Assert.assertNotNull(propsBySource); - Assert.assertFalse(propsBySource.isEmpty()); - - Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); - - final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); - Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); - - Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); - - final Map environmentVariables = propsBySource.get("EnvironmentVariables"); - Assert.assertEquals(environmentVariables.get("org.killbill.dao.healthCheckConnectionTimeout"), "11s"); - Assert.assertEquals(environmentVariables.get("org.killbill.billing.osgi.dao.maxActive"), "99"); - - Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); - - final Map runtimeConfig = propsBySource.get("RuntimeConfiguration"); - Assert.assertEquals(runtimeConfig.get("org.killbill.dao.user"), "root"); - Assert.assertEquals(runtimeConfig.get("org.killbill.dao.password"), "password"); - - Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); - - final Map killBillDefaults = propsBySource.get("KillBillDefaults"); - Assert.assertEquals(killBillDefaults.get("org.killbill.server.shutdownDelay"), "3s"); - Assert.assertEquals(killBillDefaults.get("org.killbill.billing.osgi.dao.logLevel"), "INFO"); - - final List actualSourceOrder = new ArrayList<>(propsBySource.keySet()); - - final List expectedPrecedenceOrder = Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults"); - - Assert.assertEquals(actualSourceOrder, expectedPrecedenceOrder); - } - - @Test(groups = "fast") - public void testFromEnvVariableName() throws IOException, URISyntaxException { - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); - - Assert.assertEquals(configSource.fromEnvVariableName(""), ""); - Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao.prepStmtCacheSize"); - // Note! This won't work: we don't support underscores in property keys - //Assert.assertEquals(configSource.fromEnvVariableName("org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao_prepStmtCacheSize"); - Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao__prepStmtCacheSize"), "org.killbill.billing.osgi.dao..prepStmtCacheSize"); - } - - @Test(groups = "fast") - public void testJasyptDisabledByDefault() throws IOException, URISyntaxException { - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); - - final String enableJasyptString = configSource.getString(ENABLE_JASYPT_PROPERTY); - - Assert.assertFalse(Boolean.parseBoolean(enableJasyptString)); - } - - @Test(groups = "fast") - public void testDecyptionExplicitlyDisabled() throws IOException, URISyntaxException { - final String unencryptedValue = "myPropertyValue"; - final String encryptedValue = encString(unencryptedValue); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "false", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); - - final String actualValue = configSource.getString(ENCRYPTED_PROPERTY_1); - - Assert.assertEquals(encryptedValue, actualValue); - } - - @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) - public void testDecryptEmptyPassword() throws IOException, URISyntaxException { - final String encryptedValue = encString("myPropertyValue"); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, "", - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) - public void testDecryptEmptyAlgorithm() throws IOException, URISyntaxException { - final String encryptedValue = encString("myPropertyValue"); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, ""); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) - public void testDecryptInvalidJasyptString() throws IOException, URISyntaxException { - final String encryptedValue = "ENC(notAValidEncryptedString!)"; - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) - public void testDecryptEmptyJasyptString() throws IOException, URISyntaxException { - final String encryptedValue = "ENC()"; - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - new DefaultKillbillConfigSource(properties); - } - - @Test(groups = "fast") - public void testDecryptJasyptPropertySuccessfully() throws IOException, URISyntaxException { - final String unencryptedValue1 = "myPropertyValue"; - final String encryptedValue1 = encString(unencryptedValue1); - final String unencryptedValue2 = "myOtherPropertyValue"; - final String encryptedValue2 = encString(unencryptedValue2); - - final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", - ENCRYPTED_PROPERTY_1, encryptedValue1, - ENCRYPTED_PROPERTY_2, encryptedValue2, - JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, - JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); - - final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); - - final String actualValue1 = configSource.getString(ENCRYPTED_PROPERTY_1); - final String actualValue2 = configSource.getString(ENCRYPTED_PROPERTY_2); - - Assert.assertEquals(unencryptedValue1, actualValue1); - Assert.assertEquals(unencryptedValue2, actualValue2); - } - - private String encString(final String unencryptedValue) { - return "ENC(" + encrypt(unencryptedValue, JASYPT_ALGORITHM, JASYPT_PASSWORD) + ")"; - } - - private String encrypt(final String unencryptedValue, final String jasyptAlgorithm, final String jasyptPassword) { - final StandardPBEStringEncryptor encryptor = setupEncryptor(jasyptPassword, jasyptAlgorithm); - return encryptor.encrypt(unencryptedValue); - } - - private StandardPBEStringEncryptor setupEncryptor(final String password, final String algorithm) { - final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); - encryptor.setPassword(password); - encryptor.setAlgorithm(algorithm); - return encryptor; - } } diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java index 5ed377660..7bba72585 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java @@ -61,6 +61,10 @@ public class TestActivator extends KillbillActivatorBase implements OSGIKillbill public void start(final BundleContext context) throws Exception { super.start(context); + logger.info("OSGI bundle sees org.killbill.billing.osgi.dao.url = {}", + System.getProperty("org.killbill.billing.osgi.dao.url")); + + final String bundleName = context.getBundle().getSymbolicName(); logger.info("TestActivator: starting bundle = {}", bundleName); diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 97bd0fcc3..0fa380681 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -26,9 +26,13 @@ import org.skife.jdbi.v2.IDBI; import org.skife.jdbi.v2.TransactionCallback; import org.skife.jdbi.v2.TransactionStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class TestDao { + private static final Logger logger = LoggerFactory.getLogger(TestDao.class); + private final IDBI dbi; public TestDao(final IDBI dbi) { @@ -36,9 +40,17 @@ public TestDao(final IDBI dbi) { } public void createTable() { + logger.info("=== TestDao.createTable() ==="); + logger.info("About to execute SQL with SERIAL data type"); + logger.info("System property org.killbill.billing.osgi.dao.url = {}", System.getProperty("org.killbill.billing.osgi.dao.url")); + dbi.inTransaction(new TransactionCallback() { @Override public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { + logger.info("Connection class: {}", conn.getConnection().getClass().getName()); + logger.info("Connection URL: {}", conn.getConnection().getMetaData().getURL()); + + conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + "record_id serial unique, " + diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 1ebce418b..4709b94c1 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(extraDefaults); + populateDefaultProperties(); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(extraDefaults); + populateDefaultProperties(); } @Override diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java index cb9f62c53..be6e85a1f 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java @@ -69,6 +69,10 @@ public void beforeClass() throws Exception { super.beforeClass(); final String killbillVersion = System.getProperty("killbill.version"); + System.out.println("Killbill.version is " + killbillVersion); + System.out.println("osgi prop name " + osgiConfig.getOSGIKillbillPropertyName()); + + final SetupBundleWithAssertion setupTest = new SetupBundleWithAssertion(BUNDLE_TEST_RESOURCE, osgiConfig, killbillVersion); setupTest.setupJavaBundle(); } From c22ffb0e6abf97ea84913a8ef67f66fe8ce19086 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 19:16:15 +0530 Subject: [PATCH 045/221] Added debug msg to TestJNDIManager --- .../platform/jndi/TestJNDIManager.java | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index ed9f5b8ae..a9487bad4 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -39,72 +39,4 @@ public class TestJNDIManager { - EmbeddedDB embeddedDB; - - @BeforeMethod(groups = "slow") - public void setUp() throws Exception { - SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); - - final String databaseName = "killbillosgitests"; - embeddedDB = new H2EmbeddedDB(databaseName, UUID.randomUUID().toString(), UUID.randomUUID().toString(), "jdbc:h2:mem:" + databaseName + ";DB_CLOSE_ON_EXIT=FALSE"); - embeddedDB.initialize(); - embeddedDB.start(); - } - - @AfterMethod(groups = "slow") - public void tearDown() throws Exception { - embeddedDB.stop(); - } - - @Test(groups = "slow") - public void testExportAndLookup() throws NamingException, IOException, SQLException { - System.out.println("BEFORE JNDIManager:"); - System.out.println(" Context.PROVIDER_URL = " + System.getProperty(Context.PROVIDER_URL)); - System.out.println(" Context.INITIAL_CONTEXT_FACTORY = " + System.getProperty(Context.INITIAL_CONTEXT_FACTORY)); - - final JNDIManager jndiManager = new JNDIManager(); - - System.out.println("AFTER JNDIManager:"); - System.out.println(" Context.PROVIDER_URL = " + System.getProperty(Context.PROVIDER_URL)); - System.out.println(" Context.INITIAL_CONTEXT_FACTORY = " + System.getProperty(Context.INITIAL_CONTEXT_FACTORY)); - - // final JNDIManager jndiManager = new JNDIManager(); - - // JdbcConnectionPool is not serializable unfortunately. Tests using JNDI won't work on H2 (we don't have any yet) - //final JdbcConnectionPool jdbcConnectionPool = (JdbcConnectionPool) embeddedDB.getDataSource(); - //final ReferenceableDataSourceSpy retrievedJdbcConnectionPool = testForDataSource(jndiManager, new ReferenceableDataSourceSpy(jdbcConnectionPool), ReferenceableDataSourceSpy.class); - //Assert.assertNotNull(retrievedJdbcConnectionPool.getDataSource().getConnection()); - - // JdbcDataSource is Referenceable - final JdbcDataSource dataSource = new JdbcDataSource(); - dataSource.setURL(embeddedDB.getJdbcConnectionString()); - dataSource.setUser(embeddedDB.getUsername()); - dataSource.setPassword(embeddedDB.getPassword()); - final JdbcDataSource retrievedJdbcDataSource = testForDataSource(jndiManager, dataSource, JdbcDataSource.class); - Assert.assertEquals(retrievedJdbcDataSource.getURL(), embeddedDB.getJdbcConnectionString()); - Assert.assertEquals(retrievedJdbcDataSource.getUser(), embeddedDB.getUsername()); - Assert.assertEquals(retrievedJdbcDataSource.getPassword(), embeddedDB.getPassword()); - Assert.assertNotNull(retrievedJdbcDataSource.getConnection()); - - // Try to wrap around a DataSourceSpy - final ReferenceableDataSourceSpy retrievedReferenceableDataSourceSpy = testForDataSource(jndiManager, new ReferenceableDataSourceSpy(dataSource, "something"), ReferenceableDataSourceSpy.class); - final DataSource retrievedJdbcDataSource2Delegate = retrievedReferenceableDataSourceSpy.getDataSource(); - Assert.assertTrue(retrievedJdbcDataSource2Delegate instanceof JdbcDataSource); - final JdbcDataSource retrievedJdbcDataSource2 = (JdbcDataSource) retrievedJdbcDataSource2Delegate; - Assert.assertEquals(retrievedJdbcDataSource2.getURL(), embeddedDB.getJdbcConnectionString()); - Assert.assertEquals(retrievedJdbcDataSource2.getUser(), embeddedDB.getUsername()); - Assert.assertEquals(retrievedJdbcDataSource2.getPassword(), embeddedDB.getPassword()); - Assert.assertNotNull(retrievedJdbcDataSource2.getConnection()); - } - - @SuppressWarnings("unchecked") - private T testForDataSource(final JNDIManager jndiManager, final DataSource dataSource, final Class klass) { - final String name = "a/b/c"; - jndiManager.export(name, dataSource); - - final Object retrievedDataSourceObject = jndiManager.lookup(name); - Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); - - return (T) retrievedDataSourceObject; - } } From f42460f2fa513c94924494b1fb64a39508b71289 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 19:36:13 +0530 Subject: [PATCH 046/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 320 ++++++++++++------ 1 file changed, 222 insertions(+), 98 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index e6ae6c97f..e22f19728 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -23,16 +23,21 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; +import java.util.Set; import java.util.TimeZone; import javax.annotation.Nullable; @@ -73,9 +78,15 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; + private static final List HIGH_TO_LOW_PRIORITY_ORDER = + Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults")); + private final PropertiesWithSourceCollector propertiesCollector; - private final Properties properties; + private volatile Map> cachedPropertiesBySource; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -93,64 +104,187 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); - - } + final Properties properties = new Properties(); + properties.load(UriAccessor.accessUri(Objects.requireNonNull(this.getClass().getResource(file)).toURI())); - for (final Entry entry : extraDefaultProperties.entrySet()) { - if (entry.getValue() != null) { - properties.put(entry.getKey(), entry.getValue()); - } + final Map propsMap = propertiesToMap(properties); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); } - propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); + populateDefaultProperties(extraDefaultProperties); - populateDefaultProperties(); + rebuildCache(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); + rebuildCache(); } if (Boolean.parseBoolean(getString(ENABLE_JASYPT_DECRYPTION))) { decryptJasyptProperties(); + rebuildCache(); } } @Override public String getString(final String propertyName) { - return properties.getProperty(propertyName); + final Map> bySource = getPropertiesBySource(); + + for (final Map sourceProps : bySource.values()) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + + return null; } @Override public Properties getProperties() { final Properties result = new Properties(); - // using properties.stringPropertyNames() because `result.putAll(properties)` not working when running inside - // tomcat, if we put configuration in tomcat's catalina.properties - // See: - // - https://github.com/killbill/technical-support/issues/61 - // - https://github.com/killbill/technical-support/issues/67 - // - // We have TestDefaultKillbillConfigSource#testGetProperties() that cover this, but seems like this is similar - // to one of our chicken-egg problem? (see loadPropertiesFromFileOrSystemProperties() below) - properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); + + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); + + return result; + } + + @Override + public Map> getPropertiesBySource() { + if (cachedPropertiesBySource == null) { + synchronized (lock) { + if (cachedPropertiesBySource == null) { + rebuildCache(); + } + } + } + + return Collections.unmodifiableMap(cachedPropertiesBySource); + } + + private void rebuildCache() { + cachedPropertiesBySource = computePropertiesBySource(); + } + + private void invalidateCache() { + synchronized (lock) { + cachedPropertiesBySource = null; + } + } + + private Map> computePropertiesBySource() { + final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); + runtimeBySource.forEach((source, props) -> { + if (!props.isEmpty()) { + propertiesCollector.addProperties(source, props); + } + }); + + final Map effectiveMap = new LinkedHashMap<>(); + propertiesCollector.getAllProperties().forEach(prop -> { + if (!effectiveMap.containsKey(prop.getKey())) { + effectiveMap.put(prop.getKey(), prop.getValue()); + } + }); RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!result.containsKey(key)) { - result.setProperty(key, value); + if (!effectiveMap.containsKey(key)) { + effectiveMap.put(key, value); } }); - return result; + final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + + final Map> propertyToSources = new HashMap<>(); + collectorBySource.forEach((source, properties) -> { + properties.forEach(property -> { + propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); + }); + }); + + final Set warnedConflicts = new HashSet<>(); + final Map> result = new LinkedHashMap<>(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List properties = collectorBySource.get(source); + if (properties == null || properties.isEmpty()) { + continue; + } + + final Map sourceMap = new LinkedHashMap<>(); + + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + final String effectiveValue = prop.getValue(); + + if (effectiveValue == null) { + continue; + } + + if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + continue; + } + + sourceMap.put(propertyKey, effectiveValue); + } + + final Set processedInThisSource = new HashSet<>(sourceMap.keySet()); + for (final String propertyKey : processedInThisSource) { + final Set sources = propertyToSources.get(propertyKey); + if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, new ArrayList<>(sources), source, sourceMap.get(propertyKey)); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + } + + collectorBySource.forEach((source, properties) -> { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + return; + } + + final Map sourceMap = new LinkedHashMap<>(); + for (final PropertyWithSource prop : properties) { + final String effectiveValue = effectiveMap.get(prop.getKey()); + if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { + sourceMap.put(prop.getKey(), effectiveValue); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + }); + + return Collections.unmodifiableMap(result); + } + + private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, + final Map> propertiesBySource) { + final List sourcesForKey = new ArrayList<>(); + propertiesBySource.forEach((source, props) -> { + if (props.stream().anyMatch(p -> p.getKey().equals(key))) { + sourcesForKey.add(source); + } + }); + + for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { + if (sourcesForKey.contains(prioritySource)) { + return prioritySource.equals(sourceToCheck); + } + } + + return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } - private Properties loadPropertiesFromFileOrSystemProperties() { + private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); @@ -160,11 +294,10 @@ private Properties loadPropertiesFromFileOrSystemProperties() { final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); - final String category = extractFileNameFromPath(propertiesFileLocation); final Map propsMap = propertiesToMap(properties); - propertiesCollector.addProperties(category, propsMap); + propertiesCollector.addProperties("RuntimeConfiguration", propsMap); - return properties; + return; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -172,21 +305,25 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); - - return new Properties(System.getProperties()); + propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); } @VisibleForTesting - protected void populateDefaultProperties() { + protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); + defaultProperties.putAll(extraDefaultProperties); + + final Map defaultsToAdd = new HashMap<>(); + for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultProperties.get(propertyName)); + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } } + final Map immutableProps = new HashMap<>(); + final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { @@ -212,6 +349,10 @@ protected void populateDefaultProperties() { // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); + + immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); + defaultsToAdd.put(propertyName, GMT_ID); + continue; } @@ -219,6 +360,10 @@ protected void populateDefaultProperties() { if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } + + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); + } } // WARN for missing PROP_SECURITY_EGD @@ -233,48 +378,29 @@ protected void populateDefaultProperties() { } } + if (!immutableProps.isEmpty()) { + propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); + } + defaultSystemProperties.putAll(defaultProperties); final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("DefaultSystemProperties", propsMap); + propertiesCollector.addProperties("KillBillDefaults", propsMap); } - @Override - public Map> getPropertiesBySource() { - final Map currentProps = new HashMap<>(); - properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); - - final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); - runtimeBySource.forEach((source, props) -> { - final Map filteredProps = new HashMap<>(); - props.forEach((key, value) -> { - if (!currentProps.containsKey(key)) { - filteredProps.put(key, value); - } - }); - if (!filteredProps.isEmpty()) { - propertiesCollector.addProperties(source, filteredProps); - } - }); - - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - - final Map> result = new LinkedHashMap<>(); - - propertiesBySource.forEach((source, properties) -> { - final Map sourceProperties = new LinkedHashMap<>(); - properties.forEach(prop -> { - sourceProperties.put(prop.getKey(), prop.getValue()); - }); - result.put(source, Collections.unmodifiableMap(sourceProperties)); - }); - - return Collections.unmodifiableMap(result); + private boolean hasProperty(final String propertyName) { + return propertiesCollector.getAllProperties().stream() + .anyMatch(p -> p.getKey().equals(propertyName)); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { - properties.put(propertyName, propertyValue); + final Map override = new HashMap<>(); + override.put(propertyName, String.valueOf(propertyValue)); + propertiesCollector.addProperties("RuntimeConfiguration", override); + + invalidateCache(); + rebuildCache(); } @VisibleForTesting @@ -284,6 +410,7 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.persistent.bus.external.historyTableName", "bus_ext_events_history"); properties.put(ENABLE_JASYPT_DECRYPTION, "false"); properties.put(LOOKUP_ENVIRONMENT_VARIABLES, "true"); + return properties; } @@ -304,8 +431,7 @@ protected Properties getDefaultSystemProperties() { private void overrideWithEnvironmentVariables() { // Find all Kill Bill properties in the environment variables - final Map env = System.getenv(); - + final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -317,12 +443,16 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); - properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } + @VisibleForTesting + protected Map getEnvironmentVariables() { + return System.getenv(); + } + public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -336,15 +466,27 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Enumeration keys = properties.keys(); + final Map> decryptedBySource = new HashMap<>(); + final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); // Iterate over all properties and decrypt ones that match - while (keys.hasMoreElements()) { - final String key = (String) keys.nextElement(); - final String value = (String) properties.get(key); + final List allProperties = propertiesCollector.getAllProperties(); + for (final PropertyWithSource prop : allProperties) { + final String key = prop.getKey(); + final String value = prop.getValue(); final Optional decryptableValue = decryptableValue(value); - decryptableValue.ifPresent(s -> properties.setProperty(key, encryptor.decrypt(s))); + if (decryptableValue.isPresent()) { + final String decryptedValue = encryptor.decrypt(decryptableValue.get()); + + final String source = prop.getSource(); + if (source != null) { + decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) + .put(key, decryptedValue); + } + } } + + decryptedBySource.forEach(propertiesCollector::addProperties); } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -386,29 +528,11 @@ private Optional decryptableValue(final String value) { return Optional.empty(); } - private String extractFileNameFromPath(String path) { - if (path == null || path.isEmpty()) { - return "unknown.properties"; - } - - if (path.startsWith("file://")) { - path = path.substring("file://".length()); - } - - final Path fileName = Paths.get(path).getFileName(); - if (fileName == null) { - return "unknown.properties"; - } - - return fileName.toString(); - } - private Map propertiesToMap(final Properties props) { final Map propertiesMap = new HashMap<>(); for (final Map.Entry entry : props.entrySet()) { propertiesMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } - return propertiesMap; } } From 581287d094fedb500018969a042d7aae9cfc2cb0 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 19:38:22 +0530 Subject: [PATCH 047/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 4709b94c1..1ebce418b 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(); + populateDefaultProperties(extraDefaults); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(); + populateDefaultProperties(extraDefaults); } @Override From 10711491da60551a475bb9cbe1deca2a8dd78d25 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Thu, 13 Nov 2025 22:12:43 +0530 Subject: [PATCH 048/221] Added debug msg to TestJNDIManager --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index ca72bdafe..fea76dd51 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ true + true false false From 7c05424eeec838abd3fb51fa8d4c6a059b8c4aa6 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 10:21:29 +0530 Subject: [PATCH 049/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index e22f19728..98118a532 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -308,6 +308,68 @@ private void loadPropertiesFromFileOrSystemProperties() { propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); } + /* @VisibleForTesting + protected void populateDefaultProperties() { + final Properties defaultProperties = getDefaultProperties(); + for (final String propertyName : defaultProperties.stringPropertyNames()) { + // Let the user override these properties + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultProperties.get(propertyName)); + } + } + + final Properties defaultSystemProperties = getDefaultSystemProperties(); + for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { + + // Special case to overwrite user.timezone + if (propertyName.equals(PROP_USER_TIME_ZONE)) { + if (!"GMT".equals(System.getProperty(propertyName))) { + if (GMT_WARNING == NOT_SHOWN) { + synchronized (lock) { + if (GMT_WARNING == NOT_SHOWN) { + GMT_WARNING = SHOWN; + logger.info("Overwrite of user.timezone system property with {} may break database serialization of date. Kill Bill will overwrite to GMT", + System.getProperty(propertyName)); + } + } + } + } + + // + // We now set the java system property -- regardless of whether this has been set previously or not. + // Also, setting java System property is not enough because default timezone may have been SET earlier, + // when first call to TimeZone.getDefaultRef was invoked-- which has a side effect to set it by either looking at + // existing "user.timezone" or being super smart by inferring from "user.country", "java.home", so we need to reset it. + // + System.setProperty(propertyName, GMT_ID); + TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); + continue; + } + + // Let the user override these properties + if (System.getProperty(propertyName) == null) { + System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); + } + } + + // WARN for missing PROP_SECURITY_EGD + if (System.getProperty(PROP_SECURITY_EGD) == null) { + if (ENTROPY_WARNING == NOT_SHOWN) { + synchronized (lock) { + if (ENTROPY_WARNING == NOT_SHOWN) { + ENTROPY_WARNING = SHOWN; + logger.warn("System property {} has not been set, this may cause some requests to hang because of a lack of entropy. You should probably set it to 'file:/dev/./urandom'", PROP_SECURITY_EGD); + } + } + } + } + + defaultSystemProperties.putAll(defaultProperties); + + final Map propsMap = propertiesToMap(defaultSystemProperties); + propertiesCollector.addProperties("DefaultSystemProperties", propsMap); + } +*/ @VisibleForTesting protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); @@ -382,10 +444,10 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); } - defaultSystemProperties.putAll(defaultProperties); + //defaultSystemProperties.putAll(defaultProperties); - final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("KillBillDefaults", propsMap); + //final Map propsMap = propertiesToMap(defaultSystemProperties); + propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); } private boolean hasProperty(final String propertyName) { From 1ef37146999905dc0ece737476a909f8c695cfa5 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 11:23:12 +0530 Subject: [PATCH 050/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 98118a532..204024923 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -379,7 +379,11 @@ protected void populateDefaultProperties(final Map extraDefaultP for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties - if (!hasProperty(propertyName)) { + /* if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); + }*/ + + if (getString(propertyName) == null) { defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } } @@ -423,7 +427,11 @@ protected void populateDefaultProperties(final Map extraDefaultP System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - if (!hasProperty(propertyName)) { + /*if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); + }*/ + + if (getString(propertyName) == null) { defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); } } @@ -444,10 +452,15 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); } + if (!defaultsToAdd.isEmpty()) { + propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); + } + //defaultSystemProperties.putAll(defaultProperties); //final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); + // propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); + invalidateCache(); } private boolean hasProperty(final String propertyName) { From 28173b168a61fff7bf3de92f567b7d8bd7d02a1b Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 11:40:44 +0530 Subject: [PATCH 051/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 122 ++++-------------- 1 file changed, 28 insertions(+), 94 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 204024923..e00214c71 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -39,6 +39,7 @@ import java.util.Properties; import java.util.Set; import java.util.TimeZone; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -85,7 +86,6 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "KillBillDefaults")); private final PropertiesWithSourceCollector propertiesCollector; - private volatile Map> cachedPropertiesBySource; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { @@ -145,9 +145,7 @@ public String getString(final String propertyName) { @Override public Properties getProperties() { final Properties result = new Properties(); - getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); - return result; } @@ -308,89 +306,42 @@ private void loadPropertiesFromFileOrSystemProperties() { propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); } - /* @VisibleForTesting - protected void populateDefaultProperties() { - final Properties defaultProperties = getDefaultProperties(); - for (final String propertyName : defaultProperties.stringPropertyNames()) { - // Let the user override these properties - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultProperties.get(propertyName)); - } - } - - final Properties defaultSystemProperties = getDefaultSystemProperties(); - for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { - - // Special case to overwrite user.timezone - if (propertyName.equals(PROP_USER_TIME_ZONE)) { - if (!"GMT".equals(System.getProperty(propertyName))) { - if (GMT_WARNING == NOT_SHOWN) { - synchronized (lock) { - if (GMT_WARNING == NOT_SHOWN) { - GMT_WARNING = SHOWN; - logger.info("Overwrite of user.timezone system property with {} may break database serialization of date. Kill Bill will overwrite to GMT", - System.getProperty(propertyName)); - } - } - } - } - - // - // We now set the java system property -- regardless of whether this has been set previously or not. - // Also, setting java System property is not enough because default timezone may have been SET earlier, - // when first call to TimeZone.getDefaultRef was invoked-- which has a side effect to set it by either looking at - // existing "user.timezone" or being super smart by inferring from "user.country", "java.home", so we need to reset it. - // - System.setProperty(propertyName, GMT_ID); - TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - continue; - } - - // Let the user override these properties - if (System.getProperty(propertyName) == null) { - System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); - } - } - - // WARN for missing PROP_SECURITY_EGD - if (System.getProperty(PROP_SECURITY_EGD) == null) { - if (ENTROPY_WARNING == NOT_SHOWN) { - synchronized (lock) { - if (ENTROPY_WARNING == NOT_SHOWN) { - ENTROPY_WARNING = SHOWN; - logger.warn("System property {} has not been set, this may cause some requests to hang because of a lack of entropy. You should probably set it to 'file:/dev/./urandom'", PROP_SECURITY_EGD); - } - } - } - } - - defaultSystemProperties.putAll(defaultProperties); - - final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("DefaultSystemProperties", propsMap); - } -*/ @VisibleForTesting protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); defaultProperties.putAll(extraDefaultProperties); - final Map defaultsToAdd = new HashMap<>(); + final Set existingKeys = propertiesCollector.getAllProperties().stream() + .map(PropertyWithSource::getKey) + .collect(Collectors.toSet()); - for (final String propertyName : defaultProperties.stringPropertyNames()) { - // Let the user override these properties - /* if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); - }*/ - if (getString(propertyName) == null) { - defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); + final Map defaultsToAdd = new HashMap<>(); + for (final String propertyName : defaultProperties.stringPropertyNames()) { + // Always allow getDefaultProperties() override to update values + // This handles TestKillbillConfigSource updating database URLs + final String newValue = defaultProperties.getProperty(propertyName); + if (!existingKeys.contains(propertyName)) { + defaultsToAdd.put(propertyName, newValue); + } else { + // Property exists - check if this is an update from child class override + // If the value has changed, we should update it + final String existingValue = propertiesCollector.getAllProperties().stream() + .filter(p -> p.getKey().equals(propertyName) && "KillBillDefaults".equals(p.getSource())) + .map(PropertyWithSource::getValue) + .findFirst() + .orElse(null); + + if (existingValue != null && !existingValue.equals(newValue)) { + // Value changed - update it + defaultsToAdd.put(propertyName, newValue); + } } } final Map immutableProps = new HashMap<>(); - final Properties defaultSystemProperties = getDefaultSystemProperties(); + for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { // Special case to overwrite user.timezone @@ -415,23 +366,15 @@ protected void populateDefaultProperties(final Map extraDefaultP // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - defaultsToAdd.put(propertyName, GMT_ID); - continue; } - // Let the user override these properties if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - /*if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); - }*/ - - if (getString(propertyName) == null) { + if (!existingKeys.contains(propertyName)) { defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); } } @@ -456,18 +399,9 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); } - //defaultSystemProperties.putAll(defaultProperties); - - //final Map propsMap = propertiesToMap(defaultSystemProperties); - // propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); invalidateCache(); } - private boolean hasProperty(final String propertyName) { - return propertiesCollector.getAllProperties().stream() - .anyMatch(p -> p.getKey().equals(propertyName)); - } - @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { final Map override = new HashMap<>(); @@ -544,7 +478,7 @@ private void decryptJasyptProperties() { final Map> decryptedBySource = new HashMap<>(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); - // Iterate over all properties and decrypt ones that match + final List allProperties = propertiesCollector.getAllProperties(); for (final PropertyWithSource prop : allProperties) { final String key = prop.getKey(); @@ -610,4 +544,4 @@ private Map propertiesToMap(final Properties props) { } return propertiesMap; } -} +} \ No newline at end of file From cafe6b16d8e4a988bde827f0d64af25dc83ef0d1 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 12:03:44 +0530 Subject: [PATCH 052/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 288 ++++-------------- .../test/config/TestKillbillConfigSource.java | 4 +- 2 files changed, 67 insertions(+), 225 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index e00214c71..8cb2f3edb 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -79,14 +79,8 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; - private static final List HIGH_TO_LOW_PRIORITY_ORDER = - Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", - "EnvironmentVariables", - "RuntimeConfiguration", - "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; - private volatile Map> cachedPropertiesBySource; + private final Properties properties; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -104,198 +98,99 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); } - populateDefaultProperties(extraDefaultProperties); + // Add extraDefaultProperties to properties immediately (like original) + for (final Entry entry : extraDefaultProperties.entrySet()) { + if (entry.getValue() != null) { + properties.put(entry.getKey(), entry.getValue()); + } + } + if (!extraDefaultProperties.isEmpty()) { + propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); + } - rebuildCache(); + populateDefaultProperties(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); - rebuildCache(); } if (Boolean.parseBoolean(getString(ENABLE_JASYPT_DECRYPTION))) { decryptJasyptProperties(); - rebuildCache(); } } @Override public String getString(final String propertyName) { - final Map> bySource = getPropertiesBySource(); - - for (final Map sourceProps : bySource.values()) { - final String value = sourceProps.get(propertyName); - if (value != null) { - return value; - } - } - - return null; + return properties.getProperty(propertyName); } @Override public Properties getProperties() { final Properties result = new Properties(); - getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); - return result; - } + properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); - @Override - public Map> getPropertiesBySource() { - if (cachedPropertiesBySource == null) { - synchronized (lock) { - if (cachedPropertiesBySource == null) { - rebuildCache(); - } + RuntimeConfigRegistry.getAll().forEach((key, value) -> { + if (!result.containsKey(key)) { + result.setProperty(key, value); } - } - - return Collections.unmodifiableMap(cachedPropertiesBySource); - } + }); - private void rebuildCache() { - cachedPropertiesBySource = computePropertiesBySource(); + return result; } - private void invalidateCache() { - synchronized (lock) { - cachedPropertiesBySource = null; - } - } + @Override + public Map> getPropertiesBySource() { + final Map currentProps = new HashMap<>(); + properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); - private Map> computePropertiesBySource() { final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); runtimeBySource.forEach((source, props) -> { - if (!props.isEmpty()) { - propertiesCollector.addProperties(source, props); - } - }); - - final Map effectiveMap = new LinkedHashMap<>(); - propertiesCollector.getAllProperties().forEach(prop -> { - if (!effectiveMap.containsKey(prop.getKey())) { - effectiveMap.put(prop.getKey(), prop.getValue()); - } - }); - - RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!effectiveMap.containsKey(key)) { - effectiveMap.put(key, value); + final Map filteredProps = new HashMap<>(); + props.forEach((key, value) -> { + if (!currentProps.containsKey(key)) { + filteredProps.put(key, value); + } + }); + if (!filteredProps.isEmpty()) { + propertiesCollector.addProperties(source, filteredProps); } }); - final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); - final Map> propertyToSources = new HashMap<>(); - collectorBySource.forEach((source, properties) -> { - properties.forEach(property -> { - propertyToSources.computeIfAbsent(property.getKey(), k -> new LinkedHashSet<>()).add(source); - }); - }); - - final Set warnedConflicts = new HashSet<>(); final Map> result = new LinkedHashMap<>(); - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final List properties = collectorBySource.get(source); - if (properties == null || properties.isEmpty()) { - continue; - } - - final Map sourceMap = new LinkedHashMap<>(); - - for (final PropertyWithSource prop : properties) { - final String propertyKey = prop.getKey(); - final String effectiveValue = prop.getValue(); - - if (effectiveValue == null) { - continue; - } - - if (!isEffectiveSourceForProperty(propertyKey, source, collectorBySource) && HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - continue; - } - - sourceMap.put(propertyKey, effectiveValue); - } - - final Set processedInThisSource = new HashSet<>(sourceMap.keySet()); - for (final String propertyKey : processedInThisSource) { - final Set sources = propertyToSources.get(propertyKey); - if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, new ArrayList<>(sources), source, sourceMap.get(propertyKey)); - } - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } - } - - collectorBySource.forEach((source, properties) -> { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { - return; - } - - final Map sourceMap = new LinkedHashMap<>(); - for (final PropertyWithSource prop : properties) { - final String effectiveValue = effectiveMap.get(prop.getKey()); - if (effectiveValue != null && isEffectiveSourceForProperty(prop.getKey(), source, collectorBySource)) { - sourceMap.put(prop.getKey(), effectiveValue); - } - } - - if (!sourceMap.isEmpty()) { - result.put(source, Collections.unmodifiableMap(sourceMap)); - } + propertiesBySource.forEach((source, properties) -> { + final Map sourceProperties = new LinkedHashMap<>(); + properties.forEach(prop -> { + sourceProperties.put(prop.getKey(), prop.getValue()); + }); + result.put(source, Collections.unmodifiableMap(sourceProperties)); }); return Collections.unmodifiableMap(result); } - private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, - final Map> propertiesBySource) { - final List sourcesForKey = new ArrayList<>(); - propertiesBySource.forEach((source, props) -> { - if (props.stream().anyMatch(p -> p.getKey().equals(key))) { - sourcesForKey.add(source); - } - }); - - for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { - if (sourcesForKey.contains(prioritySource)) { - return prioritySource.equals(sourceToCheck); - } - } - - return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); - } - - private void loadPropertiesFromFileOrSystemProperties() { - // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, - // but we need to build the ConfigSource first... + private Properties loadPropertiesFromFileOrSystemProperties() { final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); if (propertiesFileLocation != null) { try { - // Ignore System Properties if we're loading from a file final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); - return; + return properties; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -303,48 +198,25 @@ private void loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); + propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); + return new Properties(System.getProperties()); } @VisibleForTesting - protected void populateDefaultProperties(final Map extraDefaultProperties) { + protected void populateDefaultProperties() { final Properties defaultProperties = getDefaultProperties(); - defaultProperties.putAll(extraDefaultProperties); - - final Set existingKeys = propertiesCollector.getAllProperties().stream() - .map(PropertyWithSource::getKey) - .collect(Collectors.toSet()); - - final Map defaultsToAdd = new HashMap<>(); + final Map defaultsAdded = new HashMap<>(); for (final String propertyName : defaultProperties.stringPropertyNames()) { - // Always allow getDefaultProperties() override to update values - // This handles TestKillbillConfigSource updating database URLs - final String newValue = defaultProperties.getProperty(propertyName); - if (!existingKeys.contains(propertyName)) { - defaultsToAdd.put(propertyName, newValue); - } else { - // Property exists - check if this is an update from child class override - // If the value has changed, we should update it - final String existingValue = propertiesCollector.getAllProperties().stream() - .filter(p -> p.getKey().equals(propertyName) && "KillBillDefaults".equals(p.getSource())) - .map(PropertyWithSource::getValue) - .findFirst() - .orElse(null); - - if (existingValue != null && !existingValue.equals(newValue)) { - // Value changed - update it - defaultsToAdd.put(propertyName, newValue); - } + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultProperties.get(propertyName)); + defaultsAdded.put(propertyName, defaultProperties.getProperty(propertyName)); } } - final Map immutableProps = new HashMap<>(); final Properties defaultSystemProperties = getDefaultSystemProperties(); - for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { - // Special case to overwrite user.timezone if (propertyName.equals(PROP_USER_TIME_ZONE)) { if (!"GMT".equals(System.getProperty(propertyName))) { if (GMT_WARNING == NOT_SHOWN) { @@ -358,15 +230,10 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - // - // We now set the java system property -- regardless of whether this has been set previously or not. - // Also, setting java System property is not enough because default timezone may have been SET earlier, - // when first call to TimeZone.getDefaultRef was invoked-- which has a side effect to set it by either looking at - // existing "user.timezone" or being super smart by inferring from "user.country", "java.home", so we need to reset it. - // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); + properties.put(propertyName, GMT_ID); + defaultsAdded.put(propertyName, GMT_ID); continue; } @@ -374,12 +241,12 @@ protected void populateDefaultProperties(final Map extraDefaultP System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - if (!existingKeys.contains(propertyName)) { - defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); + if (properties.get(propertyName) == null) { + properties.put(propertyName, defaultSystemProperties.get(propertyName)); + defaultsAdded.put(propertyName, defaultSystemProperties.getProperty(propertyName)); } } - // WARN for missing PROP_SECURITY_EGD if (System.getProperty(PROP_SECURITY_EGD) == null) { if (ENTROPY_WARNING == NOT_SHOWN) { synchronized (lock) { @@ -391,25 +258,18 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - if (!immutableProps.isEmpty()) { - propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); - } - - if (!defaultsToAdd.isEmpty()) { - propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); + if (!defaultsAdded.isEmpty()) { + propertiesCollector.addProperties("KillBillDefaults", defaultsAdded); } - - invalidateCache(); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { + properties.put(propertyName, propertyValue); + final Map override = new HashMap<>(); override.put(propertyName, String.valueOf(propertyValue)); propertiesCollector.addProperties("RuntimeConfiguration", override); - - invalidateCache(); - rebuildCache(); } @VisibleForTesting @@ -428,19 +288,14 @@ protected Properties getDefaultSystemProperties() { final Properties properties = new Properties(); properties.put("user.timezone", GMT_ID); properties.put("ANTLR_USE_DIRECT_CLASS_LOADING", "true"); - // Disable log4jdbc-log4j2 by default. - // For slf4j-simple, this doesn't quite disable it (we cannot turn off the logger completely), - // but it should be off for logback (see logback.xml / logback-test.xml) properties.put("org.slf4j.simpleLogger.log.jdbc", "ERROR"); - // Sane defaults for https://code.google.com/p/log4jdbc-log4j2/ properties.put("log4jdbc.dump.sql.maxlinelength", "0"); properties.put("log4jdbc.spylogdelegator.name", "net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); return properties; } private void overrideWithEnvironmentVariables() { - // Find all Kill Bill properties in the environment variables - final Map env = getEnvironmentVariables(); + final Map env = System.getenv(); final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -452,16 +307,12 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); + properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } - @VisibleForTesting - protected Map getEnvironmentVariables() { - return System.getenv(); - } - public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -475,27 +326,18 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Map> decryptedBySource = new HashMap<>(); - + final Enumeration keys = properties.keys(); final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); - final List allProperties = propertiesCollector.getAllProperties(); - for (final PropertyWithSource prop : allProperties) { - final String key = prop.getKey(); - final String value = prop.getValue(); + while (keys.hasMoreElements()) { + final String key = (String) keys.nextElement(); + final String value = (String) properties.get(key); final Optional decryptableValue = decryptableValue(value); if (decryptableValue.isPresent()) { final String decryptedValue = encryptor.decrypt(decryptableValue.get()); - - final String source = prop.getSource(); - if (source != null) { - decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) - .put(key, decryptedValue); - } + properties.setProperty(key, decryptedValue); } } - - decryptedBySource.forEach(propertiesCollector::addProperties); } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 1ebce418b..4709b94c1 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(extraDefaults); + populateDefaultProperties(); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(extraDefaults); + populateDefaultProperties(); } @Override From 3f73f399fa7ef4c99b2d6cf244134a08ab238c0b Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 12:43:03 +0530 Subject: [PATCH 053/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 308 ++++++++++++++---- 1 file changed, 243 insertions(+), 65 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 8cb2f3edb..7a0def696 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -79,8 +79,15 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo private static volatile int GMT_WARNING = NOT_SHOWN; private static volatile int ENTROPY_WARNING = NOT_SHOWN; + private static final List HIGH_TO_LOW_PRIORITY_ORDER = + Collections.unmodifiableList(Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults")); + private final PropertiesWithSourceCollector propertiesCollector; - private final Properties properties; + + private volatile Map> cachedPropertiesBySource; public DefaultKillbillConfigSource() throws IOException, URISyntaxException { this((String) null); @@ -98,99 +105,228 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); } - // Add extraDefaultProperties to properties immediately (like original) - for (final Entry entry : extraDefaultProperties.entrySet()) { - if (entry.getValue() != null) { - properties.put(entry.getKey(), entry.getValue()); - } - } - if (!extraDefaultProperties.isEmpty()) { - propertiesCollector.addProperties("ExtraDefaultProperties", extraDefaultProperties); - } + populateDefaultProperties(extraDefaultProperties); - populateDefaultProperties(); + rebuildCache(); if (Boolean.parseBoolean(getString(LOOKUP_ENVIRONMENT_VARIABLES))) { overrideWithEnvironmentVariables(); + rebuildCache(); } if (Boolean.parseBoolean(getString(ENABLE_JASYPT_DECRYPTION))) { decryptJasyptProperties(); + rebuildCache(); } } +/* @Override public String getString(final String propertyName) { - return properties.getProperty(propertyName); + final Map> bySource = getPropertiesBySource(); + + for (final Map sourceProps : bySource.values()) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + + return null; } +*/ @Override public Properties getProperties() { final Properties result = new Properties(); - properties.stringPropertyNames().forEach(key -> result.setProperty(key, properties.getProperty(key))); - RuntimeConfigRegistry.getAll().forEach((key, value) -> { - if (!result.containsKey(key)) { - result.setProperty(key, value); - } - }); + getPropertiesBySource().forEach((source, props) -> props.forEach(result::setProperty)); return result; } @Override public Map> getPropertiesBySource() { - final Map currentProps = new HashMap<>(); - properties.stringPropertyNames().forEach(key -> currentProps.put(key, properties.getProperty(key))); + if (cachedPropertiesBySource == null) { + synchronized (lock) { + if (cachedPropertiesBySource == null) { + rebuildCache(); + } + } + } + + return Collections.unmodifiableMap(cachedPropertiesBySource); + } + + private void rebuildCache() { + cachedPropertiesBySource = computePropertiesBySource(); + } + + private void invalidateCache() { + synchronized (lock) { + cachedPropertiesBySource = null; + } + } + private Map> computePropertiesBySource() { final Map> runtimeBySource = RuntimeConfigRegistry.getAllBySource(); runtimeBySource.forEach((source, props) -> { - final Map filteredProps = new HashMap<>(); - props.forEach((key, value) -> { - if (!currentProps.containsKey(key)) { - filteredProps.put(key, value); - } - }); - if (!filteredProps.isEmpty()) { - propertiesCollector.addProperties(source, filteredProps); + if (!props.isEmpty()) { + propertiesCollector.addProperties(source, props); } }); - final Map> propertiesBySource = propertiesCollector.getPropertiesBySource(); + final Map> collectorBySource = propertiesCollector.getPropertiesBySource(); + final Map> propertyToSources = new HashMap<>(); + collectorBySource.forEach((source, properties) -> { + properties.forEach(property -> { + propertyToSources.computeIfAbsent(property.getKey(), k -> new ArrayList<>()).add(source); + }); + }); + + final Set warnedConflicts = new HashSet<>(); final Map> result = new LinkedHashMap<>(); - propertiesBySource.forEach((source, properties) -> { - final Map sourceProperties = new LinkedHashMap<>(); - properties.forEach(prop -> { - sourceProperties.put(prop.getKey(), prop.getValue()); - }); - result.put(source, Collections.unmodifiableMap(sourceProperties)); + final Set processedProperties = new HashSet<>(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final List properties = collectorBySource.get(source); + if (properties == null || properties.isEmpty()) { + continue; + } + + final Map sourceMap = new LinkedHashMap<>(); + + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + final String propertyValue = prop.getValue(); + + if (propertyValue == null) { + continue; + } + + if (!processedProperties.contains(propertyKey)) { + sourceMap.put(propertyKey, propertyValue); + processedProperties.add(propertyKey); + + final List sources = propertyToSources.get(propertyKey); + if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, sources, source, propertyValue); + } + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + } + + collectorBySource.forEach((source, properties) -> { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(source)) { + return; + } + + final Map sourceMap = new LinkedHashMap<>(); + for (final PropertyWithSource prop : properties) { + final String propertyKey = prop.getKey(); + final String propertyValue = prop.getValue(); + + if (propertyValue == null) { + continue; + } + + if (!processedProperties.contains(propertyKey)) { + sourceMap.put(propertyKey, propertyValue); + processedProperties.add(propertyKey); + } + } + + if (!sourceMap.isEmpty()) { + result.put(source, Collections.unmodifiableMap(sourceMap)); + } + }); + + RuntimeConfigRegistry.getAll().forEach((key, value) -> { + if (!processedProperties.contains(key)) { + result.computeIfAbsent("RuntimeConfigRegistry", k -> new LinkedHashMap<>()) + .put(key, value); + } }); return Collections.unmodifiableMap(result); } - private Properties loadPropertiesFromFileOrSystemProperties() { + @Override + public String getString(final String propertyName) { + final Map> bySource = getPropertiesBySource(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final Map sourceProps = bySource.get(source); + if (sourceProps != null) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + } + + for (final Map.Entry> entry : bySource.entrySet()) { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey())) { + continue; + } + final String value = entry.getValue().get(propertyName); + if (value != null) { + return value; + } + } + + return null; + } + + private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, + final Map> propertiesBySource) { + final List sourcesForKey = new ArrayList<>(); + propertiesBySource.forEach((source, props) -> { + if (props.stream().anyMatch(p -> p.getKey().equals(key))) { + sourcesForKey.add(source); + } + }); + + for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { + if (sourcesForKey.contains(prioritySource)) { + return prioritySource.equals(sourceToCheck); + } + } + + return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); + } + + private void loadPropertiesFromFileOrSystemProperties() { + // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, + // but we need to build the ConfigSource first... final String propertiesFileLocation = System.getProperty(PROPERTIES_FILE); if (propertiesFileLocation != null) { try { + // Ignore System Properties if we're loading from a file final Properties properties = new Properties(); properties.load(UriAccessor.accessUri(propertiesFileLocation)); final Map propsMap = propertiesToMap(properties); propertiesCollector.addProperties("RuntimeConfiguration", propsMap); - return properties; + return; } catch (final IOException e) { logger.warn("Unable to access properties file, defaulting to system properties", e); } catch (final URISyntaxException e) { @@ -198,25 +334,29 @@ private Properties loadPropertiesFromFileOrSystemProperties() { } } - propertiesCollector.addProperties("SystemProperties", propertiesToMap(System.getProperties())); - return new Properties(System.getProperties()); + propertiesCollector.addProperties("RuntimeConfiguration", propertiesToMap(System.getProperties())); } @VisibleForTesting - protected void populateDefaultProperties() { + protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); + defaultProperties.putAll(extraDefaultProperties); + + final Map defaultsToAdd = new HashMap<>(); - final Map defaultsAdded = new HashMap<>(); for (final String propertyName : defaultProperties.stringPropertyNames()) { - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultProperties.get(propertyName)); - defaultsAdded.put(propertyName, defaultProperties.getProperty(propertyName)); + // Let the user override these properties + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } } + final Map immutableProps = new HashMap<>(); + final Properties defaultSystemProperties = getDefaultSystemProperties(); for (final String propertyName : defaultSystemProperties.stringPropertyNames()) { + // Special case to overwrite user.timezone if (propertyName.equals(PROP_USER_TIME_ZONE)) { if (!"GMT".equals(System.getProperty(propertyName))) { if (GMT_WARNING == NOT_SHOWN) { @@ -230,23 +370,32 @@ protected void populateDefaultProperties() { } } + // + // We now set the java system property -- regardless of whether this has been set previously or not. + // Also, setting java System property is not enough because default timezone may have been SET earlier, + // when first call to TimeZone.getDefaultRef was invoked-- which has a side effect to set it by either looking at + // existing "user.timezone" or being super smart by inferring from "user.country", "java.home", so we need to reset it. + // System.setProperty(propertyName, GMT_ID); TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); - properties.put(propertyName, GMT_ID); - defaultsAdded.put(propertyName, GMT_ID); + + immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); + defaultsToAdd.put(propertyName, GMT_ID); + continue; } + // Let the user override these properties if (System.getProperty(propertyName) == null) { System.setProperty(propertyName, defaultSystemProperties.get(propertyName).toString()); } - if (properties.get(propertyName) == null) { - properties.put(propertyName, defaultSystemProperties.get(propertyName)); - defaultsAdded.put(propertyName, defaultSystemProperties.getProperty(propertyName)); + if (!hasProperty(propertyName)) { + defaultsToAdd.put(propertyName, defaultSystemProperties.getProperty(propertyName)); } } + // WARN for missing PROP_SECURITY_EGD if (System.getProperty(PROP_SECURITY_EGD) == null) { if (ENTROPY_WARNING == NOT_SHOWN) { synchronized (lock) { @@ -258,18 +407,29 @@ protected void populateDefaultProperties() { } } - if (!defaultsAdded.isEmpty()) { - propertiesCollector.addProperties("KillBillDefaults", defaultsAdded); + if (!immutableProps.isEmpty()) { + propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); } + + defaultSystemProperties.putAll(defaultProperties); + + final Map propsMap = propertiesToMap(defaultSystemProperties); + propertiesCollector.addProperties("KillBillDefaults", propsMap); + } + + private boolean hasProperty(final String propertyName) { + return propertiesCollector.getAllProperties().stream() + .anyMatch(p -> p.getKey().equals(propertyName)); } @VisibleForTesting public void setProperty(final String propertyName, final Object propertyValue) { - properties.put(propertyName, propertyValue); - final Map override = new HashMap<>(); override.put(propertyName, String.valueOf(propertyValue)); propertiesCollector.addProperties("RuntimeConfiguration", override); + + invalidateCache(); + rebuildCache(); } @VisibleForTesting @@ -288,14 +448,19 @@ protected Properties getDefaultSystemProperties() { final Properties properties = new Properties(); properties.put("user.timezone", GMT_ID); properties.put("ANTLR_USE_DIRECT_CLASS_LOADING", "true"); + // Disable log4jdbc-log4j2 by default. + // For slf4j-simple, this doesn't quite disable it (we cannot turn off the logger completely), + // but it should be off for logback (see logback.xml / logback-test.xml) properties.put("org.slf4j.simpleLogger.log.jdbc", "ERROR"); + // Sane defaults for https://code.google.com/p/log4jdbc-log4j2/ properties.put("log4jdbc.dump.sql.maxlinelength", "0"); properties.put("log4jdbc.spylogdelegator.name", "net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); return properties; } private void overrideWithEnvironmentVariables() { - final Map env = System.getenv(); + // Find all Kill Bill properties in the environment variables + final Map env = getEnvironmentVariables(); final Map kbEnvVariables = new HashMap<>(); for (final Entry entry : env.entrySet()) { @@ -307,12 +472,16 @@ private void overrideWithEnvironmentVariables() { final String value = entry.getValue(); kbEnvVariables.put(propertyName, value); - properties.setProperty(propertyName, value); } propertiesCollector.addProperties("EnvironmentVariables", kbEnvVariables); } + @VisibleForTesting + protected Map getEnvironmentVariables() { + return System.getenv(); + } + public List getAllPropertiesWithSource() { return propertiesCollector.getAllProperties(); } @@ -326,18 +495,27 @@ private void decryptJasyptProperties() { final String password = getEnvironmentVariable(JASYPT_ENCRYPTOR_PASSWORD_KEY, System.getProperty(JASYPT_ENCRYPTOR_PASSWORD_KEY)); final String algorithm = getEnvironmentVariable(JASYPT_ENCRYPTOR_ALGORITHM_KEY, System.getProperty(JASYPT_ENCRYPTOR_ALGORITHM_KEY)); - final Enumeration keys = properties.keys(); - final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); + final Map> decryptedBySource = new HashMap<>(); - while (keys.hasMoreElements()) { - final String key = (String) keys.nextElement(); - final String value = (String) properties.get(key); + final StandardPBEStringEncryptor encryptor = initializeEncryptor(password, algorithm); + // Iterate over all properties and decrypt ones that match + final List allProperties = propertiesCollector.getAllProperties(); + for (final PropertyWithSource prop : allProperties) { + final String key = prop.getKey(); + final String value = prop.getValue(); final Optional decryptableValue = decryptableValue(value); if (decryptableValue.isPresent()) { final String decryptedValue = encryptor.decrypt(decryptableValue.get()); - properties.setProperty(key, decryptedValue); + + final String source = prop.getSource(); + if (source != null) { + decryptedBySource.computeIfAbsent(source, k -> new HashMap<>()) + .put(key, decryptedValue); + } } } + + decryptedBySource.forEach(propertiesCollector::addProperties); } private StandardPBEStringEncryptor initializeEncryptor(final String password, final String algorithm) { @@ -386,4 +564,4 @@ private Map propertiesToMap(final Properties props) { } return propertiesMap; } -} \ No newline at end of file +} From 9766bb39ce0621eacc367e5d2c4fafdb4be2f03d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 12:52:46 +0530 Subject: [PATCH 054/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 4709b94c1..2b3b914b5 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(); + populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(); + populateDefaultProperties(Collections.emptyMap()); } @Override From e5795dcd39ac5d6af19f0a8cc2cecb1e5490fbd9 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 12:59:33 +0530 Subject: [PATCH 055/221] Added debug msg to TestJNDIManager --- .../billing/platform/config/DefaultKillbillConfigSource.java | 2 +- .../platform/test/config/TestKillbillConfigSource.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 7a0def696..02c3fe19b 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -167,7 +167,7 @@ public Map> getPropertiesBySource() { return Collections.unmodifiableMap(cachedPropertiesBySource); } - private void rebuildCache() { + protected void rebuildCache() { cachedPropertiesBySource = computePropertiesBySource(); } diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 2b3b914b5..48f84ccfc 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -52,6 +52,8 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { super(file); + this.extraDefaults = extraDefaults; + // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point populateDefaultProperties(Collections.emptyMap()); @@ -69,9 +71,8 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - this.extraDefaults = extraDefaults; - // extraDefaults changed, need to reload defaults populateDefaultProperties(Collections.emptyMap()); + rebuildCache(); } @Override From e9188eba2bbf665ab0c08eb981e368d1cec604d6 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 16:07:31 +0530 Subject: [PATCH 056/221] Added debug msg to TestJNDIManager --- .../platform/config/DefaultKillbillConfigSource.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 02c3fe19b..14e79626e 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -380,7 +380,7 @@ protected void populateDefaultProperties(final Map extraDefaultP TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - defaultsToAdd.put(propertyName, GMT_ID); + // defaultsToAdd.put(propertyName, GMT_ID); continue; } @@ -411,10 +411,14 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); } - defaultSystemProperties.putAll(defaultProperties); + // defaultSystemProperties.putAll(defaultProperties); - final Map propsMap = propertiesToMap(defaultSystemProperties); - propertiesCollector.addProperties("KillBillDefaults", propsMap); + // final Map propsMap = propertiesToMap(defaultSystemProperties); + // propertiesCollector.addProperties("KillBillDefaults", propsMap); + + if (!defaultsToAdd.isEmpty()) { + propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); + } } private boolean hasProperty(final String propertyName) { From 19b7b824a7574610eb9a7cf421540b14529d8c13 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 17:05:58 +0530 Subject: [PATCH 057/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 14e79626e..fbdbe0482 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -21,16 +21,12 @@ import java.io.IOException; import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -39,7 +35,6 @@ import java.util.Properties; import java.util.Set; import java.util.TimeZone; -import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -129,7 +124,6 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map> bySource = getPropertiesBySource(); @@ -143,7 +137,6 @@ public String getString(final String propertyName) { return null; } -*/ @Override public Properties getProperties() { @@ -221,9 +214,11 @@ private Map> computePropertiesBySource() { final List sources = propertyToSources.get(propertyKey); if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, sources, source, propertyValue); + if (shouldWarnAboutConflict(sources)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, sources, source, propertyValue); + } } } } @@ -268,6 +263,7 @@ private Map> computePropertiesBySource() { return Collections.unmodifiableMap(result); } +/* @Override public String getString(final String propertyName) { final Map> bySource = getPropertiesBySource(); @@ -312,6 +308,7 @@ private boolean isEffectiveSourceForProperty(final String key, final String sour return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } +*/ private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, @@ -380,7 +377,7 @@ protected void populateDefaultProperties(final Map extraDefaultP TimeZone.setDefault(TimeZone.getTimeZone(GMT_ID)); immutableProps.put(PROP_USER_TIME_ZONE, GMT_ID); - // defaultsToAdd.put(propertyName, GMT_ID); + // defaultsToAdd.put(propertyName, GMT_ID); continue; } @@ -411,10 +408,10 @@ protected void populateDefaultProperties(final Map extraDefaultP propertiesCollector.addProperties("ImmutableSystemProperties", immutableProps); } - // defaultSystemProperties.putAll(defaultProperties); + // defaultSystemProperties.putAll(defaultProperties); - // final Map propsMap = propertiesToMap(defaultSystemProperties); - // propertiesCollector.addProperties("KillBillDefaults", propsMap); + // final Map propsMap = propertiesToMap(defaultSystemProperties); + // propertiesCollector.addProperties("KillBillDefaults", propsMap); if (!defaultsToAdd.isEmpty()) { propertiesCollector.addProperties("KillBillDefaults", defaultsToAdd); @@ -568,4 +565,10 @@ private Map propertiesToMap(final Properties props) { } return propertiesMap; } + + private boolean shouldWarnAboutConflict(final List sources) { + return sources != null && + sources.contains("EnvironmentVariables") && + sources.contains("RuntimeConfiguration"); + } } From 5539ed1385498bc325a123c5344d628689dddc5b Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 17:18:12 +0530 Subject: [PATCH 058/221] Added debug msg to TestJNDIManager --- .../TestDefaultKillbillConfigSource.java | 314 ++++++++++++++++++ .../platform/jndi/TestJNDIManager.java | 59 +++- 2 files changed, 372 insertions(+), 1 deletion(-) diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index fd11442d7..40a01ed7a 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -39,4 +39,318 @@ public class TestDefaultKillbillConfigSource { + private static final String ENABLE_JASYPT_PROPERTY = "org.killbill.server.enableJasypt"; + private static final String JASYPT_ENCRYPTOR_PASSWORD_PROPERTY = "JASYPT_ENCRYPTOR_PASSWORD"; + private static final String JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY = "JASYPT_ENCRYPTOR_ALGORITHM"; + private static final String JASYPT_PASSWORD = "top_secret!"; + private static final String JASYPT_ALGORITHM = "PBEWITHMD5ANDDES"; + private static final String ENCRYPTED_PROPERTY_1 = "test.encrypted.property1"; + private static final String ENCRYPTED_PROPERTY_2 = "test.encrypted.property2"; + + @BeforeMethod(groups = "fast") + public void setup() { + // Clean out the properties we set in the tests, + // this is only necessary because the DefaultKillBillConfigSource constructor we're using ends up + // setting this.properties to System.getProperties(), which doesn't automatically get reset between tests. + System.clearProperty(ENABLE_JASYPT_PROPERTY); + System.clearProperty(JASYPT_PASSWORD); + System.clearProperty(JASYPT_ALGORITHM); + System.clearProperty(ENCRYPTED_PROPERTY_1); + System.clearProperty(ENCRYPTED_PROPERTY_2); + } + + @Test + public void testGetPropertiesBySourceContainsExpectedSources() throws URISyntaxException, IOException { + final Map runtimeConfig = new HashMap<>(); + runtimeConfig.put("org.killbill.dao.user", "root"); + + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, runtimeConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_user", "root"); + return mockEnv; + } + }; + + final Map> propsBySource = configSource.getPropertiesBySource(); + + Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); + Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); + Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); + } + + @Test(groups = "fast") + public void testGetPropertiesAndGetPropertiesBySourceAreInSync() throws URISyntaxException, IOException { + // RuntimeConfiguration + System.setProperty("org.killbill.dao.user", "root"); + System.setProperty("org.killbill.dao.password", "password"); + + // KillBillDefaults + final Map killbillDefaultConfig = new HashMap<>(); + killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); + killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); + + // ImmutableSystemProperties + killbillDefaultConfig.put("user.timezone", "GMT"); + + // EnvironmentVariables + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); + return mockEnv; + } + }; + + final Properties mergedProperties = configSource.getProperties(); + final Map> propertiesBySource = configSource.getPropertiesBySource(); + + final Map allProperties = new HashMap<>(); + propertiesBySource.forEach((source, props) -> allProperties.putAll(props)); + + for (final String key : mergedProperties.stringPropertyNames()) { + final String valueFromFlat = mergedProperties.getProperty(key); + final String valueFromSource = allProperties.get(key); + + Assert.assertNotNull(valueFromSource); + Assert.assertEquals(valueFromFlat, valueFromSource); + } + + // Verify that no property appears in multiple sources + final Map propertyCount = new HashMap<>(); + propertiesBySource.forEach((source, props) -> { + props.keySet().forEach(key -> propertyCount.put(key, propertyCount.getOrDefault(key, 0) + 1)); + }); + + propertyCount.forEach((key, count) -> { + Assert.assertEquals(count.intValue(), 1); + }); + + Assert.assertEquals(mergedProperties.size(), allProperties.size()); + } + + @Test + public void testConflictResolutionPriority() throws Exception { + // RuntimeConfiguration + System.setProperty("org.killbill.test", "lowValue"); + + final DefaultKillbillConfigSource testSource = new DefaultKillbillConfigSource((String) null) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_test", "highValue"); + return mockEnv; + } + }; + + testSource.setProperty("org.killbill.test", "lowValue"); + + final Properties properties = testSource.getProperties(); + + final String effectiveValue = properties.getProperty("org.killbill.test"); + Assert.assertEquals(effectiveValue, "highValue"); + } + + @Test(groups = "fast") + public void testGetProperties() throws URISyntaxException, IOException { + final Map configuration = new HashMap<>(); + configuration.put("1", "A"); + configuration.put("2", "B"); + + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, configuration); + + Assert.assertNotNull(configSource.getProperties()); + Assert.assertNotEquals(configSource.getProperties().size(), 0); + Assert.assertEquals(configSource.getProperties().getProperty("1"), "A"); + } + + @Test(groups = "fast") + public void testGetPropertiesBySource() throws URISyntaxException, IOException { + // RuntimeConfiguration + System.setProperty("org.killbill.dao.user", "root"); + System.setProperty("org.killbill.dao.password", "password"); + + // KillBillDefaults + final Map killbillDefaultConfig = new HashMap<>(); + killbillDefaultConfig.put("org.killbill.server.shutdownDelay", "3s"); + killbillDefaultConfig.put("org.killbill.billing.osgi.dao.logLevel", "INFO"); + + // ImmutableSystemProperties + killbillDefaultConfig.put("user.timezone", "GMT"); + + // EnvironmentVariables + final OSGIConfigProperties configSource = new DefaultKillbillConfigSource(null, killbillDefaultConfig) { + @Override + protected Map getEnvironmentVariables() { + final Map mockEnv = new HashMap<>(); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_dao_healthCheckConnectionTimeout", "11s"); + mockEnv.put(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_maxActive", "99"); + + return mockEnv; + } + }; + + final Map> propsBySource = configSource.getPropertiesBySource(); + + Assert.assertNotNull(propsBySource); + Assert.assertFalse(propsBySource.isEmpty()); + + Assert.assertTrue(propsBySource.containsKey("ImmutableSystemProperties")); + + final Map immutableProps = propsBySource.get("ImmutableSystemProperties"); + Assert.assertEquals(immutableProps.get("user.timezone"), "GMT"); + + Assert.assertTrue(propsBySource.containsKey("EnvironmentVariables")); + + final Map environmentVariables = propsBySource.get("EnvironmentVariables"); + Assert.assertEquals(environmentVariables.get("org.killbill.dao.healthCheckConnectionTimeout"), "11s"); + Assert.assertEquals(environmentVariables.get("org.killbill.billing.osgi.dao.maxActive"), "99"); + + Assert.assertTrue(propsBySource.containsKey("RuntimeConfiguration")); + + final Map runtimeConfig = propsBySource.get("RuntimeConfiguration"); + Assert.assertEquals(runtimeConfig.get("org.killbill.dao.user"), "root"); + Assert.assertEquals(runtimeConfig.get("org.killbill.dao.password"), "password"); + + Assert.assertTrue(propsBySource.containsKey("KillBillDefaults")); + + final Map killBillDefaults = propsBySource.get("KillBillDefaults"); + Assert.assertEquals(killBillDefaults.get("org.killbill.server.shutdownDelay"), "3s"); + Assert.assertEquals(killBillDefaults.get("org.killbill.billing.osgi.dao.logLevel"), "INFO"); + + final List actualSourceOrder = new ArrayList<>(propsBySource.keySet()); + + final List expectedPrecedenceOrder = Arrays.asList("ImmutableSystemProperties", + "EnvironmentVariables", + "RuntimeConfiguration", + "KillBillDefaults"); + + Assert.assertEquals(actualSourceOrder, expectedPrecedenceOrder); + } + + @Test(groups = "fast") + public void testFromEnvVariableName() throws IOException, URISyntaxException { + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); + + Assert.assertEquals(configSource.fromEnvVariableName(""), ""); + Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao.prepStmtCacheSize"); + // Note! This won't work: we don't support underscores in property keys + //Assert.assertEquals(configSource.fromEnvVariableName("org_killbill_billing_osgi_dao_prepStmtCacheSize"), "org.killbill.billing.osgi.dao_prepStmtCacheSize"); + Assert.assertEquals(configSource.fromEnvVariableName(ENVIRONMENT_VARIABLE_PREFIX + "org_killbill_billing_osgi_dao__prepStmtCacheSize"), "org.killbill.billing.osgi.dao..prepStmtCacheSize"); + } + + @Test(groups = "fast") + public void testJasyptDisabledByDefault() throws IOException, URISyntaxException { + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(); + + final String enableJasyptString = configSource.getString(ENABLE_JASYPT_PROPERTY); + + Assert.assertFalse(Boolean.parseBoolean(enableJasyptString)); + } + + @Test(groups = "fast") + public void testDecyptionExplicitlyDisabled() throws IOException, URISyntaxException { + final String unencryptedValue = "myPropertyValue"; + final String encryptedValue = encString(unencryptedValue); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "false", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); + + final String actualValue = configSource.getString(ENCRYPTED_PROPERTY_1); + + Assert.assertEquals(encryptedValue, actualValue); + } + + @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) + public void testDecryptEmptyPassword() throws IOException, URISyntaxException { + final String encryptedValue = encString("myPropertyValue"); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, "", + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast", expectedExceptions = IllegalArgumentException.class) + public void testDecryptEmptyAlgorithm() throws IOException, URISyntaxException { + final String encryptedValue = encString("myPropertyValue"); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, ""); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) + public void testDecryptInvalidJasyptString() throws IOException, URISyntaxException { + final String encryptedValue = "ENC(notAValidEncryptedString!)"; + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast", expectedExceptions = EncryptionOperationNotPossibleException.class) + public void testDecryptEmptyJasyptString() throws IOException, URISyntaxException { + final String encryptedValue = "ENC()"; + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + new DefaultKillbillConfigSource(properties); + } + + @Test(groups = "fast") + public void testDecryptJasyptPropertySuccessfully() throws IOException, URISyntaxException { + final String unencryptedValue1 = "myPropertyValue"; + final String encryptedValue1 = encString(unencryptedValue1); + final String unencryptedValue2 = "myOtherPropertyValue"; + final String encryptedValue2 = encString(unencryptedValue2); + + final Map properties = Map.of(ENABLE_JASYPT_PROPERTY, "true", + ENCRYPTED_PROPERTY_1, encryptedValue1, + ENCRYPTED_PROPERTY_2, encryptedValue2, + JASYPT_ENCRYPTOR_PASSWORD_PROPERTY, JASYPT_PASSWORD, + JASYPT_ENCRYPTOR_ALGORITHM_PROPERTY, JASYPT_ALGORITHM); + + final DefaultKillbillConfigSource configSource = new DefaultKillbillConfigSource(properties); + + final String actualValue1 = configSource.getString(ENCRYPTED_PROPERTY_1); + final String actualValue2 = configSource.getString(ENCRYPTED_PROPERTY_2); + + Assert.assertEquals(unencryptedValue1, actualValue1); + Assert.assertEquals(unencryptedValue2, actualValue2); + } + + private String encString(final String unencryptedValue) { + return "ENC(" + encrypt(unencryptedValue, JASYPT_ALGORITHM, JASYPT_PASSWORD) + ")"; + } + + private String encrypt(final String unencryptedValue, final String jasyptAlgorithm, final String jasyptPassword) { + final StandardPBEStringEncryptor encryptor = setupEncryptor(jasyptPassword, jasyptAlgorithm); + return encryptor.encrypt(unencryptedValue); + } + + private StandardPBEStringEncryptor setupEncryptor(final String password, final String algorithm) { + final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); + encryptor.setPassword(password); + encryptor.setAlgorithm(algorithm); + return encryptor; + } } diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index a9487bad4..3292508b1 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -23,7 +23,6 @@ import java.sql.SQLException; import java.util.UUID; -import javax.naming.Context; import javax.naming.NamingException; import javax.sql.DataSource; @@ -39,4 +38,62 @@ public class TestJNDIManager { + EmbeddedDB embeddedDB; + + @BeforeMethod(groups = "slow") + public void setUp() throws Exception { + SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); + + final String databaseName = "killbillosgitests"; + embeddedDB = new H2EmbeddedDB(databaseName, UUID.randomUUID().toString(), UUID.randomUUID().toString(), "jdbc:h2:mem:" + databaseName + ";DB_CLOSE_ON_EXIT=FALSE"); + embeddedDB.initialize(); + embeddedDB.start(); + } + + @AfterMethod(groups = "slow") + public void tearDown() throws Exception { + embeddedDB.stop(); + } + + @Test(groups = "slow") + public void testExportAndLookup() throws NamingException, IOException, SQLException { + final JNDIManager jndiManager = new JNDIManager(); + + // JdbcConnectionPool is not serializable unfortunately. Tests using JNDI won't work on H2 (we don't have any yet) + //final JdbcConnectionPool jdbcConnectionPool = (JdbcConnectionPool) embeddedDB.getDataSource(); + //final ReferenceableDataSourceSpy retrievedJdbcConnectionPool = testForDataSource(jndiManager, new ReferenceableDataSourceSpy(jdbcConnectionPool), ReferenceableDataSourceSpy.class); + //Assert.assertNotNull(retrievedJdbcConnectionPool.getDataSource().getConnection()); + + // JdbcDataSource is Referenceable + final JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL(embeddedDB.getJdbcConnectionString()); + dataSource.setUser(embeddedDB.getUsername()); + dataSource.setPassword(embeddedDB.getPassword()); + final JdbcDataSource retrievedJdbcDataSource = testForDataSource(jndiManager, dataSource, JdbcDataSource.class); + Assert.assertEquals(retrievedJdbcDataSource.getURL(), embeddedDB.getJdbcConnectionString()); + Assert.assertEquals(retrievedJdbcDataSource.getUser(), embeddedDB.getUsername()); + Assert.assertEquals(retrievedJdbcDataSource.getPassword(), embeddedDB.getPassword()); + Assert.assertNotNull(retrievedJdbcDataSource.getConnection()); + + // Try to wrap around a DataSourceSpy + final ReferenceableDataSourceSpy retrievedReferenceableDataSourceSpy = testForDataSource(jndiManager, new ReferenceableDataSourceSpy(dataSource, "something"), ReferenceableDataSourceSpy.class); + final DataSource retrievedJdbcDataSource2Delegate = retrievedReferenceableDataSourceSpy.getDataSource(); + Assert.assertTrue(retrievedJdbcDataSource2Delegate instanceof JdbcDataSource); + final JdbcDataSource retrievedJdbcDataSource2 = (JdbcDataSource) retrievedJdbcDataSource2Delegate; + Assert.assertEquals(retrievedJdbcDataSource2.getURL(), embeddedDB.getJdbcConnectionString()); + Assert.assertEquals(retrievedJdbcDataSource2.getUser(), embeddedDB.getUsername()); + Assert.assertEquals(retrievedJdbcDataSource2.getPassword(), embeddedDB.getPassword()); + Assert.assertNotNull(retrievedJdbcDataSource2.getConnection()); + } + + @SuppressWarnings("unchecked") + private T testForDataSource(final JNDIManager jndiManager, final DataSource dataSource, final Class klass) { + final String name = "a/b/c"; + jndiManager.export(name, dataSource); + + final Object retrievedDataSourceObject = jndiManager.lookup(name); + Assert.assertTrue(klass.isInstance(retrievedDataSourceObject), klass + " is not an instance of " + retrievedDataSourceObject); + + return (T) retrievedDataSourceObject; + } } From 51396702c2958c6f0f72bbdcc39c43242e3c7052 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 19:26:25 +0530 Subject: [PATCH 059/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 52 ++----------------- .../config/PropertiesWithSourceCollector.java | 6 +++ 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index fbdbe0482..d27240679 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -263,53 +263,6 @@ private Map> computePropertiesBySource() { return Collections.unmodifiableMap(result); } -/* - @Override - public String getString(final String propertyName) { - final Map> bySource = getPropertiesBySource(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final Map sourceProps = bySource.get(source); - if (sourceProps != null) { - final String value = sourceProps.get(propertyName); - if (value != null) { - return value; - } - } - } - - for (final Map.Entry> entry : bySource.entrySet()) { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey())) { - continue; - } - final String value = entry.getValue().get(propertyName); - if (value != null) { - return value; - } - } - - return null; - } - - private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, - final Map> propertiesBySource) { - final List sourcesForKey = new ArrayList<>(); - propertiesBySource.forEach((source, props) -> { - if (props.stream().anyMatch(p -> p.getKey().equals(key))) { - sourcesForKey.add(source); - } - }); - - for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { - if (sourcesForKey.contains(prioritySource)) { - return prioritySource.equals(sourceToCheck); - } - } - - return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); - } -*/ - private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... @@ -540,6 +493,11 @@ private String getEnvironmentVariable(final String name, final String defaultVal } value = getString(name); + /*value = propertiesCollector.getAllProperties().stream() + .filter(p -> p.getKey().equals(name)) + .map(PropertyWithSource::getValue) + .findFirst() + .orElse(null);*/ return Strings.isNullOrEmpty(value) ? defaultValue : value; } diff --git a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java index f4783209c..6ee31efe4 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java +++ b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class PropertiesWithSourceCollector { @@ -32,8 +33,13 @@ public class PropertiesWithSourceCollector { public void addProperties(final String source, final Map props) { synchronized (lock) { final List updatedProperties = new ArrayList<>(properties); + + final Set keysToAdd = props.keySet(); + updatedProperties.removeIf(p -> p.getSource().equals(source) && keysToAdd.contains(p.getKey())); + props.forEach((key, value) -> updatedProperties.add(new PropertyWithSource(source, key, value))); + this.properties = Collections.unmodifiableList(updatedProperties); } } From 68cf3e0c2798aabe35db4c9672618e8039b1f8a4 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 19:39:55 +0530 Subject: [PATCH 060/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index d27240679..f0ff495e7 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -124,7 +124,7 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map> bySource = getPropertiesBySource(); @@ -135,6 +135,33 @@ public String getString(final String propertyName) { } } + return null; + }*/ + + @Override + public String getString(final String propertyName) { + final Map> bySource = getPropertiesBySource(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final Map sourceProps = bySource.get(source); + if (sourceProps != null) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + } + + for (final Map.Entry> entry : bySource.entrySet()) { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey())) { + continue; + } + final String value = entry.getValue().get(propertyName); + if (value != null) { + return value; + } + } + return null; } From a2b9f5a578e848080dd76d6ca0ad65e8a0c5eac2 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 19:54:13 +0530 Subject: [PATCH 061/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 95 ++++++++++--------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index f0ff495e7..56af06ef0 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -124,7 +124,8 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map> bySource = getPropertiesBySource(); @@ -135,35 +136,9 @@ public String getString(final String propertyName) { } } - return null; - }*/ - - @Override - public String getString(final String propertyName) { - final Map> bySource = getPropertiesBySource(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final Map sourceProps = bySource.get(source); - if (sourceProps != null) { - final String value = sourceProps.get(propertyName); - if (value != null) { - return value; - } - } - } - - for (final Map.Entry> entry : bySource.entrySet()) { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey())) { - continue; - } - final String value = entry.getValue().get(propertyName); - if (value != null) { - return value; - } - } - return null; } +*/ @Override public Properties getProperties() { @@ -241,11 +216,9 @@ private Map> computePropertiesBySource() { final List sources = propertyToSources.get(propertyKey); if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - if (shouldWarnAboutConflict(sources)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, sources, source, propertyValue); - } + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, sources, source, propertyValue); } } } @@ -290,6 +263,51 @@ private Map> computePropertiesBySource() { return Collections.unmodifiableMap(result); } + @Override + public String getString(final String propertyName) { + final Map> bySource = getPropertiesBySource(); + + for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { + final Map sourceProps = bySource.get(source); + if (sourceProps != null) { + final String value = sourceProps.get(propertyName); + if (value != null) { + return value; + } + } + } + + for (final Map.Entry> entry : bySource.entrySet()) { + if (HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey())) { + continue; + } + final String value = entry.getValue().get(propertyName); + if (value != null) { + return value; + } + } + + return null; + } + + private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, + final Map> propertiesBySource) { + final List sourcesForKey = new ArrayList<>(); + propertiesBySource.forEach((source, props) -> { + if (props.stream().anyMatch(p -> p.getKey().equals(key))) { + sourcesForKey.add(source); + } + }); + + for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { + if (sourcesForKey.contains(prioritySource)) { + return prioritySource.equals(sourceToCheck); + } + } + + return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); + } + private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, // but we need to build the ConfigSource first... @@ -520,11 +538,6 @@ private String getEnvironmentVariable(final String name, final String defaultVal } value = getString(name); - /*value = propertiesCollector.getAllProperties().stream() - .filter(p -> p.getKey().equals(name)) - .map(PropertyWithSource::getValue) - .findFirst() - .orElse(null);*/ return Strings.isNullOrEmpty(value) ? defaultValue : value; } @@ -550,10 +563,4 @@ private Map propertiesToMap(final Properties props) { } return propertiesMap; } - - private boolean shouldWarnAboutConflict(final List sources) { - return sources != null && - sources.contains("EnvironmentVariables") && - sources.contains("RuntimeConfiguration"); - } } From 4c5c8224347d5e1c64ba71175ceb3962d54802b3 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 19:59:19 +0530 Subject: [PATCH 062/221] Added debug msg to TestJNDIManager --- .../platform/config/PropertiesWithSourceCollector.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java index 6ee31efe4..f4783209c 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java +++ b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java @@ -22,7 +22,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; public class PropertiesWithSourceCollector { @@ -33,13 +32,8 @@ public class PropertiesWithSourceCollector { public void addProperties(final String source, final Map props) { synchronized (lock) { final List updatedProperties = new ArrayList<>(properties); - - final Set keysToAdd = props.keySet(); - updatedProperties.removeIf(p -> p.getSource().equals(source) && keysToAdd.contains(p.getKey())); - props.forEach((key, value) -> updatedProperties.add(new PropertyWithSource(source, key, value))); - this.properties = Collections.unmodifiableList(updatedProperties); } } From e1ea5db21c3f4db0a2b5c15fa8b7d76a922ade91 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 20:04:20 +0530 Subject: [PATCH 063/221] Added debug msg to TestJNDIManager --- .../platform/config/TestDefaultKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index 40a01ed7a..bb02f3e8b 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -316,7 +316,7 @@ public void testDecryptEmptyJasyptString() throws IOException, URISyntaxExceptio new DefaultKillbillConfigSource(properties); } - @Test(groups = "fast") + /* @Test(groups = "fast") public void testDecryptJasyptPropertySuccessfully() throws IOException, URISyntaxException { final String unencryptedValue1 = "myPropertyValue"; final String encryptedValue1 = encString(unencryptedValue1); @@ -336,7 +336,7 @@ public void testDecryptJasyptPropertySuccessfully() throws IOException, URISynta Assert.assertEquals(unencryptedValue1, actualValue1); Assert.assertEquals(unencryptedValue2, actualValue2); - } + }*/ private String encString(final String unencryptedValue) { return "ENC(" + encrypt(unencryptedValue, JASYPT_ALGORITHM, JASYPT_PASSWORD) + ")"; From fbdb1f3279c25e0a40376d060ad3a7642f5373a6 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 20:25:58 +0530 Subject: [PATCH 064/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 20 +++++++++++++------ .../TestDefaultKillbillConfigSource.java | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 56af06ef0..6d5eef4d5 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -124,7 +124,6 @@ public DefaultKillbillConfigSource(@Nullable final String file, final Map> bySource = getPropertiesBySource(); @@ -138,7 +137,6 @@ public String getString(final String propertyName) { return null; } -*/ @Override public Properties getProperties() { @@ -216,9 +214,11 @@ private Map> computePropertiesBySource() { final List sources = propertyToSources.get(propertyKey); if (sources != null && sources.size() > 1 && !warnedConflicts.contains(propertyKey)) { - warnedConflicts.add(propertyKey); - logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", - propertyKey, sources, source, propertyValue); + if (shouldWarnAboutConflict(sources)) { + warnedConflicts.add(propertyKey); + logger.warn("Property conflict detected for '{}': defined in sources {} - using value from '{}': '{}'", + propertyKey, sources, source, propertyValue); + } } } } @@ -263,6 +263,7 @@ private Map> computePropertiesBySource() { return Collections.unmodifiableMap(result); } +/* @Override public String getString(final String propertyName) { final Map> bySource = getPropertiesBySource(); @@ -307,6 +308,7 @@ private boolean isEffectiveSourceForProperty(final String key, final String sour return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); } +*/ private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, @@ -563,4 +565,10 @@ private Map propertiesToMap(final Properties props) { } return propertiesMap; } -} + + private boolean shouldWarnAboutConflict(final List sources) { + return sources != null && + sources.contains("EnvironmentVariables") && + sources.contains("RuntimeConfiguration"); + } +} \ No newline at end of file diff --git a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java index bb02f3e8b..40a01ed7a 100644 --- a/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java +++ b/base/src/test/java/org/killbill/billing/platform/config/TestDefaultKillbillConfigSource.java @@ -316,7 +316,7 @@ public void testDecryptEmptyJasyptString() throws IOException, URISyntaxExceptio new DefaultKillbillConfigSource(properties); } - /* @Test(groups = "fast") + @Test(groups = "fast") public void testDecryptJasyptPropertySuccessfully() throws IOException, URISyntaxException { final String unencryptedValue1 = "myPropertyValue"; final String encryptedValue1 = encString(unencryptedValue1); @@ -336,7 +336,7 @@ public void testDecryptJasyptPropertySuccessfully() throws IOException, URISynta Assert.assertEquals(unencryptedValue1, actualValue1); Assert.assertEquals(unencryptedValue2, actualValue2); - }*/ + } private String encString(final String unencryptedValue) { return "ENC(" + encrypt(unencryptedValue, JASYPT_ALGORITHM, JASYPT_PASSWORD) + ")"; From b3f2a99b015e6bc59bc1c4eb41b47879bcefa03c Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 21:38:56 +0530 Subject: [PATCH 065/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 48 +------------------ .../config/PropertiesWithSourceCollector.java | 6 +++ 2 files changed, 7 insertions(+), 47 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 6d5eef4d5..24486cfc7 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -263,52 +263,6 @@ private Map> computePropertiesBySource() { return Collections.unmodifiableMap(result); } -/* - @Override - public String getString(final String propertyName) { - final Map> bySource = getPropertiesBySource(); - - for (final String source : HIGH_TO_LOW_PRIORITY_ORDER) { - final Map sourceProps = bySource.get(source); - if (sourceProps != null) { - final String value = sourceProps.get(propertyName); - if (value != null) { - return value; - } - } - } - - for (final Map.Entry> entry : bySource.entrySet()) { - if (HIGH_TO_LOW_PRIORITY_ORDER.contains(entry.getKey())) { - continue; - } - final String value = entry.getValue().get(propertyName); - if (value != null) { - return value; - } - } - - return null; - } - - private boolean isEffectiveSourceForProperty(final String key, final String sourceToCheck, - final Map> propertiesBySource) { - final List sourcesForKey = new ArrayList<>(); - propertiesBySource.forEach((source, props) -> { - if (props.stream().anyMatch(p -> p.getKey().equals(key))) { - sourcesForKey.add(source); - } - }); - - for (final String prioritySource : HIGH_TO_LOW_PRIORITY_ORDER) { - if (sourcesForKey.contains(prioritySource)) { - return prioritySource.equals(sourceToCheck); - } - } - - return !sourcesForKey.isEmpty() && sourcesForKey.get(0).equals(sourceToCheck); - } -*/ private void loadPropertiesFromFileOrSystemProperties() { // Chicken-egg problem. It would be nice to have the property in e.g. KillbillServerConfig, @@ -571,4 +525,4 @@ private boolean shouldWarnAboutConflict(final List sources) { sources.contains("EnvironmentVariables") && sources.contains("RuntimeConfiguration"); } -} \ No newline at end of file +} diff --git a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java index f4783209c..6ee31efe4 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java +++ b/base/src/main/java/org/killbill/billing/platform/config/PropertiesWithSourceCollector.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class PropertiesWithSourceCollector { @@ -32,8 +33,13 @@ public class PropertiesWithSourceCollector { public void addProperties(final String source, final Map props) { synchronized (lock) { final List updatedProperties = new ArrayList<>(properties); + + final Set keysToAdd = props.keySet(); + updatedProperties.removeIf(p -> p.getSource().equals(source) && keysToAdd.contains(p.getKey())); + props.forEach((key, value) -> updatedProperties.add(new PropertyWithSource(source, key, value))); + this.properties = Collections.unmodifiableList(updatedProperties); } } From 7def7a0f5e9cbbf5212e034e7d16824e65f6be87 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Fri, 14 Nov 2025 23:29:39 +0530 Subject: [PATCH 066/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/config/TestKillbillConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 48f84ccfc..e1ad0566f 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - populateDefaultProperties(Collections.emptyMap()); + //populateDefaultProperties(Collections.emptyMap()); rebuildCache(); } From c26823028e29ff02956579924779c9f37dad9a31 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 12:51:28 +0530 Subject: [PATCH 067/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/config/TestKillbillConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index e1ad0566f..d0189225a 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - //populateDefaultProperties(Collections.emptyMap()); + populateDefaultProperties(this.extraDefaults); rebuildCache(); } From c40d6bcab07c55f9b5447e9535e2b15722a702a6 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 15:15:36 +0530 Subject: [PATCH 068/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index d0189225a..e07ac4aa3 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -50,13 +50,13 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file); + super(file, Collections.emptyMap()); this.extraDefaults = extraDefaults; // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(Collections.emptyMap()); + //populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); From 9f4aa5b060f2d2f0476376f8445ea09963ddc70f Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 15:24:10 +0530 Subject: [PATCH 069/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index e07ac4aa3..e1ad0566f 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -50,13 +50,13 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, Collections.emptyMap()); + super(file); this.extraDefaults = extraDefaults; // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - //populateDefaultProperties(Collections.emptyMap()); + populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - populateDefaultProperties(this.extraDefaults); + //populateDefaultProperties(Collections.emptyMap()); rebuildCache(); } From a14905e009fbe1e7612dd368c448533660c250ee Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 15:39:33 +0530 Subject: [PATCH 070/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/config/TestKillbillConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index e1ad0566f..e3b06cca7 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - //populateDefaultProperties(Collections.emptyMap()); + populateDefaultProperties(extraDefaults); rebuildCache(); } From 5016d28dcfce8cc7c49708645c9792a789483660 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 15:46:08 +0530 Subject: [PATCH 071/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/config/TestKillbillConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index e3b06cca7..c08c2d80f 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -56,7 +56,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(Collections.emptyMap()); + // populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); From be18c25b0ec6c7a79702c258be801a33cdd7c05b Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 15:55:13 +0530 Subject: [PATCH 072/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index c08c2d80f..800c32577 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -56,7 +56,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - // populateDefaultProperties(Collections.emptyMap()); + populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,8 +71,8 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - populateDefaultProperties(extraDefaults); - rebuildCache(); + populateDefaultProperties(Collections.emptyMap()); + //rebuildCache(); } @Override From 6044257ba3e20c6a3aaef5bbfa10e2f2cb2d778d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 16:33:08 +0530 Subject: [PATCH 073/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 2 +- .../test/config/TestKillbillConfigSource.java | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 24486cfc7..8707ea1c4 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -80,7 +80,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; + protected final PropertiesWithSourceCollector propertiesCollector; private volatile Map> cachedPropertiesBySource; diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 800c32577..a61fda53b 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -50,13 +51,13 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file); + super(file, extraDefaults); this.extraDefaults = extraDefaults; // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(Collections.emptyMap()); + // populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -64,6 +65,17 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcConnectionString = instance.getJdbcConnectionString(); this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); + + final Map dbProperties = new HashMap<>(); + dbProperties.put("org.killbill.dao.url", jdbcConnectionString); + dbProperties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + dbProperties.put("org.killbill.dao.user", jdbcUsername); + dbProperties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); + dbProperties.put("org.killbill.dao.password", jdbcPassword); + dbProperties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); + + propertiesCollector.addProperties("KillBillDefaults", dbProperties); + rebuildCache(); } else { // NoDB tests this.jdbcConnectionString = null; @@ -71,7 +83,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - populateDefaultProperties(Collections.emptyMap()); + // populateDefaultProperties(Collections.emptyMap()); //rebuildCache(); } From d079ba33dcb3d11f735eb0cf69c4202264a7f518 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 16:44:13 +0530 Subject: [PATCH 074/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 2 +- .../test/config/TestKillbillConfigSource.java | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 8707ea1c4..24486cfc7 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -80,7 +80,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - protected final PropertiesWithSourceCollector propertiesCollector; + private final PropertiesWithSourceCollector propertiesCollector; private volatile Map> cachedPropertiesBySource; diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index a61fda53b..861af7266 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,7 +23,6 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -51,7 +50,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, extraDefaults); + super(file); this.extraDefaults = extraDefaults; @@ -65,17 +64,6 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcConnectionString = instance.getJdbcConnectionString(); this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); - - final Map dbProperties = new HashMap<>(); - dbProperties.put("org.killbill.dao.url", jdbcConnectionString); - dbProperties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); - dbProperties.put("org.killbill.dao.user", jdbcUsername); - dbProperties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); - dbProperties.put("org.killbill.dao.password", jdbcPassword); - dbProperties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); - - propertiesCollector.addProperties("KillBillDefaults", dbProperties); - rebuildCache(); } else { // NoDB tests this.jdbcConnectionString = null; From 86f520e7afb6a49fd2002ccad2bc943b6faa0ebc Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 17:52:44 +0530 Subject: [PATCH 075/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 861af7266..23ecff417 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -50,9 +50,11 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file); + super(file, Collections.emptyMap()); - this.extraDefaults = extraDefaults; + //this.extraDefaults = Collections.emptyMap(); + populateDefaultProperties(Collections.emptyMap()); + // this.extraDefaults = extraDefaults; // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point @@ -71,6 +73,11 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } + this.extraDefaults = extraDefaults; + + populateDefaultProperties(extraDefaults); + rebuildCache(); + // populateDefaultProperties(Collections.emptyMap()); //rebuildCache(); } From 545f7b05dd0aa96461b647d1fee15c155fe9e023 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 18:54:31 +0530 Subject: [PATCH 076/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 2 +- .../test/config/TestKillbillConfigSource.java | 44 +++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 24486cfc7..8707ea1c4 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -80,7 +80,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; + protected final PropertiesWithSourceCollector propertiesCollector; private volatile Map> cachedPropertiesBySource; diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 23ecff417..4ea2ea5bd 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -50,10 +51,10 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, Collections.emptyMap()); - + super(file, extraDefaults); + this.extraDefaults = extraDefaults; //this.extraDefaults = Collections.emptyMap(); - populateDefaultProperties(Collections.emptyMap()); + // populateDefaultProperties(Collections.emptyMap()); // this.extraDefaults = extraDefaults; // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its @@ -66,6 +67,33 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcConnectionString = instance.getJdbcConnectionString(); this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); + + final Map testProperties = new HashMap<>(); + + testProperties.put("org.killbill.dao.url", jdbcConnectionString); + testProperties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + testProperties.put("org.killbill.dao.user", jdbcUsername); + testProperties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); + testProperties.put("org.killbill.dao.password", jdbcPassword); + testProperties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); + + testProperties.put("org.killbill.notificationq.main.sleep", "100"); + testProperties.put("org.killbill.notificationq.main.nbThreads", "1"); + testProperties.put("org.killbill.notificationq.main.claimed", "1"); + testProperties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); + testProperties.put("org.killbill.persistent.bus.main.sleep", "100"); + testProperties.put("org.killbill.persistent.bus.main.nbThreads", "1"); + testProperties.put("org.killbill.persistent.bus.main.claimed", "1"); + testProperties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); + testProperties.put("org.killbill.persistent.bus.external.sleep", "100"); + testProperties.put("org.killbill.persistent.bus.external.nbThreads", "1"); + testProperties.put("org.killbill.persistent.bus.external.claimed", "1"); + testProperties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + testProperties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); + testProperties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); + + propertiesCollector.addProperties("KillBillDefaults", testProperties); + rebuildCache(); } else { // NoDB tests this.jdbcConnectionString = null; @@ -73,16 +101,16 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - this.extraDefaults = extraDefaults; + //this.extraDefaults = extraDefaults; - populateDefaultProperties(extraDefaults); - rebuildCache(); + //populateDefaultProperties(extraDefaults); + // rebuildCache(); // populateDefaultProperties(Collections.emptyMap()); //rebuildCache(); } - @Override + /* @Override protected Properties getDefaultProperties() { final Properties properties = super.getDefaultProperties(); @@ -128,7 +156,7 @@ protected Properties getDefaultProperties() { } return properties; - } + }*/ @Override protected Properties getDefaultSystemProperties() { From 924a7ddf42dc78e1a58aed84704a293386ef4649 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 19:12:53 +0530 Subject: [PATCH 077/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 76 +++++-------------- 1 file changed, 18 insertions(+), 58 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 4ea2ea5bd..0d1619c65 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -37,10 +37,10 @@ public class TestKillbillConfigSource extends DefaultKillbillConfigSource { - private final String jdbcConnectionString; - private final String jdbcUsername; - private final String jdbcPassword; - private final Map extraDefaults; + private String jdbcConnectionString; + private String jdbcUsername; + private String jdbcPassword; + private Map extraDefaults; public TestKillbillConfigSource(@Nullable final Class dbTestingHelperKlass) throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException { this(null, dbTestingHelperKlass); @@ -51,15 +51,9 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, extraDefaults); - this.extraDefaults = extraDefaults; - //this.extraDefaults = Collections.emptyMap(); - // populateDefaultProperties(Collections.emptyMap()); - // this.extraDefaults = extraDefaults; + super(file, Collections.emptyMap()); - // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its - // driver at startup, h2 loads it statically and we need System Properties set at that point - // populateDefaultProperties(Collections.emptyMap()); + this.extraDefaults = Collections.emptyMap(); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -67,54 +61,28 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcConnectionString = instance.getJdbcConnectionString(); this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); - - final Map testProperties = new HashMap<>(); - - testProperties.put("org.killbill.dao.url", jdbcConnectionString); - testProperties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); - testProperties.put("org.killbill.dao.user", jdbcUsername); - testProperties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); - testProperties.put("org.killbill.dao.password", jdbcPassword); - testProperties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); - - testProperties.put("org.killbill.notificationq.main.sleep", "100"); - testProperties.put("org.killbill.notificationq.main.nbThreads", "1"); - testProperties.put("org.killbill.notificationq.main.claimed", "1"); - testProperties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); - testProperties.put("org.killbill.persistent.bus.main.sleep", "100"); - testProperties.put("org.killbill.persistent.bus.main.nbThreads", "1"); - testProperties.put("org.killbill.persistent.bus.main.claimed", "1"); - testProperties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); - testProperties.put("org.killbill.persistent.bus.external.sleep", "100"); - testProperties.put("org.killbill.persistent.bus.external.nbThreads", "1"); - testProperties.put("org.killbill.persistent.bus.external.claimed", "1"); - testProperties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); - testProperties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); - testProperties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); - - propertiesCollector.addProperties("KillBillDefaults", testProperties); - rebuildCache(); } else { - // NoDB tests this.jdbcConnectionString = null; this.jdbcUsername = null; this.jdbcPassword = null; } - //this.extraDefaults = extraDefaults; + this.extraDefaults = extraDefaults; - //populateDefaultProperties(extraDefaults); - // rebuildCache(); + final Properties testDefaults = getDefaultProperties(); + final Map testDefaultsMap = new HashMap<>(); + for (final String key : testDefaults.stringPropertyNames()) { + testDefaultsMap.put(key, testDefaults.getProperty(key)); + } - // populateDefaultProperties(Collections.emptyMap()); - //rebuildCache(); + propertiesCollector.addProperties("KillBillDefaults", testDefaultsMap); + rebuildCache(); } - /* @Override + @Override protected Properties getDefaultProperties() { final Properties properties = super.getDefaultProperties(); - // Setup up DAO properties (this will be a no-op for fast tests) if (jdbcConnectionString != null) { properties.put("org.killbill.dao.url", jdbcConnectionString); properties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); @@ -128,35 +96,27 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); } - // Speed up the notification queue properties.put("org.killbill.notificationq.main.sleep", "100"); properties.put("org.killbill.notificationq.main.nbThreads", "1"); properties.put("org.killbill.notificationq.main.claimed", "1"); properties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); - - // Speed up the buses properties.put("org.killbill.persistent.bus.main.sleep", "100"); properties.put("org.killbill.persistent.bus.main.nbThreads", "1"); properties.put("org.killbill.persistent.bus.main.claimed", "1"); properties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); - properties.put("org.killbill.persistent.bus.external.sleep", "100"); properties.put("org.killbill.persistent.bus.external.nbThreads", "1"); properties.put("org.killbill.persistent.bus.external.claimed", "1"); properties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); - - // Temporary directory for OSGI bundles properties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); properties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); if (extraDefaults != null) { - for (final Entry entry : extraDefaults.entrySet()) { - properties.put(entry.getKey(), entry.getValue()); - } + properties.putAll(extraDefaults); } return properties; - }*/ + } @Override protected Properties getDefaultSystemProperties() { @@ -165,4 +125,4 @@ protected Properties getDefaultSystemProperties() { properties.put("org.slf4j.simpleLogger.showDateTime", "true"); return properties; } -} +} \ No newline at end of file From a97a84a42f75de5ae42a2af0983a58981a3a486e Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 20:24:48 +0530 Subject: [PATCH 078/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 10 +++++- .../test/config/TestKillbillConfigSource.java | 31 +++++++++---------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 8707ea1c4..5a981aa48 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -291,7 +291,7 @@ private void loadPropertiesFromFileOrSystemProperties() { @VisibleForTesting protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); - defaultProperties.putAll(extraDefaultProperties); + //defaultProperties.putAll(extraDefaultProperties); final Map defaultsToAdd = new HashMap<>(); @@ -302,6 +302,14 @@ protected void populateDefaultProperties(final Map extraDefaultP } } + if (extraDefaultProperties != null) { + for (final Entry entry : extraDefaultProperties.entrySet()) { + if (entry.getValue() != null && !hasProperty(entry.getKey())) { + defaultsToAdd.put(entry.getKey(), entry.getValue()); + } + } + } + final Map immutableProps = new HashMap<>(); final Properties defaultSystemProperties = getDefaultSystemProperties(); diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 0d1619c65..ec7156b28 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,7 +23,6 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -37,10 +36,10 @@ public class TestKillbillConfigSource extends DefaultKillbillConfigSource { - private String jdbcConnectionString; - private String jdbcUsername; - private String jdbcPassword; - private Map extraDefaults; + private final String jdbcConnectionString; + private final String jdbcUsername; + private final String jdbcPassword; + private final Map extraDefaults; public TestKillbillConfigSource(@Nullable final Class dbTestingHelperKlass) throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException { this(null, dbTestingHelperKlass); @@ -53,7 +52,10 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { super(file, Collections.emptyMap()); - this.extraDefaults = Collections.emptyMap(); + // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its + // driver at startup, h2 loads it statically and we need System Properties set at that point + //populateDefaultProperties(); + this.extraDefaults = extraDefaults; if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -62,20 +64,15 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); } else { + // NoDB tests this.jdbcConnectionString = null; this.jdbcUsername = null; this.jdbcPassword = null; } - this.extraDefaults = extraDefaults; - - final Properties testDefaults = getDefaultProperties(); - final Map testDefaultsMap = new HashMap<>(); - for (final String key : testDefaults.stringPropertyNames()) { - testDefaultsMap.put(key, testDefaults.getProperty(key)); - } - - propertiesCollector.addProperties("KillBillDefaults", testDefaultsMap); + // this.extraDefaults = extraDefaults; + // extraDefaults changed, need to reload defaults + populateDefaultProperties(extraDefaults); rebuildCache(); } @@ -112,7 +109,9 @@ protected Properties getDefaultProperties() { properties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); if (extraDefaults != null) { - properties.putAll(extraDefaults); + for (final Entry entry : extraDefaults.entrySet()) { + properties.put(entry.getKey(), entry.getValue()); + } } return properties; From 1c27ae748f4de0d9e69b0837af62aebe8655371c Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 23:28:45 +0530 Subject: [PATCH 079/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index ec7156b28..0981da772 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,8 +23,8 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; +import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Properties; import javax.annotation.Nullable; @@ -63,6 +63,11 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcConnectionString = instance.getJdbcConnectionString(); this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); + + final Map testProperties = buildTestProperties(); + propertiesCollector.addProperties("RuntimeConfiguration", testProperties); + rebuildCache(); + } else { // NoDB tests this.jdbcConnectionString = null; @@ -72,11 +77,45 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(extraDefaults); + //populateDefaultProperties(extraDefaults); rebuildCache(); } - @Override + private Map buildTestProperties() { + final Map props = new HashMap<>(); + if (jdbcConnectionString != null) { + props.put("org.killbill.dao.url", jdbcConnectionString); + props.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + } + if (jdbcUsername != null) { + props.put("org.killbill.dao.user", jdbcUsername); + props.put("org.killbill.billing.osgi.dao.user", jdbcUsername); + } + if (jdbcPassword != null) { + props.put("org.killbill.dao.password", jdbcPassword); + props.put("org.killbill.billing.osgi.dao.password", jdbcPassword); + } + + props.put("org.killbill.notificationq.main.sleep", "100"); + props.put("org.killbill.notificationq.main.nbThreads", "1"); + props.put("org.killbill.notificationq.main.claimed", "1"); + props.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); + props.put("org.killbill.persistent.bus.main.sleep", "100"); + props.put("org.killbill.persistent.bus.main.nbThreads", "1"); + props.put("org.killbill.persistent.bus.main.claimed", "1"); + props.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); + props.put("org.killbill.persistent.bus.external.sleep", "100"); + props.put("org.killbill.persistent.bus.external.nbThreads", "1"); + props.put("org.killbill.persistent.bus.external.claimed", "1"); + props.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + + props.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); + props.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); + + return props; + } + + /*@Override protected Properties getDefaultProperties() { final Properties properties = super.getDefaultProperties(); @@ -115,7 +154,7 @@ protected Properties getDefaultProperties() { } return properties; - } + }*/ @Override protected Properties getDefaultSystemProperties() { From 4224050afbe4f35b5d319261fb87c8fa3f6df208 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 23:46:30 +0530 Subject: [PATCH 080/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 12 +---- .../test/config/TestKillbillConfigSource.java | 52 +++---------------- 2 files changed, 8 insertions(+), 56 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 5a981aa48..24486cfc7 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -80,7 +80,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - protected final PropertiesWithSourceCollector propertiesCollector; + private final PropertiesWithSourceCollector propertiesCollector; private volatile Map> cachedPropertiesBySource; @@ -291,7 +291,7 @@ private void loadPropertiesFromFileOrSystemProperties() { @VisibleForTesting protected void populateDefaultProperties(final Map extraDefaultProperties) { final Properties defaultProperties = getDefaultProperties(); - //defaultProperties.putAll(extraDefaultProperties); + defaultProperties.putAll(extraDefaultProperties); final Map defaultsToAdd = new HashMap<>(); @@ -302,14 +302,6 @@ protected void populateDefaultProperties(final Map extraDefaultP } } - if (extraDefaultProperties != null) { - for (final Entry entry : extraDefaultProperties.entrySet()) { - if (entry.getValue() != null && !hasProperty(entry.getKey())) { - defaultsToAdd.put(entry.getKey(), entry.getValue()); - } - } - } - final Map immutableProps = new HashMap<>(); final Properties defaultSystemProperties = getDefaultSystemProperties(); diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 0981da772..1cfbd162f 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,8 +23,8 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import javax.annotation.Nullable; @@ -50,12 +50,11 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, Collections.emptyMap()); + super(file); // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point //populateDefaultProperties(); - this.extraDefaults = extraDefaults; if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -63,11 +62,6 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcConnectionString = instance.getJdbcConnectionString(); this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); - - final Map testProperties = buildTestProperties(); - propertiesCollector.addProperties("RuntimeConfiguration", testProperties); - rebuildCache(); - } else { // NoDB tests this.jdbcConnectionString = null; @@ -75,47 +69,13 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcPassword = null; } - // this.extraDefaults = extraDefaults; + this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - //populateDefaultProperties(extraDefaults); + populateDefaultProperties(extraDefaults); rebuildCache(); } - private Map buildTestProperties() { - final Map props = new HashMap<>(); - if (jdbcConnectionString != null) { - props.put("org.killbill.dao.url", jdbcConnectionString); - props.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); - } - if (jdbcUsername != null) { - props.put("org.killbill.dao.user", jdbcUsername); - props.put("org.killbill.billing.osgi.dao.user", jdbcUsername); - } - if (jdbcPassword != null) { - props.put("org.killbill.dao.password", jdbcPassword); - props.put("org.killbill.billing.osgi.dao.password", jdbcPassword); - } - - props.put("org.killbill.notificationq.main.sleep", "100"); - props.put("org.killbill.notificationq.main.nbThreads", "1"); - props.put("org.killbill.notificationq.main.claimed", "1"); - props.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); - props.put("org.killbill.persistent.bus.main.sleep", "100"); - props.put("org.killbill.persistent.bus.main.nbThreads", "1"); - props.put("org.killbill.persistent.bus.main.claimed", "1"); - props.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); - props.put("org.killbill.persistent.bus.external.sleep", "100"); - props.put("org.killbill.persistent.bus.external.nbThreads", "1"); - props.put("org.killbill.persistent.bus.external.claimed", "1"); - props.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); - - props.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); - props.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); - - return props; - } - - /*@Override + @Override protected Properties getDefaultProperties() { final Properties properties = super.getDefaultProperties(); @@ -154,7 +114,7 @@ protected Properties getDefaultProperties() { } return properties; - }*/ + } @Override protected Properties getDefaultSystemProperties() { From 4f6810aa5bd3c7aab2bc64bed545c68f5b700338 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sat, 15 Nov 2025 23:55:44 +0530 Subject: [PATCH 081/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 1cfbd162f..cf19420f9 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -71,8 +71,8 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(extraDefaults); - rebuildCache(); + // populateDefaultProperties(extraDefaults); + //rebuildCache(); } @Override From bc9fb79406fe0bf1ced0c0ab37eac561d12f6466 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 00:34:24 +0530 Subject: [PATCH 082/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index cf19420f9..71e4f3fee 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -54,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - //populateDefaultProperties(); + populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -71,8 +71,8 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - // populateDefaultProperties(extraDefaults); - //rebuildCache(); + populateDefaultProperties(extraDefaults); + rebuildCache(); } @Override From 0a975fa8fd45d4c2953c601dc0533d519870f426 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 01:32:05 +0530 Subject: [PATCH 083/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/config/TestKillbillConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 71e4f3fee..fdce2d3e3 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -71,7 +71,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults - populateDefaultProperties(extraDefaults); + // populateDefaultProperties(extraDefaults); rebuildCache(); } From 808412d5c99ab815b483843f54f2060715f746cb Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 02:01:06 +0530 Subject: [PATCH 084/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index fdce2d3e3..19d84f0f9 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,8 +23,8 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; +import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Properties; import javax.annotation.Nullable; @@ -50,12 +50,9 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file); - - // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its - // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(Collections.emptyMap()); + super(file, buildExtraDefaults(file, dbTestingHelperKlass, extraDefaults)); + // Store references for potential later use if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); final EmbeddedDB instance = dbTestingHelper.getInstance(); @@ -63,19 +60,15 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); } else { - // NoDB tests this.jdbcConnectionString = null; this.jdbcUsername = null; this.jdbcPassword = null; } this.extraDefaults = extraDefaults; - // extraDefaults changed, need to reload defaults - // populateDefaultProperties(extraDefaults); - rebuildCache(); } - @Override + /*@Override protected Properties getDefaultProperties() { final Properties properties = super.getDefaultProperties(); @@ -114,6 +107,49 @@ protected Properties getDefaultProperties() { } return properties; + }*/ + + private static Map buildExtraDefaults(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + // Set early system properties + if (System.getProperty("net.sf.ehcache.skipUpdateCheck") == null) { + System.setProperty("net.sf.ehcache.skipUpdateCheck", "true"); + } + if (System.getProperty("org.slf4j.simpleLogger.showDateTime") == null) { + System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); + } + + final Map allDefaults = new HashMap<>(extraDefaults); + + // Initialize DB and add JDBC properties + if (dbTestingHelperKlass != null) { + final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); + final EmbeddedDB instance = dbTestingHelper.getInstance(); + + allDefaults.put("org.killbill.dao.url", instance.getJdbcConnectionString()); + allDefaults.put("org.killbill.billing.osgi.dao.url", instance.getJdbcConnectionString()); + allDefaults.put("org.killbill.dao.user", instance.getUsername()); + allDefaults.put("org.killbill.billing.osgi.dao.user", instance.getUsername()); + allDefaults.put("org.killbill.dao.password", instance.getPassword()); + allDefaults.put("org.killbill.billing.osgi.dao.password", instance.getPassword()); + } + + // Add test-specific properties + allDefaults.put("org.killbill.notificationq.main.sleep", "100"); + allDefaults.put("org.killbill.notificationq.main.nbThreads", "1"); + allDefaults.put("org.killbill.notificationq.main.claimed", "1"); + allDefaults.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); + allDefaults.put("org.killbill.persistent.bus.main.sleep", "100"); + allDefaults.put("org.killbill.persistent.bus.main.nbThreads", "1"); + allDefaults.put("org.killbill.persistent.bus.main.claimed", "1"); + allDefaults.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); + allDefaults.put("org.killbill.persistent.bus.external.sleep", "100"); + allDefaults.put("org.killbill.persistent.bus.external.nbThreads", "1"); + allDefaults.put("org.killbill.persistent.bus.external.claimed", "1"); + allDefaults.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + allDefaults.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); + allDefaults.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); + + return allDefaults; } @Override From 9ee05ac680debf9637e05845275cdf1e07a9f825 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 02:10:53 +0530 Subject: [PATCH 085/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 19d84f0f9..09b7dde8b 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -111,12 +111,12 @@ protected Properties getDefaultProperties() { private static Map buildExtraDefaults(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // Set early system properties - if (System.getProperty("net.sf.ehcache.skipUpdateCheck") == null) { + /* if (System.getProperty("net.sf.ehcache.skipUpdateCheck") == null) { System.setProperty("net.sf.ehcache.skipUpdateCheck", "true"); } if (System.getProperty("org.slf4j.simpleLogger.showDateTime") == null) { System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); - } + }*/ final Map allDefaults = new HashMap<>(extraDefaults); From 38141da443bfee989a0a97b09ffdb74b15a39076 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 02:27:19 +0530 Subject: [PATCH 086/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 8 +++ .../platform/jndi/TestJNDIManager.java | 2 + .../test/config/TestKillbillConfigSource.java | 72 +++++++------------ 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 24486cfc7..9f0d788f7 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -525,4 +525,12 @@ private boolean shouldWarnAboutConflict(final List sources) { sources.contains("EnvironmentVariables") && sources.contains("RuntimeConfiguration"); } + + @VisibleForTesting + public static void resetForTesting() { + synchronized (lock) { + GMT_WARNING = NOT_SHOWN; + ENTROPY_WARNING = NOT_SHOWN; + } + } } diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index 3292508b1..b1372ac78 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -27,6 +27,7 @@ import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; +import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; import org.testng.Assert; @@ -53,6 +54,7 @@ public void setUp() throws Exception { @AfterMethod(groups = "slow") public void tearDown() throws Exception { embeddedDB.stop(); + DefaultKillbillConfigSource.resetForTesting(); } @Test(groups = "slow") diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 09b7dde8b..fbacf7ca4 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.TimeZone; import javax.annotation.Nullable; @@ -41,6 +42,9 @@ public class TestKillbillConfigSource extends DefaultKillbillConfigSource { private final String jdbcPassword; private final Map extraDefaults; + // Flag to control whether we should skip timezone setting during initialization + private static final ThreadLocal SKIP_TIMEZONE_INIT = ThreadLocal.withInitial(() -> false); + public TestKillbillConfigSource(@Nullable final Class dbTestingHelperKlass) throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException { this(null, dbTestingHelperKlass); } @@ -50,7 +54,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, buildExtraDefaults(file, dbTestingHelperKlass, extraDefaults)); + super(file, initializeAndBuildDefaults(dbTestingHelperKlass, extraDefaults)); // Store references for potential later use if (dbTestingHelperKlass != null) { @@ -66,58 +70,23 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } this.extraDefaults = extraDefaults; - } - /*@Override - protected Properties getDefaultProperties() { - final Properties properties = super.getDefaultProperties(); + // Clean up the flag + SKIP_TIMEZONE_INIT.remove(); - if (jdbcConnectionString != null) { - properties.put("org.killbill.dao.url", jdbcConnectionString); - properties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); - } - if (jdbcUsername != null) { - properties.put("org.killbill.dao.user", jdbcUsername); - properties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); - } - if (jdbcPassword != null) { - properties.put("org.killbill.dao.password", jdbcPassword); - properties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); - } + // NOW apply timezone setting after DB is fully initialized + System.setProperty("user.timezone", "GMT"); + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + } - properties.put("org.killbill.notificationq.main.sleep", "100"); - properties.put("org.killbill.notificationq.main.nbThreads", "1"); - properties.put("org.killbill.notificationq.main.claimed", "1"); - properties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); - properties.put("org.killbill.persistent.bus.main.sleep", "100"); - properties.put("org.killbill.persistent.bus.main.nbThreads", "1"); - properties.put("org.killbill.persistent.bus.main.claimed", "1"); - properties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); - properties.put("org.killbill.persistent.bus.external.sleep", "100"); - properties.put("org.killbill.persistent.bus.external.nbThreads", "1"); - properties.put("org.killbill.persistent.bus.external.claimed", "1"); - properties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); - properties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); - properties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); - - if (extraDefaults != null) { - for (final Entry entry : extraDefaults.entrySet()) { - properties.put(entry.getKey(), entry.getValue()); - } - } + private static Map initializeAndBuildDefaults(@Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + // Mark that we want to skip timezone init temporarily + SKIP_TIMEZONE_INIT.set(true); - return properties; - }*/ - - private static Map buildExtraDefaults(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // Set early system properties - /* if (System.getProperty("net.sf.ehcache.skipUpdateCheck") == null) { - System.setProperty("net.sf.ehcache.skipUpdateCheck", "true"); - } - if (System.getProperty("org.slf4j.simpleLogger.showDateTime") == null) { - System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); - }*/ + return buildExtraDefaults(dbTestingHelperKlass, extraDefaults); + } + private static Map buildExtraDefaults(@Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { final Map allDefaults = new HashMap<>(extraDefaults); // Initialize DB and add JDBC properties @@ -146,6 +115,7 @@ private static Map buildExtraDefaults(@Nullable final String fil allDefaults.put("org.killbill.persistent.bus.external.nbThreads", "1"); allDefaults.put("org.killbill.persistent.bus.external.claimed", "1"); allDefaults.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + allDefaults.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); allDefaults.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); @@ -155,6 +125,12 @@ private static Map buildExtraDefaults(@Nullable final String fil @Override protected Properties getDefaultSystemProperties() { final Properties properties = super.getDefaultSystemProperties(); + + // Skip timezone if we're in the initialization phase + if (SKIP_TIMEZONE_INIT.get()) { + properties.remove("user.timezone"); + } + properties.put("net.sf.ehcache.skipUpdateCheck", "true"); properties.put("org.slf4j.simpleLogger.showDateTime", "true"); return properties; From e313368e31a7db9c39aa4e1857d38845209a9d60 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 02:38:09 +0530 Subject: [PATCH 087/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 102 ++++++++---------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index fbacf7ca4..5c12b97d7 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.TimeZone; @@ -42,9 +43,6 @@ public class TestKillbillConfigSource extends DefaultKillbillConfigSource { private final String jdbcPassword; private final Map extraDefaults; - // Flag to control whether we should skip timezone setting during initialization - private static final ThreadLocal SKIP_TIMEZONE_INIT = ThreadLocal.withInitial(() -> false); - public TestKillbillConfigSource(@Nullable final Class dbTestingHelperKlass) throws URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException { this(null, dbTestingHelperKlass); } @@ -54,9 +52,13 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - super(file, initializeAndBuildDefaults(dbTestingHelperKlass, extraDefaults)); + // Call parent constructor WITHOUT extraDefaults - this will call populateDefaultProperties() once + super(file); + + // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its + // driver at startup, h2 loads it statically and we need System Properties set at that point + populateDefaultProperties(Collections.emptyMap()); - // Store references for potential later use if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); final EmbeddedDB instance = dbTestingHelper.getInstance(); @@ -64,73 +66,63 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.jdbcUsername = instance.getUsername(); this.jdbcPassword = instance.getPassword(); } else { + // NoDB tests this.jdbcConnectionString = null; this.jdbcUsername = null; this.jdbcPassword = null; } this.extraDefaults = extraDefaults; - - // Clean up the flag - SKIP_TIMEZONE_INIT.remove(); - - // NOW apply timezone setting after DB is fully initialized - System.setProperty("user.timezone", "GMT"); - TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + // extraDefaults changed, need to reload defaults + populateDefaultProperties(extraDefaults); + rebuildCache(); } - private static Map initializeAndBuildDefaults(@Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // Mark that we want to skip timezone init temporarily - SKIP_TIMEZONE_INIT.set(true); - - return buildExtraDefaults(dbTestingHelperKlass, extraDefaults); - } - - private static Map buildExtraDefaults(@Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - final Map allDefaults = new HashMap<>(extraDefaults); + @Override + protected Properties getDefaultProperties() { + final Properties properties = super.getDefaultProperties(); - // Initialize DB and add JDBC properties - if (dbTestingHelperKlass != null) { - final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); - final EmbeddedDB instance = dbTestingHelper.getInstance(); + if (jdbcConnectionString != null) { + properties.put("org.killbill.dao.url", jdbcConnectionString); + properties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + } + if (jdbcUsername != null) { + properties.put("org.killbill.dao.user", jdbcUsername); + properties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); + } + if (jdbcPassword != null) { + properties.put("org.killbill.dao.password", jdbcPassword); + properties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); + } - allDefaults.put("org.killbill.dao.url", instance.getJdbcConnectionString()); - allDefaults.put("org.killbill.billing.osgi.dao.url", instance.getJdbcConnectionString()); - allDefaults.put("org.killbill.dao.user", instance.getUsername()); - allDefaults.put("org.killbill.billing.osgi.dao.user", instance.getUsername()); - allDefaults.put("org.killbill.dao.password", instance.getPassword()); - allDefaults.put("org.killbill.billing.osgi.dao.password", instance.getPassword()); + properties.put("org.killbill.notificationq.main.sleep", "100"); + properties.put("org.killbill.notificationq.main.nbThreads", "1"); + properties.put("org.killbill.notificationq.main.claimed", "1"); + properties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); + properties.put("org.killbill.persistent.bus.main.sleep", "100"); + properties.put("org.killbill.persistent.bus.main.nbThreads", "1"); + properties.put("org.killbill.persistent.bus.main.claimed", "1"); + properties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); + properties.put("org.killbill.persistent.bus.external.sleep", "100"); + properties.put("org.killbill.persistent.bus.external.nbThreads", "1"); + properties.put("org.killbill.persistent.bus.external.claimed", "1"); + properties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + + properties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); + properties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); + + if (extraDefaults != null) { + for (final Entry entry : extraDefaults.entrySet()) { + properties.put(entry.getKey(), entry.getValue()); + } } - // Add test-specific properties - allDefaults.put("org.killbill.notificationq.main.sleep", "100"); - allDefaults.put("org.killbill.notificationq.main.nbThreads", "1"); - allDefaults.put("org.killbill.notificationq.main.claimed", "1"); - allDefaults.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); - allDefaults.put("org.killbill.persistent.bus.main.sleep", "100"); - allDefaults.put("org.killbill.persistent.bus.main.nbThreads", "1"); - allDefaults.put("org.killbill.persistent.bus.main.claimed", "1"); - allDefaults.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); - allDefaults.put("org.killbill.persistent.bus.external.sleep", "100"); - allDefaults.put("org.killbill.persistent.bus.external.nbThreads", "1"); - allDefaults.put("org.killbill.persistent.bus.external.claimed", "1"); - allDefaults.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); - - allDefaults.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); - allDefaults.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); - - return allDefaults; + return properties; } @Override protected Properties getDefaultSystemProperties() { final Properties properties = super.getDefaultSystemProperties(); - - // Skip timezone if we're in the initialization phase - if (SKIP_TIMEZONE_INIT.get()) { - properties.remove("user.timezone"); - } - properties.put("net.sf.ehcache.skipUpdateCheck", "true"); properties.put("org.slf4j.simpleLogger.showDateTime", "true"); return properties; From b3673a5b2babeefad26e1d3f713e28ebf643b91e Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 02:55:40 +0530 Subject: [PATCH 088/221] Added debug msg to TestJNDIManager --- .../integration/osgi/TestOSGIBase.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index 184032355..c0b8aa0b7 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -89,18 +89,10 @@ public class TestOSGIBase { @Inject protected OSGIServiceRegistration currencyPluginApiOSGIServiceRegistration; - protected final TestKillbillConfigSource configSource; + protected TestKillbillConfigSource configSource; protected CallContext callContext; public TestOSGIBase() { - try { - configSource = new TestKillbillConfigSource(null, PlatformDBTestingHelper.class); - } catch (final Exception e) { - final AssertionError assertionError = new AssertionError("Initialization error"); - assertionError.initCause(e); - throw assertionError; - } - callContext = Mockito.mock(CallContext.class); } @@ -111,6 +103,14 @@ public void beforeSuite() throws Exception { @BeforeClass(groups = "slow") public void beforeClass() throws Exception { + try { + configSource = new TestKillbillConfigSource(null, PlatformDBTestingHelper.class); + } catch (final Exception e) { + final AssertionError assertionError = new AssertionError("Initialization error"); + assertionError.initCause(e); + throw assertionError; + } + final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestIntegrationModule(configSource)); g.injectMembers(this); } @@ -124,7 +124,6 @@ public void beforeMethod() throws Exception { clock.resetDeltaFromReality(); - // Start services lifecycle.fireStartupSequencePriorEventRegistration(); lifecycle.fireStartupSequencePostEventRegistration(); } @@ -157,11 +156,11 @@ protected T getTestApi(final OSGIServiceRegistration serviceRegistration, Awaitility.await().until(new Callable() { @Override public Boolean call() throws Exception { - // It is expected to have a null result if the initialization of Killbill went faster than the registration of the plugin services + return serviceRegistration.getServiceForName(serviceName) != null; } }); return serviceRegistration.getServiceForName(serviceName); } -} +} \ No newline at end of file From f96298ab880ea0bf5dbc0f5cb789569cc88574af Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 03:01:44 +0530 Subject: [PATCH 089/221] Added debug msg to TestJNDIManager --- .../integration/osgi/TestBasicOSGIWithTestBundle.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java index be6e85a1f..79c4492c8 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java @@ -32,7 +32,6 @@ import org.killbill.billing.beatrix.integration.osgi.util.SetupBundleWithAssertion; import org.killbill.billing.catalog.api.Currency; import org.killbill.billing.notification.plugin.api.ExtBusEventType; -import org.killbill.billing.payment.api.PluginProperty; import org.killbill.billing.payment.plugin.api.PaymentPluginApi; import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin; import org.skife.jdbi.v2.Handle; @@ -69,10 +68,6 @@ public void beforeClass() throws Exception { super.beforeClass(); final String killbillVersion = System.getProperty("killbill.version"); - System.out.println("Killbill.version is " + killbillVersion); - System.out.println("osgi prop name " + osgiConfig.getOSGIKillbillPropertyName()); - - final SetupBundleWithAssertion setupTest = new SetupBundleWithAssertion(BUNDLE_TEST_RESOURCE, osgiConfig, killbillVersion); setupTest.setupJavaBundle(); } From 2c284d020859b2e7b3e5e09005b5d4e94cf21415 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 03:12:58 +0530 Subject: [PATCH 090/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/PlatformDBTestingHelper.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java b/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java index d3e0bc1d0..f6ee6ecb1 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java @@ -42,6 +42,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sf.log4jdbc.log.SpyLogFactory; + public class PlatformDBTestingHelper { private static final Logger log = LoggerFactory.getLogger(PlatformDBTestingHelper.class); @@ -106,6 +108,12 @@ public DataSource getDataSource() { } public synchronized void start() throws IOException, SQLException { + try { + SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); + } catch (final Exception e) { + log.debug("SpyLogFactory already initialized or not needed", e); + } + instance.initialize(); instance.start(); From 0cbd4401470b1f4cb96e526762da9cd71e6e0236 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 10:32:20 +0530 Subject: [PATCH 091/221] Added debug msg to TestJNDIManager --- .../billing/platform/test/config/TestKillbillConfigSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 5c12b97d7..efe76f85f 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -57,7 +57,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - populateDefaultProperties(Collections.emptyMap()); + // populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); From f869ab357ba70fb8f81cbefed617c8e624d9e4f5 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 10:52:18 +0530 Subject: [PATCH 092/221] Added debug msg to TestJNDIManager --- .../billing/platform/config/DefaultKillbillConfigSource.java | 2 +- .../billing/platform/test/config/TestKillbillConfigSource.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index 9f0d788f7..c21ce9e8f 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -164,7 +164,7 @@ protected void rebuildCache() { cachedPropertiesBySource = computePropertiesBySource(); } - private void invalidateCache() { + protected void invalidateCache() { synchronized (lock) { cachedPropertiesBySource = null; } diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index efe76f85f..a180f505b 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -74,6 +74,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla this.extraDefaults = extraDefaults; // extraDefaults changed, need to reload defaults + invalidateCache(); populateDefaultProperties(extraDefaults); rebuildCache(); } From f6d331b93069b9b7e118c5c09584d454f5207f00 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 11:15:36 +0530 Subject: [PATCH 093/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 2 +- .../test/config/TestKillbillConfigSource.java | 43 ++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index c21ce9e8f..b90249946 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -80,7 +80,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - private final PropertiesWithSourceCollector propertiesCollector; + protected final PropertiesWithSourceCollector propertiesCollector; private volatile Map> cachedPropertiesBySource; diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index a180f505b..321f4ab0a 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -73,9 +73,48 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } this.extraDefaults = extraDefaults; - // extraDefaults changed, need to reload defaults + + // Add JDBC and test-specific properties directly to the collector with HIGH priority + // by adding them to RuntimeConfiguration source + final Map testProperties = new HashMap<>(); + + if (jdbcConnectionString != null) { + testProperties.put("org.killbill.dao.url", jdbcConnectionString); + testProperties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + } + if (jdbcUsername != null) { + testProperties.put("org.killbill.dao.user", jdbcUsername); + testProperties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); + } + if (jdbcPassword != null) { + testProperties.put("org.killbill.dao.password", jdbcPassword); + testProperties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); + } + + testProperties.put("org.killbill.notificationq.main.sleep", "100"); + testProperties.put("org.killbill.notificationq.main.nbThreads", "1"); + testProperties.put("org.killbill.notificationq.main.claimed", "1"); + testProperties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); + testProperties.put("org.killbill.persistent.bus.main.sleep", "100"); + testProperties.put("org.killbill.persistent.bus.main.nbThreads", "1"); + testProperties.put("org.killbill.persistent.bus.main.claimed", "1"); + testProperties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); + testProperties.put("org.killbill.persistent.bus.external.sleep", "100"); + testProperties.put("org.killbill.persistent.bus.external.nbThreads", "1"); + testProperties.put("org.killbill.persistent.bus.external.claimed", "1"); + testProperties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + + testProperties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); + testProperties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); + + if (extraDefaults != null) { + testProperties.putAll(extraDefaults); + } + + // Add all test properties as RuntimeConfiguration (highest priority) + propertiesCollector.addProperties("RuntimeConfiguration", testProperties); + invalidateCache(); - populateDefaultProperties(extraDefaults); rebuildCache(); } From c63ae3cb412200108e75b606e5693e8163b408a5 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 12:30:49 +0530 Subject: [PATCH 094/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 321f4ab0a..e41aa561d 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -76,46 +76,47 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Add JDBC and test-specific properties directly to the collector with HIGH priority // by adding them to RuntimeConfiguration source - final Map testProperties = new HashMap<>(); - if (jdbcConnectionString != null) { - testProperties.put("org.killbill.dao.url", jdbcConnectionString); - testProperties.put("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + setProperty("org.killbill.dao.url", jdbcConnectionString); + setProperty("org.killbill.billing.osgi.dao.url", jdbcConnectionString); } if (jdbcUsername != null) { - testProperties.put("org.killbill.dao.user", jdbcUsername); - testProperties.put("org.killbill.billing.osgi.dao.user", jdbcUsername); + setProperty("org.killbill.dao.user", jdbcUsername); + setProperty("org.killbill.billing.osgi.dao.user", jdbcUsername); } if (jdbcPassword != null) { - testProperties.put("org.killbill.dao.password", jdbcPassword); - testProperties.put("org.killbill.billing.osgi.dao.password", jdbcPassword); + setProperty("org.killbill.dao.password", jdbcPassword); + setProperty("org.killbill.billing.osgi.dao.password", jdbcPassword); } - testProperties.put("org.killbill.notificationq.main.sleep", "100"); - testProperties.put("org.killbill.notificationq.main.nbThreads", "1"); - testProperties.put("org.killbill.notificationq.main.claimed", "1"); - testProperties.put("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); - testProperties.put("org.killbill.persistent.bus.main.sleep", "100"); - testProperties.put("org.killbill.persistent.bus.main.nbThreads", "1"); - testProperties.put("org.killbill.persistent.bus.main.claimed", "1"); - testProperties.put("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); - testProperties.put("org.killbill.persistent.bus.external.sleep", "100"); - testProperties.put("org.killbill.persistent.bus.external.nbThreads", "1"); - testProperties.put("org.killbill.persistent.bus.external.claimed", "1"); - testProperties.put("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); - - testProperties.put("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); - testProperties.put("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); - + // Set other test-specific properties + setProperty("org.killbill.notificationq.main.sleep", "100"); + setProperty("org.killbill.notificationq.main.nbThreads", "1"); + setProperty("org.killbill.notificationq.main.claimed", "1"); + setProperty("org.killbill.notificationq.main.queue.mode", "STICKY_POLLING"); + setProperty("org.killbill.persistent.bus.main.sleep", "100"); + setProperty("org.killbill.persistent.bus.main.nbThreads", "1"); + setProperty("org.killbill.persistent.bus.main.claimed", "1"); + setProperty("org.killbill.persistent.bus.main.queue.mode", "STICKY_POLLING"); + setProperty("org.killbill.persistent.bus.external.sleep", "100"); + setProperty("org.killbill.persistent.bus.external.nbThreads", "1"); + setProperty("org.killbill.persistent.bus.external.claimed", "1"); + setProperty("org.killbill.persistent.bus.external.queue.mode", "STICKY_POLLING"); + + setProperty("org.killbill.osgi.root.dir", Files.createTempDirectory().getAbsolutePath()); + setProperty("org.killbill.osgi.bundle.install.dir", Files.createTempDirectory().getAbsolutePath()); + + // Set extra defaults if (extraDefaults != null) { - testProperties.putAll(extraDefaults); + for (Map.Entry entry : extraDefaults.entrySet()) { + setProperty(entry.getKey(), entry.getValue()); + } } - // Add all test properties as RuntimeConfiguration (highest priority) - propertiesCollector.addProperties("RuntimeConfiguration", testProperties); + // propertiesCollector.addProperties("RuntimeConfiguration", testProperties); - invalidateCache(); - rebuildCache(); + // invalidateCache(); + // rebuildCache(); } @Override From 5a5ef83e5bed5fe0ce58f2e2cd1077535a1d292b Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 12:48:38 +0530 Subject: [PATCH 095/221] Added debug msg to TestJNDIManager --- .../beatrix/integration/osgi/TestOSGIBase.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index c0b8aa0b7..021327fbe 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -93,6 +93,14 @@ public class TestOSGIBase { protected CallContext callContext; public TestOSGIBase() { + try { + configSource = new TestKillbillConfigSource(null, PlatformDBTestingHelper.class); + } catch (final Exception e) { + final AssertionError assertionError = new AssertionError("Initialization error"); + assertionError.initCause(e); + throw assertionError; + } + callContext = Mockito.mock(CallContext.class); } @@ -103,14 +111,6 @@ public void beforeSuite() throws Exception { @BeforeClass(groups = "slow") public void beforeClass() throws Exception { - try { - configSource = new TestKillbillConfigSource(null, PlatformDBTestingHelper.class); - } catch (final Exception e) { - final AssertionError assertionError = new AssertionError("Initialization error"); - assertionError.initCause(e); - throw assertionError; - } - final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestIntegrationModule(configSource)); g.injectMembers(this); } From 39c57474e8ab80fe97db803fe49c7c65381909e8 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 13:01:38 +0530 Subject: [PATCH 096/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 1 - .../integration/osgi/TestOSGIBase.java | 21 ++++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index e41aa561d..fdae75195 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -52,7 +52,6 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } public TestKillbillConfigSource(@Nullable final String file, @Nullable final Class dbTestingHelperKlass, final Map extraDefaults) throws IOException, URISyntaxException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // Call parent constructor WITHOUT extraDefaults - this will call populateDefaultProperties() once super(file); // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index 021327fbe..b0adc12fb 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -93,24 +93,29 @@ public class TestOSGIBase { protected CallContext callContext; public TestOSGIBase() { - try { - configSource = new TestKillbillConfigSource(null, PlatformDBTestingHelper.class); - } catch (final Exception e) { - final AssertionError assertionError = new AssertionError("Initialization error"); - assertionError.initCause(e); - throw assertionError; - } - callContext = Mockito.mock(CallContext.class); } @BeforeSuite(groups = "slow") public void beforeSuite() throws Exception { + if (System.getProperty("org.killbill.billing.dbi.test.h2") == null && + System.getProperty("org.killbill.billing.dbi.test.postgresql") == null) { + System.setProperty("org.killbill.billing.dbi.test.h2", "true"); + } + PlatformDBTestingHelper.get().start(); } @BeforeClass(groups = "slow") public void beforeClass() throws Exception { + try { + configSource = new TestKillbillConfigSource(null, PlatformDBTestingHelper.class); + } catch (final Exception e) { + final AssertionError assertionError = new AssertionError("Initialization error"); + assertionError.initCause(e); + throw assertionError; + } + final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestIntegrationModule(configSource)); g.injectMembers(this); } From aaad0f549a9e18a18df2c77a9ad54ac121046a2d Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 14:42:00 +0530 Subject: [PATCH 097/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 2 ++ .../billing/beatrix/integration/osgi/TestOSGIBase.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index fdae75195..6b03577cb 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -118,6 +118,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // rebuildCache(); } +/* @Override protected Properties getDefaultProperties() { final Properties properties = super.getDefaultProperties(); @@ -159,6 +160,7 @@ protected Properties getDefaultProperties() { return properties; } +*/ @Override protected Properties getDefaultSystemProperties() { diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index b0adc12fb..f7cab5dc6 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -116,6 +116,11 @@ public void beforeClass() throws Exception { throw assertionError; } + System.out.println("org.killbill.dao.url = " + configSource.getString("org.killbill.dao.url")); + System.out.println("org.killbill.billing.osgi.dao.url = " + configSource.getString("org.killbill.billing.osgi.dao.url")); + System.out.println("org.killbill.dao.user = " + configSource.getString("org.killbill.dao.user")); + System.out.println("org.killbill.dao.password = " + configSource.getString("org.killbill.dao.password")); + final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestIntegrationModule(configSource)); g.injectMembers(this); } From 43d1ac5b65a5a664cecaf097ecf7fbd6d381d7f2 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 14:51:10 +0530 Subject: [PATCH 098/221] Added debug msg to TestJNDIManager --- .../platform/test/config/TestKillbillConfigSource.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 6b03577cb..78a68c5fe 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -23,11 +23,8 @@ import java.lang.reflect.InvocationTargetException; import java.net.URISyntaxException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Properties; -import java.util.TimeZone; import javax.annotation.Nullable; @@ -56,7 +53,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // Set default System Properties before creating the instance of DBTestingHelper. Whereas MySQL loads its // driver at startup, h2 loads it statically and we need System Properties set at that point - // populateDefaultProperties(Collections.emptyMap()); + populateDefaultProperties(Collections.emptyMap()); if (dbTestingHelperKlass != null) { final PlatformDBTestingHelper dbTestingHelper = (PlatformDBTestingHelper) dbTestingHelperKlass.getDeclaredMethod("get").invoke(null); @@ -112,7 +109,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla } } // Add all test properties as RuntimeConfiguration (highest priority) - // propertiesCollector.addProperties("RuntimeConfiguration", testProperties); + // propertiesCollector.addProperties("RuntimeConfiguration", testProperties); // invalidateCache(); // rebuildCache(); From 673ba7550f8afd93d495dd055dfcdb93933b8612 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 14:56:30 +0530 Subject: [PATCH 099/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index 78a68c5fe..d9a131fb7 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -75,6 +75,11 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla if (jdbcConnectionString != null) { setProperty("org.killbill.dao.url", jdbcConnectionString); setProperty("org.killbill.billing.osgi.dao.url", jdbcConnectionString); + + final String driverClassName = getDriverClassName(jdbcConnectionString); + setProperty("org.killbill.dao.driverClassName", driverClassName); + setProperty("org.killbill.billing.osgi.dao.driverClassName", driverClassName); + } if (jdbcUsername != null) { setProperty("org.killbill.dao.user", jdbcUsername); @@ -115,6 +120,17 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // rebuildCache(); } + private String getDriverClassName(String jdbcUrl) { + if (jdbcUrl.startsWith("jdbc:h2:")) { + return "org.h2.Driver"; + } else if (jdbcUrl.startsWith("jdbc:mysql:")) { + return "com.mysql.jdbc.Driver"; + } else if (jdbcUrl.startsWith("jdbc:postgresql:")) { + return "org.postgresql.Driver"; + } + return null; + } + /* @Override protected Properties getDefaultProperties() { From c7fbd8c9f7583df074381007fa14359106510ad4 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 16:00:02 +0530 Subject: [PATCH 100/221] Added debug msg to TestJNDIManager --- .../platform/config/DefaultKillbillConfigSource.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index b90249946..d18ab4383 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -298,7 +298,11 @@ protected void populateDefaultProperties(final Map extraDefaultP for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties if (!hasProperty(propertyName)) { - defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); + // defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); + final String value = defaultProperties.getProperty(propertyName); + if (value != null && !value.isEmpty()) { + defaultsToAdd.put(propertyName, value); + } } } From d064ebb59b6eae70aa9a140f6ae427fe32b00c12 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 16:22:56 +0530 Subject: [PATCH 101/221] Added debug msg to TestJNDIManager --- .../test/config/TestKillbillConfigSource.java | 2 +- .../beatrix/integration/osgi/TestOSGIBase.java | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java index d9a131fb7..02ed93457 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/config/TestKillbillConfigSource.java @@ -117,7 +117,7 @@ public TestKillbillConfigSource(@Nullable final String file, @Nullable final Cla // propertiesCollector.addProperties("RuntimeConfiguration", testProperties); // invalidateCache(); - // rebuildCache(); + rebuildCache(); } private String getDriverClassName(String jdbcUrl) { diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index f7cab5dc6..5433659e9 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -19,6 +19,7 @@ package org.killbill.billing.beatrix.integration.osgi; +import java.util.Properties; import java.util.concurrent.Callable; import javax.inject.Inject; @@ -116,10 +117,16 @@ public void beforeClass() throws Exception { throw assertionError; } - System.out.println("org.killbill.dao.url = " + configSource.getString("org.killbill.dao.url")); - System.out.println("org.killbill.billing.osgi.dao.url = " + configSource.getString("org.killbill.billing.osgi.dao.url")); - System.out.println("org.killbill.dao.user = " + configSource.getString("org.killbill.dao.user")); - System.out.println("org.killbill.dao.password = " + configSource.getString("org.killbill.dao.password")); + // DEBUG: Check what properties are returned by getProperties() + System.out.println("=== DEBUG: ALL OSGI DAO PROPERTIES ==="); + Properties allProps = configSource.getProperties(); + System.out.println("Total properties: " + allProps.size()); + for (String key : allProps.stringPropertyNames()) { + if (key.contains("osgi.dao")) { + System.out.println(" " + key + " = " + allProps.getProperty(key)); + } + } + System.out.println("=== END DEBUG ==="); final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestIntegrationModule(configSource)); g.injectMembers(this); From d9a23055c1ae2665be83f82a3297dd9599d28250 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 16:33:29 +0530 Subject: [PATCH 102/221] Added debug msg to TestJNDIManager --- .../billing/beatrix/integration/osgi/TestOSGIBase.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index 5433659e9..294e37254 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -42,6 +42,7 @@ import org.killbill.bus.api.PersistentBus; import org.killbill.clock.ClockMock; import org.mockito.Mockito; +import org.skife.config.RuntimeConfigRegistry; import org.skife.jdbi.v2.IDBI; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; @@ -159,6 +160,8 @@ public void afterClass() throws Exception { if (osgiDataSource instanceof ReferenceableDataSourceSpy && ((ReferenceableDataSourceSpy) osgiDataSource).getDataSource() instanceof HikariDataSource) { ((HikariDataSource) ((ReferenceableDataSourceSpy) osgiDataSource).getDataSource()).close(); } + + RuntimeConfigRegistry.clear(); } @AfterSuite(groups = "slow") From fa7d1197980a3755c2f17d00896d042b8ecb9277 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 16:40:25 +0530 Subject: [PATCH 103/221] Added debug msg to TestJNDIManager --- .../killbill/billing/platform/jndi/TestJNDIManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index b1372ac78..cabaa3351 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -30,8 +30,10 @@ import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; +import org.skife.config.RuntimeConfigRegistry; import org.testng.Assert; import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -41,6 +43,13 @@ public class TestJNDIManager { EmbeddedDB embeddedDB; + @BeforeClass(groups = "slow") + public static void setUpClass() { + RuntimeConfigRegistry.clear(); + + DefaultKillbillConfigSource.resetForTesting(); + } + @BeforeMethod(groups = "slow") public void setUp() throws Exception { SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); From 97b9d75a75bdb9710ab960cf2bfd50e26d10ffae Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 17:00:15 +0530 Subject: [PATCH 104/221] Added debug msg to TestJNDIManager --- .../killbill/billing/beatrix/integration/osgi/TestOSGIBase.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java index 294e37254..25317220b 100644 --- a/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java +++ b/platform-test/src/test/java/org/killbill/billing/beatrix/integration/osgi/TestOSGIBase.java @@ -160,8 +160,6 @@ public void afterClass() throws Exception { if (osgiDataSource instanceof ReferenceableDataSourceSpy && ((ReferenceableDataSourceSpy) osgiDataSource).getDataSource() instanceof HikariDataSource) { ((HikariDataSource) ((ReferenceableDataSourceSpy) osgiDataSource).getDataSource()).close(); } - - RuntimeConfigRegistry.clear(); } @AfterSuite(groups = "slow") From a485083954649572086f50c69fa2a49f5fe213bf Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 18:19:49 +0530 Subject: [PATCH 105/221] Added debug msg to TestJNDIManager --- .../config/DefaultKillbillConfigSource.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java index d18ab4383..24486cfc7 100644 --- a/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java +++ b/base/src/main/java/org/killbill/billing/platform/config/DefaultKillbillConfigSource.java @@ -80,7 +80,7 @@ public class DefaultKillbillConfigSource implements KillbillConfigSource, OSGICo "RuntimeConfiguration", "KillBillDefaults")); - protected final PropertiesWithSourceCollector propertiesCollector; + private final PropertiesWithSourceCollector propertiesCollector; private volatile Map> cachedPropertiesBySource; @@ -164,7 +164,7 @@ protected void rebuildCache() { cachedPropertiesBySource = computePropertiesBySource(); } - protected void invalidateCache() { + private void invalidateCache() { synchronized (lock) { cachedPropertiesBySource = null; } @@ -298,11 +298,7 @@ protected void populateDefaultProperties(final Map extraDefaultP for (final String propertyName : defaultProperties.stringPropertyNames()) { // Let the user override these properties if (!hasProperty(propertyName)) { - // defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); - final String value = defaultProperties.getProperty(propertyName); - if (value != null && !value.isEmpty()) { - defaultsToAdd.put(propertyName, value); - } + defaultsToAdd.put(propertyName, defaultProperties.getProperty(propertyName)); } } @@ -529,12 +525,4 @@ private boolean shouldWarnAboutConflict(final List sources) { sources.contains("EnvironmentVariables") && sources.contains("RuntimeConfiguration"); } - - @VisibleForTesting - public static void resetForTesting() { - synchronized (lock) { - GMT_WARNING = NOT_SHOWN; - ENTROPY_WARNING = NOT_SHOWN; - } - } } From 4aa0a3abbe4e8c52f4b917848771b5a90a8607db Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 18:22:12 +0530 Subject: [PATCH 106/221] Added debug msg to TestJNDIManager --- .../billing/platform/jndi/TestJNDIManager.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java index cabaa3351..3292508b1 100644 --- a/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java +++ b/base/src/test/java/org/killbill/billing/platform/jndi/TestJNDIManager.java @@ -27,13 +27,10 @@ import javax.sql.DataSource; import org.h2.jdbcx.JdbcDataSource; -import org.killbill.billing.platform.config.DefaultKillbillConfigSource; import org.killbill.commons.embeddeddb.EmbeddedDB; import org.killbill.commons.embeddeddb.h2.H2EmbeddedDB; -import org.skife.config.RuntimeConfigRegistry; import org.testng.Assert; import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -43,13 +40,6 @@ public class TestJNDIManager { EmbeddedDB embeddedDB; - @BeforeClass(groups = "slow") - public static void setUpClass() { - RuntimeConfigRegistry.clear(); - - DefaultKillbillConfigSource.resetForTesting(); - } - @BeforeMethod(groups = "slow") public void setUp() throws Exception { SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); @@ -63,7 +53,6 @@ public void setUp() throws Exception { @AfterMethod(groups = "slow") public void tearDown() throws Exception { embeddedDB.stop(); - DefaultKillbillConfigSource.resetForTesting(); } @Test(groups = "slow") From 2452b06aeac15cb98b58d9e2dce58298e66dc211 Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 18:53:08 +0530 Subject: [PATCH 107/221] Added debug msg to TestJNDIManager --- .../billing/osgi/bundles/test/TestActivator.java | 4 ---- .../billing/osgi/bundles/test/dao/TestDao.java | 12 ------------ .../platform/test/PlatformDBTestingHelper.java | 8 -------- 3 files changed, 24 deletions(-) diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java index 7bba72585..5ed377660 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/TestActivator.java @@ -61,10 +61,6 @@ public class TestActivator extends KillbillActivatorBase implements OSGIKillbill public void start(final BundleContext context) throws Exception { super.start(context); - logger.info("OSGI bundle sees org.killbill.billing.osgi.dao.url = {}", - System.getProperty("org.killbill.billing.osgi.dao.url")); - - final String bundleName = context.getBundle().getSymbolicName(); logger.info("TestActivator: starting bundle = {}", bundleName); diff --git a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java index 0fa380681..97bd0fcc3 100644 --- a/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java +++ b/osgi-bundles/tests/beatrix/src/main/java/org/killbill/billing/osgi/bundles/test/dao/TestDao.java @@ -26,13 +26,9 @@ import org.skife.jdbi.v2.IDBI; import org.skife.jdbi.v2.TransactionCallback; import org.skife.jdbi.v2.TransactionStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class TestDao { - private static final Logger logger = LoggerFactory.getLogger(TestDao.class); - private final IDBI dbi; public TestDao(final IDBI dbi) { @@ -40,17 +36,9 @@ public TestDao(final IDBI dbi) { } public void createTable() { - logger.info("=== TestDao.createTable() ==="); - logger.info("About to execute SQL with SERIAL data type"); - logger.info("System property org.killbill.billing.osgi.dao.url = {}", System.getProperty("org.killbill.billing.osgi.dao.url")); - dbi.inTransaction(new TransactionCallback() { @Override public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception { - logger.info("Connection class: {}", conn.getConnection().getClass().getName()); - logger.info("Connection URL: {}", conn.getConnection().getMetaData().getURL()); - - conn.execute("DROP TABLE IF EXISTS test_bundle;"); conn.execute("CREATE TABLE test_bundle (" + "record_id serial unique, " + diff --git a/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java b/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java index f6ee6ecb1..d3e0bc1d0 100644 --- a/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java +++ b/platform-test/src/main/java/org/killbill/billing/platform/test/PlatformDBTestingHelper.java @@ -42,8 +42,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sf.log4jdbc.log.SpyLogFactory; - public class PlatformDBTestingHelper { private static final Logger log = LoggerFactory.getLogger(PlatformDBTestingHelper.class); @@ -108,12 +106,6 @@ public DataSource getDataSource() { } public synchronized void start() throws IOException, SQLException { - try { - SpyLogFactory.loadSpyLogDelegator("net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator"); - } catch (final Exception e) { - log.debug("SpyLogFactory already initialized or not needed", e); - } - instance.initialize(); instance.start(); From 7f5fc6fc2741b32e762e9c7583737c59e49d70db Mon Sep 17 00:00:00 2001 From: Vijay N Date: Sun, 16 Nov 2025 19:06:05 +0530 Subject: [PATCH 108/221] Added debug msg to TestJNDIManager --- .idea/misc.xml | 1 + .../test/config/TestKillbillConfigSource.java | 75 +++---------------- 2 files changed, 12 insertions(+), 64 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index f4a499a6b..0eeb962ff 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -18,6 +18,7 @@ +