Skip to content

XCOMMONS-3289: Allow overwritting xwiki.properties and xwiki.cfg properties via environment variable and Java system properties #1315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions xwiki-commons-core/xwiki-commons-configuration/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<description>XWiki Commons - Configuration - Parent POM</description>
<modules>
<module>xwiki-commons-configuration-api</module>
<module>xwiki-commons-configuration-default</module>
</modules>
</project>

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Map;

import org.xwiki.component.annotation.Role;
import org.xwiki.stability.Unstable;

/**
* @version $Id$
Expand Down Expand Up @@ -66,9 +67,9 @@ default <T> T getProperty(String key, Class<T> valueClass, T defaultValue)
{
if (containsKey(key)) {
return getProperty(key, valueClass);
} else {
return getProperty(key, defaultValue);
}

return defaultValue;
}

/**
Expand All @@ -84,6 +85,17 @@ default <T> T getProperty(String key, Class<T> valueClass, T defaultValue)
*/
List<String> getKeys();

/**
* @param prefix the prefix to filter the keys
* @return the list of available keys in the configuration source that start with the passed prefix
* @since 17.5.0RC1
*/
@Unstable
default List<String> getKeys(String prefix)
{
return getKeys().stream().filter(key -> key.startsWith(prefix)).toList();
}

/**
* @param key the key to check
* @return true if the key is present in the configuration source or false otherwise
Expand All @@ -95,6 +107,17 @@ default <T> T getProperty(String key, Class<T> valueClass, T defaultValue)
*/
boolean isEmpty();

/**
* @param prefix the prefix to filter the keys
* @return true if the configuration source doesn't have any key or false otherwise
* @since 17.5.0RC1
*/
@Unstable
default boolean isEmpty(String prefix)
{
return getKeys(prefix).isEmpty();
}

/**
* Set a property, this will replace any previously set values.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.configuration.internal;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.xwiki.configuration.ConfigurationSource;

/**
* Base class for composing (aka chaining) several Configuration Sources. The order of sources is important. Sources
* located before other sources take priority.
*
* @version $Id$
* @since 7.4M1
Copy link
Member Author

@tmortagne tmortagne Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version may look a bit strange, but it's actually a move from platform.

*/
public abstract class AbstractCompositeConfigurationSource extends AbstractConfigurationSource
implements Iterable<ConfigurationSource>
{
@Override
public boolean containsKey(String key)
{
boolean result = false;

for (ConfigurationSource source : this) {
if (source.containsKey(key)) {
result = true;
break;
}
}

return result;
}

@Override
public <T> T getProperty(String key)
{
T result = null;

for (ConfigurationSource source : this) {
if (source.containsKey(key)) {
result = source.<T>getProperty(key);
break;
}
}

return result;
}

@Override
public <T> T getProperty(String key, Class<T> valueClass)
{
T result = null;

for (ConfigurationSource source : this) {
if (source.containsKey(key)) {
result = source.getProperty(key, valueClass);
break;
}
}

// List and Properties must return empty collections and not null values.
if (result == null) {
result = getDefault(valueClass);
}

return result;
}

@Override
public <T> T getProperty(String key, T defaultValue)
{
T result = null;

for (ConfigurationSource source : this) {
if (source.containsKey(key)) {
result = source.<T>getProperty(key, defaultValue);
break;
}
}

if (result == null) {
result = defaultValue;
}

return result;
}

@Override
public List<String> getKeys()
{
// We use a linked hash set in order to keep the keys in the order in which they were defined in the sources.
Set<String> keys = new LinkedHashSet<>();

for (ConfigurationSource source : this) {
keys.addAll(source.getKeys());
}

return new ArrayList<>(keys);
}

@Override
public List<String> getKeys(String prefix)
{
// We use a linked hash set in order to keep the keys in the order in which they were defined in the sources.
Set<String> keys = new LinkedHashSet<>();

for (ConfigurationSource source : this) {
keys.addAll(source.getKeys(prefix));
}

return new ArrayList<>(keys);
}

@Override
public boolean isEmpty()
{
for (ConfigurationSource source : this) {
if (!source.isEmpty()) {
return false;
}
}

return true;
}

@Override
public boolean isEmpty(String prefix)
{
for (ConfigurationSource source : this) {
if (!source.isEmpty(prefix)) {
return false;
}
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.configuration.internal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.xwiki.configuration.ConfigurationSource;

/**
* Allows composing (aka chaining) several Configuration Sources. The order of sources is important. Sources located
* before other sources take priority.
*
* @version $Id$
* @since 2.0M1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version may look a bit strange, but it's actually a move from platform.

*/
public class CompositeConfigurationSource extends AbstractCompositeConfigurationSource
{
/**
* The order of sources is important. Sources located before other sources take priority.
*/
protected List<ConfigurationSource> sources = new ArrayList<>();

/**
* @param source the source to add to the list of sources
*/
public void addConfigurationSource(ConfigurationSource source)
{
this.sources.add(source);
}

@Override
public Iterator<ConfigurationSource> iterator()
{
return this.sources.iterator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ public List<String> getKeys()
return executeRead(() -> getWrappedConfigurationSource().getKeys());
}

@Override
public List<String> getKeys(String prefix)
{
return executeRead(() -> getWrappedConfigurationSource().getKeys(prefix));
}

@Override
public boolean containsKey(String key)
{
Expand All @@ -102,6 +108,12 @@ public boolean isEmpty()
return executeRead(() -> getWrappedConfigurationSource().isEmpty());
}

@Override
public boolean isEmpty(String prefix)
{
return executeRead(() -> getWrappedConfigurationSource().isEmpty(prefix));
}

@Override
public void setProperties(Map<String, Object> properties) throws ConfigurationSaveException
{
Expand Down
Loading