Skip to content
Draft
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ Make sure to set the `$JAVA_HOME` environment variable.
* If on Windows, a Powershell version >= 7.1.3 installed that you can download from the below link.
[Powershell](https://github.com/PowerShell/PowerShell)

## Configuration system
A new configuration system has been implemented.
If you want to set up another database than H2 (Highly recommended for anything else than sandbox usage),
you can do it easily by creating a .conf file.
You can find examples in `framework/entity/config/samples`.
Define a delegator and a datasource, and you're all set.

Please note the following rules:
- The config files follow the [Hocon Syntax](https://learnxinyminutes.com/hocon/), a Json superset, and uses TypeSafe
implementation by default. Hocon is able to use environment variables.
- You can disable this behavior by setting 'ofbiz.config.overload.enable' in `start.properties` to false.
- This will allow you to use the vanilla Xml-only config system.
- **All files** with .conf extension and located in the `config` folder of a plugin or framework component will be read.
- You can pass files names as launching parameters with `-c` argument.
- Example for local developement : `./gradlew ofbiz --args="-c file=/tmp/myfile.conf"`
- Example for binary args (prod) : `ofbiz -c file=myfile.conf`'
- Warning : Files loaded in this way will be prioritized over files that are not specified.
- This means that if `myfile.conf` (passed as startup parameter) and `myOtherFile.conf` (in a plugin) both define a `foo` parameters,
it is the value in `myfile.conf` that will be used.
- For now, you can use this system to override entitygine.xml configs, and serviceengine.xml configs.

<!-- tag: quickstart -->
## Quick start
Expand Down
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ dependencies {
compileOnly project(path: subProject.path, configuration: 'pluginLibsCompileOnly')
}

implementation 'com.typesafe:config:1.4.3'

junitReport 'junit:junit:4.13.2'
junitReport 'org.apache.ant:ant-junit:1.10.15'

Expand Down
4 changes: 3 additions & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation 'com.sun.mail:javax.mail:1.6.2'
implementation 'com.rometools:rome:2.1.0'
implementation 'com.thoughtworks.xstream:xstream:1.4.21'
implementation 'com.typesafe:config:1.4.3'
implementation 'commons-cli:commons-cli:1.11.0'
implementation 'commons-net:commons-net:3.13.0'
implementation 'commons-validator:commons-validator:1.10.1'
Expand Down Expand Up @@ -68,7 +69,8 @@ dependencies {
implementation 'org.apache.xmlgraphics:batik-bridge:1.19'
implementation 'org.apache.xmlgraphics:fop:2.11'
implementation 'org.clojure:clojure:1.12.4'
implementation 'org.apache.groovy:groovy-all:5.0.5'
// implementation 'org.apache.groovy:groovy-all:5.0.5'
implementation 'org.apache.groovy:groovy-all:6.0.0-alpha-1'
implementation 'org.freemarker:freemarker:2.3.34' // Remember to change the version number in FreeMarkerWorker class when upgrading. See OFBIZ-10019 if >= 2.4
implementation 'org.owasp.esapi:esapi:2.7.0.0'
implementation 'org.springframework:spring-test:6.2.18'
Expand Down
2 changes: 2 additions & 0 deletions framework/base/ofbiz-component.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ under the License.

<test-suite loader="main" location="testdef/basetests.xml"/>

<!-- load the configuration overload system -->
<container name="config-container" loaders="main,load-data,test" class="org.apache.ofbiz.base.container.ConfigContainer"/>
<!-- load the naming (JNDI) server -->
<container name="naming-container" loaders="rmi" class="org.apache.ofbiz.base.container.NamingServiceContainer">
<property name="host" value="0.0.0.0"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.ofbiz.base.config;

import org.apache.ofbiz.entity.GenericEntityConfException;
import org.w3c.dom.Element;

import java.util.Map;


/**
* Abstract class that must be implemented by every class wishing to use the centralized configuration reader tool.
* Each class implementing this interface must :
* <ul>
* <li>Possess a {@code public static final String ELEMENT_NAME} containing the name of the XML element to which
* the class refers to. Often the tag name. For example for the ConnectionFactory class, it will be "connection-factory"</li>
* <li>If needed a {@code public static final String ELEMENT_FIELD_ID_NAME} containing the attribut name of the XML element
* that identify as unique the element. Else the {@code name} string will be used</li>
* <li>Have a first constructor with an {@code Element} and a {@code String} as parameters. It will be used to
* construct the object from composite source.</li>
* <li>Have a second constructor with a {@code Map} and a {@code String} as parameters. It will be used to
* construct the object from overloaded configs.</li>
* <li>Implement {@link AbstractConfigElement#loadFromXml(Element, String)} by calling the constructor with {@link Element}</li>
* <li>Implement {@link AbstractConfigElement#loadFromConfig(Map, String)} by calling the constructor with {@link Map}</li>
* </ul>
*/
public abstract class AbstractConfigElement {

/**
* Must invoke the constructor reading from an XML file
*
* @param element the {@link Element} represented by the class
* @param xPathParent the {@link javax.xml.xpath.XPath} of the parent element class invoking this method
* @return an instance of the class from which it was called from
* @throws GenericEntityConfException
*/
static AbstractConfigElement loadFromXml(Element element, String xPathParent) throws GenericEntityConfException {
return null;
}

/**
* Invoke the constructor reading from a config overload file
*
* @param configMap a Map representing the element to create, likely coming from the Config object.
* @param xPath the {@link javax.xml.xpath.XPath} to this element in the configuration file
* @return an instance of the class from which it was called from
* @throws GenericEntityConfException
*/
static AbstractConfigElement loadFromConfig(Map<String, Object> configMap, String xPath) throws GenericEntityConfException {
return null;
}

/**
* Return the name of the element, if the class doesn't possess a name, a unique attribute not shared among other element will be returned
*
* @return a unique String to identify the element
*/
public abstract String getName();

/**
* Retrieve the name of the last element in the xPath, this name can be :
* <ul>
* <li>Either the last chain of characters at the end of the xPath (for example "foo/bar" will find bar)</li>
* <li>Or the unique identifier searched in the xPath (for example "foo/bar[@name='baz']" will return baz</li>
* </ul>
*
* @param xPath the path from which to find the name of the last element
* @return the name of the last element in the xPath
*/
public static String getNameFromXPath(String xPath) {
String lastMember = xPath.substring(xPath.lastIndexOf("/") + 1);
int firstSimpleQuote = lastMember.indexOf('\'');
int lastSimpleQuote = lastMember.lastIndexOf('\'');
if (firstSimpleQuote != -1 && lastSimpleQuote != -1) {
lastMember = lastMember.substring(firstSimpleQuote + 1, lastSimpleQuote);
}
return lastMember.intern();
}

/**
* Override if the element is a list of elements like run-from-pool list.
* This will make the config system ignore Xml implementations if config overloads are presents.
*
* @return if the config system allows multiple sources and should merge those sources
*/
public boolean allowMultipleSources() {
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.ofbiz.base.config;

import java.util.List;
import java.util.Map;

import org.w3c.dom.Element;

/**
* Abstract class that mustn't be instantiated.
* Instead, it must be extended for each resource file, such as entityengine or serviceengine.
* The child ConfigGetters define the resource name, and the root xPath
*/
public abstract class AbstractXmlConfigGetter {
private final String ressource;
private final String rootXPath;
private final ConfigurationInterface config;

protected AbstractXmlConfigGetter(String ressource, String rootXPath) {
this.ressource = ressource;
this.rootXPath = rootXPath;
config = ConfigurationFactory.getInstance();
}

/**
* @param key the key of the value that is looked up for.
* @return the value that is found
*/
public String getValue(String key) {
return config.getValue(ressource, key);
}

/**
* See {@link AbstractXmlConfigGetter#getValue(Map, String, Object, Class)}
*/
public String getValue(Map<String, Object> configObject, String key) {
return getValue(configObject, key, "", String.class);
}

/**
* Get a config value in the {@code configObject} if it has the key, else search the config system.
*
* @param configObject a Map coming from the config system.
* @param key the key of the value that is looked up for.
* @param defaultValue the value return if not found
* @param targetClass the output class of the looked up element.
* @return the single value found for {@code key}
*/
public <T> T getValue(Map<String, Object> configObject, String key, T defaultValue, Class<T> targetClass) {
if (configObject == null) {
return getValue(key, defaultValue, targetClass);
}
return config.getValue(configObject, key, targetClass, defaultValue);
}

/**
* Get a config value in the config system.
*
* @param key the key of the value that is looked up for.
* @param defaultValue the value return if not found
* @param targetClass the output class of the looked up element.
* @return the single value found for {@code key}
*/
public <T> T getValue(String key, T defaultValue, Class<T> targetClass) {
return config.getValue(ressource, key, targetClass, defaultValue);
}

/**
* See {@link AbstractXmlConfigGetter#getObjectSubElement(String, Element, Class)}
*/
public <T> T getObjectSubElement(Element parent, Class<T> targetClass) {
return getObjectSubElement(rootXPath, parent, targetClass);
}

/**
* Get a config value in the config system.
*
* @param parentXPath the xPath that serves as root for the wanted config element
* @param parent the parent element to look into
* @param targetClass the output class of the looked up element.
* @return the single value found for {@code xPath} in {@code parent}
*/
public <T> T getObjectSubElement(String parentXPath, Element parent, Class<T> targetClass) {
return config.getConfigElementAsObjectOfClass(ressource, parentXPath, parent, targetClass);
}

/**
* See {@link AbstractXmlConfigGetter#getSubElementsAsListEntries(String, Element, Class)}
*/
public <T> List<T> getSubElementsAsListEntries(Element parent, Class<T> targetClass) {
return getSubElementsAsListEntries(rootXPath, parent, targetClass);
}

/**
* Get a config List in the config system.
*
* @param parentXPath the xPath that serves as root for the wanted config element
* @param parent the parent element to look into
* @param targetClass the output class of the looked up element.
* @return a list of {@code targetClass} found in {@code parent}
*/
public <T> List<T> getSubElementsAsListEntries(String parentXPath, Element parent, Class<T> targetClass) {
return config.getConfigElementsAsListEntriesOfClass(ressource, parentXPath, parent, targetClass);
}

/**
* See {@link AbstractXmlConfigGetter#getSubElementsAsMapValues(String, Element, Class)}
*/
public <T> Map<String, T> getSubElementsAsMapValues(Element parent, Class<T> targetClass) {
return getSubElementsAsMapValues(rootXPath, parent, targetClass);
}

/**
* Get a config Map from the config system.
*
* @param parentXPath the xPath that serves as root for the wanted config element
* @param parent the parent element to look into
* @param targetClass the output class of the looked up element.
* @return a Map of {@code targetClass} found in {@code parent}. The element names serves as key.
*/
public <T> Map<String, T> getSubElementsAsMapValues(String parentXPath, Element parent, Class<T> targetClass) {
return config.getConfigElementsAsMapValuesOfClass(ressource, parentXPath, parent, targetClass);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.ofbiz.base.config;

import static org.apache.ofbiz.base.util.UtilProperties.getPropertyAsBoolean;

/**
* Utility class, maily for test and Mockito purposes
*/
public final class ConfigHelper {

/**
* Small utility method, mainly for testing purposes
*/
public static boolean checkStrictXmlStructure() {
return getPropertyAsBoolean("configuration.properties", "ofbiz.config.xml.check.strict", true);
}
}
Loading
Loading