diff --git a/README.md b/README.md index da8dd3c0ae9..88dc3252276 100644 --- a/README.md +++ b/README.md @@ -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. ## Quick start diff --git a/build.gradle b/build.gradle index 10b13996637..1276dd0fd9c 100644 --- a/build.gradle +++ b/build.gradle @@ -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' diff --git a/dependencies.gradle b/dependencies.gradle index b890c4dac12..3e458ae90ee 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -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' @@ -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' diff --git a/framework/base/ofbiz-component.xml b/framework/base/ofbiz-component.xml index 5fceb1cad08..5592732b61c 100644 --- a/framework/base/ofbiz-component.xml +++ b/framework/base/ofbiz-component.xml @@ -26,6 +26,8 @@ under the License. + + diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/AbstractConfigElement.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/AbstractConfigElement.java new file mode 100644 index 00000000000..0138f2d2cb5 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/AbstractConfigElement.java @@ -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 : + *
    + *
  • 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"
  • + *
  • 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
  • + *
  • 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.
  • + *
  • 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.
  • + *
  • Implement {@link AbstractConfigElement#loadFromXml(Element, String)} by calling the constructor with {@link Element}
  • + *
  • Implement {@link AbstractConfigElement#loadFromConfig(Map, String)} by calling the constructor with {@link Map}
  • + *
+ */ +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 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 : + *
    + *
  • Either the last chain of characters at the end of the xPath (for example "foo/bar" will find bar)
  • + *
  • Or the unique identifier searched in the xPath (for example "foo/bar[@name='baz']" will return baz
  • + *
+ * + * @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; + } + +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/AbstractXmlConfigGetter.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/AbstractXmlConfigGetter.java new file mode 100644 index 00000000000..84561f63790 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/AbstractXmlConfigGetter.java @@ -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 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 getValue(Map configObject, String key, T defaultValue, Class 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 getValue(String key, T defaultValue, Class targetClass) { + return config.getValue(ressource, key, targetClass, defaultValue); + } + + /** + * See {@link AbstractXmlConfigGetter#getObjectSubElement(String, Element, Class)} + */ + public T getObjectSubElement(Element parent, Class 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 getObjectSubElement(String parentXPath, Element parent, Class targetClass) { + return config.getConfigElementAsObjectOfClass(ressource, parentXPath, parent, targetClass); + } + + /** + * See {@link AbstractXmlConfigGetter#getSubElementsAsListEntries(String, Element, Class)} + */ + public List getSubElementsAsListEntries(Element parent, Class 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 List getSubElementsAsListEntries(String parentXPath, Element parent, Class targetClass) { + return config.getConfigElementsAsListEntriesOfClass(ressource, parentXPath, parent, targetClass); + } + + /** + * See {@link AbstractXmlConfigGetter#getSubElementsAsMapValues(String, Element, Class)} + */ + public Map getSubElementsAsMapValues(Element parent, Class 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 Map getSubElementsAsMapValues(String parentXPath, Element parent, Class targetClass) { + return config.getConfigElementsAsMapValuesOfClass(ressource, parentXPath, parent, targetClass); + } + +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigHelper.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigHelper.java new file mode 100644 index 00000000000..845f7b321fa --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigHelper.java @@ -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); + } +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationFactory.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationFactory.java new file mode 100644 index 00000000000..625595846a5 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationFactory.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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.io.File; +import java.util.List; + +/** + * A {@link ConfigurationInterface} factory. + */ +public class ConfigurationFactory { + + private static ConfigurationInterface configInstance = null; + + /** + * Gets {@code configInstance}. Create an empty one if needed. + * + * @return the instance of {@code configInstance} + */ + public static ConfigurationInterface getInstance() { + return getInstance(List.of()); + } + + /** + * Gets {@code configInstance}. Create an new one if needed by loading file at {@code configPath} + * + * @param configPath the override configuration file + * @return the instance of {@code configInstance} + */ + public static ConfigurationInterface getInstance(File configPath) { + if (configInstance == null) { + synchronized (ConfigurationFactory.class) { + configInstance = configPath == null + ? new DefaultConfiguration() + : new DefaultConfiguration(configPath); + } + } + return configInstance; + } + + /** + * Gets {@code configInstance}. Create a new one if needed by loading all files in {@code configPaths} + * + * @param configPaths list of override configuration files + * @return the instance of {@code configInstance} + */ + public static ConfigurationInterface getInstance(List configPaths) { + if (configInstance == null) { + synchronized (ConfigurationFactory.class) { + configInstance = new DefaultConfiguration(configPaths); + } + } + return configInstance; + } + + /** + * @return resets and returns a new empty {@code configInstance} + */ + public static ConfigurationInterface resetAndGet() { + return resetAndGet(List.of()); + } + + /** + * @return resets and returns a new empty {@code configInstance} + */ + public static ConfigurationInterface resetAndGet(List configPaths) { + configInstance = null; + return getInstance(configPaths); + } + +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationInterface.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationInterface.java new file mode 100644 index 00000000000..e1c2cf6c7dd --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationInterface.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * 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; + +/** + * Main interface for configuration overload system. + */ +public interface ConfigurationInterface { + + /** + * See {@link ConfigurationInterface#getValue(String, String, Class, Object)} + */ + String getValue(String resourceName, String key); + + /** + * See {@link ConfigurationInterface#getValue(String, String, Class, Object)} + */ + String getValue(String resourceName, String key, String defaultValue); + + /** + * See {@link ConfigurationInterface#getValue(String, String, Class, Object)} + */ + T getValue(String resourceName, String key, Class targetClass); + + /** + * Retrieve the value contained in {@code resourceName} using {@code key} + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine') + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @param targetClass the output class of the looked up element. + * @param defaultValue the value to return if no value was found for the given key. + * @return the value contained in the override file if any is found.
+ * Else, returns the value at {@code key} in {@code resourceName}
+ * Else return {@code defaultValue} + */ + T getValue(String resourceName, String key, Class targetClass, T defaultValue); + + /** + * Retrieve the value contained in resourceName using key + * + * @param configObject an object representation of an XML element. Likely comes from an overload config file. + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @param targetClass the output class of the looked up element. + * @param defaultValue the value to return if no value was found for the given key. + * @return the value contained in the override file if any is found.
+ * Else, returns the value at {@code key} in {@code resourceName}
+ * Else return {@code defaultValue} + */ + T getValue(Map configObject, String key, Class targetClass, T defaultValue); + + /** + * Get the attribute {@code useOverride} + * + * @return the attribute indicating if the overload config system should be enabled + */ + boolean getUseOverrideValue(); + + /** + * Set the attribute {@code useOverride} + * + * @param useOverride set the parameter indicating if the overload config system should be enabled + */ + void setUseOverrideValue(boolean useOverride); + + /** + * Clears the configuration cache + */ + void clearCache(); + + /** + * Constructs and / or gets an inheritor of {@link AbstractConfigElement}. All the configurations will be read and used. + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine') + * @param xPath the xPath of the wanted value + * @param parent the {@code Element} from which to retrieve the child, must be {@code null} when creating a new element using a conf file + * @param targetClass the class of the child object that will be created from {@code parent} + * @return an instance of {@code targetClass} or {@code null} if no element was found + */ + T getConfigElementAsObjectOfClass(String resourceName, String xPath, Object parent, Class targetClass); + + /** + * Retrieve one or more XML elements and create a list of objects out of them using a class specified in parameter + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine') + * @param xPath the xPath of the wanted value + * @param parent the {@code Element} from which to retrieve the child, must be {@code null} when creating a new element using a conf file + * @param targetClass the class of the child object that will be created from {@code parent} + * @return a list of objects of targetClass or an empty list no elements were found + * @return a List of {@code targetClass} or an empty List if no element was found + */ + List getConfigElementsAsListEntriesOfClass(String resourceName, String xPath, Object parent, Class targetClass); + + /** + * Retrieve one or more XML elements and create a map of objects paired with their name (or unique attribute) out of + * them using a class specified in parameter + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine') + * @param xPath the xPath of the wanted value + * @param parent the {@code Element} from which to retrieve the child, must be {@code null} when creating a new element using a conf file + * @param targetClass the class of the child object that will be created from {@code parent} + * @return a Map of objects of {@code targetClass}, with their names as keys, or an empty Map if no element was found + */ + Map getConfigElementsAsMapValuesOfClass(String resourceName, String xPath, Object parent, Class targetClass); +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationReaderInterface.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationReaderInterface.java new file mode 100644 index 00000000000..ec55b020565 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/ConfigurationReaderInterface.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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; + +/** + * Interface for classes that read configuration. + * Each implementation handles a specific configuration type. + */ +public interface ConfigurationReaderInterface { + + /** + * Inits a reader from a resource file. + * + * @param resourceName the XML, Java properties, JSON (or HOCON) file to load + */ + void init(String resourceName); + + /** + * Retrieve the value from reader at {@code key} + * + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @return a {@code String} representation of the value contained in the key or {@code null} if it was not found + */ + String getValue(String key); + + /** + * Retrieves an inheritor of {@link AbstractConfigElement} representing a configuration element of the wanted class. + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @param parent the parent that will be searched for the wanted elements. Can be an {@link org.w3c.dom.Element} in case of xml representation + * or a {@link java.util.Map} in case of configuration overload. + * @param targetClass the class of the element that is lookup up. + * @return an instance of {@code targetClass} or {@code null} if the element wasn't found + */ + T findSingleElementWithClassInParent(String key, Object parent, Class targetClass); + + /** + * Retrieves an inheritor List of {@link AbstractConfigElement} representing a list of configuration elements of the wanted class. + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @param parent the parent that will be searched for the wanted elements. Can be an {@link org.w3c.dom.Element} in case of xml representation + * or a {@link java.util.Map} in case of configuration overload. + * @param targetClass the class of the element that is lookup up. + * @return a List of {@code targetClass} or an empty List if the element wasn't found + */ + List findElementsWithTypeInParent(String key, Object parent, Class targetClass); + + /** + * Clears the cache of the reader. + */ + void clearCache(); +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/DefaultConfiguration.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/DefaultConfiguration.java new file mode 100644 index 00000000000..dd11d102dbc --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/DefaultConfiguration.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * 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.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FilenameUtils; +import org.apache.ofbiz.base.conversion.ConversionException; +import org.apache.ofbiz.base.conversion.Converters; +import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.UtilGenerics; +import org.apache.ofbiz.base.util.cache.UtilCache; + +import javax.xml.xpath.XPathExpressionException; + +public class DefaultConfiguration implements ConfigurationInterface { + public static final String MODULE = DefaultConfiguration.class.getName(); + private static final UtilCache CONFIGURATION_TYPE = UtilCache.createUtilCache("base.config.type", 0, 0); + private final TypesafeConfigImplReader typesafeConfReader; + private boolean useOverrideValue = true; + + public DefaultConfiguration() { + typesafeConfReader = new TypesafeConfigImplReader(); + } + + public DefaultConfiguration(File configPath) { + typesafeConfReader = new TypesafeConfigImplReader(configPath); + } + + public DefaultConfiguration(List configPaths) { + typesafeConfReader = new TypesafeConfigImplReader(configPaths); + } + + @Override + public String getValue(String resourceName, String key) { + return getValue(resourceName, key, String.class, ""); + } + + @Override + public String getValue(String resourceName, String key, String defaultValue) { + return getValue(resourceName, key, String.class, defaultValue); + } + + @Override + public T getValue(String resourceName, String key, Class targetClass) { + return getValue(resourceName, key, targetClass, null); + } + + @Override + public T getValue(String resourceName, String key, Class targetClass, T defaultValue) { + String value = null; + if (getUseOverrideValue()) { + value = getValueFromOverride(resourceName, key); + } + if (value == null) { + value = getRelevantReader(resourceName).getValue(key); + } + if (targetClass == String.class) { + return value == null + ? defaultValue + : UtilGenerics.cast(value); + } + try { + return value == null + ? defaultValue + : Converters.getConverter(String.class, targetClass).convert(value); + } catch (ClassNotFoundException | ConversionException e) { + Debug.logError(String.format("Try to convert a String %s to %s with error %s", value, targetClass, e), MODULE); + } + return null; + } + + @Override + public T getValue(Map configObject, String key, Class targetClass, T defaultValue) { + Object value = null; + try { + value = typesafeConfReader.getValue(configObject, TypesafeConfigImplReader.convertToConfigPath(key)); + } catch (XPathExpressionException e) { + Debug.logError("Error getting value from typesafe configs", MODULE); + } + if (targetClass == String.class) { + return value == null ? defaultValue : UtilGenerics.cast(value); + } + try { + return value == null + ? defaultValue + : Converters.getConverter(String.class, targetClass).convert(value.toString()); + } catch (ClassNotFoundException | ConversionException e) { + Debug.logError(String.format("Try to convert a String %s to %s with error %s", value, targetClass, e), MODULE); + } + return null; + } + + /** + * Retrieve the value contained in {@code resourceName} at {@code key} in the override reader. + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine') + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @return the value contained by the key or {@code null} if it was not found + */ + private String getValueFromOverride(String resourceName, String key) { + return typesafeConfReader.getValue(resourceName, key); + } + + /** + * Find the correct type of config reader implementation to use given a resource file + * + * @param resourceName the XML or Java properties file to read + * @return the appropriate implementation of {@code ConfigurationReaderInterface} depending on the extension of {@code resourceName} + */ + private ConfigurationReaderInterface getRelevantReader(String resourceName) { + ConfigurationReaderInterface readerToUse = CONFIGURATION_TYPE.get(resourceName); + if (readerToUse != null) { + return readerToUse; + } + String ext = FilenameUtils.getExtension(resourceName); + readerToUse = switch (ext) { + case "xml" -> new XmlConfigurationReader(resourceName); + default -> new PropertiesConfigurationReader(resourceName); + }; + CONFIGURATION_TYPE.put(resourceName, readerToUse); + return readerToUse; + } + + /** + * return true if we need to return the override value or origin value + * + * @return true is we override values + */ + public boolean getUseOverrideValue() { + return useOverrideValue; //use for test + } + + /** + * Set true if we want override values + * + * @param useOverrideValue parameter indicating if the class should override its configuration (true by default) + */ + public void setUseOverrideValue(boolean useOverrideValue) { + this.useOverrideValue = useOverrideValue; //use for test + } + + /** + * Empty all config cache + */ + public void clearCache() { + CONFIGURATION_TYPE.clear(); + typesafeConfReader.clearCache(); + } + + @Override + public T getConfigElementAsObjectOfClass(String resourceName, String xPath, Object parent, Class targetClass) { + return getRelevantReader(resourceName).findSingleElementWithClassInParent(xPath, parent, targetClass); + } + + @Override + public List getConfigElementsAsListEntriesOfClass(String resourceName, String xPath, Object parent, Class targetClass) { + return typesafeConfReader.collectElementsAsListEntriesWithConfigsApplied(resourceName, xPath, targetClass, + getRelevantReader(resourceName).findElementsWithTypeInParent(xPath, parent, targetClass)); + } + + @Override + public Map getConfigElementsAsMapValuesOfClass(String resourceName, String xPath, Object parent, Class targetClass) { + List childList = getConfigElementsAsListEntriesOfClass(resourceName, xPath, parent, targetClass); + Map uniqueMap = new LinkedHashMap<>(); + try { + Method method = targetClass.getMethod("getName"); + for (T value : childList) { + uniqueMap.put((String) method.invoke(value), value); + } + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + uniqueMap.putAll(typesafeConfReader.collectElementsAsMapValuesWithConfigsApplied(resourceName, xPath, targetClass, uniqueMap)); + return uniqueMap; + } +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/PropertiesConfigurationReader.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/PropertiesConfigurationReader.java new file mode 100644 index 00000000000..c39693c5120 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/PropertiesConfigurationReader.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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.Properties; +import org.apache.ofbiz.base.util.UtilProperties; + +public class PropertiesConfigurationReader implements ConfigurationReaderInterface { + private Properties props; + + PropertiesConfigurationReader(String resourceName) { + init(resourceName); + } + + @Override + public void init(String resourceName) { + Properties vanilla = UtilProperties.getProperties(resourceName); + props = vanilla != null ? vanilla : new Properties(); + } + + @Override + public String getValue(String key) { + String value = props.getProperty(key); + return value == null ? "" : value.intern().trim(); + } + + @Override + public T findSingleElementWithClassInParent(String key, Object parent, Class targetClass) { + return null; + } + + @Override + public List findElementsWithTypeInParent(String key, Object parent, Class targetClass) { + return List.of(); + } + + @Override + public void clearCache() { + } +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/TypesafeConfigImplReader.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/TypesafeConfigImplReader.java new file mode 100644 index 00000000000..97c8a311121 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/TypesafeConfigImplReader.java @@ -0,0 +1,387 @@ +/******************************************************************************* + * 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 com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.ConfigObject; +import com.typesafe.config.ConfigValue; +import com.typesafe.config.ConfigValueType; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.regex.MatchResult; +import java.util.regex.Pattern; + +import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.UtilGenerics; +import org.apache.ofbiz.base.util.UtilMisc; +import org.apache.ofbiz.base.util.UtilValidate; +import org.apache.ofbiz.base.util.cache.UtilCache; + +import javax.xml.xpath.XPathExpressionException; + +public class TypesafeConfigImplReader implements ConfigurationReaderInterface { + private static final String MODULE = TypesafeConfigImplReader.class.getName(); + private Config globalConfig = ConfigFactory.empty(); + private static final UtilCache CONFIGURATION_CACHE = UtilCache.createUtilCache("base.config", 0, 0); + private static final int MAX_PATH_LENGTH = 500; + + public TypesafeConfigImplReader() { + this((String) null); + } + + public TypesafeConfigImplReader(String confOverload) { + init(confOverload); + } + + public TypesafeConfigImplReader(File confOverload) { + init(List.of(confOverload)); + } + + public TypesafeConfigImplReader(List confOverloads) { + init(confOverloads); + } + + @Override + public void init(String resourceName) { + globalConfig = UtilValidate.isNotEmpty(resourceName) + ? ConfigFactory.load(resourceName) + : ConfigFactory.load(); + } + + /** + * Specific initializer for Typesafe reader implementation, since its must be able to load multiple files. + * + * @param confFiles the list of files that will be loaded. + */ + public void init(List confFiles) { + Debug.logInfo("Collect available configuration", MODULE); + List configsToLoad = UtilMisc.toList( + ConfigFactory.systemEnvironment(), + ConfigFactory.systemProperties()); + if (UtilValidate.isNotEmpty(confFiles)) { + confFiles.sort(Comparator.comparing(File::getName)); + for (File confFile : confFiles) { + configsToLoad.add(ConfigFactory.parseFile(confFile)); + } + } + configsToLoad.stream() + .filter(Objects::nonNull) + .toList() + .forEach(config -> { + globalConfig = globalConfig.withFallback(config); + Debug.logInfo("Loaded config element with origin: " + config.origin(), MODULE); + }); + globalConfig = globalConfig.resolve(); + Debug.logInfo("Configurations loaded and merged", MODULE); + } + + @Override + public String getValue(String key) { + return getValue("", key); + } + + @Override + public List findElementsWithTypeInParent(String key, Object parent, Class targetClass) { + return List.of(); + } + + @Override + public T findSingleElementWithClassInParent(String xPath, Object parent, Class targetClass) { + return null; + } + + /** + * Retrieve a value from the loaded typesafe configs. + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine'). + * @param key the key associated with the value that is looked for. Can be in standard java property format or xPath. + * @return a {@code String} representation of the value at key or {@code null} if it was not found + */ + public String getValue(String resourceName, String key) { + String configMapKey = String.format("%s#%s", resourceName, key); + if (CONFIGURATION_CACHE.containsKey(configMapKey)) { + return CONFIGURATION_CACHE.get(configMapKey); + } + + String configPath = convertToConfigPath(resourceName, key); + String value = null; + if (configPath == null) { + return ""; + } else { + if (!globalConfig.hasPath(configPath) && !key.startsWith("/")) { // property case, escaping xml xpaths + configPath = convertToConfigPath(resourceName, '"' + key + '"'); + } + if (globalConfig.hasPath(configPath)) { + value = globalConfig.getString(configPath).intern(); + } + } + CONFIGURATION_CACHE.put(configMapKey, value); + return value; + } + + /** + * Return a value in {@code configObject} using {@code key}. Here, {@code configObject} is a Map, likely comming from + * a typesafe config object. + * + * @param configObject the representation of a config loaded in the system. + * @param key the key that is wanted in the wanted map + * @return a value that can be either {@code String}, {@code Number}, {@code Boolean}, {@code Map}, + * {@code List} or {@code null} if the value was not found. + */ + public Object getValue(Map configObject, String key) { + return configObject.get(key); + } + + /** + * Collects all elements of wanted type, regardless of their configuration origin or file. + * Applies all overloads from typesafe config system. + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine'). + * @param xPath the xPath of the elements + * @param targetClass the class of the wanted elements, that will be used in the return List + * @param existingObjectList the list (if any) of already existing objects (loaded from XML for example). It will be used to + * check if any overload must be applied to an existing object. + * @return a list of objects of the wanted class, with all configs applied + */ + public List collectElementsAsListEntriesWithConfigsApplied(String resourceName, String xPath, + Class targetClass, List existingObjectList) { + Map existingObjectMap = new HashMap<>(); + try { + String childElementName = (String) targetClass.getDeclaredField("ELEMENT_NAME").get(null); + String confPath = convertToConfigPath(resourceName, xPath.concat("/" + childElementName)); + if (!globalConfig.hasPath(confPath)) { + return existingObjectList; + } + Method nameGetter = targetClass.getMethod("getName"); + Method multipleSourceGetter = targetClass.getMethod("allowMultipleSources"); + boolean allowMultipleSources; + for (T object : existingObjectList) { + String objectKey = (String) nameGetter.invoke(object); + existingObjectMap.put(objectKey, object); + if (object == existingObjectList.get(0)) { + allowMultipleSources = (boolean) multipleSourceGetter.invoke(object); + if (!allowMultipleSources) { + return collectAnonymousElementList(resourceName, xPath, targetClass); + } + } + } + existingObjectMap.putAll(collectElementsAsMapValuesWithConfigsApplied(resourceName, xPath, targetClass, existingObjectMap)); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + return new LinkedList<>(existingObjectMap.values()); + } + + /** + * Collects simple config elements that don't have precise naming keys (such as run-from-pool, or read-data) + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine'). + * @param xPath the xPath of the elements + * @param targetClass the class of the wanted elements, that will be used in the return List + * @return a List of objects of {@code targetClass} + */ + public List collectAnonymousElementList(String resourceName, String xPath, Class targetClass) { + List objectList = new LinkedList<>(); + try { + String childElementName = (String) targetClass.getDeclaredField("ELEMENT_NAME").get(null); + String childElementFieldIdName = null; + if (Arrays.stream(targetClass.getDeclaredFields()) + .anyMatch(field -> field.getName().equals("ELEMENT_FIELD_ID_NAME"))) { + childElementFieldIdName = (String) targetClass.getDeclaredField("ELEMENT_FIELD_ID_NAME").get(null); + } + if (childElementFieldIdName == null) { + childElementFieldIdName = "name"; + } + String confPath = convertToConfigPath(resourceName, xPath.concat("/" + childElementName)); + if (!globalConfig.hasPath(confPath)) { + return objectList; + } + ConfigValue configValue = globalConfig.getValue(confPath); + List configObjectList = configValue.valueType().equals(ConfigValueType.LIST) + ? globalConfig.getObjectList(confPath) + : List.of(globalConfig.getObject(confPath)); + + for (ConfigObject configObject : configObjectList) { + String newXPath = getNewXPath(xPath, childElementFieldIdName, childElementName, + String.valueOf(configObject.get(childElementFieldIdName).unwrapped())); + T tObject = UtilGenerics.cast(targetClass.getMethod("loadFromConfig", Map.class, String.class) + .invoke(null, configObject.unwrapped(), newXPath)); + objectList.add(tObject); + } + } catch (IllegalAccessException | NoSuchFieldException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + return objectList; + } + + + /** + * Collects all elements of wanted type, regardless of their configuration origin or file. + * Applies all overloads from typesafe config system. + * + * @param resourceName the name of the resource to look in for the element (eg: 'entityengine'). + * @param xPath the xPath of the elements + * @param targetClass the class of the wanted elements, that will be used in the return List + * @param existingObjectMap the map (if any) of already existing objects (loaded from XML for example). It will be used to + * check if any overload must be applied to an existing object. + * @return a Map of objects of the wanted class, with all configs applied. The config object name will be used as map key. + */ + public Map collectElementsAsMapValuesWithConfigsApplied(String resourceName, String xPath, + Class targetClass, Map existingObjectMap) { + Map objectMap = new LinkedHashMap<>(); + try { + String childElementName = (String) targetClass.getDeclaredField("ELEMENT_NAME").get(null); + String childElementFieldIdName = null; + if (Arrays.stream(targetClass.getDeclaredFields()) + .anyMatch(field -> field.getName().equals("ELEMENT_FIELD_ID_NAME"))) { + childElementFieldIdName = (String) targetClass.getDeclaredField("ELEMENT_FIELD_ID_NAME").get(null); + } + if (childElementFieldIdName == null) { + childElementFieldIdName = "name"; + } + String confPath = convertToConfigPath(resourceName, xPath.concat("/" + childElementName)); + if (!globalConfig.hasPath(confPath)) { + return objectMap; + } + ConfigValue configValue = globalConfig.getValue(confPath); + List configObjectList = configValue.valueType().equals(ConfigValueType.LIST) + ? globalConfig.getObjectList(confPath) + : List.of(globalConfig.getObject(confPath)); + for (ConfigObject configObject : configObjectList) { + + Map configMap = configObject.unwrapped(); + configMap.keySet().removeAll(existingObjectMap.keySet()); + Set configKeySet = configMap.keySet(); + for (String configKey : configKeySet) { + Object currentObj = configMap.get(configKey); + Map currentMap; + if (currentObj instanceof Map) { + currentMap = UtilGenerics.cast(currentObj); + if (!currentMap.containsKey(childElementFieldIdName)) { + currentMap.put(childElementFieldIdName, configKey); + } + } else { + currentMap = Map.of(childElementFieldIdName, currentObj); + } + String newXPath = getNewXPath(xPath, childElementFieldIdName, childElementName, configKey); + T tObject = UtilGenerics.cast(targetClass.getMethod("loadFromConfig", Map.class, String.class) + .invoke(null, currentMap, newXPath)); + objectMap.put(configKey, tObject); + } + } + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + return objectMap; + } + + private static String getNewXPath(String xPath, String configKey, String childElementName, String elementValue) { + return xPath + "/" + childElementName + "[@" + configKey + "='" + elementValue + "']"; + } + + /** + * Convert an XML xPath to Hocon syntax by doing the followings : + *
    + *
  • Replace all {@code /} by {@code .}
  • + *
  • Remove the {@code /} (or {@code .} depending on if the above step is already done) at position 0
  • + *
  • Replace all instances of {@code [x]} by {@code .y} (considering {@code y=x-1})
  • + *
  • Replace all instances of {@code foo[@name='bar']} by {@code foo.bar} (a sequence other than {@code @name} + * is considered as an error)
  • + *
  • Remove all {@code @}
  • + *
+ * + * @param path the key (properties) or xPath (XML) of the file to override + * @return a converted path understandable by Lightbend config methods or null if there is an error in the path + */ + public static String convertToConfigPath(String resourceName, String path) { + try { + if (UtilValidate.isEmpty(resourceName)) { + return convertToConfigPath(path); + } + return (resourceName.contains(".") + ? resourceName.substring(0, resourceName.lastIndexOf('.')) + : resourceName) + + "." + convertToConfigPath(path); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + /** + * See {@link TypesafeConfigImplReader#convertToConfigPath(String, String)} + */ + public static String convertToConfigPath(String path) throws XPathExpressionException { + if (path == null || !path.contains("/")) { + return path; + } + if (path.length() >= MAX_PATH_LENGTH) { + throw new XPathExpressionException("Security error, given to long, max path size allowed is " + MAX_PATH_LENGTH); + } + + Pattern pattern = Pattern.compile("\\[@(.+)=([^\"'].+[^\"'])]", Pattern.CASE_INSENSITIVE); + String convertedPath = pattern.matcher(path) + .replaceAll("[@$1=\"$2\"]") + .replace("'", "\"") + .replaceAll("\\[@name=|\\[@group-name=|\\[@reader-name=", "."); + pattern = Pattern.compile("\\[@[a-z]+=.+]", Pattern.CASE_INSENSITIVE); + if (pattern.matcher(convertedPath).find()) { + return null; + } + + // find all number list and decrease the number + pattern = Pattern.compile("\\[\\d+]"); + List matchResults = pattern.matcher(convertedPath).results().toList(); + if (!matchResults.isEmpty()) { + int index = 0; + StringBuilder parsedListPath = new StringBuilder(); + for (MatchResult matchResult : matchResults) { + int listIndex = Integer.parseInt(matchResult.group().replaceAll("[\\[\\]]", "")); + listIndex--; + parsedListPath.append(convertedPath, index, matchResult.start()) + .append(".") + .append(listIndex); + index = matchResult.end(); + } + convertedPath = parsedListPath.append(convertedPath.substring(index)).toString(); + } + + String jsonPath = convertedPath.replace("/", ".") + .replaceAll("[@|,\\]]", ""); + return jsonPath.substring(1); + } + + /** + * Clear config cache + */ + public void clearCache() { + CONFIGURATION_CACHE.clear(); + } +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/XmlConfigurationReader.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/XmlConfigurationReader.java new file mode 100644 index 00000000000..f07098d55db --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/XmlConfigurationReader.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * 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.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.UtilGenerics; +import org.apache.ofbiz.base.util.UtilXml; +import org.w3c.dom.Element; + +public class XmlConfigurationReader implements ConfigurationReaderInterface { + public static final String MODULE = XmlConfigurationReader.class.getName(); + private static final List ALLOWED_ROOT_ELEMENT_NAMES = List.of("entity-config", "service-config"); + private Element rootElement; + private XPath xPath; + + /** + * Create a XmlConfigurationReader object using {@code init(String resourceName}) + */ + public XmlConfigurationReader(String resourceName) { + init(resourceName); + } + + @Override + public void init(String resourceName) { + rootElement = XmlFileReader.read(resourceName); + xPath = XPathFactory.newInstance().newXPath(); + } + + @Override + public String getValue(String key) { + String value = ""; + try { + sanitize(key); + value = xPath.compile(key).evaluate(rootElement); + } catch (XPathExpressionException e) { + Debug.logError("Expression cannot be evaluated: " + e, MODULE); + } + return value.isEmpty() ? null : value.intern(); + } + + @Override + public void clearCache() { } + + @Override + public T findSingleElementWithClassInParent(String xPath, Object parent, Class targetClass) { + T child = null; + try { + String childElementName = (String) targetClass.getDeclaredField("ELEMENT_NAME").get(null); + Element childElement = UtilXml.firstChildElement((Element) parent, childElementName); + if (childElement != null) { + child = UtilGenerics.cast(targetClass.getMethod("loadFromXml", Element.class, String.class).invoke(null, childElement, xPath)); + } + } catch (NoSuchFieldException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + return child; + } + + @Override + public List findElementsWithTypeInParent(String xPath, Object parent, Class targetClass) { + if (parent == null) { + return new LinkedList<>(); + } + List childModelObject = new ArrayList<>(); + try { + String childElementName = (String) targetClass.getDeclaredField("ELEMENT_NAME").get(null); + Method loadFromXml = targetClass.getMethod("loadFromXml", Element.class, String.class); + for (Element childElement : UtilXml.childElementList((Element) parent, childElementName)) { + childModelObject.add(UtilGenerics.cast(loadFromXml.invoke(null, childElement, xPath))); + } + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + return childModelObject; + } + + /** + * Prevents Xpath injection vulnerability + * + * @param key the xpath to check + * @throws XPathExpressionException if the xpath presents a security threat + */ + private void sanitize(String key) throws XPathExpressionException { + String root = key.split("/")[1]; + if (key.contains("../") || !ALLOWED_ROOT_ELEMENT_NAMES.contains(root)) { + throw new XPathExpressionException("Security error, given xpath not allowed"); + } + } + +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/config/XmlFileReader.java b/framework/base/src/main/java/org/apache/ofbiz/base/config/XmlFileReader.java new file mode 100644 index 00000000000..a7e8663d0c1 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/config/XmlFileReader.java @@ -0,0 +1,34 @@ +package org.apache.ofbiz.base.config; + +import java.io.IOException; +import java.net.URL; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.UtilURL; +import org.apache.ofbiz.base.util.UtilXml; +import org.xml.sax.SAXException; +import org.w3c.dom.Element; + +/** + * Small utility class for wrapping reading Xml files. + * Mainly for testing purposes (mockito) + */ +public class XmlFileReader { + public static final String MODULE = XmlFileReader.class.getName(); + + public static Element read(String resourceName) { + URL confUrl = UtilURL.fromResource(resourceName); + if (confUrl == null) { + Debug.logError("Could not find the " + resourceName + " file", MODULE); + throw new RuntimeException("Could not find the " + resourceName + " file"); + } + try { + return UtilXml.readXmlDocument(confUrl, true, true).getDocumentElement(); + } catch (ParserConfigurationException | IOException | SAXException e) { + Debug.logError("Exception thrown while reading " + resourceName + ": " + e, MODULE); + throw new RuntimeException(e); + } + } + +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/container/ConfigContainer.java b/framework/base/src/main/java/org/apache/ofbiz/base/container/ConfigContainer.java new file mode 100644 index 00000000000..ff913c813b9 --- /dev/null +++ b/framework/base/src/main/java/org/apache/ofbiz/base/container/ConfigContainer.java @@ -0,0 +1,125 @@ +/* + * 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.container; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.ofbiz.base.config.ConfigurationFactory; +import org.apache.ofbiz.base.start.Config; +import org.apache.ofbiz.base.start.Start; +import org.apache.ofbiz.base.start.StartupCommand; +import org.apache.ofbiz.base.util.Debug; +import org.apache.ofbiz.base.util.FileUtil; +import org.apache.ofbiz.base.util.StringUtil; + +import static org.apache.ofbiz.base.start.StartupCommandUtil.StartupOption.CONFIG; + +import org.apache.ofbiz.base.util.UtilURL; +import org.apache.ofbiz.base.util.UtilValidate; + +/** + * This component inits the unified configuration system. + * It must run before all other components in order to load all needed configs (like entityengine or serviceengine). + * Once it is loaded, other container can access the configs that has been built. + */ +public class ConfigContainer implements Container { + + private static final String MODULE = ConfigContainer.class.getName(); + private Config cfg = Start.getInstance().getConfig(); + + private String name; + + @Override + public void init(List ofbizCommands, String name, String configFile) throws ContainerException { + this.name = name; + Map configProps = ofbizCommands.stream() + .filter(command -> command.getName().equals(CONFIG.getName())) + .map(StartupCommand::getProperties) + .findFirst().orElse(Map.of()); + if ((configProps.isEmpty() + && !cfg.isConfigOverloadEnable()) + || "false".equals(configProps.get("enable"))) { + Debug.logWarning("[Config override] disabled ", MODULE); + return; + } + Debug.logWarning("[Config override] enabled ", MODULE); + + List hoconFiles = new ArrayList<>(parseFileListToLoadFromStartArguments(configProps.get("file"))); + try { + List configFilesInConfDir = FileUtil.findFiles("conf", null, "config", null); + if (!configFilesInConfDir.isEmpty()) { + hoconFiles.addAll(configFilesInConfDir); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + if (UtilValidate.isEmpty(hoconFiles)) { + Debug.logWarning("[Config override] config file loaded", MODULE); + return; + } + if (Debug.warningOn()) { + hoconFiles.forEach(file -> Debug.logWarning("[Config override] Loading file: " + file, MODULE)); + } + ConfigurationFactory.resetAndGet(hoconFiles).setUseOverrideValue(true); + } + + @Override + public boolean start() throws ContainerException { + return true; + } + + @Override + public void stop() throws ContainerException { + } + + @Override + public String getName() { + return name; + } + + private static List parseFileListToLoadFromStartArguments(String fileProp) throws ContainerException { + List fileNames = new ArrayList<>(); + Optional.ofNullable(fileProp).ifPresent(props -> fileNames.addAll(StringUtil.split(props, ","))); + + List fileUrls = new ArrayList<>(); + for (String file : fileNames) { + URL url = UtilURL.fromResource(file); + if (url == null) { + throw new ContainerException("Unable to locate config file: " + file); + } else { + fileUrls.add(url); + } + } + return fileUrls.stream().map(url -> { + try { + return new File(url.toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }).toList(); + } + +} diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilProperties.java b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilProperties.java index ce9b63e7423..42891889871 100644 --- a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilProperties.java +++ b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilProperties.java @@ -42,6 +42,7 @@ import java.util.ResourceBundle; import java.util.Set; +import org.apache.ofbiz.base.config.ConfigurationFactory; import org.apache.ofbiz.base.location.FlexibleLocation; import org.apache.ofbiz.base.util.cache.UtilCache; import org.apache.ofbiz.base.util.collections.ResourceBundleMapWrapper; @@ -268,26 +269,10 @@ public static BigDecimal getPropertyAsBigDecimal(String resource, String name, B * @return The value of the property in the properties file */ public static String getPropertyValue(String resource, String name) { - if (UtilValidate.isEmpty(resource)) { + if (UtilValidate.isEmpty(resource) || UtilValidate.isEmpty(name)) { return ""; } - if (UtilValidate.isEmpty(name)) { - return ""; - } - - Properties properties = getProperties(resource); - if (properties == null) { - return ""; - } - - String value = null; - - try { - value = properties.getProperty(name); - } catch (Exception e) { - Debug.logInfo(e, MODULE); - } - return value == null ? "" : value.trim(); + return ConfigurationFactory.getInstance().getValue(resource, name); } /** diff --git a/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigPropertiesReaderTest.groovy b/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigPropertiesReaderTest.groovy new file mode 100644 index 00000000000..f22f628bf06 --- /dev/null +++ b/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigPropertiesReaderTest.groovy @@ -0,0 +1,62 @@ +package org.apache.ofbiz.base.util.config + +import org.apache.ofbiz.base.config.TypesafeConfigImplReader +import org.apache.ofbiz.base.util.UtilProperties +import org.junit.jupiter.api.Test + +// codenarc-disable UnnecessaryGString +class ConfigPropertiesReaderTest extends ConfigReaderTest { + + private TypesafeConfigImplReader confReader + + @Test + void testGetNormalPropertyValue() { + confReader = initReaderAndProperties('{}', 'tast', 'some.test.property=somevalue') + assert UtilProperties.getPropertyValue('tast', 'some.test.property') == 'somevalue' + } + + @Test + void testGetHoconPropertyValueInline() { + confReader = initReaderAndProperties("""{ + "tast": { + "some.test.property": "somevalue" + } +}""") + assert UtilProperties.getPropertyValue('tast', 'some.test.property') == 'somevalue' + } + + @Test + void testGetHoconPropertyValueNested() { + confReader = initReaderAndProperties("""{ + "tast": { + "some" : { + "test" : { "property": "somevalue" } + } + } +}""") + assert UtilProperties.getPropertyValue('tast', 'some.test.property') == 'somevalue' + } + + @Test + void testGetHoconPropertyValueInlineOverload() { + confReader = initReaderAndProperties("""{ + "tast": { + "some.test.property": "somevalueOVERLOADED" + } +}""", 'tast', 'some.test.property=somevalue') + assert UtilProperties.getPropertyValue('tast', 'some.test.property') == 'somevalueOVERLOADED' + } + + @Test + void testGetHoconPropertyValueNestedOverload() { + confReader = initReaderAndProperties("""{ + "tast": { + "some" : { + "test" : { "property": "somevalueOVERLOADEDAGAIN" } + } + } +}""", 'tast', 'some.test.property=somevalue') + assert UtilProperties.getPropertyValue('tast', 'some.test.property') == 'somevalueOVERLOADEDAGAIN' + } + +} diff --git a/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigReaderPathHandlingTest.groovy b/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigReaderPathHandlingTest.groovy new file mode 100644 index 00000000000..8f68bed9b7c --- /dev/null +++ b/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigReaderPathHandlingTest.groovy @@ -0,0 +1,128 @@ +/******************************************************************************* + * 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.util.config + +import static org.apache.ofbiz.base.config.TypesafeConfigImplReader.convertToConfigPath + +import org.apache.ofbiz.base.config.TypesafeConfigImplReader +import org.junit.jupiter.api.Test + +class ConfigReaderPathHandlingTest extends ConfigReaderTest { + + private TypesafeConfigImplReader confReader + + @Test + void testXPathConversionToHoconConfigWithSimpleCase() { + assert convertToConfigPath('/test-config/path-test/@value') == 'test-config.path-test.value' + assert convertToConfigPath('/test-config/path-test/value') == 'test-config.path-test.value' + } + + @Test + void testXPathConversionToHoconConfigWithAttributeName() { + assert convertToConfigPath('/test-config/path-test[@name=node-test]/path-node-test/@value') + == 'test-config.path-test."node-test".path-node-test.value' + assert convertToConfigPath('/test-config/path-test[@name=node-test]/path-node-test/value') + == 'test-config.path-test."node-test".path-node-test.value' + assert convertToConfigPath('/test-config/path-test[@name=\'node-test\']/path-node-test/@value') + == 'test-config.path-test."node-test".path-node-test.value' + assert convertToConfigPath('/test-config/path-test[@name=\"node-test\"]/path-node-test/@value') + == 'test-config.path-test."node-test".path-node-test.value' + } + + @Test + void testXPathConversionToHoconConfigWithAttributeNameAndDot() { + assert convertToConfigPath('/test-config/path-test[@name=node.test]/path-node-test/@value') + == 'test-config.path-test."node.test".path-node-test.value' + } + + @Test + void testXPathConversionToHoconConfigWithBadAttributeName() { + assert convertToConfigPath('/test-config/path-test[@badKey=\"node-test\"]/path-node-test/@value') + == null + } + + @Test + void testXPathConversionToHoconConfigWithListElement() { + assert convertToConfigPath('/test-config/path-test[1]/@name') == 'test-config.path-test.0.name' + } + + @Test + void testXPathConversionToHoconConfigWithTwoListElement() { + assert convertToConfigPath('/test-config/path-test[3]/node[4]/@name') == 'test-config.path-test.2.node.3.name' + } + + @Test + void testPropertiesToHoconConfig() { + assert convertToConfigPath('test.value') == 'test.value' + } + + @Test + void testGetSimpleValueFromHocon() { + confReader = initReader('''{"test": {"simple-test": {"name": "value-test"}}}''') + assert confReader.getValue('test', + '/simple-test/@name') == 'value-test' + } + + @Test + void testGetNullValueFromHocon() { + confReader = initReader('''{"test": {"null-test": {"name": "value-test"}}}''') + assert confReader.getValue('test', + '/null-test/@description') == null + } + + /* FIXME + @Test + void testGetIntegerValueFromHocon() { + confReader = initReader('''{"test": {"integer-test": {"name": "1"}}}''') + assert confReader.getValue('test', + '/integer-test/@name', Integer.class) == 1 + }*/ + + @Test + void testGetValueFromMapListFromHocon() { + confReader = initReader('''{"test": {"list-test": {"test1": {"description": "desc1"}, + "test2": {"description": "desc2"}}}}''') + assert confReader.getValue('test', + '/list-test[@name=test1]/description') == 'desc1' + /* FIXME + assert confReader.getValue('test', + '/list-test[1]/description') == 'desc1' + */ + } + + /* FIXME + @Test + void testGetValueFromListMap() { + confReader = initReader('''{"test": {"list-test": [{"name": "test1", "description": "desc1"}, + {"name": "test2", "description": "desc2"}]}}''') + assert confReader.getValue('test', + '/list-test[@name=test1]/description') == 'desc1' + }*/ + + @Test + void testGetIncorrectKeyFromListFromHocon() { + confReader = initReader('''{"test": {"list-test": {"test1": {"description": "desc1"}, + "test2": {"description": "desc2"}}}}''') + assert confReader.getValue('test', + '/list-test[@name=test3]/description') == null + assert confReader.getValue('test', + '/list-test[@name=test1]/missing') == null + } + +} diff --git a/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigReaderTest.groovy b/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigReaderTest.groovy new file mode 100644 index 00000000000..a7b0f46970f --- /dev/null +++ b/framework/base/src/test/groovy/org/apache/ofbiz/base/util/config/ConfigReaderTest.groovy @@ -0,0 +1,85 @@ +/******************************************************************************* + * 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.util.config + +import static org.mockito.Mockito.any + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import com.typesafe.config.ConfigParseOptions +import org.apache.ofbiz.base.config.ConfigurationInterface +import org.apache.ofbiz.base.config.ConfigurationFactory +import org.apache.ofbiz.base.config.TypesafeConfigImplReader +import org.apache.ofbiz.base.util.UtilProperties +import org.junit.jupiter.api.AfterEach +import org.mockito.MockedStatic +import org.mockito.Mockito + +class ConfigReaderTest { + + private MockedStatic mockConfig + private MockedStatic mockProperties + + @AfterEach + void closeMock() { + mockConfig?.close() + mockProperties?.close() + } + + private void initHoconConfig(String hoconContent) { + Config mockedConfig = hoconContent + ? ConfigFactory.parseString(hoconContent) + : ConfigFactory.empty() + mockConfig = Mockito.mockStatic(ConfigFactory) + mockConfig.when(ConfigFactory.load((String) any())) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory.parseFile((File) any())) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory.parseFile((File) any(), (ConfigParseOptions) any())) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory::load) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory::empty()) + .thenReturn(mockedConfig) + } + + protected ConfigurationInterface setConfig(String hoconContent) { + initHoconConfig(hoconContent) + ConfigurationInterface configuration = ConfigurationFactory.resetAndGet() + configuration.clearCache() + return configuration + } + + protected TypesafeConfigImplReader initReader(String hoconContent) { + setConfig(hoconContent) + return new TypesafeConfigImplReader() + } + + protected TypesafeConfigImplReader initReaderAndProperties(String hoconContent, String propFileName = null, + String propFileContent = null) { + mockProperties = Mockito.mockStatic(UtilProperties, Mockito.CALLS_REAL_METHODS) + if (propFileName && propFileContent) { + Properties props = new Properties() + props.load(new ByteArrayInputStream(propFileContent.getBytes())) + mockProperties.when(() -> UtilProperties.getProperties(propFileName)).thenReturn(props) + } + return initReader(hoconContent) + } + +} diff --git a/framework/entity/config/entityengine.xml b/framework/entity/config/entityengine.xml index 724fba47e62..ddf8fe5a19c 100644 --- a/framework/entity/config/entityengine.xml +++ b/framework/entity/config/entityengine.xml @@ -125,38 +125,6 @@ access. For a detailed description see the core/docs/entityconfig.html file. * Not set uses database default --> - - - - - - - - - - - - - - - - - - - --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/framework/entity/config/samples/datasource/datasource.DB2.conf.sample b/framework/entity/config/samples/datasource/datasource.DB2.conf.sample new file mode 100644 index 00000000000..8b7dca98945 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.DB2.conf.sample @@ -0,0 +1,46 @@ +/* +According to http://markmail.org/message/s75sf6zhtizzkqbv Since version V6R1 (AS/400, db2) there is no need for an own fieldtype.xml +just use the h2-fieldtypes. +Beware use-indices-unique="false" is needed because of + Derby bug with null values in a unique index, not sure it's needed with DB2 +*/ +{ + "entityengine": { + "entity-config": { + "datasource": { + "DB2": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "schema-name": "OFBIZ", + "field-type-name": "h2", + "check-on-start": "true", + "add-missing-on-start": "true", + "use-pk-constraint-names": "false", + "use-indices-unique": "false", + "alias-view-columns": "false", + "use-order-by-nulls": "true", + "offset-style": "fetch", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { +// There is an open source version of the jdbc driver at sourceforge: http://sourceforge.net/projects/jt400/ + "jdbc-driver": "com.ibm.as400.access.AS400JDBCDriver", + "jdbc-uri": "jdbc:as400:192.168.1.10;prompt=false;libraries=ofbiz;lazyclose=true;translate binary=true", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localadvantage.conf.sample b/framework/entity/config/samples/datasource/datasource.localadvantage.conf.sample new file mode 100644 index 00000000000..c0f2740c721 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localadvantage.conf.sample @@ -0,0 +1,39 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localadvantage": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "advantage", + "check-on-start": "true", + "add-missing-on-start": "true", + "check-indices-on-start": "false", + "use-foreign-keys": "false", + "use-foreign-key-indices": "true", + "join-style": "ansi-no-parenthesis", + "alias-view-columns": "false", + "always-use-constraint-keyword": "true", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { + "jdbc-driver": "com.extendedsystems.jdbc.advantage.ADSDriver", + "jdbc-uri": "jdbc:extendedsystems:advantage://localhost:6262;catalog=c:\\advantage\\OFBIZ.ADD;TableType=adt", + "jdbc-username": "ADSSYS", + "jdbc-password": "adssys", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localaxion.conf.sample b/framework/entity/config/samples/datasource/datasource.localaxion.conf.sample new file mode 100644 index 00000000000..f1de32aa7de --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localaxion.conf.sample @@ -0,0 +1,34 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localaxion": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "axion", + "check-on-start": "true", + "add-missing-on-start": "true", + "use-pk-constraint-names": "false", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { + "jdbc-driver": "org.axiondb.jdbc.AxionDriver", + "jdbc-uri": "jdbc:axiondb:ofbiz:data/axion/ofbiz", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localfirebird.conf.sample b/framework/entity/config/samples/datasource/datasource.localfirebird.conf.sample new file mode 100644 index 00000000000..c95b1521e9b --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localfirebird.conf.sample @@ -0,0 +1,47 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localfirebird": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "firebird", + "check-on-start": "true", + "use-foreign-key-indices": "false", + "add-missing-on-start": "true", + "alias-view-columns": "false", + "join-style": "ansi", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { + "jdbc-driver": "org.firebirdsql.jdbc.FBDriver", +// Sample remote URI: jdbc-uri="jdbc:firebirdsql://localhost:3050//opt/interbase/data/ofbiz.gdb" --> + "jdbc-uri": "jdbc:firebirdsql:127.0.0.1:C:\\data\\ofbiz.gdb", + "jdbc-username": "SYSDBA", + "jdbc-password": "masterkey", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000", + } +// Orion Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "comp/env/jdbc/xa/localfirebird", +// "isolation-level": "ReadCommitted", +// }, +// "tyrex-dataSource": { +// "dataSource-name": "firebird", +// "isolation-level": "ReadCommitted", +// } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localhsql.conf.sample b/framework/entity/config/samples/datasource/datasource.localhsql.conf.sample new file mode 100644 index 00000000000..1cbe1aa953c --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localhsql.conf.sample @@ -0,0 +1,55 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localhsql": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "hsql", + "check-on-start": "true", + "add-missing-on-start": "true", + "check-indices-on-start": "true", + "use-foreign-keys": "true", + "use-foreign-key-indices": "true", + "use-fk-initially-deferred": "false", + "join-style": "ansi-no-parenthesis", + "alias-view-columns": "true", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { + "jdbc-driver": "org.hsqldb.jdbcDriver", + "jdbc-uri": "jdbc:hsqldb:runtime/data/hsqldb/ofbiz", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } +// "jndi-jdbc": { +// "jndi-server-name": "localjndi", +// "jndi-name": "java:/HsqlDataSource", +// "isolation-level" = "ReadUncommitted" +// } +// Orion Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "comp/env/jdbc/xa/localhsql" +// "isolation-level": "ReadUncommitted" +// } +// Weblogic Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "localjndi", +// "jndi-name": "localhsqlDataSource" +// } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localmssql.conf.sample b/framework/entity/config/samples/datasource/datasource.localmssql.conf.sample new file mode 100644 index 00000000000..a66ac1e2785 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localmssql.conf.sample @@ -0,0 +1,63 @@ +/* +The following has been tested with SQL Server 2005 + MS SQL Server JDBC Driver 1.1 +Tips: +1. Make sure your SQL Server has mixed mode authentication as per this post: +http://aspadvice.com/blogs/plitwin/archive/2006/09/10/Login-failed-_2E002E002E00_-not-associated-with-a-trusted-SQL-server-connection.aspx +2. Make sure you have copied JDBC driver jar to entity/lib/jdbc +3. Make sure you have installed JDBC Driver XA support as per MSSQL_JDBC_HOME/enu/xa/xa_install.sql +4. Make sure that you have created and authorized the ofbiz database and login +5. Make sure that schema-name, jdbc-username, jdbc-password and databaseName are all correct! + +Notes: +a. The reason for putting SelectMethod=cursor property in URL is explained here: +http://forum.java.sun.com/thread.jspa?forumID=48&threadID=184797 +b. If using an old version of the driver, beware of this resource leak: +http://support.microsoft.com/kb/820773/ +c. Demo data are in conflict with Entity Unique Index when loading data +This is a known issue with MsSQL since https://markmail.org/message/mezqxb3eyzz3xpv6 +The problem does not exist with Derby, nor with the mostly open source DBMS used with OFBiz: Postres, MySQL, MariaDB, etc. +So we will not change the current OOTB setting and suggest to simply change your own configuration. See OFBIZ-11998 for more. +*/ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localmssql": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "schema-name": "dbo", + "field-type-name": "mssql", + "check-on-start": "true", + "add-missing-on-start": "true", + "join-style": "ansi", + "alias-view-columns": "false", + "use-fk-initially-deferred": "false", + "read-data": [ + { "reader-name": "tenant" }, + { "reader-name": "seed" }, + { "reader-name": "seed-initial" }, + { "reader-name": "demo" }, + { "reader-name": "ext" }, + { "reader-name": "ext-test" }, + { "reader-name": "ext-demo" } + ], + "inline-jdbc": { + "jdbc-driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "jdbc-uri": "jdbc:sqlserver://localhost:1791;databaseName=ofbiz;SelectMethod=cursor;", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } + // Orion Style JNDI name + // "jndi-jdbc": { + // "jndi-server-name": "default", + // "jndi-name": "comp/env/jdbc/xa/localmssql", + // "isolation-level": "ReadCommitted" + // } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localmysql.conf.sample b/framework/entity/config/samples/datasource/datasource.localmysql.conf.sample new file mode 100644 index 00000000000..366fd115104 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localmysql.conf.sample @@ -0,0 +1,49 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localmysql": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "mysql", + "check-on-start": "true", + "add-missing-on-start": "true", + "check-pks-on-start": "false", + "use-foreign-keys": "true", + "join-style": "ansi-no-parenthesis", + "alias-view-columns": "false", + "drop-fk-use-foreign-key-keyword": "true", + "table-type": "InnoDB", + "character-set": "utf8", + "collate": "utf8_general_ci", + "offset-style": "limit", + "read-data": [ + { "reader-name": "tenant" }, + { "reader-name": "seed" }, + { "reader-name": "seed-initial" }, + { "reader-name": "demo" }, + { "reader-name": "ext" }, + { "reader-name": "ext-test" }, + { "reader-name": "ext-demo" } + ], + "inline-jdbc": { + "jdbc-driver": "com.mysql.cj.jdbc.Driver", + "jdbc-uri": "jdbc:mysql://127.0.0.1/ofbiz?autoReconnect=true&characterEncoding=UTF-8", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + // Please note that at least one person has experienced a problem with this value with MySQL and had to set it to -1 in order to avoid this issue. + // For more look at http://markmail.org/thread/5sivpykv7xkl66px and http://commons.apache.org/dbcp/configuration.html--> + "time-between-eviction-runs-millis": "600000" + } +// "jndi-jdbc": { +// "jndi-server-name": "localjndi", +// "jndi-name": "java:/MySqlDataSource", +// "isolation-level": "Serializable" +// } + } + } + } + } +} \ No newline at end of file diff --git a/framework/entity/config/samples/datasource/datasource.localoracle.conf.sample b/framework/entity/config/samples/datasource/datasource.localoracle.conf.sample new file mode 100644 index 00000000000..4610a2963e7 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localoracle.conf.sample @@ -0,0 +1,36 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localoracle": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "schema-name": "OFBIZ", + "field-type-name": "oracle", + "check-on-start": "true", + "add-missing-on-start": "true", + "alias-view-columns": "false", + "join-style": "ansi", + "use-order-by-nulls": "true", + "read-data": [ + { "reader-name": "tenant" }, + { "reader-name": "seed" }, + { "reader-name": "seed-initial" }, + { "reader-name": "demo" }, + { "reader-name": "ext" }, + { "reader-name": "ext-test" }, + { "reader-name": "ext-demo" } + ], + "inline-jdbc": { + "jdbc-driver": "oracle.jdbc.driver.OracleDriver", + "jdbc-uri": "jdbc:oracle:thin:@127.0.0.1:1521:ofbiz", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000", + } + } + } + } + } +} \ No newline at end of file diff --git a/framework/entity/config/samples/datasource/datasource.localoracledd.conf.sample b/framework/entity/config/samples/datasource/datasource.localoracledd.conf.sample new file mode 100644 index 00000000000..847c17021f3 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localoracledd.conf.sample @@ -0,0 +1,36 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localoracledd": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "schema-name": "OFBIZ", + "field-type-name": "oracle", + "check-on-start": "true", + "add-missing-on-start": "true", + "alias-view-columns": "false", + "join-style": "ansi", + "use-order-by-nulls": "true", + "read-data": [ + { "reader-name": "tenant" }, + { "reader-name": "seed" }, + { "reader-name": "seed-initial" }, + { "reader-name": "demo" }, + { "reader-name": "ext" }, + { "reader-name": "ext-test" }, + { "reader-name": "ext-demo" } + ], + "inline-jdbc": { + "jdbc-driver": "oracle.jdbc.driver.OracleDriver", + "jdbc-uri": "jdbc:oracle:datadirect:@127.0.0.1:1521:ofbiz", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } + } + } + } + } +} \ No newline at end of file diff --git a/framework/entity/config/samples/datasource/datasource.localpostgres.conf.sample b/framework/entity/config/samples/datasource/datasource.localpostgres.conf.sample new file mode 100644 index 00000000000..a8ec028466e --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localpostgres.conf.sample @@ -0,0 +1,69 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localpostgres": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "schema-name": "public", + "field-type-name": "postgres", + "check-on-start": "true", + "add-missing-on-start": "true", + "use-fk-initially-deferred": "false", + "alias-view-columns": "false", + "join-style": "ansi", + "use-binary-type-for-blob": "true", + "use-order-by-nulls": "true", + "offset-style": "limit", +// Comment out the result-fetch-size attribute for jdbc driver versions older than 8.0. +// Not recommended to use those though. They are archived unsupported versions: http://jdbc.postgresql.org/download.html + "result-fetch-size": "50", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { + "jdbc-driver": "org.postgresql.Driver", + "jdbc-uri": "jdbc:postgresql://127.0.0.1/ofbiz", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "java:comp/env/jdbc/localpostgres", +// "isolation-level": "ReadCommitted" +// }, +// Orion Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "comp/env/jdbc/xa/localpostgres", +// "isolation-level": "ReadCommitted" +// }, +// Weblogic Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "localweblogic", +// "jndi-name": "PostgresDataSource" +// }, +// JRun4 Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "jdbc/localpostgres", +// "isolation-level": "ReadCommitted" +// }, +// "tyrex-dataSource": { +// "dataSource-name": "localpostgres", +// "isolation-level": "ReadCommitted" +// } + } + } + } + } +} diff --git a/framework/entity/config/samples/datasource/datasource.localsapdb.conf.sample b/framework/entity/config/samples/datasource/datasource.localsapdb.conf.sample new file mode 100644 index 00000000000..10d2a4b3df7 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localsapdb.conf.sample @@ -0,0 +1,73 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localsapdb": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "sapdb", + "check-on-start": "true", + "add-missing-on-start": "true", + "fk-style": "name_fk", + "use-fk-initially-deferred": "false", + "join-style": "ansi-no-parenthesis", + "read-data": [ + { + "reader-name": "tenant" + }, + { + "reader-name": "seed" + }, + { + "reader-name": "seed-initial" + }, + { + "reader-name": "demo" + }, + { + "reader-name": "ext" + }, + { + "reader-name": "ext-test" + }, + { + "reader-name": "ext-demo" + } + ], + "inline-jdbc": { + "jdbc-driver": "com.sap.dbtech.jdbc.DriverSapDB", + "jdbc-uri": "jdbc:sapdb://localhost/OFBIZ", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } +// Orion Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "comp/env/jdbc/xa/localsapdb", +// "isolation-level": "ReadCommitted" +// }, +// Weblogic Style JNDI name +// "jndi-jdbc": { +// jndi-server-name= +// "localweblogic" +// jndi-name= +// "SapDBDataSource" +// }, +// JRun4 Style JNDI name +// "jndi-jdbc": { +// "jndi-server-name": "default", +// "jndi-name": "jdbc/localsapdb", +// "isolation-level": "ReadCommitted" +// }, +// "tyrex-dataSource": { +// "dataSource-name": "localsapdb", +// "isolation-level": "ReadCommitted" +// } + } + } + } + } +} \ No newline at end of file diff --git a/framework/entity/config/samples/datasource/datasource.localsybase.conf.sample b/framework/entity/config/samples/datasource/datasource.localsybase.conf.sample new file mode 100644 index 00000000000..45c34ddb973 --- /dev/null +++ b/framework/entity/config/samples/datasource/datasource.localsybase.conf.sample @@ -0,0 +1,37 @@ +{ + "entityengine": { + "entity-config": { + "datasource": { + "localsybase": { + "helper-class": "org.apache.ofbiz.entity.datasource.GenericHelperDAO", + "field-type-name": "sybase", + "schema-name": "dbo", + "check-on-start": "true", + "add-missing-on-start": "true", + "use-fk-initially-deferred": "false", + "join-style": "ansi", + "read-data": [ + {"reader-name": "tenant"}, + {"reader-name": "seed"}, + {"reader-name": "seed-initial"}, + {"reader-name": "demo"}, + {"reader-name": "ext"}, + {"reader-name": "ext-test"}, + {"reader-name": "ext-demo"} + ], + "inline-jdbc": { + // Out of date, check https://mvnrepository.com/search?q=com.sybase for up to date versions + "jdbc-driver": "com.sybase.jdbc2.jdbc.SybDriver", + "jdbc-uri": "jdbc:sybase:Tds:10.1.1.10:11222/ofbiz?DYNAMIC_PREPARE=true", + "jdbc-username": "ofbiz", + "jdbc-password": "ofbiz1", + "isolation-level": "ReadCommitted", + "pool-minsize": "2", + "pool-maxsize": "250", + "time-between-eviction-runs-millis": "600000" + } + } + } + } + } +} diff --git a/framework/entity/config/samples/examples/delegator.default.postgres.conf.example b/framework/entity/config/samples/examples/delegator.default.postgres.conf.example new file mode 100644 index 00000000000..ca21984ba91 --- /dev/null +++ b/framework/entity/config/samples/examples/delegator.default.postgres.conf.example @@ -0,0 +1,31 @@ +{ + "entityengine": { + "entity-config": { + "delegator": { + "default": { + "entity-model-reader": "entity-model-reader-over", + "entity-group-reader": "entity-group-reader-over", + "entity-eca-reader": "entity-eca-reader-over", + "distributed-cache-clear-enabled": "true", + "group-map": { + "org.apache.ofbiz": {"datasource-name": "localpostgres"}, + "org.apache.ofbiz.olap": {"datasource-name": "localpostgres"}, + "org.apache.ofbiz.tenant": {"datasource-name": "localpostgres"}, + } + } + "default-no-eca": { + "entity-model-reader": "entity-model-reader-over", + "entity-group-reader": "entity-group-reader-over", + "entity-eca-reader": "entity-eca-reader-over", + "entity-eca-enabled": "true", + "distributed-cache-clear-enabled": "true", + "group-map": { + "org.apache.ofbiz": {"datasource-name": "localpostgres"}, + "org.apache.ofbiz.olap": {"datasource-name": "localpostgres"}, + "org.apache.ofbiz.tenant": {"datasource-name": "localpostgres"}, + } + } + } + } + } +} diff --git a/framework/entity/fieldtype/fieldtypedaffodil.xml b/framework/entity/fieldtype/fieldtypedaffodil.xml deleted file mode 100644 index 7a969684aca..00000000000 --- a/framework/entity/fieldtype/fieldtypedaffodil.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/GenericDelegator.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/GenericDelegator.java index 5e9c6f08e8c..4a0e7ecdd1c 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/GenericDelegator.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/GenericDelegator.java @@ -58,6 +58,7 @@ import org.apache.ofbiz.entity.config.model.Datasource; import org.apache.ofbiz.entity.config.model.DelegatorElement; import org.apache.ofbiz.entity.config.model.EntityConfig; +import org.apache.ofbiz.entity.config.model.GroupMap; import org.apache.ofbiz.entity.datasource.GenericHelper; import org.apache.ofbiz.entity.datasource.GenericHelperFactory; import org.apache.ofbiz.entity.datasource.GenericHelperInfo; @@ -491,7 +492,10 @@ public Map getModelEntityMapByGroup(String groupName) throw */ @Override public String getGroupHelperName(String groupName) { - return this.delegatorInfo.getGroupDataSource(groupName); + GroupMap groupMap = this.delegatorInfo.getGroupDataSource(groupName); + return groupMap != null + ? groupMap.getDatasourceName() + : null; } @Override diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ConnectionFactory.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ConnectionFactory.java index 2c362afdde0..8b2d20e143d 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ConnectionFactory.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ConnectionFactory.java @@ -18,31 +18,52 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <connection-factory> element. * * @see entity-config.xsd */ @ThreadSafe -public final class ConnectionFactory { +public final class ConnectionFactory extends AbstractConfigElement { + + public static final String ELEMENT_NAME = "connection-factory"; + private final String xPath; - private final String className; // type = xs:string + private final String className; - ConnectionFactory(Element element) throws GenericEntityConfException { + ConnectionFactory(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String className = element.getAttribute("class").intern(); + xPath = xPathParent.concat("/" + ELEMENT_NAME); + + EntityConfigGetter config = EntityConfigGetter.getInstance(); + String className = config.getValue(xPath + "/@class"); if (className.isEmpty()) { throw new GenericEntityConfException(" element class attribute is empty" + lineNumberText); } this.className = className; } - /** Returns the value of the class attribute. */ + public static ConnectionFactory loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new ConnectionFactory(element, xPathParent); + } + + public static ConnectionFactory loadFromConfig(Map configMap, String name) { + return null; // TODO: Implement when needed + } + public String getClassName() { - return this.className; + return className; + } + + @Override + public String getName() { + return "connection-factory"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Datasource.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Datasource.java index 2244d7766c0..b58107426be 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Datasource.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Datasource.java @@ -21,9 +21,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; +import org.apache.ofbiz.base.config.ConfigHelper; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.base.util.UtilGenerics; +import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -33,21 +37,25 @@ * @see entity-config.xsd */ @ThreadSafe -public final class Datasource { +public final class Datasource extends AbstractConfigElement { - private final String name; // type = xs:string - private final String helperClass; // type = xs:string - private final String fieldTypeName; // type = xs:string + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "datasource"; + private final String xPath; + + private final String name; + private final String helperClass; + private final String fieldTypeName; private final boolean useSchemas; - private final String schemaName; // type = xs:string + private final String schemaName; private final boolean checkOnStart; private final boolean addMissingOnStart; private final boolean usePkConstraintNames; private final boolean checkPksOnStart; - private final int constraintNameClipLength; // type = xs:nonNegativeInteger + private final int constraintNameClipLength; private final boolean useProxyCursor; - private final String proxyCursorName; // type = xs:string - private final int resultFetchSize; // type = xs:integer + private final String proxyCursorName; + private final int resultFetchSize; private final boolean useForeignKeys; private final boolean useForeignKeyIndices; private final boolean checkFksOnStart; @@ -64,97 +72,65 @@ public final class Datasource { private final boolean useBinaryTypeForBlob; private final boolean useOrderByNulls; private final String offsetStyle; - private final String tableType; // type = xs:string - private final String characterSet; // type = xs:string - private final String collate; // type = xs:string - private final int maxWorkerPoolSize; // type = xs:integer - private final List sqlLoadPathList; // - private final List readDataList; // - private final InlineJdbc inlineJdbc; // - private final JndiJdbc jndiJdbc; // - private final TyrexDataSource tyrexDataSource; // - - Datasource(Element element) throws GenericEntityConfException { + private final String tableType; + private final String characterSet; + private final String collate; + private final int maxWorkerPoolSize; + private final List sqlLoadPathList; + private final List readDataList; + private final InlineJdbc inlineJdbc; + private final JndiJdbc jndiJdbc; + private final TyrexDataSource tyrexDataSource; + + Datasource(Element element, String xPathParent) throws GenericEntityConfException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String name = element.getAttribute("name").intern(); + String name = element.getAttribute("name"); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - String helperClass = element.getAttribute("helper-class").intern(); - if (helperClass.isEmpty()) { + this.xPath = xPathParent.concat("/datasource[@name='" + name + "']"); + String helperClass = config.getValue(this.xPath + "/@helper-class"); + if (helperClass.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element helper-class attribute is empty" + lineNumberText); } this.helperClass = helperClass; - String fieldTypeName = element.getAttribute("field-type-name").intern(); - if (fieldTypeName.isEmpty()) { + String fieldTypeName = config.getValue(this.xPath + "/@field-type-name"); + if (fieldTypeName.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element field-type-name attribute is empty" + lineNumberText); } this.fieldTypeName = fieldTypeName; - this.useSchemas = !"false".equals(element.getAttribute("use-schemas")); - this.schemaName = element.getAttribute("schema-name").intern(); - this.checkOnStart = !"false".equals(element.getAttribute("check-on-start")); - this.addMissingOnStart = "true".equals(element.getAttribute("add-missing-on-start")); - this.usePkConstraintNames = !"false".equals(element.getAttribute("use-pk-constraint-names")); - this.checkPksOnStart = !"false".equals(element.getAttribute("check-pks-on-start")); - String constraintNameClipLength = element.getAttribute("constraint-name-clip-length"); - if (constraintNameClipLength.isEmpty()) { - this.constraintNameClipLength = 30; - } else { - try { - this.constraintNameClipLength = Integer.parseInt(constraintNameClipLength); - } catch (Exception e) { - throw new GenericEntityConfException(" element constraint-name-clip-length attribute is invalid" + lineNumberText); - } - } - this.useProxyCursor = "true".equalsIgnoreCase(element.getAttribute("use-proxy-cursor")); - String proxyCursorName = element.getAttribute("proxy-cursor-name").intern(); - if (proxyCursorName.isEmpty()) { - proxyCursorName = "p_cursor"; - } - this.proxyCursorName = proxyCursorName; - String resultFetchSize = element.getAttribute("result-fetch-size"); - if (resultFetchSize.isEmpty()) { - this.resultFetchSize = -1; - } else { - try { - this.resultFetchSize = Integer.parseInt(resultFetchSize); - } catch (Exception e) { - throw new GenericEntityConfException(" element result-fetch-size attribute is invalid" + lineNumberText); - } - } - this.useForeignKeys = !"false".equals(element.getAttribute("use-foreign-keys")); - this.useForeignKeyIndices = !"false".equals(element.getAttribute("use-foreign-key-indices")); - this.checkFksOnStart = "true".equals(element.getAttribute("check-fks-on-start")); - this.checkFkIndicesOnStart = "true".equals(element.getAttribute("check-fk-indices-on-start")); - String fkStyle = element.getAttribute("fk-style").intern(); - if (fkStyle.isEmpty()) { - fkStyle = "name_constraint"; - } - this.fkStyle = fkStyle; - this.useFkInitiallyDeferred = "true".equals(element.getAttribute("use-fk-initially-deferred")); - this.useIndices = !"false".equals(element.getAttribute("use-indices")); - this.useIndicesUnique = !"false".equals(element.getAttribute("use-indices-unique")); - this.checkIndicesOnStart = "true".equals(element.getAttribute("check-indices-on-start")); - String joinStyle = element.getAttribute("join-style").intern(); - if (joinStyle.isEmpty()) { - joinStyle = "ansi"; - } - this.joinStyle = joinStyle; - this.aliasViewColumns = "true".equals(element.getAttribute("alias-view-columns")); - this.alwaysUseConstraintKeyword = "true".equals(element.getAttribute("always-use-constraint-keyword")); - this.dropFkUseForeignKeyKeyword = "true".equals(element.getAttribute("drop-fk-use-foreign-key-keyword")); - this.useBinaryTypeForBlob = "true".equals(element.getAttribute("use-binary-type-for-blob")); - this.useOrderByNulls = "true".equals(element.getAttribute("use-order-by-nulls")); - String offsetStyle = element.getAttribute("offset-style").intern(); - if (offsetStyle.isEmpty()) { - offsetStyle = "none"; - } - this.offsetStyle = offsetStyle; - this.tableType = element.getAttribute("table-type").intern(); - this.characterSet = element.getAttribute("character-set").intern(); - this.collate = element.getAttribute("collate").intern(); - String maxWorkerPoolSize = element.getAttribute("max-worker-pool-size").intern(); + useSchemas = "true".equals(config.getValue(this.xPath + "/@use-schemas")); + schemaName = config.getValue(this.xPath + "/@schema-name"); + checkOnStart = "true".equals(config.getValue(this.xPath + "/@check-on-start")); + addMissingOnStart = "true".equals(config.getValue(this.xPath + "/@add-missing-on-start")); + usePkConstraintNames = "true".equals(config.getValue(this.xPath + "/@use-pk-constraint-names")); + checkPksOnStart = "true".equals(config.getValue(this.xPath + "/@check-pks-on-start")); + constraintNameClipLength = config.getValue(this.xPath + "/@constraint-name-clip-length", 30, Integer.class); + useProxyCursor = "true".equalsIgnoreCase(config.getValue(this.xPath + "/@use-proxy-cursor")); + proxyCursorName = config.getValue(this.xPath + "/@proxy-cursor-name", "p_cursor", String.class); + resultFetchSize = config.getValue(this.xPath + "/@result-fetch-size", -1, Integer.class); + useForeignKeys = "true".equals(config.getValue(this.xPath + "/@use-foreign-keys")); + useForeignKeyIndices = "true".equals(config.getValue(this.xPath + "/@use-foreign-key-indices")); + checkFksOnStart = "true".equals(config.getValue(this.xPath + "/@check-fks-on-start")); + checkFkIndicesOnStart = "true".equals(config.getValue(this.xPath + "/@check-fks-indices-on-start")); + fkStyle = config.getValue(this.xPath + "/@fk-style", "name_constraint", String.class); + useFkInitiallyDeferred = "true".equals(config.getValue(this.xPath + "/@use-fk-initially-deferred")); + useIndices = "true".equals(config.getValue(this.xPath + "/@use-indices")); + useIndicesUnique = "true".equals(config.getValue(this.xPath + "/@use-indices-unique")); + checkIndicesOnStart = "true".equals(config.getValue(this.xPath + "/@check-indices-on-start")); + joinStyle = config.getValue(this.xPath + "/@join-style", "ansi", String.class); + aliasViewColumns = "true".equals(config.getValue(this.xPath + "/@alias-view-columns")); + alwaysUseConstraintKeyword = "true".equals(config.getValue(this.xPath + "/@always-use-constraint-keyword")); + dropFkUseForeignKeyKeyword = "true".equals(config.getValue(this.xPath + "/@drop-fk-use-foreign-key-keyword")); + useBinaryTypeForBlob = "true".equals(config.getValue(this.xPath + "/@use-binary-type-for-blob")); + useOrderByNulls = "true".equals(config.getValue(this.xPath + "/@use-order-by-nulls")); + offsetStyle = config.getValue(this.xPath + "/@offset-style", "none", String.class); + tableType = config.getValue(this.xPath + "/@table-type"); + characterSet = config.getValue(this.xPath + "/@character-set"); + collate = config.getValue(this.xPath + "/@collate"); + String maxWorkerPoolSize = config.getValue(this.xPath + "/@max-worker-pool-size"); if (maxWorkerPoolSize.isEmpty()) { this.maxWorkerPoolSize = 1; } else { @@ -170,46 +146,38 @@ public final class Datasource { throw new GenericEntityConfException(" element max-worker-pool-size attribute is invalid" + lineNumberText); } } - List sqlLoadPathElementList = UtilXml.childElementList(element, "sql-load-path"); - if (sqlLoadPathElementList.isEmpty()) { + List sqlLoadPathList = config.getSubElementsAsListEntries(xPath, element, SqlLoadPath.class); + if (sqlLoadPathList.isEmpty()) { this.sqlLoadPathList = Collections.emptyList(); } else { - List sqlLoadPathList = new ArrayList<>(sqlLoadPathElementList.size()); - for (Element sqlLoadPathElement : sqlLoadPathElementList) { - sqlLoadPathList.add(new SqlLoadPath(sqlLoadPathElement)); - } this.sqlLoadPathList = Collections.unmodifiableList(sqlLoadPathList); } - List readDataElementList = UtilXml.childElementList(element, "read-data"); - if (readDataElementList.isEmpty()) { + List readDataList = config.getSubElementsAsListEntries(xPath, element, ReadData.class); + if (readDataList.isEmpty()) { this.readDataList = Collections.emptyList(); } else { - List readDataList = new ArrayList<>(readDataElementList.size()); - for (Element readDataElement : readDataElementList) { - readDataList.add(new ReadData(readDataElement)); - } this.readDataList = Collections.unmodifiableList(readDataList); } int jdbcElementCount = 0; - Element inlineJdbcElement = UtilXml.firstChildElement(element, "inline-jdbc"); - if (inlineJdbcElement == null) { + InlineJdbc inlineJdbc = config.getObjectSubElement(xPath, element, InlineJdbc.class); + if (inlineJdbc == null) { this.inlineJdbc = null; } else { - this.inlineJdbc = new InlineJdbc(inlineJdbcElement); + this.inlineJdbc = inlineJdbc; jdbcElementCount++; } - Element jndiJdbcElement = UtilXml.firstChildElement(element, "jndi-jdbc"); - if (jndiJdbcElement == null) { + JndiJdbc jndiJdbc = config.getObjectSubElement(xPath, element, JndiJdbc.class); + if (jndiJdbc == null) { this.jndiJdbc = null; } else { - this.jndiJdbc = new JndiJdbc(jndiJdbcElement); + this.jndiJdbc = jndiJdbc; jdbcElementCount++; } - Element tyrexElement = UtilXml.firstChildElement(element, "tyrex-dataSource"); - if (tyrexElement == null) { - this.tyrexDataSource = null; + TyrexDataSource tyrex = config.getObjectSubElement(xPath, element, TyrexDataSource.class); + if (tyrex == null) { + tyrexDataSource = null; } else { - this.tyrexDataSource = new TyrexDataSource(tyrexElement); + this.tyrexDataSource = tyrex; jdbcElementCount++; } if (jdbcElementCount > 1) { @@ -218,193 +186,276 @@ public final class Datasource { } } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + Datasource(Map configObject, String xPath) throws GenericEntityConfException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); + this.xPath = xPath; + name = getNameFromXPath(xPath); + if (name.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element name attribute is empty"); + } + String helperClass = config.getValue(configObject, "/@helper-class"); + if (helperClass.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element helper-class attribute is empty"); + } + this.helperClass = helperClass; + String fieldTypeName = config.getValue(configObject, "/@field-type-name"); + if (fieldTypeName.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element field-type-name attribute is empty"); + } + this.fieldTypeName = fieldTypeName; + useSchemas = "true".equals(config.getValue(configObject, "/@use-schemas")); + schemaName = config.getValue(configObject, "/@schema-name"); + checkOnStart = "true".equals(config.getValue(configObject, "/@check-on-start")); + addMissingOnStart = "true".equals(config.getValue(configObject, "/@add-missing-on-start")); + usePkConstraintNames = "true".equals(config.getValue(configObject, "/@use-pk-constraint-names")); + checkPksOnStart = "true".equals(config.getValue(configObject, "/@check-pks-on-start")); + constraintNameClipLength = config.getValue(configObject, "/@constraint-name-clip-length", 30, Integer.class); + useProxyCursor = "true".equalsIgnoreCase(config.getValue(configObject, "/@use-proxy-cursor")); + proxyCursorName = config.getValue(configObject, "/@proxy-cursor-name", "p_cursor", String.class); + resultFetchSize = config.getValue(configObject, "/@result-fetch-size", -1, Integer.class); + useForeignKeys = "true".equals(config.getValue(configObject, "/@use-foreign-keys")); + useForeignKeyIndices = "true".equals(config.getValue(configObject, "/@use-foreign-key-indices")); + checkFksOnStart = "true".equals(config.getValue(configObject, "/@check-fks-on-start")); + checkFkIndicesOnStart = "true".equals(config.getValue(configObject, "/@check-fks-indices-on-start")); + fkStyle = config.getValue(configObject, "/@fk-style", "name_constraint", String.class); + useFkInitiallyDeferred = "true".equals(config.getValue(configObject, "/@use-fk-initially-deferred")); + useIndices = "true".equals(config.getValue(configObject, "/@use-indices")); + useIndicesUnique = "true".equals(config.getValue(configObject, "/@use-indices-unique")); + checkIndicesOnStart = "true".equals(config.getValue(configObject, "/@check-indices-on-start")); + joinStyle = config.getValue(configObject, "/@join-style", "ansi", String.class); + aliasViewColumns = "true".equals(config.getValue(configObject, "/@alias-view-columns")); + alwaysUseConstraintKeyword = "true".equals(config.getValue(configObject, "/@always-use-constraint-keyword")); + dropFkUseForeignKeyKeyword = "true".equals(config.getValue(configObject, "/@drop-fk-use-foreign-keyword")); + useBinaryTypeForBlob = "true".equals(config.getValue(configObject, "/@use-binary-type-for-blob")); + useOrderByNulls = "true".equals(config.getValue(configObject, "/@use-order-by-nulls")); + offsetStyle = config.getValue(configObject, "/@offset-style", "none", String.class); + tableType = config.getValue(configObject, "/@table-type"); + characterSet = config.getValue(configObject, "/@character-set"); + collate = config.getValue(configObject, "/@collate"); + String maxWorkerPoolSize = config.getValue(configObject, "/@max-worker-pool-size"); + if (maxWorkerPoolSize.isEmpty()) { + this.maxWorkerPoolSize = 1; + } else { + try { + int maxWorkerPoolSizeInt = Integer.parseInt(maxWorkerPoolSize); + if (maxWorkerPoolSizeInt == 0) { + maxWorkerPoolSizeInt = 1; + } else if (maxWorkerPoolSizeInt < 0) { + maxWorkerPoolSizeInt = Math.abs(maxWorkerPoolSizeInt) * Runtime.getRuntime().availableProcessors(); + } + this.maxWorkerPoolSize = maxWorkerPoolSizeInt; + } catch (NumberFormatException e) { + throw new GenericEntityConfException(" element max-worker-pool-size attribute is invalid"); + } + } + List sqlLoadPathList = config.getSubElementsAsListEntries(xPath.concat("/sql-load-path"), null, SqlLoadPath.class); + this.sqlLoadPathList = sqlLoadPathList.isEmpty() + ? Collections.emptyList() + : Collections.unmodifiableList(sqlLoadPathList); + + List readDataList = new ArrayList<>(); + List> readDataConfs = UtilGenerics.cast(configObject.get("read-data")); + if (UtilValidate.isNotEmpty(readDataConfs)) { + for (Map readDataConf : readDataConfs) { + readDataList.add(ReadData.loadFromConfig(readDataConf, "/read-data")); + } + } + + this.readDataList = readDataList.isEmpty() + ? Collections.emptyList() + : Collections.unmodifiableList(readDataList); + + int jdbcElementCount = 0; + + Map inlineJdbcConfig = UtilGenerics.cast(configObject.get("inline-jdbc")); + if (UtilValidate.isNotEmpty(inlineJdbcConfig)) { + this.inlineJdbc = InlineJdbc.loadFromConfig(inlineJdbcConfig, "/inline-jdbc"); + jdbcElementCount++; + } else { + this.inlineJdbc = null; + } + + Map jndiJdbcConfig = UtilGenerics.cast(configObject.get("jndi-jdbc")); + if (UtilValidate.isNotEmpty(jndiJdbcConfig)) { + this.jndiJdbc = JndiJdbc.loadFromConfig(jndiJdbcConfig, "/jndi-jdbc"); + jdbcElementCount++; + } else { + this.jndiJdbc = null; + } + + Map tyrexJdbcConfig = UtilGenerics.cast(configObject.get("tyrex-jdbc")); + if (UtilValidate.isNotEmpty(tyrexJdbcConfig)) { + this.tyrexDataSource = TyrexDataSource.loadFromConfig(tyrexJdbcConfig, "/tyrex-jdbc"); + jdbcElementCount++; + } else { + this.tyrexDataSource = null; + } + + if (jdbcElementCount > 1) { + throw new GenericEntityConfException(" element is invalid: Only one of , , " + + " is allowed"); + } + } + + public static Datasource loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new Datasource(element, xPathParent); + } + + public static Datasource loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new Datasource(configMap, xPath); } - /** Returns the value of the helper-class attribute. */ public String getHelperClass() { - return this.helperClass; + return helperClass; } - /** Returns the value of the field-type-name attribute. */ public String getFieldTypeName() { - return this.fieldTypeName; + return fieldTypeName; } - /** Returns the value of the use-schemas attribute. */ public boolean getUseSchemas() { - return this.useSchemas; + return useSchemas; } - /** Returns the value of the schema-name attribute. */ public String getSchemaName() { - return this.schemaName; + return schemaName; } - /** Returns the value of the check-on-start attribute. */ public boolean getCheckOnStart() { - return this.checkOnStart; + return checkOnStart; } - /** Returns the value of the add-missing-on-start attribute. */ public boolean getAddMissingOnStart() { - return this.addMissingOnStart; + return addMissingOnStart; } - /** Returns the value of the use-pk-constraint-names attribute. */ public boolean getUsePkConstraintNames() { - return this.usePkConstraintNames; + return usePkConstraintNames; } - /** Returns the value of the check-pks-on-start attribute. */ public boolean getCheckPksOnStart() { - return this.checkPksOnStart; + return checkPksOnStart; } - /** Returns the value of the constraint-name-clip-length attribute. */ public int getConstraintNameClipLength() { - return this.constraintNameClipLength; + return constraintNameClipLength; } - /** Returns the value of the use-proxy-cursor attribute. */ public boolean getUseProxyCursor() { - return this.useProxyCursor; + return useProxyCursor; } - /** Returns the value of the proxy-cursor-name attribute. */ public String getProxyCursorName() { - return this.proxyCursorName; + return proxyCursorName; } - /** Returns the value of the result-fetch-size attribute. */ public int getResultFetchSize() { - return this.resultFetchSize; + return resultFetchSize; } - /** Returns the value of the use-foreign-keys attribute. */ public boolean getUseForeignKeys() { - return this.useForeignKeys; + return useForeignKeys; } - /** Returns the value of the use-foreign-key-indices attribute. */ public boolean getUseForeignKeyIndices() { - return this.useForeignKeyIndices; + return useForeignKeyIndices; } - /** Returns the value of the check-fks-on-start attribute. */ public boolean getCheckFksOnStart() { - return this.checkFksOnStart; + return checkFksOnStart; } - /** Returns the value of the check-fk-indices-on-start attribute. */ public boolean getCheckFkIndicesOnStart() { - return this.checkFkIndicesOnStart; + return checkFkIndicesOnStart; } - /** Returns the value of the fk-style attribute. */ public String getFkStyle() { - return this.fkStyle; + return fkStyle; } - /** Returns the value of the use-fk-initially-deferred attribute. */ public boolean getUseFkInitiallyDeferred() { - return this.useFkInitiallyDeferred; + return useFkInitiallyDeferred; } - /** Returns the value of the use-indices attribute. */ public boolean getUseIndices() { - return this.useIndices; + return useIndices; } - /** Returns the value of the use-indices-unique attribute. */ public boolean getUseIndicesUnique() { - return this.useIndicesUnique; + return useIndicesUnique; } - /** Returns the value of the check-indices-on-start attribute. */ public boolean getCheckIndicesOnStart() { - return this.checkIndicesOnStart; + return checkIndicesOnStart; } - /** Returns the value of the join-style attribute. */ public String getJoinStyle() { - return this.joinStyle; + return joinStyle; } - /** Returns the value of the alias-view-columns attribute. */ public boolean getAliasViewColumns() { - return this.aliasViewColumns; + return aliasViewColumns; } - /** Returns the value of the always-use-constraint-keyword attribute. */ public boolean getAlwaysUseConstraintKeyword() { - return this.alwaysUseConstraintKeyword; + return alwaysUseConstraintKeyword; } - /** Returns the value of the drop-fk-use-foreign-key-keyword attribute. */ public boolean getDropFkUseForeignKeyKeyword() { - return this.dropFkUseForeignKeyKeyword; + return dropFkUseForeignKeyKeyword; } - /** Returns the value of the use-binary-type-for-blob attribute. */ public boolean getUseBinaryTypeForBlob() { - return this.useBinaryTypeForBlob; + return useBinaryTypeForBlob; } - /** Returns the value of the use-order-by-nulls attribute. */ public boolean getUseOrderByNulls() { - return this.useOrderByNulls; + return useOrderByNulls; } - /** Returns the value of the offset-style attribute. */ public String getOffsetStyle() { - return this.offsetStyle; + return offsetStyle; } - /** Returns the value of the table-type attribute. */ public String getTableType() { - return this.tableType; + return tableType; } - /** Returns the value of the character-set attribute. */ public String getCharacterSet() { - return this.characterSet; + return characterSet; } - /** Returns the value of the collate attribute. */ public String getCollate() { - return this.collate; + return collate; } - /** Returns the value of the max-worker-pool-size attribute. */ public int getMaxWorkerPoolSize() { - return this.maxWorkerPoolSize; + return maxWorkerPoolSize; } - /** Returns the <sql-load-path> child elements. */ public List getSqlLoadPathList() { - return this.sqlLoadPathList; + return sqlLoadPathList; } - /** Returns the <read-data> child elements. */ public List getReadDataList() { - return this.readDataList; + return readDataList; } - /** Returns the <inline-jdbc> child element. */ public InlineJdbc getInlineJdbc() { - return this.inlineJdbc; + return inlineJdbc; } - /** Returns the <jndi-jdbc> child element. */ public JndiJdbc getJndiJdbc() { - return this.jndiJdbc; + return jndiJdbc; } - /** Returns the <tyrex-dataSource> child element. */ public TyrexDataSource getTyrexDataSource() { - return this.tyrexDataSource; + return tyrexDataSource; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DebugXaResources.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DebugXaResources.java index 624964bf5d8..78ad887d59f 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DebugXaResources.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DebugXaResources.java @@ -18,31 +18,51 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <debug-xa-resources> element. * * @see entity-config.xsd */ @ThreadSafe -public final class DebugXaResources { +public final class DebugXaResources extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "debug-xa-resources"; + private final String xPath; - private final boolean value; // type = xs:string + private final boolean value; - DebugXaResources(Element element) throws GenericEntityConfException { + DebugXaResources(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String value = element.getAttribute("value").intern(); + xPath = xPathParent.concat("/debug-xa-resources"); + String value = config.getValue(xPath + "/@value"); if (value.isEmpty()) { throw new GenericEntityConfException(" element value attribute is empty" + lineNumberText); } this.value = "true".equals(value); } - /** Returns the value of the value attribute. */ public boolean getValue() { - return this.value; + return value; + } + + public static DebugXaResources loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new DebugXaResources(element, xPathParent); + } + + public static DebugXaResources loadFromConfig(Map configMap, String xPath) { + return null; + } + + @Override + public String getName() { + return "debug-xa-resources"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DelegatorElement.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DelegatorElement.java index 358e2b4c320..fe22dc4b65a 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DelegatorElement.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/DelegatorElement.java @@ -18,14 +18,13 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; +import org.apache.ofbiz.base.config.ConfigHelper; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -35,149 +34,167 @@ * @see entity-config.xsd */ @ThreadSafe -public final class DelegatorElement { +public final class DelegatorElement extends AbstractConfigElement { - private final String name; // type = xs:string - private final String entityModelReader; // type = xs:string - private final String entityGroupReader; // type = xs:string - private final String entityEcaReader; // type = xs:string + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "delegator"; + private final String xPath; + + private final String name; + private final String entityModelReader; + private final String entityGroupReader; + private final String entityEcaReader; private final boolean entityEcaEnabled; - private final String entityEcaHandlerClassName; // type = xs:string + private final String entityEcaHandlerClassName; private final boolean distributedCacheClearEnabled; - private final String distributedCacheClearClassName; // type = xs:string - private final String distributedCacheClearUserLoginId; // type = xs:string - private final String sequencedIdPrefix; // type = xs:string - private final String defaultGroupName; // type = xs:string - private final String keyEncryptingKey; // type = xs:string - private final List groupMapList; // - private final Map groupMapMap; // - - DelegatorElement(Element element) throws GenericEntityConfException { + private final String distributedCacheClearClassName; + private final String distributedCacheClearUserLoginId; + private final String sequencedIdPrefix; + private final String defaultGroupName; + private final String keyEncryptingKey; + private final List groupMapList; + private final Map groupMapMap; + + DelegatorElement(Element element, String xPathParent) throws GenericEntityConfException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String name = element.getAttribute("name").intern(); - if (name.isEmpty()) { + if (name.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - String entityModelReader = element.getAttribute("entity-model-reader").intern(); - if (entityModelReader.isEmpty()) { + this.xPath = xPathParent.concat("/delegator[@name='" + name + "']"); + String entityModelReader = config.getValue(this.xPath + "/@entity-model-reader"); + if (entityModelReader.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element entity-model-reader attribute is empty" + lineNumberText); } this.entityModelReader = entityModelReader; - String entityGroupReader = element.getAttribute("entity-group-reader").intern(); - if (entityGroupReader.isEmpty()) { + String entityGroupReader = config.getValue(this.xPath + "/@entity-group-reader"); + if (entityGroupReader.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element entity-group-reader attribute is empty" + lineNumberText); } this.entityGroupReader = entityGroupReader; - this.entityEcaReader = element.getAttribute("entity-eca-reader").intern(); - this.entityEcaEnabled = !"false".equalsIgnoreCase(element.getAttribute("entity-eca-enabled")); - String entityEcaHandlerClassName = element.getAttribute("entity-eca-handler-class-name").intern(); - if (entityEcaHandlerClassName.isEmpty()) { - entityEcaHandlerClassName = "org.apache.ofbiz.entityext.eca.DelegatorEcaHandler"; + entityEcaReader = config.getValue(this.xPath + "/@entity-eca-reader"); + entityEcaEnabled = !"false".equalsIgnoreCase(config.getValue(this.xPath + "/@entity-eca-enabled")); + entityEcaHandlerClassName = config.getValue(this.xPath + "/@entity-eca-handler-class-name", + "org.apache.ofbiz.entityext.eca.DelegatorEcaHandler", String.class); + distributedCacheClearEnabled = "true".equalsIgnoreCase(config.getValue(this.xPath + "/@distributed-cache-clear-enabled")); + distributedCacheClearClassName = config.getValue( + this.xPath + "/@distributed-cache-clear-class-name", "org.apache.ofbiz.entityext.cache.EntityCacheServices", String.class); + distributedCacheClearUserLoginId = config.getValue(this.xPath + + "/@distributed-cache-clear-user-login-id", "system", String.class); + sequencedIdPrefix = config.getValue(this.xPath + "/@sequenced-id-prefix"); + defaultGroupName = config.getValue(this.xPath + "/@default-group-name", "org.apache.ofbiz", String.class); + keyEncryptingKey = config.getValue(this.xPath + "/@key-encrypting-key"); + List groupMapList = config.getSubElementsAsListEntries(this.xPath, element, GroupMap.class); + if (groupMapList.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element child elements are missing" + lineNumberText); } - this.entityEcaHandlerClassName = entityEcaHandlerClassName; - this.distributedCacheClearEnabled = "true".equalsIgnoreCase(element.getAttribute("distributed-cache-clear-enabled")); - String distributedCacheClearClassName = element.getAttribute("distributed-cache-clear-class-name").intern(); - if (distributedCacheClearClassName.isEmpty()) { - distributedCacheClearClassName = "org.apache.ofbiz.entityext.cache.EntityCacheServices"; + this.groupMapList = Collections.unmodifiableList(groupMapList); + groupMapMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(this.xPath, element, GroupMap.class)); + } + + DelegatorElement(Map configObject, String xPath) throws GenericEntityConfException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element name attribute is empty"); } - this.distributedCacheClearClassName = distributedCacheClearClassName; - String distributedCacheClearUserLoginId = element.getAttribute("distributed-cache-clear-user-login-id").intern(); - if (distributedCacheClearUserLoginId.isEmpty()) { - distributedCacheClearUserLoginId = "system"; + this.name = name; + String entityModelReader = config.getValue(configObject, "/@entity-model-reader"); + if (entityModelReader.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element entity-model-reader attribute is empty"); } - this.distributedCacheClearUserLoginId = distributedCacheClearUserLoginId; - this.sequencedIdPrefix = element.getAttribute("sequenced-id-prefix").intern(); - String defaultGroupName = element.getAttribute("default-group-name").intern(); - if (defaultGroupName.isEmpty()) { - defaultGroupName = "org.apache.ofbiz"; + this.entityModelReader = entityModelReader; + String entityGroupReader = config.getValue(configObject, "/@entity-group-reader"); + if (entityGroupReader.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element entity-group-reader attribute is empty"); } - this.defaultGroupName = defaultGroupName; - this.keyEncryptingKey = element.getAttribute("key-encrypting-key").intern(); - List groupMapElementList = UtilXml.childElementList(element, "group-map"); - if (groupMapElementList.isEmpty()) { - throw new GenericEntityConfException(" element child elements are missing" + lineNumberText); - } else { - List groupMapList = new ArrayList<>(groupMapElementList.size()); - Map groupMapMap = new HashMap<>(); - for (Element groupMapElement : groupMapElementList) { - GroupMap groupMap = new GroupMap(groupMapElement); - groupMapList.add(groupMap); - groupMapMap.put(groupMap.getGroupName(), groupMap.getDatasourceName()); - } - this.groupMapList = Collections.unmodifiableList(groupMapList); - this.groupMapMap = Collections.unmodifiableMap(groupMapMap); + this.entityGroupReader = entityGroupReader; + entityEcaReader = config.getValue(configObject, "/@entity-eca-reader"); + entityEcaEnabled = !"false".equalsIgnoreCase(config.getValue(configObject, "/@entity-eca-enabled")); + entityEcaHandlerClassName = config.getValue(configObject, "/@entity-eca-handler-class-name", + "org.apache.ofbiz.entityext.eca.DelegatorEcaHandler", String.class); + distributedCacheClearEnabled = "true".equalsIgnoreCase(config.getValue(configObject, "/@distributed-cache-clear-enabled")); + distributedCacheClearClassName = config.getValue(configObject, "/@distributed-cache-clear-class-name", + "org.apache.ofbiz.entityext.cache.EntityCacheServices", String.class); + distributedCacheClearUserLoginId = config.getValue(configObject, + "/@distributed-cache-clear-user-login-id", "system", String.class); + sequencedIdPrefix = config.getValue(configObject, "/@sequenced-id-prefix"); + defaultGroupName = config.getValue(configObject, "/@default-group-name", "org.apache.ofbiz", String.class); + keyEncryptingKey = config.getValue(configObject, "/@key-encrypting-key"); + + List groupMapList = config.getSubElementsAsListEntries(this.xPath, null, GroupMap.class); + if (groupMapList.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element child elements are missing"); } + this.groupMapList = Collections.unmodifiableList(groupMapList); + this.groupMapMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(this.xPath, null, GroupMap.class)); } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + public static DelegatorElement loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new DelegatorElement(element, xPathParent); + } + + public static DelegatorElement loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new DelegatorElement(configMap, xPath); } - /** Returns the value of the entity-model-reader attribute. */ public String getEntityModelReader() { - return this.entityModelReader; + return entityModelReader; } - /** Returns the value of the entity-group-reader attribute. */ public String getEntityGroupReader() { - return this.entityGroupReader; + return entityGroupReader; } - /** Returns the value of the entity-eca-reader attribute. */ public String getEntityEcaReader() { - return this.entityEcaReader; + return entityEcaReader; } - /** Returns the value of the entity-eca-enabled attribute. */ public boolean getEntityEcaEnabled() { - return this.entityEcaEnabled; + return entityEcaEnabled; } - /** Returns the value of the entity-eca-handler-class-name attribute. */ public String getEntityEcaHandlerClassName() { - return this.entityEcaHandlerClassName; + return entityEcaHandlerClassName; } - /** Returns the value of the distributed-cache-clear-enabled attribute. */ public boolean getDistributedCacheClearEnabled() { - return this.distributedCacheClearEnabled; + return distributedCacheClearEnabled; } - /** Returns the value of the distributed-cache-clear-class-name attribute. */ public String getDistributedCacheClearClassName() { - return this.distributedCacheClearClassName; + return distributedCacheClearClassName; } - /** Returns the value of the distributed-cache-clear-user-login-id attribute. */ public String getDistributedCacheClearUserLoginId() { - return this.distributedCacheClearUserLoginId; + return distributedCacheClearUserLoginId; } - /** Returns the value of the sequenced-id-prefix attribute. */ public String getSequencedIdPrefix() { - return this.sequencedIdPrefix; + return sequencedIdPrefix; } - /** Returns the value of the default-group-name attribute. */ public String getDefaultGroupName() { - return this.defaultGroupName; + return defaultGroupName; } - /** Returns the value of the key-encrypting-key attribute. */ public String getKeyEncryptingKey() { - return this.keyEncryptingKey; + return keyEncryptingKey; } - /** Returns the <group-map> child elements. */ public List getGroupMapList() { - return this.groupMapList; + return groupMapList; } - /** Returns the specified <group-map> datasource-name attribute value, - * or null if the <group-map> element does not exist . */ - public String getGroupDataSource(String groupName) { - return this.groupMapMap.get(groupName); + public GroupMap getGroupDataSource(String groupName) { + return groupMapMap.get(groupName); + } + + @Override + public String getName() { + return name; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfig.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfig.java index 1861e176cf1..b7193343651 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfig.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfig.java @@ -18,18 +18,14 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import java.net.URL; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.ofbiz.base.config.ConfigHelper; +import org.apache.ofbiz.base.config.XmlFileReader; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilProperties; -import org.apache.ofbiz.base.util.UtilURL; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -40,192 +36,117 @@ */ @ThreadSafe public final class EntityConfig { - public static final String ENTITY_ENGINE_XML_FILENAME = "entityengine.xml"; - private static final String MODULE = EntityConfig.class.getName(); + public static final String ENTITY_ENGINE_XML_FILENAME = "entityengine.xml"; + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); - private static final EntityConfig INSTANCE = createNewInstance(); - private final List resourceLoaderList; // - private final Map resourceLoaderMap; // - private final TransactionFactory transactionFactory; // - private final ConnectionFactory connectionFactory; // - private final DebugXaResources debugXaResources; // - private final List delegatorList; // - private final Map delegatorMap; // - private final List entityModelReaderList; // - private final Map entityModelReaderMap; // - private final List entityGroupReaderList; // - private final Map entityGroupReaderMap; // - private final List entityEcaReaderList; // - private final Map entityEcaReaderMap; // - private final List entityDataReaderList; // - private final Map entityDataReaderMap; // - private final List fieldTypeList; // - private final Map fieldTypeMap; // - private final List datasourceList; // + private final List resourceLoaderList; + private final Map resourceLoaderMap; + private final TransactionFactory transactionFactory; + private final ConnectionFactory connectionFactory; + private final DebugXaResources debugXaResources; + private final List delegatorList; + private final Map delegatorMap; + private final List entityModelReaderList; + private final Map entityModelReaderMap; + private final List entityGroupReaderList; + private final Map entityGroupReaderMap; + private final List entityEcaReaderList; + private final Map entityEcaReaderMap; + private final List entityDataReaderList; + private final Map entityDataReaderMap; + private final List fieldTypeList; + private final Map fieldTypeMap; + private final List datasourceList; private final Map datasourceMap; - private EntityConfig() throws GenericEntityConfException { - Element element; - URL confUrl = UtilURL.fromResource(ENTITY_ENGINE_XML_FILENAME); - if (confUrl == null) { - throw new GenericEntityConfException("Could not find the " + ENTITY_ENGINE_XML_FILENAME + " file"); - } - try { - element = UtilXml.readXmlDocument(confUrl, true, true).getDocumentElement(); - } catch (Exception e) { - throw new GenericEntityConfException("Exception thrown while reading " + ENTITY_ENGINE_XML_FILENAME + ": ", e); - } - - List resourceLoaderElementList = UtilXml.childElementList(element, "resource-loader"); - if (resourceLoaderElementList.isEmpty()) { + public EntityConfig() throws GenericEntityConfException { + Element entityConfigRootXmlElement = XmlFileReader.read(ENTITY_ENGINE_XML_FILENAME); + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); + List resourceLoaderList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, ResourceLoader.class); + if (resourceLoaderList.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element child elements are missing"); - } else { - List resourceLoaderList = new ArrayList<>(resourceLoaderElementList.size()); - Map resourceLoaderMap = new HashMap<>(); - for (Element resourceLoaderElement : resourceLoaderElementList) { - ResourceLoader resourceLoader = new ResourceLoader(resourceLoaderElement); - resourceLoaderList.add(resourceLoader); - resourceLoaderMap.put(resourceLoader.getName(), resourceLoader); - } - this.resourceLoaderList = Collections.unmodifiableList(resourceLoaderList); - this.resourceLoaderMap = Collections.unmodifiableMap(resourceLoaderMap); } - Element transactionFactoryElement = UtilXml.firstChildElement(element, "transaction-factory"); - if (transactionFactoryElement == null) { + this.resourceLoaderList = Collections.unmodifiableList(resourceLoaderList); + resourceLoaderMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, + ResourceLoader.class)); + + TransactionFactory transactionFactory = config.getObjectSubElement(entityConfigRootXmlElement, TransactionFactory.class); + if (transactionFactory == null && checkStructure) { throw new GenericEntityConfException(" element child element is missing"); - } else { - this.transactionFactory = new TransactionFactory(transactionFactoryElement); - } - Element connectionFactoryElement = UtilXml.firstChildElement(element, "connection-factory"); - if (connectionFactoryElement != null) { - this.connectionFactory = new ConnectionFactory(connectionFactoryElement); - } else { - this.connectionFactory = null; } - Element debugXaResourcesElement = UtilXml.firstChildElement(element, "debug-xa-resources"); - if (debugXaResourcesElement == null) { + this.transactionFactory = transactionFactory; + connectionFactory = config.getObjectSubElement(entityConfigRootXmlElement, ConnectionFactory.class); + + DebugXaResources debugXaResources = config.getObjectSubElement(entityConfigRootXmlElement, DebugXaResources.class); + if (debugXaResources == null && checkStructure) { throw new GenericEntityConfException(" element child element is missing"); - } else { - this.debugXaResources = new DebugXaResources(debugXaResourcesElement); } - List delegatorElementList = UtilXml.childElementList(element, "delegator"); - if (delegatorElementList.isEmpty()) { + this.debugXaResources = debugXaResources; + + List delegatorList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, DelegatorElement.class); + if (delegatorList.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element child elements are missing"); - } else { - List delegatorList = new ArrayList<>(delegatorElementList.size()); - Map delegatorMap = new HashMap<>(); - for (Element delegatorElement : delegatorElementList) { - DelegatorElement delegator = new DelegatorElement(delegatorElement); - delegatorList.add(delegator); - delegatorMap.put(delegator.getName(), delegator); - } - this.delegatorList = Collections.unmodifiableList(delegatorList); - this.delegatorMap = Collections.unmodifiableMap(delegatorMap); } - List entityModelReaderElementList = UtilXml.childElementList(element, "entity-model-reader"); - if (entityModelReaderElementList.isEmpty()) { + this.delegatorList = Collections.unmodifiableList(delegatorList); + delegatorMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, DelegatorElement.class)); + + List entityModelReaderList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, + EntityModelReader.class); + if (entityModelReaderList.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element child elements are missing"); - } else { - List entityModelReaderList = new ArrayList<>(entityModelReaderElementList.size()); - Map entityModelReaderMap = new HashMap<>(); - for (Element entityModelReaderElement : entityModelReaderElementList) { - EntityModelReader entityModelReader = new EntityModelReader(entityModelReaderElement); - entityModelReaderList.add(entityModelReader); - entityModelReaderMap.put(entityModelReader.getName(), entityModelReader); - } - this.entityModelReaderList = Collections.unmodifiableList(entityModelReaderList); - this.entityModelReaderMap = Collections.unmodifiableMap(entityModelReaderMap); } - List entityGroupReaderElementList = UtilXml.childElementList(element, "entity-group-reader"); - if (entityGroupReaderElementList.isEmpty()) { + this.entityModelReaderList = Collections.unmodifiableList(entityModelReaderList); + entityModelReaderMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, + EntityModelReader.class)); + + List entityGroupReaderList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, + EntityGroupReader.class); + if (entityGroupReaderList.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element child elements are missing"); - } else { - List entityGroupReaderList = new ArrayList<>(entityGroupReaderElementList.size()); - Map entityGroupReaderMap = new HashMap<>(); - for (Element entityGroupReaderElement : entityGroupReaderElementList) { - EntityGroupReader entityGroupReader = new EntityGroupReader(entityGroupReaderElement); - entityGroupReaderList.add(entityGroupReader); - entityGroupReaderMap.put(entityGroupReader.getName(), entityGroupReader); - } - this.entityGroupReaderList = Collections.unmodifiableList(entityGroupReaderList); - this.entityGroupReaderMap = Collections.unmodifiableMap(entityGroupReaderMap); } - List entityEcaReaderElementList = UtilXml.childElementList(element, "entity-eca-reader"); - if (entityEcaReaderElementList.isEmpty()) { - this.entityEcaReaderList = Collections.emptyList(); - this.entityEcaReaderMap = Collections.emptyMap(); + this.entityGroupReaderList = Collections.unmodifiableList(entityGroupReaderList); + entityGroupReaderMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, + EntityGroupReader.class)); + + List entityEcaReaderList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, EntityEcaReader.class); + this.entityEcaReaderList = Collections.unmodifiableList(entityEcaReaderList); + if (entityEcaReaderList.isEmpty() && checkStructure) { + entityEcaReaderMap = Map.of(); } else { - List entityEcaReaderList = new ArrayList<>(entityEcaReaderElementList.size()); - Map entityEcaReaderMap = new HashMap<>(); - for (Element entityEcaReaderElement : entityEcaReaderElementList) { - EntityEcaReader entityEcaReader = new EntityEcaReader(entityEcaReaderElement); - entityEcaReaderList.add(new EntityEcaReader(entityEcaReaderElement)); - entityEcaReaderMap.put(entityEcaReader.getName(), entityEcaReader); - } - this.entityEcaReaderList = Collections.unmodifiableList(entityEcaReaderList); - this.entityEcaReaderMap = Collections.unmodifiableMap(entityEcaReaderMap); + entityEcaReaderMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, + EntityEcaReader.class)); } - List entityDataReaderElementList = UtilXml.childElementList(element, "entity-data-reader"); - if (entityDataReaderElementList.isEmpty()) { - this.entityDataReaderList = Collections.emptyList(); - this.entityDataReaderMap = Collections.emptyMap(); + List entityDataReaderList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, + EntityDataReader.class); + this.entityDataReaderList = Collections.unmodifiableList(entityDataReaderList); + if (entityDataReaderList.isEmpty()) { + entityDataReaderMap = Map.of(); } else { - List entityDataReaderList = new ArrayList<>(entityDataReaderElementList.size()); - Map entityDataReaderMap = new HashMap<>(); - for (Element entityDataReaderElement : entityDataReaderElementList) { - EntityDataReader entityDataReader = new EntityDataReader(entityDataReaderElement); - entityDataReaderList.add(entityDataReader); - entityDataReaderMap.put(entityDataReader.getName(), entityDataReader); - } - this.entityDataReaderList = Collections.unmodifiableList(entityDataReaderList); - this.entityDataReaderMap = Collections.unmodifiableMap(entityDataReaderMap); + entityDataReaderMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, + EntityDataReader.class)); } - List fieldTypeElementList = UtilXml.childElementList(element, "field-type"); - if (fieldTypeElementList.isEmpty()) { + List fieldTypeList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, FieldType.class); + if (fieldTypeList.isEmpty() && checkStructure) { throw new GenericEntityConfException(" element child elements are missing"); - } else { - List fieldTypeList = new ArrayList<>(fieldTypeElementList.size()); - Map fieldTypeMap = new HashMap<>(); - for (Element fieldTypeElement : fieldTypeElementList) { - FieldType fieldType = new FieldType(fieldTypeElement); - fieldTypeList.add(fieldType); - fieldTypeMap.put(fieldType.getName(), fieldType); - } - this.fieldTypeList = Collections.unmodifiableList(fieldTypeList); - this.fieldTypeMap = Collections.unmodifiableMap(fieldTypeMap); } - List datasourceElementList = UtilXml.childElementList(element, "datasource"); - if (datasourceElementList.isEmpty()) { - throw new GenericEntityConfException(" element child elements are missing"); - } else { - List datasourceList = new ArrayList<>(datasourceElementList.size()); - Map datasourceMap = new HashMap<>(); - for (Element datasourceElement : datasourceElementList) { - Datasource datasource = new Datasource(datasourceElement); - datasourceList.add(datasource); - datasourceMap.put(datasource.getName(), datasource); - } - this.datasourceList = Collections.unmodifiableList(datasourceList); - this.datasourceMap = Collections.unmodifiableMap(datasourceMap); - } - } + this.fieldTypeList = Collections.unmodifiableList(fieldTypeList); + fieldTypeMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, FieldType.class)); - private static EntityConfig createNewInstance() { - EntityConfig entityConfig = null; - try { - entityConfig = new EntityConfig(); - } catch (GenericEntityConfException gece) { - Debug.logError(gece, MODULE); + List datasourceList = config.getSubElementsAsListEntries(entityConfigRootXmlElement, Datasource.class); + if (datasourceList.isEmpty() && checkStructure) { + throw new GenericEntityConfException(" element child elements are missing"); } - return entityConfig; + this.datasourceList = Collections.unmodifiableList(datasourceList); + datasourceMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(entityConfigRootXmlElement, Datasource.class)); } public static EntityConfig getInstance() throws GenericEntityConfException { - if (INSTANCE == null) { + EntityConfig entityConfig = EntityConfigFactory.getInstance(); + if (entityConfig == null) { throw new GenericEntityConfException("EntityConfig is not initialized."); } - return INSTANCE; + return entityConfig; } public static String createConfigFileLineNumberText(Element element) { @@ -235,97 +156,78 @@ public static String createConfigFileLineNumberText(Element element) { return ""; } - /** Returns the specified <resource-loader> child element, or null if no child element was found. */ public ResourceLoader getResourceLoader(String name) { return this.resourceLoaderMap.get(name); } - /** Returns the <resource-loader> child elements. */ public List getResourceLoaderList() { return this.resourceLoaderList; } - /** Returns the <transaction-factory> child element, or null if no child element was found. */ public TransactionFactory getTransactionFactory() { return this.transactionFactory; } - /** Returns the <connection-factory> child element, or null if no child element was found. */ public ConnectionFactory getConnectionFactory() { return this.connectionFactory; } - /** Returns the <debug-xa-resources> child element, or null if no child element was found. */ public DebugXaResources getDebugXaResources() { return this.debugXaResources; } - /** Returns the specified <delegator> child element, or null if no child element was found. */ public DelegatorElement getDelegator(String name) { return this.delegatorMap.get(name); } - /** Returns the <delegator> child elements. */ public List getDelegatorList() { return this.delegatorList; } - /** Returns the specified <entity-model-reader> child element, or null if no child element was found. */ public EntityModelReader getEntityModelReader(String name) { return this.entityModelReaderMap.get(name); } - /** Returns the <entity-model-reader> child elements. */ public List getEntityModelReaderList() { return this.entityModelReaderList; } - /** Returns the specified <entity-group-reader> child element, or null if no child element was found. */ public EntityGroupReader getEntityGroupReader(String name) { return this.entityGroupReaderMap.get(name); } - /** Returns the <entity-group-reader> child elements. */ public List getEntityGroupReaderList() { return this.entityGroupReaderList; } - /** Returns the specified <entity-eca-reader> child element, or null if no child element was found. */ public EntityEcaReader getEntityEcaReader(String name) { return this.entityEcaReaderMap.get(name); } - /** Returns the <entity-eca-reader> child elements. */ public List getEntityEcaReaderList() { return this.entityEcaReaderList; } - /** Returns the specified <entity-data-reader> child element, or null if no child element was found. */ public EntityDataReader getEntityDataReader(String name) { return this.entityDataReaderMap.get(name); } - /** Returns the <entity-data-reader> child elements. */ public List getEntityDataReaderList() { return this.entityDataReaderList; } - /** Returns the specified <field-type> child element, or null if no child element was found. */ public FieldType getFieldType(String name) { return this.fieldTypeMap.get(name); } - /** Returns the <field-type> child elements. */ public List getFieldTypeList() { return this.fieldTypeList; } - /** Returns the <datasource> child elements. */ public List getDatasourceList() { return this.datasourceList; } - /** Returns the specified <datasource> child element or null if it does not exist. */ public static Datasource getDatasource(String name) { try { return getInstance().datasourceMap.get(name); @@ -335,13 +237,6 @@ public static Datasource getDatasource(String name) { } } - /** - * Returns the configured JDBC password. - * @param inlineJdbcElement - * @return The configured JDBC password. - * @throws GenericEntityConfException If the password was not found. - * @see entity-config.xsd - */ public static String getJdbcPassword(InlineJdbc inlineJdbcElement) throws GenericEntityConfException { String jdbcPassword = inlineJdbcElement.getJdbcPassword(); if (!jdbcPassword.isEmpty()) { @@ -361,7 +256,6 @@ public static String getJdbcPassword(InlineJdbc inlineJdbcElement) throws Generi return jdbcPassword; } - /** Returns the <datasource> child elements as a Map. */ public Map getDatasourceMap() { return this.datasourceMap; } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfigFactory.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfigFactory.java new file mode 100644 index 00000000000..1b04b3c6a93 --- /dev/null +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfigFactory.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.entity.config.model; + +import org.apache.ofbiz.entity.GenericEntityConfException; + +public class EntityConfigFactory { + private static EntityConfig instance = null; + + public static EntityConfig createInstance() throws GenericEntityConfException { + return new EntityConfig(); + } + + public static EntityConfig getInstance() throws GenericEntityConfException { + if (instance == null) { + instance = createInstance(); + } + return instance; + } +} diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfigGetter.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfigGetter.java new file mode 100644 index 00000000000..5c09c93e255 --- /dev/null +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityConfigGetter.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.entity.config.model; + +import org.apache.ofbiz.base.config.AbstractXmlConfigGetter; +import org.apache.ofbiz.base.lang.ThreadSafe; + +/** + * A singleton class that models the <entity-config> element. + * + * @see entity-config.xsd + */ +@ThreadSafe +public final class EntityConfigGetter extends AbstractXmlConfigGetter { + public static final String ENTITY_ENGINE_XML_FILENAME = "entityengine.xml"; + private static final EntityConfigGetter INSTANCE = initInstance(); + + + public EntityConfigGetter() { + super(ENTITY_ENGINE_XML_FILENAME, "/entity-config"); + } + + public static EntityConfigGetter getInstance() { + return INSTANCE; + } + + public static EntityConfigGetter initInstance() { + synchronized (EntityConfigGetter.class) { + return new EntityConfigGetter(); + } + } + +} diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityDataReader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityDataReader.java index 91dbccb2a85..f9c8c5b7b89 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityDataReader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityDataReader.java @@ -18,12 +18,12 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -33,45 +33,62 @@ * @see entity-config.xsd */ @ThreadSafe -public final class EntityDataReader { +public final class EntityDataReader extends AbstractConfigElement { - private final String name; // type = xs:string - private final List resourceList; // + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "entity-data-reader"; + private final String xPath; + + private final String name; + private final List resourceList; public EntityDataReader(String name) throws GenericEntityConfException { if (name == null || name.isEmpty()) { throw new GenericEntityConfException("EntityDataReader name cannot be empty"); } this.name = name; - this.resourceList = Collections.emptyList(); + resourceList = Collections.emptyList(); + xPath = null; } - EntityDataReader(Element element) throws GenericEntityConfException { + EntityDataReader(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String name = element.getAttribute("name").intern(); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - List resourceElementList = UtilXml.childElementList(element, "resource"); - if (resourceElementList.isEmpty()) { - this.resourceList = Collections.emptyList(); - } else { - List resourceList = new ArrayList<>(resourceElementList.size()); - for (Element resourceElement : resourceElementList) { - resourceList.add(new Resource(resourceElement)); - } - this.resourceList = Collections.unmodifiableList(resourceList); + xPath = xPathParent.concat("/entity-data-reader[@name='" + name + "'"); + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), element, Resource.class); + this.resourceList = resourceList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resourceList); + } + + EntityDataReader(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new GenericEntityConfException(" element name attribute is empty"); } + this.name = name; + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), null, Resource.class); + this.resourceList = resourceList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resourceList); } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + public static EntityDataReader loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new EntityDataReader(element, xPathParent); + } + + public static EntityDataReader loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new EntityDataReader(configMap, xPath); } - /** Returns the <resource> child elements. */ public List getResourceList() { - return this.resourceList; + return resourceList; + } + + @Override + public String getName() { + return name; } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityEcaReader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityEcaReader.java index 53ec2dfa318..c246def0f67 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityEcaReader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityEcaReader.java @@ -18,12 +18,11 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; - +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -33,37 +32,57 @@ * @see entity-config.xsd */ @ThreadSafe -public final class EntityEcaReader { +public final class EntityEcaReader extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "entity-eca-reader"; + private final String xPath; - private final String name; // type = xs:string - private final List resourceList; // + private final String name; + private final List resourceList; - EntityEcaReader(Element element) throws GenericEntityConfException { + EntityEcaReader(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String name = element.getAttribute("name").intern(); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - List resourceElementList = UtilXml.childElementList(element, "resource"); - if (resourceElementList.isEmpty()) { + xPath = xPathParent.concat("/entity-eca-reader[@name='" + name + "']"); + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), element, Resource.class); + this.resourceList = resourceList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resourceList); + } + + EntityEcaReader(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new GenericEntityConfException(" element name attribute is empty"); + } + this.name = name; + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), null, Resource.class); + if (resourceList.isEmpty()) { this.resourceList = Collections.emptyList(); } else { - List resourceList = new ArrayList<>(resourceElementList.size()); - for (Element resourceElement : resourceElementList) { - resourceList.add(new Resource(resourceElement)); - } this.resourceList = Collections.unmodifiableList(resourceList); } } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + public static EntityEcaReader loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new EntityEcaReader(element, xPathParent); + } + + public static EntityEcaReader loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new EntityEcaReader(configMap, xPath); } - /** Returns the <resource> child elements. */ public List getResourceList() { - return this.resourceList; + return resourceList; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityGroupReader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityGroupReader.java index b12724cabf7..ad34cc54378 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityGroupReader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityGroupReader.java @@ -18,12 +18,12 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -33,51 +33,73 @@ * @see entity-config.xsd */ @ThreadSafe -public final class EntityGroupReader { +public final class EntityGroupReader extends AbstractConfigElement { - private final String name; // type = xs:string - private final String loader; // type = xs:string - private final String location; // type = xs:string - private final List resourceList; // + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "entity-group-reader"; + private final String xPath; - EntityGroupReader(Element element) throws GenericEntityConfException { + private final String name; + private final String loader; + private final String location; + private final List resourceList; + + EntityGroupReader(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String name = element.getAttribute("name").intern(); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - this.loader = element.getAttribute("loader").intern(); - this.location = element.getAttribute("location").intern(); - List resourceElementList = UtilXml.childElementList(element, "resource"); - if (resourceElementList.isEmpty()) { + this.xPath = xPathParent.concat("/entity-group-reader[@name='" + name + "']"); + loader = config.getValue(xPath + "/@loader"); + location = config.getValue(xPath + "/@location"); + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), element, Resource.class); + if (resourceList.isEmpty()) { this.resourceList = Collections.emptyList(); } else { - List resourceList = new ArrayList<>(resourceElementList.size()); - for (Element resourceElement : resourceElementList) { - resourceList.add(new Resource(resourceElement)); - } this.resourceList = Collections.unmodifiableList(resourceList); } } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + EntityGroupReader(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new GenericEntityConfException(" element name attribute is empty"); + } + this.name = name; + loader = config.getValue(configObject, "/@loader"); + location = config.getValue(configObject, "/@location"); + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), null, Resource.class); + this.resourceList = resourceList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resourceList); + } + + public static EntityGroupReader loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException { + return new EntityGroupReader(element, xPathParent); + } + + public static EntityGroupReader loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException { + return new EntityGroupReader(configMap, xPath); } - /** Returns the value of the loader attribute. */ public String getLoader() { - return this.loader; + return loader; } - /** Returns the value of the location attribute. */ public String getLocation() { - return this.location; + return location; } - /** Returns the <resource> child elements. */ public List getResourceList() { - return this.resourceList; + return resourceList; + } + + @Override + public String getName() { + return name; } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityModelReader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityModelReader.java index cebc36512e4..b06623ef00c 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityModelReader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/EntityModelReader.java @@ -18,12 +18,12 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -33,37 +33,55 @@ * @see entity-config.xsd */ @ThreadSafe -public final class EntityModelReader { +public final class EntityModelReader extends AbstractConfigElement { - private final String name; // type = xs:string - private final List resourceList; // + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "entity-model-reader"; + private final String xPath; - EntityModelReader(Element element) throws GenericEntityConfException { + private final String name; + private final List resourceList; + + EntityModelReader(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String name = element.getAttribute("name").intern(); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - List resourceElementList = UtilXml.childElementList(element, "resource"); - if (resourceElementList.isEmpty()) { - this.resourceList = Collections.emptyList(); - } else { - List resourceList = new ArrayList<>(resourceElementList.size()); - for (Element resourceElement : resourceElementList) { - resourceList.add(new Resource(resourceElement)); - } - this.resourceList = Collections.unmodifiableList(resourceList); + xPath = xPathParent.concat("/entity-model-reader[@name='" + name + "']"); + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), element, Resource.class); + this.resourceList = resourceList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resourceList); + } + + EntityModelReader(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String name = configObject.containsKey("name") + ? configObject.get("name").toString() + : getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new GenericEntityConfException(" element name attribute is empty"); } + this.name = name; + List resourceList = config.getSubElementsAsListEntries(xPath.concat("/resource"), null, Resource.class); + this.resourceList = resourceList.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resourceList); } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + public static EntityModelReader loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new EntityModelReader(element, xPathParent); + } + + public static EntityModelReader loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new EntityModelReader(configMap, xPath); } - /** Returns the <resource> child elements. */ public List getResourceList() { - return this.resourceList; + return resourceList; + } + + @Override + public String getName() { + return name; } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/FieldType.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/FieldType.java index ec4bc884d90..7a85d188325 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/FieldType.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/FieldType.java @@ -18,6 +18,8 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -28,43 +30,76 @@ * @see entity-config.xsd */ @ThreadSafe -public final class FieldType { +public final class FieldType extends AbstractConfigElement { - private final String name; // type = xs:string - private final String loader; // type = xs:string - private final String location; // type = xs:string + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "field-type"; + private final String xPath; - FieldType(Element element) throws GenericEntityConfException { + private final String name; + private final String loader; + private final String location; + + FieldType(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String name = element.getAttribute("name").intern(); + String name = element.getAttribute("name"); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - String loader = element.getAttribute("loader").intern(); + xPath = xPathParent.concat("/field-type[@name='" + name + "']"); + String loader = config.getValue(this.xPath + "/@loader"); if (loader.isEmpty()) { throw new GenericEntityConfException(" element loader attribute is empty" + lineNumberText); } this.loader = loader; - String location = element.getAttribute("location").intern(); + String location = config.getValue(this.xPath + "/@location"); if (location.isEmpty()) { throw new GenericEntityConfException(" element location attribute is empty" + lineNumberText); } this.location = location; } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + FieldType(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new GenericEntityConfException(" element name attribute is empty"); + } + this.name = name; + String loader = config.getValue(configObject, "/@loader"); + if (loader.isEmpty()) { + throw new GenericEntityConfException(" element loader attribute is empty "); + } + this.loader = loader; + String location = config.getValue(configObject, "/@location"); + if (location.isEmpty()) { + throw new GenericEntityConfException(" element location attribute is empty"); + } + this.location = location; + } + + public static FieldType loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException { + return new FieldType(element, xPathParent); + } + + public static FieldType loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException { + return new FieldType(configMap, xPath); } - /** Returns the value of the loader attribute. */ public String getLoader() { - return this.loader; + return loader; } - /** Returns the value of the location attribute. */ public String getLocation() { - return this.location; + return location; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/GroupMap.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/GroupMap.java index a6d906569d9..1953a04213d 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/GroupMap.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/GroupMap.java @@ -18,42 +18,78 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <group-map> element. * * @see entity-config.xsd */ @ThreadSafe -public final class GroupMap { +public final class GroupMap extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "group-map"; + public static final String ELEMENT_FIELD_ID_NAME = "group-name"; - private final String groupName; // type = xs:string - private final String datasourceName; // type = xs:string + private final String xPath; + private final String groupName; + private final String datasourceName; - GroupMap(Element element) throws GenericEntityConfException { + GroupMap(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); + String groupName = element.getAttribute("group-name").intern(); + this.xPath = xPathParent.concat("/group-map[@group-name='" + groupName + "']"); + this.groupName = groupName; if (groupName.isEmpty()) { throw new GenericEntityConfException(" element group-name attribute is empty" + lineNumberText); } - this.groupName = groupName; - String datasourceName = element.getAttribute("datasource-name").intern(); + String datasourceName = config.getValue(this.xPath + "/@datasource-name"); if (datasourceName.isEmpty()) { throw new GenericEntityConfException(" element datasource-name attribute is empty" + lineNumberText); } this.datasourceName = datasourceName; } - /** Returns the value of the group-name attribute. */ + GroupMap(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String groupName = config.getValue(configObject, "/@group-name"); + if (groupName.isEmpty()) { + throw new GenericEntityConfException(" element group-name attribute is empty"); + } + this.groupName = groupName; + String datasourceName = config.getValue(configObject, "/@datasource-name"); + if (datasourceName.isEmpty()) { + throw new GenericEntityConfException(" element datasource-name attribute is empty"); + } + this.datasourceName = datasourceName; + } + + public static GroupMap loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new GroupMap(element, xPathParent); + } + + public static GroupMap loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new GroupMap(configMap, xPath); + } + public String getGroupName() { - return this.groupName; + return groupName; } - /** Returns the value of the datasource-name attribute. */ public String getDatasourceName() { - return this.datasourceName; + return datasourceName; } + + @Override + public String getName() { + return groupName; + } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/InlineJdbc.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/InlineJdbc.java index 6f621270f78..a439e2c22d2 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/InlineJdbc.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/InlineJdbc.java @@ -22,6 +22,8 @@ import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <inline-jdbc> element. * @@ -30,243 +32,197 @@ @ThreadSafe public final class InlineJdbc extends JdbcElement { - private final String jdbcDriver; // type = xs:string - private final String jdbcUri; // type = xs:string - private final String jdbcUsername; // type = xs:string - private final String jdbcPassword; // type = xs:string - private final String jdbcPasswordLookup; // type = xs:string - private final int poolMaxsize; // type = xs:nonNegativeInteger - private final int poolMinsize; // type = xs:nonNegativeInteger - private final int idleMaxsize; // type = xs:nonNegativeInteger - private final int timeBetweenEvictionRunsMillis; // type = xs:nonNegativeInteger - private final int softMinEvictableIdleTimeMillis; // type = xs:nonNegativeInteger - private final int poolSleeptime; // type = xs:nonNegativeInteger - private final int poolLifetime; // type = xs:nonNegativeInteger - private final int poolDeadlockMaxwait; // type = xs:nonNegativeInteger - private final int poolDeadlockRetrywait; // type = xs:nonNegativeInteger - private final String poolJdbcTestStmt; // type = xs:string - private final boolean testOnCreate; // type = xs:boolean - private final boolean testOnBorrow; // type = xs:boolean - private final boolean testOnReturn; // type = xs:boolean - private final boolean testWhileIdle; // type = xs:boolean - private final String poolXaWrapperClass; // type = xs:string - - InlineJdbc(Element element) throws GenericEntityConfException { - super(element); + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "inline-jdbc"; + + private final String jdbcDriver; + private final String jdbcUri; + private final String jdbcUsername; + private final String jdbcPassword; + private final String jdbcPasswordLookup; + private final int poolMaxsize; + private final int poolMinsize; + private final int idleMaxsize; + private final int timeBetweenEvictionRunsMillis; + private final int softMinEvictableIdleTimeMillis; + private final int poolSleeptime; + private final int poolLifetime; + private final int poolDeadlockMaxwait; + private final int poolDeadlockRetrywait; + private final String poolJdbcTestStmt; + private final boolean testOnCreate; + private final boolean testOnBorrow; + private final boolean testOnReturn; + private final boolean testWhileIdle; + private final String poolXaWrapperClass; + + InlineJdbc(Element element, String xPathParent) throws GenericEntityConfException { + super(element, xPathParent.concat("/inline-jdbc")); String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String jdbcDriver = element.getAttribute("jdbc-driver").intern(); + String jdbcDriver = config.getValue(getXPath() + "/@jdbc-driver"); if (jdbcDriver.isEmpty()) { throw new GenericEntityConfException(" element jdbc-driver attribute is empty" + lineNumberText); } this.jdbcDriver = jdbcDriver; - String jdbcUri = element.getAttribute("jdbc-uri").intern(); + String jdbcUri = config.getValue(getXPath() + "/@jdbc-uri"); if (jdbcUri.isEmpty()) { throw new GenericEntityConfException(" element jdbc-uri attribute is empty" + lineNumberText); } this.jdbcUri = jdbcUri; - String jdbcUsername = element.getAttribute("jdbc-username").intern(); + String jdbcUsername = config.getValue(getXPath() + "/@jdbc-username"); if (jdbcUsername.isEmpty()) { throw new GenericEntityConfException(" element jdbc-username attribute is empty" + lineNumberText); } this.jdbcUsername = jdbcUsername; - this.jdbcPassword = element.getAttribute("jdbc-password").intern(); - this.jdbcPasswordLookup = element.getAttribute("jdbc-password-lookup").intern(); - String poolMaxsize = element.getAttribute("pool-maxsize"); - if (poolMaxsize.isEmpty()) { - this.poolMaxsize = 50; - } else { - try { - this.poolMaxsize = Integer.parseInt(poolMaxsize); - } catch (Exception e) { - throw new GenericEntityConfException(" element pool-maxsize attribute is invalid" + lineNumberText); - } - } - String poolMinsize = element.getAttribute("pool-minsize"); - if (poolMinsize.isEmpty()) { - this.poolMinsize = 2; - } else { - try { - this.poolMinsize = Integer.parseInt(poolMinsize); - } catch (Exception e) { - throw new GenericEntityConfException(" element pool-minsize attribute is invalid" + lineNumberText); - } - } - String idleMaxsize = element.getAttribute("idle-maxsize"); - if (idleMaxsize.isEmpty()) { - this.idleMaxsize = this.poolMaxsize / 2; - } else { - try { - this.idleMaxsize = Integer.parseInt(idleMaxsize); - } catch (Exception e) { - throw new GenericEntityConfException(" element idle-maxsize attribute is invalid" + lineNumberText); - } - } - String timeBetweenEvictionRunsMillis = element.getAttribute("time-between-eviction-runs-millis"); - if (timeBetweenEvictionRunsMillis.isEmpty()) { - this.timeBetweenEvictionRunsMillis = 600000; - } else { - try { - this.timeBetweenEvictionRunsMillis = Integer.parseInt(timeBetweenEvictionRunsMillis); - } catch (Exception e) { - throw new GenericEntityConfException(" element time-between-eviction-runs-millis attribute is invalid" + lineNumberText); - } - } - String softMinEvictableIdleTimeMillis = element.getAttribute("soft-min-evictable-idle-time-millis"); - if (softMinEvictableIdleTimeMillis.isEmpty()) { - this.softMinEvictableIdleTimeMillis = 600000; - } else { - try { - this.softMinEvictableIdleTimeMillis = Integer.parseInt(softMinEvictableIdleTimeMillis); - } catch (Exception e) { - throw new GenericEntityConfException(" element soft-min-evictable-idle-time-millis attribute is invalid" - + lineNumberText); - } - } - String poolSleeptime = element.getAttribute("pool-sleeptime"); - if (poolSleeptime.isEmpty()) { - this.poolSleeptime = 300000; - } else { - try { - this.poolSleeptime = Integer.parseInt(poolSleeptime); - } catch (Exception e) { - throw new GenericEntityConfException(" element pool-sleeptime attribute is invalid" + lineNumberText); - } - } - String poolLifetime = element.getAttribute("pool-lifetime"); - if (poolLifetime.isEmpty()) { - this.poolLifetime = 600000; - } else { - try { - this.poolLifetime = Integer.parseInt(poolLifetime); - } catch (Exception e) { - throw new GenericEntityConfException(" element pool-lifetime attribute is invalid" + lineNumberText); - } + jdbcPassword = config.getValue(getXPath() + "/@jdbc-password"); + jdbcPasswordLookup = config.getValue(getXPath() + "/@jdbc-password-lookup"); + + poolMaxsize = config.getValue(getXPath() + "/@pool-maxsize", 50, Integer.class); + poolMinsize = config.getValue(getXPath() + "/@pool-minsize", 2, Integer.class); + idleMaxsize = config.getValue(getXPath() + "/@idle-maxsize", poolMaxsize / 2, Integer.class); + + timeBetweenEvictionRunsMillis = config.getValue(getXPath() + "/@time-between-eviction-runs-millis", 600000, Integer.class); + softMinEvictableIdleTimeMillis = config.getValue(getXPath() + "/@soft-min-evictable-idle-time-millis", 600000, Integer.class); + poolSleeptime = config.getValue(getXPath() + "/@pool-sleeptime", 300000, Integer.class); + poolLifetime = config.getValue(getXPath() + "/@pool-lifetime", 600000, Integer.class); + poolDeadlockMaxwait = config.getValue(getXPath() + "/@pool-deadlock-maxwait", 300000, Integer.class); + poolDeadlockRetrywait = config.getValue(getXPath() + "/@pool-deadlock-retrywait", 10000, Integer.class); + poolJdbcTestStmt = config.getValue(getXPath() + "/@pool-jdbc-test-stmt"); + testOnCreate = "true".equals(config.getValue(getXPath() + "/@test-on-create")); + testOnBorrow = "true".equals(config.getValue(getXPath() + "/@test-on-borrow")); + testOnReturn = "true".equals(config.getValue(getXPath() + "/@test-on-return")); + testWhileIdle = "true".equals(config.getValue(getXPath() + "/@test-while-idle")); + poolXaWrapperClass = config.getValue(getXPath() + "/@pool-xa-wrapper-class"); + } + + InlineJdbc(Map configObject, String xPath) throws GenericEntityConfException { + super(configObject, xPath); + String jdbcDriver = config.getValue(configObject, "/@jdbc-driver"); + if (jdbcDriver.isEmpty()) { + throw new GenericEntityConfException(" element jdbc-driver attribute is empty"); } - String poolDeadlockMaxwait = element.getAttribute("pool-deadlock-maxwait"); - if (poolDeadlockMaxwait.isEmpty()) { - this.poolDeadlockMaxwait = 300000; - } else { - try { - this.poolDeadlockMaxwait = Integer.parseInt(poolDeadlockMaxwait); - } catch (Exception e) { - throw new GenericEntityConfException(" element pool-deadlock-maxwait attribute is invalid" + lineNumberText); - } + this.jdbcDriver = jdbcDriver; + String jdbcUri = config.getValue(configObject, "/@jdbc-uri"); + if (jdbcUri.isEmpty()) { + throw new GenericEntityConfException(" element jdbc-uri attribute is empty"); } - String poolDeadlockRetrywait = element.getAttribute("pool-deadlock-retrywait"); - if (poolDeadlockRetrywait.isEmpty()) { - this.poolDeadlockRetrywait = 10000; - } else { - try { - this.poolDeadlockRetrywait = Integer.parseInt(poolDeadlockRetrywait); - } catch (Exception e) { - throw new GenericEntityConfException(" element pool-deadlock-retrywait attribute is invalid" + lineNumberText); - } + this.jdbcUri = jdbcUri; + String jdbcUsername = config.getValue(configObject, "/@jdbc-username"); + if (jdbcUsername.isEmpty()) { + throw new GenericEntityConfException(" element jdbc-username attribute is empty"); } - this.poolJdbcTestStmt = element.getAttribute("pool-jdbc-test-stmt").intern(); - this.testOnCreate = "true".equals(element.getAttribute("test-on-create")); - this.testOnBorrow = "true".equals(element.getAttribute("test-on-borrow")); - this.testOnReturn = "true".equals(element.getAttribute("test-on-return")); - this.testWhileIdle = "true".equals(element.getAttribute("test-while-idle")); - this.poolXaWrapperClass = element.getAttribute("pool-xa-wrapper-class").intern(); + this.jdbcUsername = jdbcUsername; + jdbcPassword = config.getValue(configObject, "/@jdbc-password"); + jdbcPasswordLookup = config.getValue(configObject, "/@jdbc-password-lookup"); + + poolMaxsize = config.getValue(configObject, "/@pool-maxsize", 50, Integer.class); + poolMinsize = config.getValue(configObject, "/@pool-minsize", 2, Integer.class); + idleMaxsize = config.getValue(configObject, "/@idle-maxsize", poolMaxsize / 2, Integer.class); + + timeBetweenEvictionRunsMillis = config.getValue(configObject, "/@time-between-eviction-runs-millis", 600000, Integer.class); + softMinEvictableIdleTimeMillis = config.getValue(configObject, "/@soft-min-evictable-idle-time-millis", 600000, Integer.class); + poolSleeptime = config.getValue(configObject, "/@pool-sleeptime", 300000, Integer.class); + poolLifetime = config.getValue(configObject, "/@pool-lifetime", 600000, Integer.class); + poolDeadlockMaxwait = config.getValue(configObject, "/@pool-deadlock-maxwait", 300000, Integer.class); + poolDeadlockRetrywait = config.getValue(configObject, "/@pool-deadlock-retrywait", 10000, Integer.class); + poolJdbcTestStmt = config.getValue(configObject, "/@pool-jdbc-test-stmt"); + testOnCreate = "true".equals(config.getValue(configObject, "/@test-on-create")); + testOnBorrow = "true".equals(config.getValue(configObject, "/@test-on-borrow")); + testOnReturn = "true".equals(config.getValue(configObject, "/@test-on-return")); + testWhileIdle = "true".equals(config.getValue(configObject, "/@test-while-idle")); + poolXaWrapperClass = config.getValue(configObject, "/@pool-xa-wrapper-class"); } - /** Returns the value of the jdbc-driver attribute. */ public String getJdbcDriver() { - return this.jdbcDriver; + return jdbcDriver; } - /** Returns the value of the jdbc-uri attribute. */ public String getJdbcUri() { - return this.jdbcUri; + return jdbcUri; } - /** Returns the value of the jdbc-username attribute. */ public String getJdbcUsername() { - return this.jdbcUsername; + return jdbcUsername; } - /** Returns the value of the jdbc-password attribute. */ public String getJdbcPassword() { - return this.jdbcPassword; + return jdbcPassword; } - /** Returns the value of the jdbc-password-lookup attribute. */ public String getJdbcPasswordLookup() { - return this.jdbcPasswordLookup; + return jdbcPasswordLookup; } - /** Returns the value of the pool-maxsize attribute. */ public int getPoolMaxsize() { - return this.poolMaxsize; + return poolMaxsize; } - /** Returns the value of the pool-minsize attribute. */ public int getPoolMinsize() { - return this.poolMinsize; + return poolMinsize; } - /** Returns the value of the idle-maxsize attribute. */ public int getIdleMaxsize() { - return this.idleMaxsize; + return idleMaxsize; } - /** Returns the value of the time-between-eviction-runs-millis attribute. */ public int getTimeBetweenEvictionRunsMillis() { - return this.timeBetweenEvictionRunsMillis; + return timeBetweenEvictionRunsMillis; } - /** Returns the value of the time-between-eviction-runs-millis attribute. */ public int getSoftMinEvictableIdleTimeMillis() { - return this.softMinEvictableIdleTimeMillis; + return softMinEvictableIdleTimeMillis; } - /** Returns the value of the pool-sleeptime attribute. */ public int getPoolSleeptime() { - return this.poolSleeptime; + return poolSleeptime; } - /** Returns the value of the pool-lifetime attribute. */ public int getPoolLifetime() { - return this.poolLifetime; + return poolLifetime; } - /** Returns the value of the pool-deadlock-maxwait attribute. */ public int getPoolDeadlockMaxwait() { - return this.poolDeadlockMaxwait; + return poolDeadlockMaxwait; } - /** Returns the value of the pool-deadlock-retrywait attribute. */ public int getPoolDeadlockRetrywait() { - return this.poolDeadlockRetrywait; + return poolDeadlockRetrywait; } - /** Returns the value of the pool-jdbc-test-stmt attribute. */ public String getPoolJdbcTestStmt() { - return this.poolJdbcTestStmt; + return poolJdbcTestStmt; } - /** Returns the value of the test-on-create attribute. */ public boolean getTestOnCreate() { - return this.testOnCreate; + return testOnCreate; } - /** Returns the value of the test-on-create attribute. */ public boolean getTestOnBorrow() { - return this.testOnBorrow; + return testOnBorrow; } - /** Returns the value of the test-on-create attribute. */ public boolean getTestOnReturn() { - return this.testOnReturn; + return testOnReturn; } - /** Returns the value of the test-on-create attribute. */ public boolean getTestWhileIdle() { - return this.testWhileIdle; + return testWhileIdle; } - /** Returns the value of the pool-xa-wrapper-class attribute. */ public String getPoolXaWrapperClass() { - return this.poolXaWrapperClass; + return poolXaWrapperClass; + } + + public static InlineJdbc loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new InlineJdbc(element, xPathParent); + } + + public static InlineJdbc loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new InlineJdbc(configMap, xPath); + } + + @Override + public String getName() { + return "inline-jdbc"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JdbcElement.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JdbcElement.java index 6c3f9c9fcbc..520ded0602b 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JdbcElement.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JdbcElement.java @@ -18,35 +18,54 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; -import org.apache.ofbiz.entity.GenericEntityConfException; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.w3c.dom.Element; +import java.util.Map; + /** * An abstract class for <datasource> JDBC child elements. * * @see entity-config.xsd */ -public abstract class JdbcElement { +public abstract class JdbcElement extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + private final String xPath; private final String isolationLevel; private final String lineNumber; - protected JdbcElement(Element element) throws GenericEntityConfException { - this.isolationLevel = element.getAttribute("isolation-level").intern(); + + protected JdbcElement(Element element, String xPathParent) { + xPath = xPathParent; + isolationLevel = config.getValue(xPath + "/@isolation-level"); Object lineNumber = element.getUserData("startLine"); this.lineNumber = lineNumber == null ? "unknown" : lineNumber.toString(); } + protected JdbcElement(Map configObject, String xPath) { + this.xPath = xPath; + isolationLevel = config.getValue(configObject, "/@isolation-level"); + lineNumber = "unknown"; + } + /** Returns the value of the isolation-level attribute. */ public String getIsolationLevel() { - return this.isolationLevel; + return isolationLevel; } /** - * Returns the configuration file line number for this element. * @return The configuration file line number for this element */ public String getLineNumber() { - return this.lineNumber; + return lineNumber; + } + + /** + * @return The current xpath of this element + */ + public String getXPath() { + return xPath; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JndiJdbc.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JndiJdbc.java index 821b521170c..f52f6f46825 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JndiJdbc.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/JndiJdbc.java @@ -22,6 +22,8 @@ import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <jndi-jdbc> element. * @@ -30,31 +32,59 @@ @ThreadSafe public final class JndiJdbc extends JdbcElement { - private final String jndiServerName; // type = xs:string - private final String jndiName; // type = xs:string + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "jndi-jdbc"; + + private final String jndiServerName; + private final String jndiName; - JndiJdbc(Element element) throws GenericEntityConfException { - super(element); + JndiJdbc(Element element, String xPathParent) throws GenericEntityConfException { + super(element, xPathParent.concat("/jndi-jdbc")); String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String jndiServerName = element.getAttribute("jndi-server-name").intern(); + String jndiServerName = config.getValue(getXPath() + "/@jndi-server-name"); if (jndiServerName.isEmpty()) { throw new GenericEntityConfException(" element jndi-server-name attribute is empty" + lineNumberText); } this.jndiServerName = jndiServerName; - String jndiName = element.getAttribute("jndi-name").intern(); + String jndiName = config.getValue(getXPath() + "/@jndi-name"); if (jndiName.isEmpty()) { throw new GenericEntityConfException(" element jndi-name attribute is empty" + lineNumberText); } this.jndiName = jndiName; } - /** Returns the value of the jndi-server-name attribute. */ + JndiJdbc(Map configObject, String xPath) throws GenericEntityConfException { + super(configObject, xPath); + String jndiServerName = config.getValue(configObject, "/@jndi-server-name"); + if (jndiServerName.isEmpty()) { + throw new GenericEntityConfException(" element jndi-server-name attribute is empty"); + } + this.jndiServerName = jndiServerName; + String jndiName = config.getValue(configObject, "/@jndi-name"); + if (jndiName.isEmpty()) { + throw new GenericEntityConfException(" element jndi-name attribute is empty"); + } + this.jndiName = jndiName; + } + + public static JndiJdbc loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new JndiJdbc(element, xPathParent); + } + + public static JndiJdbc loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new JndiJdbc(configMap, xPath); + } + public String getJndiServerName() { - return this.jndiServerName; + return jndiServerName; } - /** Returns the value of the jndi-name attribute. */ public String getJndiName() { - return this.jndiName; + return jndiName; + } + + @Override + public String getName() { + return "jndi-jdbc"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ReadData.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ReadData.java index f855fb23356..c15222111b3 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ReadData.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ReadData.java @@ -18,31 +18,66 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <read-data> element. * * @see entity-config.xsd */ @ThreadSafe -public final class ReadData { +public final class ReadData extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "read-data"; + private final String xPath; + public static final String ELEMENT_FIELD_ID_NAME = "reader-name"; - private final String readerName; // type = xs:string + private final String readerName; - ReadData(Element element) throws GenericEntityConfException { + ReadData(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String readerName = element.getAttribute("reader-name").intern(); + String readerName = element.getAttribute("reader-name"); + xPath = xPathParent.concat("/read-data[@reader-name='" + readerName + "']"); if (readerName.isEmpty()) { throw new GenericEntityConfException(" element reader-name attribute is empty" + lineNumberText); } this.readerName = readerName; } - /** Returns the value of the reader-name attribute. */ + ReadData(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String readerName = config.getValue(configObject, "/@reader-name"); + if (readerName.isEmpty()) { + throw new GenericEntityConfException(" element reader-name attribute is empty"); + } + this.readerName = readerName; + } + public String getReaderName() { - return this.readerName; + return readerName; + } + + public static ReadData loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new ReadData(element, xPathParent); + } + + public static ReadData loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new ReadData(configMap, xPath); + } + + public boolean allowMultipleSources() { + return false; + } + + @Override + public String getName() { + return readerName; } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Resource.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Resource.java index 5fbf666fe46..f34971b4454 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Resource.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/Resource.java @@ -18,42 +18,75 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <resource> element. * * @see entity-config.xsd */ @ThreadSafe -public final class Resource { +public final class Resource extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "resource"; + private final String xPath; - private final String loader; // type = xs:string - private final String location; // type = xs:string + private final String loader; + private final String location; - Resource(Element element) throws GenericEntityConfException { + Resource(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String loader = element.getAttribute("loader").intern(); + String location = element.getAttribute("location"); + xPath = xPathParent.concat("/resource[@location='" + location + "']"); + String loader = config.getValue(xPath.concat("/@loader")); if (loader.isEmpty()) { throw new GenericEntityConfException(" element loader attribute is empty" + lineNumberText); } this.loader = loader; - String location = element.getAttribute("location").intern(); if (location.isEmpty()) { throw new GenericEntityConfException(" element location attribute is empty" + lineNumberText); } this.location = location; } - /** Returns the value of the loader attribute. */ + Resource(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String loader = config.getValue(configObject, "loader"); + if (loader.isEmpty()) { + throw new GenericEntityConfException(" element loader attribute is empty"); + } + this.loader = loader; + String location = config.getValue(configObject, "location"); + if (location.isEmpty()) { + throw new GenericEntityConfException(" element location attribute is empty"); + } + this.location = location; + } + public String getLoader() { - return this.loader; + return loader; } - /** Returns the value of the location attribute. */ public String getLocation() { - return this.location; + return location; + } + + public static Resource loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new Resource(element, xPathParent); + } + + public static Resource loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new Resource(configMap, xPath); + } + + @Override + public String getName() { + return location; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ResourceLoader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ResourceLoader.java index 04cb6850299..fb0d85611f9 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ResourceLoader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/ResourceLoader.java @@ -18,56 +18,86 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <resource-loader> element. * * @see entity-config.xsd */ @ThreadSafe -public final class ResourceLoader { +public final class ResourceLoader extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "resource-loader"; + private final String xPath; - private final String name; // type = xs:string - private final String className; // type = xs:string - private final String prependEnv; // type = xs:string - private final String prefix; // type = xs:string + private final String name; + private final String className; + private final String prependEnv; + private final String prefix; - ResourceLoader(Element element) throws GenericEntityConfException { + ResourceLoader(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String name = element.getAttribute("name").intern(); if (name.isEmpty()) { throw new GenericEntityConfException(" element name attribute is empty" + lineNumberText); } this.name = name; - String className = element.getAttribute("class").intern(); + xPath = xPathParent.concat("/" + ELEMENT_NAME + "[@name=\"" + name + "\"]"); + String className = config.getValue(xPath + "/@class"); if (className.isEmpty()) { throw new GenericEntityConfException(" element class attribute is empty" + lineNumberText); } this.className = className; - this.prependEnv = element.getAttribute("prepend-env").intern(); - this.prefix = element.getAttribute("prefix").intern(); + prependEnv = config.getValue(xPath + "/@prepend-env"); + prefix = config.getValue(xPath + "/@prefix"); } - /** Returns the value of the name attribute. */ - public String getName() { - return this.name; + ResourceLoader(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new GenericEntityConfException(" element name attribute is empty"); + } + this.name = name; + String className = config.getValue(configObject, "/@class"); + if (className.isEmpty()) { + throw new GenericEntityConfException(" element class attribute is empty"); + } + this.className = className; + prependEnv = config.getValue(configObject, "/@prepend-env"); + prefix = config.getValue(configObject, "/@prefix"); + } + + public static ResourceLoader loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new ResourceLoader(element, xPathParent); + } + + public static ResourceLoader loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new ResourceLoader(configMap, xPath); } - /** Returns the value of the class attribute. */ public String getClassName() { - return this.className; + return className; } - /** Returns the value of the prepend-env attribute. */ public String getPrependEnv() { - return this.prependEnv; + return prependEnv; } - /** Returns the value of the prefix attribute. */ public String getPrefix() { - return this.prefix; + return prefix; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/SqlLoadPath.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/SqlLoadPath.java index 0f74169d366..bdccc5f847a 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/SqlLoadPath.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/SqlLoadPath.java @@ -18,38 +18,68 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <sql-load-path> element. * * @see entity-config.xsd */ @ThreadSafe -public final class SqlLoadPath { +public final class SqlLoadPath extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "sql-load-path"; + private final String xPath; - private final String path; // type = xs:string - private final String prependEnv; // type = xs:string + private final String path; + private final String prependEnv; - SqlLoadPath(Element element) throws GenericEntityConfException { + SqlLoadPath(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); String path = element.getAttribute("path").intern(); + xPath = xPathParent.concat("/sql-load-path[@path='" + path + "']"); if (path.isEmpty()) { throw new GenericEntityConfException(" element path attribute is empty" + lineNumberText); } this.path = path; - this.prependEnv = element.getAttribute("prepend-env").intern(); + prependEnv = config.getValue(xPath + "/@prepend-env"); + } + + SqlLoadPath(Map configObject, String xPath) throws GenericEntityConfException { + this.xPath = xPath; + String path = config.getValue(configObject, "path"); + if (path.isEmpty()) { + throw new GenericEntityConfException(" element path attribute is empty"); + } + this.path = path; + prependEnv = config.getValue(configObject, "prepend-env"); + } + + public static SqlLoadPath loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new SqlLoadPath(element, xPathParent); + } + + public static SqlLoadPath loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new SqlLoadPath(configMap, xPath); } - /** Returns the value of the path attribute. */ public String getPath() { - return this.path; + return path; } - /** Returns the value of the prepend-env attribute. */ public String getPrependEnv() { - return this.prependEnv; + return prependEnv; } + + @Override + public String getName() { + return path; + } + } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionFactory.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionFactory.java index 4bebd8b032e..f5e28992695 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionFactory.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionFactory.java @@ -18,8 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import java.util.Map; + +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; @@ -29,45 +31,55 @@ * @see entity-config.xsd */ @ThreadSafe -public final class TransactionFactory { +public final class TransactionFactory extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "transaction-factory"; + private final String xPath; - private final String className; // type = xs:string - private final UserTransactionJndi userTransactionJndi; // - private final TransactionManagerJndi transactionManagerJndi; // + private final String className; + private final UserTransactionJndi userTransactionJndi; + private final TransactionManagerJndi transactionManagerJndi; - TransactionFactory(Element element) throws GenericEntityConfException { + TransactionFactory(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String className = element.getAttribute("class").intern(); + xPath = xPathParent.concat("/transaction-factory"); + String className = config.getValue(xPath + "/@class"); if (className.isEmpty()) { throw new GenericEntityConfException(" element class attribute is empty" + lineNumberText); } this.className = className; - Element userTransactionJndiElement = UtilXml.firstChildElement(element, "user-transaction-jndi"); - if (userTransactionJndiElement == null) { - this.userTransactionJndi = null; - } else { - this.userTransactionJndi = new UserTransactionJndi(userTransactionJndiElement); - } - Element transactionManagerJndiElement = UtilXml.firstChildElement(element, "transaction-manager-jndi"); - if (transactionManagerJndiElement == null) { - this.transactionManagerJndi = null; - } else { - this.transactionManagerJndi = new TransactionManagerJndi(transactionManagerJndiElement); - } + this.userTransactionJndi = config.getObjectSubElement(xPath.concat("/user-transaction-jndi"), + element, UserTransactionJndi.class); + this.transactionManagerJndi = config.getObjectSubElement(xPath.concat("/transaction-manager-jndi"), + element, TransactionManagerJndi.class); + } + + public static TransactionFactory loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException { + return new TransactionFactory(element, xPathParent); + } + + public static TransactionFactory loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException { + return null; } - /** Returns the value of the class attribute. */ public String getClassName() { - return this.className; + return className; } - /** Returns the <user-transaction-jndi> child element, or null if no child element was found. */ public UserTransactionJndi getUserTransactionJndi() { - return this.userTransactionJndi; + return userTransactionJndi; } - /** Returns the <transaction-manager-jndi> child element, or null if no child element was found. */ public TransactionManagerJndi getTransactionManagerJndi() { - return this.transactionManagerJndi; + return transactionManagerJndi; + } + + + @Override + public String getName() { + return "transaction-factory"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionManagerJndi.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionManagerJndi.java index d9cf95b20ab..4c61a91852d 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionManagerJndi.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TransactionManagerJndi.java @@ -18,42 +18,61 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <transaction-manager-jndi> element. * * @see entity-config.xsd */ @ThreadSafe -public final class TransactionManagerJndi { +public final class TransactionManagerJndi extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "transaction-manager-jndi"; + private final String xPath; - private final String jndiServerName; // type = xs:string - private final String jndiName; // type = xs:string + private final String jndiServerName; + private final String jndiName; - TransactionManagerJndi(Element element) throws GenericEntityConfException { + TransactionManagerJndi(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String jndiServerName = element.getAttribute("jndi-server-name").intern(); + xPath = xPathParent.concat("transaction-manager-jndi"); + String jndiServerName = config.getValue(xPath + "/@jndi-server-name"); if (jndiServerName.isEmpty()) { throw new GenericEntityConfException(" element jndi-server-name attribute is empty" + lineNumberText); } this.jndiServerName = jndiServerName; - String jndiName = element.getAttribute("jndi-name").intern(); + String jndiName = config.getValue(xPath + "/@jndi-name"); if (jndiName.isEmpty()) { throw new GenericEntityConfException(" element jndi-name attribute is empty" + lineNumberText); } this.jndiName = jndiName; } - /** Returns the value of the jndi-server-name attribute. */ + public static TransactionManagerJndi loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new TransactionManagerJndi(element, xPathParent); + } + + public static TransactionManagerJndi loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return null; + } + public String getJndiServerName() { - return this.jndiServerName; + return jndiServerName; } - /** Returns the value of the jndi-name attribute. */ public String getJndiName() { - return this.jndiName; + return jndiName; + } + + @Override + public String getName() { + return "transaction-manager-jndi"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TyrexDataSource.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TyrexDataSource.java index 489bfbae74d..cd39daacafa 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TyrexDataSource.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/TyrexDataSource.java @@ -22,6 +22,8 @@ import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <tyrex-dataSource> element. * @@ -30,20 +32,44 @@ @ThreadSafe public final class TyrexDataSource extends JdbcElement { - private final String dataSourceName; // type = xs:string + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "tyrex-dataSource"; + + private final String dataSourceName; - TyrexDataSource(Element element) throws GenericEntityConfException { - super(element); + TyrexDataSource(Element element, String xPathParent) throws GenericEntityConfException { + super(element, xPathParent.concat("tyrex-dataSource")); String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String dataSourceName = element.getAttribute("dataSource-name").intern(); + String dataSourceName = config.getValue(getXPath() + "dataSource-name"); if (dataSourceName.isEmpty()) { throw new GenericEntityConfException(" element dataSource-name attribute is empty" + lineNumberText); } this.dataSourceName = dataSourceName; } - /** Returns the value of the dataSource-name attribute. */ + TyrexDataSource(Map configObject, String xPath) throws GenericEntityConfException { + super(configObject, xPath); + String dataSourceName = config.getValue(configObject, "dataSource-name"); + if (dataSourceName.isEmpty()) { + throw new GenericEntityConfException(" element dataSource-name attribute is empty"); + } + this.dataSourceName = dataSourceName; + } + + public static TyrexDataSource loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new TyrexDataSource(element, xPathParent); + } + + public static TyrexDataSource loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return new TyrexDataSource(configMap, xPath); + } + public String getDataSourceName() { - return this.dataSourceName; + return dataSourceName; + } + + @Override + public String getName() { + return "tyrex-dataSource"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/UserTransactionJndi.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/UserTransactionJndi.java index c204573c546..fd294fd2594 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/UserTransactionJndi.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/config/model/UserTransactionJndi.java @@ -18,42 +18,61 @@ *******************************************************************************/ package org.apache.ofbiz.entity.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.entity.GenericEntityConfException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <user-transaction-jndi> element. * * @see entity-config.xsd */ @ThreadSafe -public final class UserTransactionJndi { +public final class UserTransactionJndi extends AbstractConfigElement { + + private final EntityConfigGetter config = EntityConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "user-transaction-jndi"; + private final String xPath; - private final String jndiServerName; // type = xs:string - private final String jndiName; // type = xs:string + private final String jndiServerName; + private final String jndiName; - UserTransactionJndi(Element element) throws GenericEntityConfException { + UserTransactionJndi(Element element, String xPathParent) throws GenericEntityConfException { String lineNumberText = EntityConfig.createConfigFileLineNumberText(element); - String jndiServerName = element.getAttribute("jndi-server-name").intern(); + xPath = xPathParent.concat("/user-transaction-jndi"); + String jndiServerName = config.getValue(xPath + "/@jndi-server-name"); if (jndiServerName.isEmpty()) { throw new GenericEntityConfException(" element jndi-server-name attribute is empty" + lineNumberText); } this.jndiServerName = jndiServerName; - String jndiName = element.getAttribute("jndi-name").intern(); + String jndiName = config.getValue("/@jndi-name"); if (jndiName.isEmpty()) { throw new GenericEntityConfException(" element jndi-name attribute is empty" + lineNumberText); } this.jndiName = jndiName; } - /** Returns the value of the jndi-server-name attribute. */ + public static TransactionManagerJndi loadFromXml(Element element, String xPathParent) throws GenericEntityConfException { + return new TransactionManagerJndi(element, xPathParent); + } + + public static TransactionManagerJndi loadFromConfig(Map configMap, String xPath) throws GenericEntityConfException { + return null; + } + public String getJndiServerName() { - return this.jndiServerName; + return jndiServerName; } - /** Returns the value of the jndi-name attribute. */ public String getJndiName() { - return this.jndiName; + return jndiName; + } + + @Override + public String getName() { + return "user-transaction-jndi"; } } diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/datasource/GenericDAO.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/datasource/GenericDAO.java index 404bb82d553..969f87d4313 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/datasource/GenericDAO.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/datasource/GenericDAO.java @@ -699,7 +699,7 @@ public void partialSelect(GenericEntity entity, Set keys) throws Generic /* ====================================================================== */ /** - * Finds GenericValues by the conditions specified in the EntityCondition object, the the EntityCondition javadoc for more details. + * Finds GenericValues by the conditions specified in the EntityCondition object, the EntityCondition javadoc for more details. * @param modelEntity The ModelEntity of the Entity as defined in the entity XML file * @param whereEntityCondition The EntityCondition object that specifies how to constrain this query before any groupings are done (if this is * a view entity with group-by aliases) diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/ConnectionFactoryLoader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/ConnectionFactoryLoader.java index ccae44568b2..eb5e65b8b9d 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/ConnectionFactoryLoader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/jdbc/ConnectionFactoryLoader.java @@ -26,7 +26,6 @@ * ConnectionFactoryLoader - utility class that loads the connection manager and provides to client code a reference to it (ConnectionFactory) */ public class ConnectionFactoryLoader { - // Debug MODULE name private static final String MODULE = ConnectionFactoryLoader.class.getName(); private static final ConnectionFactory CONN_FACTORY = createConnectionFactory(); diff --git a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java index dbf84d09ca0..af300f6c369 100644 --- a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java +++ b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelGroupReader.java @@ -141,6 +141,7 @@ public Map getGroupCache(String delegatorName) { Node curChild = docElement.getFirstChild(); if (curChild != null) { utilTimer.timerString("[ModelGroupReader.getGroupCache] Before start of entity loop"); + Set errors = new HashSet<>(); do { if (curChild.getNodeType() == Node.ELEMENT_NODE && "entity-group".equals(curChild.getNodeName())) { Element curEntity = (Element) curChild; @@ -149,8 +150,9 @@ public Map getGroupCache(String delegatorName) { try { if (null == EntityConfig.getInstance().getDelegator(delegatorName).getGroupDataSource(groupName)) { - Debug.logError("The declared group name " + groupName - + " has no corresponding group-map in entityengine.xml: ", MODULE); + errors.add("The declared group name " + groupName + + " has no corresponding group-map in entityengine.xml for delegator [" + + delegatorName + "]"); } } catch (GenericEntityConfException e) { Debug.logWarning(e, "Exception thrown while getting group name: ", MODULE); @@ -162,6 +164,9 @@ public Map getGroupCache(String delegatorName) { } curChild = curChild.getNextSibling(); } while (curChild != null); + if (!errors.isEmpty()) { + errors.forEach(error -> Debug.logError(error, MODULE)); + } } else { Debug.logWarning("[ModelGroupReader.getGroupCache] No child nodes found.", MODULE); } diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/BaseEntityConfigReaderTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/BaseEntityConfigReaderTest.groovy new file mode 100644 index 00000000000..fb7360a5e70 --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/BaseEntityConfigReaderTest.groovy @@ -0,0 +1,111 @@ +/******************************************************************************* + * 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.entity.model.test + +import static org.mockito.Mockito.any + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import org.apache.ofbiz.base.config.ConfigurationInterface +import org.apache.ofbiz.base.config.ConfigurationFactory +import org.apache.ofbiz.base.config.XmlFileReader +import org.apache.ofbiz.base.util.UtilXml +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.apache.ofbiz.entity.config.model.EntityConfigFactory +import org.apache.ofbiz.entity.config.model.EntityConfigGetter +import org.apache.ofbiz.base.config.ConfigHelper +import org.junit.jupiter.api.AfterEach +import org.mockito.MockedStatic +import org.mockito.Mockito +import org.w3c.dom.Document + +class BaseEntityConfigReaderTest { + + private static final String XML_HEADER = ''' + ''' + private static final String XML_FOOTER = '' + private MockedStatic mockXmlReader + private MockedStatic mockHelper + private MockedStatic mockConfig + private MockedStatic mockConfigGetter + + @AfterEach + void closeMock() { + mockXmlReader?.close() + mockConfigGetter?.close() + mockHelper?.close() + mockConfig?.close() + } + + private String prepareXmlContent(String xmlContent) { + return XML_HEADER + xmlContent + XML_FOOTER + } + + private Document readXmlContent(String xmlContent) { + return UtilXml.readXmlDocument(prepareXmlContent(xmlContent), false) + } + + private void mockHoconConfig(String hoconContent) { + Config mockedConfig = hoconContent + ? ConfigFactory.parseString( + """{"entityengine": {"entity-config": {${hoconContent}}}}""") + : ConfigFactory.empty() + mockConfig = Mockito.mockStatic(ConfigFactory) + mockConfig.when(ConfigFactory.load((String) any())) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory::load) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory::empty) + .thenReturn(mockedConfig) + } + + private void mockXmlConfigFiles(String xmlContent) { + mockHelper = Mockito.mockStatic(ConfigHelper) + mockHelper.when(ConfigHelper::checkStrictXmlStructure) + .thenReturn(Boolean.FALSE) + Document xmlDoc = readXmlContent(xmlContent) + mockXmlReader = Mockito.mockStatic(XmlFileReader) + mockXmlReader.when(XmlFileReader.read(any())) + .thenReturn(xmlDoc.getDocumentElement()) + } + + /** + * Prepare test environment by mocking configs. + * Note that nothing is actually loaded in the classes. + * The content are only return instead of actual files. + * @param xmlContent the xml file that will be used (by mocking calls to readers) + * @param hoconContent the hocon content that will be loaded (by mocking) in configs + * @param override if the hocon config should override XML. + * @return an initialized instance of EntityConfig + */ + protected EntityConfig mockXmlAndHoconCgf(String xmlContent, String hoconContent, boolean override = true) { + mockXmlConfigFiles(xmlContent) + mockHoconConfig(hoconContent) + ConfigurationInterface configuration = ConfigurationFactory.resetAndGet() // creation of configuration is enough + configuration.clearCache() + configuration.setUseOverrideValue(override) + + mockConfigGetter = Mockito.mockStatic(EntityConfigGetter) + mockConfigGetter.when(EntityConfigGetter::getInstance) + .thenReturn(new EntityConfigGetter()) + return EntityConfigFactory.createInstance() + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderConnectionFactoryTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderConnectionFactoryTest.groovy new file mode 100644 index 00000000000..b3b6e398c0e --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderConnectionFactoryTest.groovy @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.ConnectionFactory +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderConnectionFactoryTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadConnectionFactoryClassNameFromXml() { + EntityConfig config = mockXmlAndHoconCgf('', '') + ConnectionFactory connectionFactory = config.getConnectionFactory() + + assert connectionFactory.getClassName() == 'org.apache.ofbiz.test.test1' + } + + @Test + void testLoadConnectionFactoryClassNameFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf('', + '''"connection-factory": {"class": "org.apache.ofbiz.test.override"}''') + ConnectionFactory connectionFactory = config.getConnectionFactory() + + assert connectionFactory.getClassName() == 'org.apache.ofbiz.test.override' + } + + @Test + void testLoadConnectionFactoryClassNameWithOverrideConfigNoneUsed() { + EntityConfig config = mockXmlAndHoconCgf('', + '''"connection-factory": {"class": "org.apache.ofbiz.test.overridenoneread"}''' + , false) + ConnectionFactory connectionFactory = config.getConnectionFactory() + + assert connectionFactory.getClassName() == 'org.apache.ofbiz.test.test3' + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDatasourceTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDatasourceTest.groovy new file mode 100644 index 00000000000..d26bf9d955b --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDatasourceTest.groovy @@ -0,0 +1,636 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.Datasource +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderDatasourceTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadDatasourceFromXmlWithFalseValue() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + + ''', '') + Map datasources = config.getDatasourceMap() + Datasource datasource = datasources.datasourcetest + + assert datasource + datasource.with { + assert getName() == 'datasourcetest' + assert !getAddMissingOnStart() + assert !getAliasViewColumns() + assert !getAlwaysUseConstraintKeyword() + assert getCharacterSet() == 'character-set-test' + assert !getCheckFkIndicesOnStart() + assert !getCheckFksOnStart() + assert !getCheckIndicesOnStart() + assert !getCheckOnStart() + assert !getCheckPksOnStart() + assert getCollate() == 'collate-test' + assert getConstraintNameClipLength() == 888 + assert !getDropFkUseForeignKeyKeyword() + assert getFieldTypeName() == 'field-type-test' + assert getFkStyle() == 'fk-style-test' + assert getHelperClass() == 'org.apache.ofbiz.entity.datasource.GenericTest' + assert getJoinStyle() == 'join-test' + assert getMaxWorkerPoolSize() == 999 + assert getOffsetStyle() == 'offset-style-test' + assert getProxyCursorName() == 'proxy-test' + assert getResultFetchSize() == 99 + assert getSchemaName() == 'schema-test' + // TODO assert getSqlLoadPathList() + assert getTableType() == 'table-test' + assert !getUseBinaryTypeForBlob() + assert !getUseFkInitiallyDeferred() + assert !getUseForeignKeyIndices() + assert !getUseForeignKeys() + assert !getUseIndices() + assert !getUseIndicesUnique() + assert !getUseOrderByNulls() + assert !getUsePkConstraintNames() + assert !getUseProxyCursor() + assert !getUseSchemas() + assert getReadDataList()?.size() == 2 + } + } + + @Test + void testLoadDatasourceFromXmlWithTrueValue() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + + ''', '') + Map datasources = config.getDatasourceMap() + Datasource datasource = datasources.datasourcetest + + assert datasource + datasource.with { + assert getName() == 'datasourcetest' + assert getAddMissingOnStart() + assert getAliasViewColumns() + assert getAlwaysUseConstraintKeyword() + assert getCharacterSet() == 'character-set-test' + assert getCheckFkIndicesOnStart() + assert getCheckFksOnStart() + assert getCheckIndicesOnStart() + assert getCheckOnStart() + assert getCheckPksOnStart() + assert getCollate() == 'collate-test' + assert getConstraintNameClipLength() == 888 + assert getDropFkUseForeignKeyKeyword() + assert getFieldTypeName() == 'field-type-test' + assert getFkStyle() == 'fk-style-test' + assert getHelperClass() == 'org.apache.ofbiz.entity.datasource.GenericTest' + assert getJoinStyle() == 'join-test' + assert getMaxWorkerPoolSize() == 999 + assert getOffsetStyle() == 'offset-style-test' + assert getProxyCursorName() == 'proxy-test' + assert getResultFetchSize() == 99 + assert getSchemaName() == 'schema-test' + // TODO assert getSqlLoadPathList() + assert getTableType() == 'table-test' + assert getUseBinaryTypeForBlob() + assert getUseFkInitiallyDeferred() + assert getUseForeignKeyIndices() + assert getUseForeignKeys() + assert getUseIndices() + assert getUseIndicesUnique() + assert getUseOrderByNulls() + assert getUsePkConstraintNames() + assert getUseProxyCursor() + assert getUseSchemas() + assert getReadDataList()?.size() == 2 + } + } + + @Test + void testLoadDatasourceWithOverrideValue() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', + '''"datasource" : { + "datasourcetestover": { + "add-missing-on-start": "false", + "alias-view-columns": "false", + "always-use-constraint-keyword": "false", + "character-set": "character-set-overridetest", + "check-fks-indices-on-start": "false", + "check-fks-on-start": "false", + "check-indices-on-start": "false", + "check-on-start": "false", + "check-pks-on-start": "false", + "collate": "collate-overridetest", + "constraint-name-clip-length": "777", + "drop-fk-use-foreign-key-keyword": "false", + "field-type-name": "field-type-overridetest", + "fk-style": "fk-style-overridetest", + "helper-class": "org.apache.ofbiz.entity.datasource.GenericTest", + "join-style": "join-overridetest", + "max-worker-pool-size": "777", + "offset-style": "offset-style-overridetest", + "proxy-cursor-name": "proxy-overridetest", + "result-fetch-size": "77", + "schema-name": "schema-overridetest", + "table-type": "table-overridetest", + "use-binary-type-for-blob": "false", + "use-fk-initially-deferred": "false", + "use-foreign-key-indices": "false", + "use-foreign-keys": "false", + "use-indices": "false", + "use-indices-unique": "false", + "use-order-by-nulls": "false", + "use-pk-constraint-names": "false", + "use-proxy-cursor": "false", + "use-schemas": "false" + }} +''') + Map datasources = config.getDatasourceMap() + Datasource datasource = datasources.datasourcetestover + + assert datasource + datasource.with { + assert getName() == 'datasourcetestover' + assert !getAddMissingOnStart() + assert !getAliasViewColumns() + assert !getAlwaysUseConstraintKeyword() + assert getCharacterSet() == 'character-set-overridetest' + assert !getCheckFkIndicesOnStart() + assert !getCheckFksOnStart() + assert !getCheckIndicesOnStart() + assert !getCheckOnStart() + assert !getCheckPksOnStart() + assert getCollate() == 'collate-overridetest' + assert getConstraintNameClipLength() == 777 + assert !getDropFkUseForeignKeyKeyword() + assert getFieldTypeName() == 'field-type-overridetest' + assert getFkStyle() == 'fk-style-overridetest' + assert getHelperClass() == 'org.apache.ofbiz.entity.datasource.GenericTest' + assert getJoinStyle() == 'join-overridetest' + assert getMaxWorkerPoolSize() == 777 + assert getOffsetStyle() == 'offset-style-overridetest' + assert getProxyCursorName() == 'proxy-overridetest' + assert getResultFetchSize() == 77 + assert getSchemaName() == 'schema-overridetest' + // TODO assert getSqlLoadPathList() + assert getTableType() == 'table-overridetest' + assert !getUseBinaryTypeForBlob() + assert !getUseFkInitiallyDeferred() + assert !getUseForeignKeyIndices() + assert !getUseForeignKeys() + assert !getUseIndices() + assert !getUseIndicesUnique() + assert !getUseOrderByNulls() + assert !getUsePkConstraintNames() + assert !getUseProxyCursor() + assert !getUseSchemas() + } + } + + @Test + void testLoadingLineJdbcFromXmlWithFalseValue() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + ''', '') + Datasource datasource = config.getDatasourceMap()?.testinlinejdbc1 + assert datasource + datasource.getInlineJdbc().with { + assert getIdleMaxsize() == 11 + assert getJdbcDriver() == 'jdbc-driver-test' + assert getJdbcPassword() == 'jdbc-password-test' + assert getJdbcPasswordLookup() == 'jdbc-password-lookup-test' + assert getJdbcUri() == 'jdbc-uri-test' + assert getJdbcUsername() == 'jdbc-username-test' + assert getPoolDeadlockMaxwait() == 16 + assert getPoolDeadlockRetrywait() == 17 + assert getPoolJdbcTestStmt() == 'pool-test' + assert getPoolLifetime() == 15 + assert getPoolMaxsize() == 10 + assert getPoolMinsize() == 1 + assert getPoolSleeptime() == 14 + assert getSoftMinEvictableIdleTimeMillis() == 13 + assert !getTestOnBorrow() + assert !getTestOnCreate() + assert !getTestOnReturn() + assert !getTestWhileIdle() + assert getTimeBetweenEvictionRunsMillis() == 12 + } + } + + @Test + void testLoadInLineJdbcFromXmlWithTrueValue() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + ''', '') + Datasource datasource = config.getDatasourceMap()?.testinlinejdbc2 + assert datasource + datasource.getInlineJdbc().with { + assert getTestOnBorrow() + assert getTestOnCreate() + assert getTestOnReturn() + assert getTestWhileIdle() + } + } + + @Test + void testLoadInLineJdbcFromXmlWithOverrideValue() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + ''', '''"datasource" : { + "testinlinejdbcoveride": { + "inline-jdbc": { + "idle-maxsize": "91" + "jdbc-driver": "jdbc-driver-over" + "jdbc-password": "jdbc-password-over" + "jdbc-password-lookup": "jdbc-password-lookup-over" + "jdbc-uri": "jdbc-uri-over" + "jdbc-username": "jdbc-username-over" + "pool-deadlock-maxwait": "92" + "pool-deadlock-retrywait": "93" + "pool-jdbc-test-stmt": "pool-jdbc-test-stmt-over" + "pool-lifetime": "94" + "pool-maxsize": "95" + "pool-minsize": "96" + "pool-sleeptime": "97" + "soft-min-evictable-idle-time-millis": "98" + "test-on-borrow": "true" + "test-on-create": "true" + "test-on-return": "true" + "test-while-idle": "true" + "time-between-eviction-runs-millis": "99" + } + } + }''') + Datasource datasource = config.getDatasourceMap()?.testinlinejdbcoveride + assert datasource + datasource.getInlineJdbc().with { + assert getIdleMaxsize() == 91 + assert getJdbcDriver() == 'jdbc-driver-over' + assert getJdbcPassword() == 'jdbc-password-over' + assert getJdbcPasswordLookup() == 'jdbc-password-lookup-over' + assert getJdbcUri() == 'jdbc-uri-over' + assert getJdbcUsername() == 'jdbc-username-over' + assert getPoolDeadlockMaxwait() == 92 + assert getPoolDeadlockRetrywait() == 93 + assert getPoolJdbcTestStmt() == 'pool-jdbc-test-stmt-over' + assert getPoolLifetime() == 94 + assert getPoolMaxsize() == 95 + assert getPoolMinsize() == 96 + assert getPoolSleeptime() == 97 + assert getSoftMinEvictableIdleTimeMillis() == 98 + assert getTestOnBorrow() + assert getTestOnCreate() + assert getTestOnReturn() + assert getTestWhileIdle() + assert getTimeBetweenEvictionRunsMillis() == 99 + } + } + + @Test + void testCreateDatasourceFromConfigWithoutOverride() { + EntityConfig config = mockXmlAndHoconCgf(''' ''', + '''"datasource" : { + "datasourcewithnoxmlequivalent": { + // base params + "add-missing-on-start": "false", + "alias-view-columns": "false", + "always-use-constraint-keyword": "false", + "character-set": "character-set-overridetest", + "check-fks-indices-on-start": "false", + "check-fks-on-start": "false", + "check-indices-on-start": "false", + "check-on-start": "false", + "check-pks-on-start": "false", + "collate": "collate-overridetest", + "constraint-name-clip-length": "777", + "drop-fk-use-foreign-key-keyword": "false", + "field-type-name": "field-type-overridetest", + "fk-style": "fk-style-overridetest", + "helper-class": "org.apache.ofbiz.entity.datasource.GenericTest", + "join-style": "join-overridetest", + "max-worker-pool-size": "777", + "offset-style": "offset-style-overridetest", + "proxy-cursor-name": "proxy-overridetest", + "result-fetch-size": "77", + "schema-name": "schema-overridetest", + "table-type": "table-overridetest", + "use-binary-type-for-blob": "false", + "use-fk-initially-deferred": "false", + "use-foreign-key-indices": "false", + "use-foreign-keys": "false", + "use-indices": "false", + "use-indices-unique": "false", + "use-order-by-nulls": "false", + "use-pk-constraint-names": "false", + "use-proxy-cursor": "false", + "use-schemas": "false", + // inline jdbc + "inline-jdbc": { + "jdbc-driver": "postgresnew-driver", + "jdbc-password": "postgresnew-jdbc-password", + "jdbc-uri": "postgresnew-jdbc-uri", + "jdbc-username": "postgresnew-jdbc-username", + "jdbc-password-lookup": "postgresnew-jdbc-password-lookup", + "pool-jdbc-test-stmt": "postgresnew-pool-jdbc-test-stmt", + "idle-maxsize": "91", + "pool-deadlock-maxwait": "92", + "pool-deadlock-retrywait": "93", + "pool-lifetime": "94", + "pool-maxsize": "95", + "pool-minsize": "96", + "pool-sleeptime": "97", + "soft-min-evictable-idle-time-millis": "98", + "time-between-eviction-runs-millis": "99", + "test-on-borrow": "true", + "test-on-create": "true", + "test-on-return": "true", + "test-while-idle": "true" + } + } + }''') + Datasource datasource = config.getDatasourceMap()?.datasourcewithnoxmlequivalent + assert datasource + datasource.with { + assert getName() == 'datasourcewithnoxmlequivalent' + assert !getAddMissingOnStart() + assert !getAliasViewColumns() + assert !getAlwaysUseConstraintKeyword() + assert getCharacterSet() == 'character-set-overridetest' + assert !getCheckFkIndicesOnStart() + assert !getCheckFksOnStart() + assert !getCheckIndicesOnStart() + assert !getCheckOnStart() + assert !getCheckPksOnStart() + assert getCollate() == 'collate-overridetest' + assert getConstraintNameClipLength() == 777 + assert !getDropFkUseForeignKeyKeyword() + assert getFieldTypeName() == 'field-type-overridetest' + assert getFkStyle() == 'fk-style-overridetest' + assert getHelperClass() == 'org.apache.ofbiz.entity.datasource.GenericTest' + assert getJoinStyle() == 'join-overridetest' + assert getMaxWorkerPoolSize() == 777 + assert getOffsetStyle() == 'offset-style-overridetest' + assert getProxyCursorName() == 'proxy-overridetest' + assert getResultFetchSize() == 77 + assert getSchemaName() == 'schema-overridetest' + assert getTableType() == 'table-overridetest' + assert !getUseBinaryTypeForBlob() + assert !getUseFkInitiallyDeferred() + assert !getUseForeignKeyIndices() + assert !getUseForeignKeys() + assert !getUseIndices() + assert !getUseIndicesUnique() + assert !getUseOrderByNulls() + assert !getUsePkConstraintNames() + assert !getUseProxyCursor() + assert !getUseSchemas() + } + datasource.getInlineJdbc().with { + assert getJdbcDriver() == 'postgresnew-driver' + assert getJdbcPassword() == 'postgresnew-jdbc-password' + assert getJdbcPasswordLookup() == 'postgresnew-jdbc-password-lookup' + assert getJdbcUri() == 'postgresnew-jdbc-uri' + assert getJdbcUsername() == 'postgresnew-jdbc-username' + assert getPoolJdbcTestStmt() == 'postgresnew-pool-jdbc-test-stmt' + assert getIdleMaxsize() == 91 + assert getPoolDeadlockMaxwait() == 92 + assert getPoolDeadlockRetrywait() == 93 + assert getPoolLifetime() == 94 + assert getPoolMaxsize() == 95 + assert getPoolMinsize() == 96 + assert getPoolSleeptime() == 97 + assert getSoftMinEvictableIdleTimeMillis() == 98 + assert getTimeBetweenEvictionRunsMillis() == 99 + assert getTestOnBorrow() + assert getTestOnCreate() + assert getTestOnReturn() + assert getTestWhileIdle() + } + } + + @Test + void testCreateDatasourceFromConfigWithoutOverrideWithReadDataList() { + EntityConfig config = mockXmlAndHoconCgf(''' ''', + '''"datasource" : { + "datasourcewithreaddata": { + "read-data": [ + { "reader-name": "tenant" }, + { "reader-name": "seed" }, + { "reader-name": "seed-initial" }, + ] + "inline-jdbc": { + "jdbc-driver": "postgresnew-driver", + "jdbc-password": "postgresnew-jdbc-password", + "jdbc-uri": "postgresnew-jdbc-uri", + "jdbc-username": "postgresnew-jdbc-username", + } + } + }''') + Datasource datasource = config.getDatasourceMap()?.datasourcewithreaddata + assert datasource + datasource.with { + assert ['tenant', 'seed', 'seed-initial'] == getReadDataList()*.readerName + assert getReadDataList()?.size() == 3 + } + datasource.getInlineJdbc().with { + assert getJdbcDriver() == 'postgresnew-driver' + assert getJdbcPassword() == 'postgresnew-jdbc-password' + assert getJdbcUri() == 'postgresnew-jdbc-uri' + assert getJdbcUsername() == 'postgresnew-jdbc-username' + } + } + + @Test + void testLoadDatatasourceWithReadDataWithXmlAndConfigs() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + + ''', + '''"datasource" : { + "datasourcetestldwrdwxac": { + "read-data" : [ + {"reader-name":"over-reader1"}, + {"reader-name":"over-reader2"} + ] + } + } +''') + Map datasources = config.getDatasourceMap() + Datasource datasource = datasources.datasourcetestldwrdwxac + + assert datasource + datasource.with { + assert ['over-reader1', 'over-reader2'] == getReadDataList()*.readerName + assert getReadDataList().size() == 2 + } + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDebugXaResourcesTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDebugXaResourcesTest.groovy new file mode 100644 index 00000000000..6c4537b9fe9 --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDebugXaResourcesTest.groovy @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderDebugXaResourcesTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadDebugXaResourcesFromXmlWithFalse() { + EntityConfig config = mockXmlAndHoconCgf('', '') + assert !config.getDebugXaResources().value + } + + @Test + void testLoadDebugXaResourcesFromXmlWithTrue() { + EntityConfig config = mockXmlAndHoconCgf('', '') + assert config.getDebugXaResources().value + } + + @Test + void testLoadDebugXaResourcesFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf('', + '''"debug-xa-resources": {"value": "false"}''') + + assert !config.getDebugXaResources().value + } + + @Test + void testLoadDebugXaResourcesWithOverrideConfigNoneUsed() { + EntityConfig config = mockXmlAndHoconCgf('', + '''"debug-xa-resources": {"value": "false"}''', false) + + assert config.getDebugXaResources().value + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDelegatorElementTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDelegatorElementTest.groovy new file mode 100644 index 00000000000..ef3332db228 --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderDelegatorElementTest.groovy @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.DelegatorElement +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.apache.ofbiz.entity.config.model.GroupMap +import org.junit.jupiter.api.Test + +class ConfigReaderDelegatorElementTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadDelegatorElementFromXml() { + EntityConfig config = mockXmlAndHoconCgf('''''', '') + assert config.getDelegator('delegator-test') + config.getDelegator('delegator-test').with { + assert getName() == 'delegator-test' + assert getDefaultGroupName() == 'default-group-name-test' + assert getDistributedCacheClearClassName() == 'distributed-cache-clear-class-name-test' + assert getDistributedCacheClearEnabled() + assert getDistributedCacheClearUserLoginId() == 'distributed-cache-clear-user-login-id-test' + assert getEntityEcaEnabled() + assert getEntityEcaHandlerClassName() == 'entity-eca-handler-class-name-test' + assert getEntityEcaReader() == 'entity-eca-reader-test' + assert getKeyEncryptingKey() == 'key-encrypting-key-test' + assert getSequencedIdPrefix() == 'sequenced-id-prefix-test' + } + } + + @Test + void testLoadDelegatorElementFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf('''''', '''"delegator": { + "delegator-over": { + "default-group-name": "default-group-name-over" + "distributed-cache-clear-class-name": "distributed-cache-clear-class-name-over" + "distributed-cache-clear-enabled": "true" + "distributed-cache-clear-user-login-id": "distributed-cache-clear-user-login-id-over" + "entity-eca-enabled": "true" + "entity-eca-handler-class-name": "entity-eca-handler-class-name-over" + "entity-eca-reader": "entity-eca-reader-over" + "entity-group-reader": "entity-group-reader-over" + "entity-model-reader": "entity-model-reader-over" + "key-encrypting-key": "key-encrypting-key-over" + "sequenced-id-prefix": "sequenced-id-prefix-over" + }}''') + assert config.getDelegator('delegator-over') + config.getDelegator('delegator-over').with { + assert getName() == 'delegator-over' + assert getDefaultGroupName() == 'default-group-name-over' + assert getDistributedCacheClearClassName() == 'distributed-cache-clear-class-name-over' + assert getDistributedCacheClearEnabled() + assert getDistributedCacheClearUserLoginId() == 'distributed-cache-clear-user-login-id-over' + assert getEntityEcaEnabled() + assert getEntityEcaHandlerClassName() == 'entity-eca-handler-class-name-over' + assert getEntityEcaReader() == 'entity-eca-reader-over' + assert getKeyEncryptingKey() == 'key-encrypting-key-over' + assert getSequencedIdPrefix() == 'sequenced-id-prefix-over' + } + } + + @Test + void testLoadDelegatorGroupMapFromXmlConfig() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + ''', '') + assert config.getDelegator('delegator-test-group-map-xml') + List groupMapList = config.getDelegator('delegator-test-group-map-xml').getGroupMapList() + assert groupMapList.size() == 2 + groupMapList[0].with { + assert getGroupName() == 'group-name-test1' + assert getDatasourceName() == 'datasource-name-test' + } + groupMapList[1].with { + assert getGroupName() == 'group-name-test2' + assert getDatasourceName() == 'datasource-name-test' + } + } + + @Test + void testLoadDelegatorGroupMapFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf(''' + + + ''', '''"delegator": { + "delegator-test-group-map-over": { + "group-map": { + "group-name-test1": { + "datasource-name": "datasource-name-over" + }, + "group-name-test3": { + "datasource-name": "datasource-name-over" + } + } + }} + ''') + DelegatorElement delegatorElement = config.getDelegator('delegator-test-group-map-over') + assert delegatorElement + List groupMapList = delegatorElement.getGroupMapList() + assert groupMapList.size() == 3 + assert groupMapList.find { it.getGroupName() == 'group-name-test1' } + ?.getDatasourceName() == 'datasource-name-over' + assert groupMapList.find { it.getGroupName() == 'group-name-test2' } + ?.getDatasourceName() == 'datasource-name-test' + assert groupMapList.find { it.getGroupName() == 'group-name-test3' } + ?.getDatasourceName() == 'datasource-name-over' + + assert delegatorElement.getGroupDataSource('group-name-test1')?.getDatasourceName() + == 'datasource-name-over' + assert delegatorElement.getGroupDataSource('group-name-test2')?.getDatasourceName() + == 'datasource-name-test' + assert delegatorElement.getGroupDataSource('group-name-test3')?.getDatasourceName() + == 'datasource-name-over' + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityDataReaderTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityDataReaderTest.groovy new file mode 100644 index 00000000000..ba218134308 --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityDataReaderTest.groovy @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderEntityDataReaderTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadEntityDataReaderFromXml() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', '') + + assert config.getEntityDataReaderList().size() == 2 + config.getEntityDataReader('test1') + config.getEntityDataReader('test2') + } + + @Test + void testLoadEntityDataReaderFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', + '''entity-data-reader: [{"name": "test1"}, {"name": "test3"}]''') + + assert config.getEntityDataReaderList().size() == 3 + config.getEntityDataReader('test1') + config.getEntityDataReader('test2') + config.getEntityDataReader('test3') + } + +} + diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityEcaReaderTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityEcaReaderTest.groovy new file mode 100644 index 00000000000..380708818fc --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityEcaReaderTest.groovy @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderEntityEcaReaderTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadEntityEcaReaderFromXml() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', '') + + assert config.getEntityEcaReaderList().size() == 2 + config.getEntityEcaReader('test1') + config.getEntityEcaReader('test2') + } + + @Test + void testLoadEntityEcaReaderFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', + '''entity-eca-reader: [{"name": "test1"}, {"name": "test3"}]''') + + assert config.getEntityEcaReaderList().size() == 3 + config.getEntityEcaReader('test1') + config.getEntityEcaReader('test2') + config.getEntityEcaReader('test3') + } + +} + diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityGroupReaderTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityGroupReaderTest.groovy new file mode 100644 index 00000000000..bcb07ffe344 --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityGroupReaderTest.groovy @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderEntityGroupReaderTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadEntityGroupReaderFromXml() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', '') + + assert config.getEntityGroupReaderList().size() == 2 + config.getEntityGroupReader('test1')?.with { + assert getLocation() == 'location-test1' + assert getLoader() == 'loader-test1' + } + config.getEntityGroupReader('test2')?.with { + assert getLocation() == 'location-test2' + assert getLoader() == 'loader-test2' + } + } + + @Test + void testLoadEntityGroupReaderFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', + '''"entity-group-reader": { + "test1": {"loader": "loader-over1", + "location": "location-over1"}, + "test3": {"loader": "loader-over3", + "location": "location-over3"}}''') + + assert config.getEntityGroupReaderList().size() == 3 + assert config.getEntityGroupReader('test1') + assert config.getEntityGroupReader('test2') + assert config.getEntityGroupReader('test3') + config.getEntityGroupReader('test1')?.with { + assert getLocation() == 'location-over1' + assert getLoader() == 'loader-over1' + } + config.getEntityGroupReader('test2')?.with { + assert getLocation() == 'location-test2' + assert getLoader() == 'loader-test2' + } + config.getEntityGroupReader('test3')?.with { + assert getLocation() == 'location-over3' + assert getLoader() == 'loader-over3' + } + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityModelReaderTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityModelReaderTest.groovy new file mode 100644 index 00000000000..ca0ed6a0575 --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderEntityModelReaderTest.groovy @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderEntityModelReaderTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadEntityModelReaderFromXml() { + EntityConfig config = mockXmlAndHoconCgf('', '') + + assert config.getEntityModelReaderList().size() == 2 + assert config.getEntityModelReader('test1') + assert config.getEntityModelReader('test2') + } + + @Test + void testLoadEntityModelReaderFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf('', + '"entity-model-reader": [ {"name": "test3"} ]') + + assert config.getEntityModelReaderList().size() == 3 + assert config.getEntityModelReader('test1') + assert config.getEntityModelReader('test2') + assert config.getEntityModelReader('test3') + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderFieldTypeTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderFieldTypeTest.groovy new file mode 100644 index 00000000000..16d5433ce8e --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderFieldTypeTest.groovy @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderFieldTypeTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadFieldTypeFromXml() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', '') + + assert config.getFieldTypeList().size() == 2 + config.getFieldType('test1')?.with { + assert getLocation() == 'location-test1' + assert getLoader() == 'loader-test1' + } + config.getFieldType('test2')?.with { + assert getLocation() == 'location-test2' + assert getLoader() == 'loader-test2' + } + } + + @Test + void testLoadFieldTypeFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf(''' + + ''', + '''"field-type": { + "test1": {"loader": "loader-over1", + "location": "location-over1"}, + "test3": {"loader": "loader-over3", + "location": "location-over3"}}''') + + assert config.getFieldTypeList().size() == 3 + assert config.getFieldType('test1') + assert config.getFieldType('test2') + assert config.getFieldType('test3') + config.getFieldType('test1')?.with { + assert getLocation() == 'location-over1' + assert getLoader() == 'loader-over1' + } + config.getFieldType('test2')?.with { + assert getLocation() == 'location-test2' + assert getLoader() == 'loader-test2' + } + config.getFieldType('test3')?.with { + assert getLocation() == 'location-over3' + assert getLoader() == 'loader-over3' + } + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderResourceLoaderTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderResourceLoaderTest.groovy new file mode 100644 index 00000000000..450aff6a70b --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderResourceLoaderTest.groovy @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.apache.ofbiz.entity.config.model.ResourceLoader +import org.junit.jupiter.api.Test + +class ConfigReaderResourceLoaderTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadResourceLoaderFromXml() { + EntityConfig config = mockXmlAndHoconCgf('''''' + , '') + ResourceLoader resourceLoader = config.getResourceLoader('name-test') + + assert resourceLoader + resourceLoader.with { + assert getName() == 'name-test' + assert getClassName() == 'class-test' + assert getPrependEnv() == 'prepend-env-test' + assert getPrefix() == 'prefix-test' + } + } + + @Test + void testLoadResourceLoaderFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf('''''', + '''"resource-loader": { + "name-over" : { + "class": "class-over", + "prepend-env": "prepend-env-over", + "prefix": "prefix-over" + } + } ''') + ResourceLoader resourceLoader = config.getResourceLoader('name-over') + + assert resourceLoader + resourceLoader.with { + assert getName() == 'name-over' + assert getClassName() == 'class-over' + assert getPrependEnv() == 'prepend-env-over' + assert getPrefix() == 'prefix-over' + } + } + +} diff --git a/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderTransactionFactoryTest.groovy b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderTransactionFactoryTest.groovy new file mode 100644 index 00000000000..c74e0cb0e6b --- /dev/null +++ b/framework/entity/src/test/groovy/org/apache/ofbiz/entity/model/test/ConfigReaderTransactionFactoryTest.groovy @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.entity.model.test + +import org.apache.ofbiz.entity.config.model.TransactionFactory +import org.apache.ofbiz.entity.config.model.EntityConfig +import org.junit.jupiter.api.Test + +class ConfigReaderTransactionFactoryTest extends BaseEntityConfigReaderTest { + + @Test + void testLoadTransactionFactoryClassNameFromXml() { + EntityConfig config = mockXmlAndHoconCgf('', '') + TransactionFactory transactionFactory = config.getTransactionFactory() + + assert transactionFactory.getClassName() == 'org.apache.ofbiz.test.test1' + } + + @Test + void testLoadTransactionFactoryClassNameFromOverrideConfig() { + EntityConfig config = mockXmlAndHoconCgf('', + '''"transaction-factory": {"class": "org.apache.ofbiz.test.override"}''') + TransactionFactory transactionFactory = config.getTransactionFactory() + + assert transactionFactory.getClassName() == 'org.apache.ofbiz.test.override' + } + + @Test + void testLoadTransactionFactoryClassNameWithOverrideConfigNoneUsed() { + EntityConfig config = mockXmlAndHoconCgf('', + '''"transaction-factory": {"class": "org.apache.ofbiz.test.overridenoneread"}''' + , false) + TransactionFactory transactionFactory = config.getTransactionFactory() + + assert transactionFactory.getClassName() == 'org.apache.ofbiz.test.test3' + } + +} diff --git a/framework/entityext/src/main/java/org/apache/ofbiz/entityext/data/EntityDataLoadContainer.java b/framework/entityext/src/main/java/org/apache/ofbiz/entityext/data/EntityDataLoadContainer.java index e740865ecda..b9359bea4c5 100644 --- a/framework/entityext/src/main/java/org/apache/ofbiz/entityext/data/EntityDataLoadContainer.java +++ b/framework/entityext/src/main/java/org/apache/ofbiz/entityext/data/EntityDataLoadContainer.java @@ -229,9 +229,8 @@ private static Delegator getDelegator(Configuration.Property delegatorNameProp, throws ContainerException { if (overrideDelegator != null) { return DelegatorFactory.getDelegator(overrideDelegator); - } else { - return getDelegatorFromProp(delegatorNameProp); } + return getDelegatorFromProp(delegatorNameProp); } private static Delegator getDelegatorFromProp(Configuration.Property delegatorNameProp) throws ContainerException { diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/DispatchContext.java b/framework/service/src/main/java/org/apache/ofbiz/service/DispatchContext.java index 3d88b53261d..d6bb3af4230 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/DispatchContext.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/DispatchContext.java @@ -44,6 +44,7 @@ import org.apache.ofbiz.security.Security; import org.apache.ofbiz.service.config.ServiceConfigUtil; import org.apache.ofbiz.service.config.model.GlobalServices; +import org.apache.ofbiz.service.config.model.ServiceConfigGetter; import org.apache.ofbiz.service.eca.ServiceEcaUtil; import org.w3c.dom.Document; @@ -247,7 +248,7 @@ private Map getGlobalServiceMap() { throw new RuntimeException(e.getMessage()); } for (GlobalServices globalServices : globalServicesList) { - ResourceHandler handler = new MainResourceHandler(ServiceConfigUtil.getServiceEngineXmlFileName(), globalServices.getLoader(), + ResourceHandler handler = new MainResourceHandler(ServiceConfigGetter.getServiceEngineXmlFileName(), globalServices.getLoader(), globalServices.getLocation()); futures.add(ExecutionPool.GLOBAL_FORK_JOIN.submit(createServiceReaderCallable(handler))); } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/ServiceConfigUtil.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/ServiceConfigUtil.java index e7359126b36..bc7c2e74968 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/ServiceConfigUtil.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/ServiceConfigUtil.java @@ -18,21 +18,16 @@ *******************************************************************************/ package org.apache.ofbiz.service.config; -import java.net.URL; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; - import org.apache.ofbiz.base.config.GenericConfigException; import org.apache.ofbiz.base.util.Assert; import org.apache.ofbiz.base.util.Debug; -import org.apache.ofbiz.base.util.UtilURL; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.base.util.cache.UtilCache; import org.apache.ofbiz.service.config.model.Engine; import org.apache.ofbiz.service.config.model.ServiceConfig; +import org.apache.ofbiz.service.config.model.ServiceConfigFactory; import org.apache.ofbiz.service.config.model.ServiceEngine; -import org.w3c.dom.Document; -import org.w3c.dom.Element; /** * A ServiceConfig factory and related utility methods. @@ -47,20 +42,21 @@ public final class ServiceConfigUtil { private static final String MODULE = ServiceConfigUtil.class.getName(); private static final String ENGINE = "default"; - private static final String SERVICE_ENGINE_XML_FILENAME = "serviceengine.xml"; // Keep the ServiceConfig instance in a cache - so the configuration can be reloaded at run-time. // There will be only one ServiceConfig instance in the cache. private static final UtilCache SERVICE_CONFIG_CACHE = UtilCache.createUtilCache("service.ServiceConfig", 0, 0, false); private static final List CONFIG_LISTENERS = new CopyOnWriteArrayList<>(); - private ServiceConfigUtil() { } + private ServiceConfigUtil() { + } /** * Returns the specified parameter value from the specified engine, or null * if the engine or parameter are not found. + * * @param engineName * @param parameterName - * @return + * @return parameter value for the given engine * @throws GenericConfigException */ public static String getEngineParameter(String engineName, String parameterName) throws GenericConfigException { @@ -73,21 +69,16 @@ public static String getEngineParameter(String engineName, String parameterName) /** * Returns the ServiceConfig instance. + * * @throws GenericConfigException */ public static ServiceConfig getServiceConfig() throws GenericConfigException { - ServiceConfig instance = SERVICE_CONFIG_CACHE.get("instance"); - if (instance == null) { - Element serviceConfigElement = getXmlDocument().getDocumentElement(); - instance = ServiceConfig.create(serviceConfigElement); - SERVICE_CONFIG_CACHE.putIfAbsent("instance", instance); - instance = SERVICE_CONFIG_CACHE.get("instance"); - for (ServiceConfigListener listener : CONFIG_LISTENERS) { - try { - listener.onServiceConfigChange(instance); - } catch (Exception e) { - Debug.logError(e, "Exception thrown while notifying listener " + listener + ": ", MODULE); - } + ServiceConfig instance = ServiceConfigFactory.getInstance(); + for (ServiceConfigListener listener : CONFIG_LISTENERS) { + try { + listener.onServiceConfigChange(instance); + } catch (Exception e) { + Debug.logError(e, "Exception thrown while notifying listener " + listener + ": ", MODULE); } } return instance; @@ -95,6 +86,7 @@ public static ServiceConfig getServiceConfig() throws GenericConfigException { /** * Returns the default service engine configuration (named "default"). + * * @throws GenericConfigException */ public static ServiceEngine getServiceEngine() throws GenericConfigException { @@ -104,27 +96,17 @@ public static ServiceEngine getServiceEngine() throws GenericConfigException { /** * Returns the specified ServiceEngine configuration instance, * or null if the configuration does not exist. + * * @throws GenericConfigException */ public static ServiceEngine getServiceEngine(String name) throws GenericConfigException { return getServiceConfig().getServiceEngine(name); } - private static Document getXmlDocument() throws GenericConfigException { - URL confUrl = UtilURL.fromResource(SERVICE_ENGINE_XML_FILENAME); - if (confUrl == null) { - throw new GenericConfigException("Could not find the " + SERVICE_ENGINE_XML_FILENAME + " file"); - } - try { - return UtilXml.readXmlDocument(confUrl, true, true); - } catch (Exception e) { - throw new GenericConfigException("Exception thrown while reading " + SERVICE_ENGINE_XML_FILENAME + ": ", e); - } - } - /** * Register a ServiceConfigListener instance. The instance will be notified * when the serviceengine.xml file is reloaded. + * * @param listener */ public static void registerServiceConfigListener(ServiceConfigListener listener) { @@ -136,7 +118,4 @@ public static String getEngine() { return ENGINE; } - public static String getServiceEngineXmlFileName() { - return SERVICE_ENGINE_XML_FILENAME; - } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Authorization.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Authorization.java index ae43f5f7d4a..f937eb3c8bd 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Authorization.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Authorization.java @@ -18,7 +18,11 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; + +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,19 +30,48 @@ * An object that models the <authorization> element. */ @ThreadSafe -public final class Authorization { +public final class Authorization extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "authorization"; + private final String xPath; private final String serviceName; - Authorization(Element authElement) throws ServiceConfigException { - String serviceName = authElement.getAttribute("service-name").intern(); + Authorization(Element authElement, String xPathParent) throws ServiceConfigException { + xPath = xPathParent.concat("/authorization"); + String serviceName = config.getValue(xPath + "/@service-name"); + if (serviceName.isEmpty()) { + throw new ServiceConfigException(" element service-name attribute is empty"); + } + this.serviceName = serviceName; + } + + Authorization(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String serviceName = getNameFromXPath(xPath + "/service-name"); if (serviceName.isEmpty()) { throw new ServiceConfigException(" element service-name attribute is empty"); } this.serviceName = serviceName; } + public static Authorization loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new Authorization(element, xPathParent); + } + + public static Authorization loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new Authorization(configMap, xPath); + } + public String getServiceName() { return serviceName; } + + @Override + public String getName() { + return "authorization"; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Engine.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Engine.java index c667e2875c5..ecc2a74dc57 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Engine.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Engine.java @@ -18,14 +18,13 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; - +import org.apache.ofbiz.base.config.AbstractConfigElement; +import org.apache.ofbiz.base.config.ConfigHelper; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -33,47 +32,73 @@ * An object that models the <engine> element. */ @ThreadSafe -public final class Engine { +public final class Engine extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "engine"; + private final String xPath; private final String className; private final String name; private final List parameters; private final Map parameterMap; - Engine(Element engineElement) throws ServiceConfigException { + Engine(Element engineElement, String xPathParent) throws ServiceConfigException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); String name = engineElement.getAttribute("name").intern(); + if (name.isEmpty() && checkStructure) { + throw new ServiceConfigException(" element name attribute is empty"); + } + this.name = name; + xPath = xPathParent.concat("/engine[@name='" + name + "']"); + String className = config.getValue(xPath.concat("/@class")); + if (className.isEmpty() && checkStructure) { + throw new ServiceConfigException(" element class attribute is empty"); + } + this.className = className; + List parameterList = config.getSubElementsAsListEntries(xPath, engineElement, Parameter.class); + parameters = Collections.unmodifiableList(parameterList); + if (parameterList.isEmpty()) { + parameterMap = Collections.emptyMap(); + } else { + this.parameterMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(xPath, + engineElement, Parameter.class)); + } + } + + Engine(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - String className = engineElement.getAttribute("class").intern(); + String className = config.getValue(configObject, "/@class"); if (className.isEmpty()) { throw new ServiceConfigException(" element class attribute is empty"); } this.className = className; - List parameterElementList = UtilXml.childElementList(engineElement, "parameter"); - if (parameterElementList.isEmpty()) { - this.parameters = Collections.emptyList(); - this.parameterMap = Collections.emptyMap(); + List parameterList = config.getSubElementsAsListEntries(xPath, null, Parameter.class); + parameters = Collections.unmodifiableList(parameterList); + if (parameterList.isEmpty()) { + parameterMap = Collections.emptyMap(); } else { - List parameters = new ArrayList<>(parameterElementList.size()); - Map parameterMap = new HashMap<>(); - for (Element parameterElement : parameterElementList) { - Parameter parameter = new Parameter(parameterElement); - parameters.add(parameter); - parameterMap.put(parameter.getName(), parameter); - } - this.parameters = Collections.unmodifiableList(parameters); - this.parameterMap = Collections.unmodifiableMap(parameterMap); + this.parameterMap = Collections.unmodifiableMap(config.getSubElementsAsMapValues(xPath, null, Parameter.class)); } } - public String getClassName() { - return className; + public static Engine loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new Engine(element, xPathParent); } - public String getName() { - return name; + public static Engine loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new Engine(configMap, xPath); + } + + public String getClassName() { + return className; } public Parameter getParameter(String parameterName) { @@ -81,14 +106,19 @@ public Parameter getParameter(String parameterName) { } public List getParameters() { - return this.parameters; + return parameters; } public String getParameterValue(String parameterName) { Parameter parameter = parameterMap.get(parameterName); - if (parameter != null) { - return parameter.getValue(); - } - return null; + return parameter != null + ? parameter.getValue() + : null; + } + + @Override + public String getName() { + return name; } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/GlobalServices.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/GlobalServices.java index 4cc55527142..7eb70c632bf 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/GlobalServices.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/GlobalServices.java @@ -18,7 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,22 +29,51 @@ * An object that models the <global-services> element. */ @ThreadSafe -public final class GlobalServices { +public final class GlobalServices extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "global-services"; + private final String xPath; private final String loader; private final String location; - GlobalServices(Element globalServicesElement) throws ServiceConfigException { - String loader = globalServicesElement.getAttribute("loader").intern(); + GlobalServices(Element globalServicesElement, String xPathParent) throws ServiceConfigException { + String location = globalServicesElement.getAttribute("location").intern(); + if (location.isEmpty()) { + throw new ServiceConfigException(" element location attribute is empty"); + } + this.location = location; + xPath = xPathParent.concat("/global-services[@location='" + location + "']"); + String loader = config.getValue(xPath.concat("/@loader")); if (loader.isEmpty()) { throw new ServiceConfigException(" element loader attribute is empty"); } this.loader = loader; - String location = globalServicesElement.getAttribute("location").intern(); + } + + GlobalServices(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String location = getNameFromXPath(xPath); if (location.isEmpty()) { throw new ServiceConfigException(" element location attribute is empty"); } this.location = location; + String loader = config.getValue(configObject, "/@loader"); + if (loader.isEmpty()) { + throw new ServiceConfigException(" element loader attribute is empty"); + } + this.loader = loader; + } + + public static GlobalServices loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new GlobalServices(element, xPathParent); + } + + public static GlobalServices loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new GlobalServices(configMap, xPath); } public String getLoader() { @@ -51,4 +83,9 @@ public String getLoader() { public String getLocation() { return location; } + + @Override + public String getName() { + return location; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/JmsService.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/JmsService.java index 331001b7dad..8150cbfedcb 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/JmsService.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/JmsService.java @@ -18,12 +18,13 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -31,44 +32,61 @@ * An object that models the <jms-service> element. */ @ThreadSafe -public final class JmsService { +public final class JmsService extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "jms-service"; + private final String xPath; private final String name; private final String sendMode; private final List servers; - JmsService(Element jmsServiceElement) throws ServiceConfigException { + JmsService(Element jmsServiceElement, String xPathParent) throws ServiceConfigException { String name = jmsServiceElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - String sendMode = jmsServiceElement.getAttribute("send-mode").intern(); - if (sendMode.isEmpty()) { - sendMode = "none"; - } - this.sendMode = sendMode; - List serverElementList = UtilXml.childElementList(jmsServiceElement, "server"); - if (serverElementList.isEmpty()) { - this.servers = Collections.emptyList(); - } else { - List servers = new ArrayList<>(serverElementList.size()); - for (Element serverElement : serverElementList) { - servers.add(new Server(serverElement)); - } - this.servers = Collections.unmodifiableList(servers); + xPath = xPathParent.concat("/jms-service[@name='" + name + "']"); + sendMode = config.getValue(xPath.concat("/@send-mode"), "none", String.class); + List serverList = config.getSubElementsAsListEntries(xPath, jmsServiceElement, Server.class); + servers = Collections.unmodifiableList(serverList); + } + + JmsService(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); } + this.name = name; + sendMode = config.getValue(configObject, "/@send-mode", "none", String.class); + List serverList = config.getSubElementsAsListEntries(xPath, null, Server.class); + servers = Collections.unmodifiableList(serverList); } - public String getName() { - return name; + public static JmsService loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new JmsService(element, xPathParent); + } + + public static JmsService loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new JmsService(configMap, xPath); } public String getSendMode() { - return this.sendMode; + return sendMode; } public List getServers() { - return this.servers; + return servers; + } + + @Override + public String getName() { + return name; } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notification.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notification.java index 877280e7d3a..da664db395d 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notification.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notification.java @@ -18,7 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,29 +29,62 @@ * An object that models the <notification> element. */ @ThreadSafe -public final class Notification { +public final class Notification extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "notification"; + private final String xPath; private final String screen; private final String service; private final String subject; - Notification(Element notificationElement) throws ServiceConfigException { - String subject = notificationElement.getAttribute("subject").intern(); + Notification(Element notificationElement, String xPathParent) throws ServiceConfigException { + xPath = xPathParent.concat("/notification"); + String subject = config.getValue(xPath.concat("/@subject")); if (subject.isEmpty()) { throw new ServiceConfigException(" element subject attribute is empty"); } this.subject = subject; - String screen = notificationElement.getAttribute("screen").intern(); + String screen = config.getValue(xPath.concat("/@screen")); + if (screen.isEmpty()) { + throw new ServiceConfigException(" element screen attribute is empty"); + } + this.screen = screen; + String service = config.getValue(xPath.concat("/@service")); + if (service.isEmpty()) { + service = "sendMailFromScreen"; + } + this.service = service; + } + + Notification(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String subject = config.getValue(configObject, "/@subject"); if (subject.isEmpty()) { + throw new ServiceConfigException(" element subject attribute is empty"); + } + this.subject = subject; + String screen = config.getValue(configObject, "/@screen"); + if (screen.isEmpty()) { throw new ServiceConfigException(" element screen attribute is empty"); } this.screen = screen; - String service = notificationElement.getAttribute("service").intern(); + String service = config.getValue(configObject, "/@service"); if (service.isEmpty()) { service = "sendMailFromScreen"; } this.service = service; + } + public static Notification loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new Notification(element, xPathParent); + } + + public static Notification loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new Notification(configMap, xPath); } public String getScreen() { @@ -56,10 +92,15 @@ public String getScreen() { } public String getService() { - return this.service; + return service; } public String getSubject() { return subject; } + + @Override + public String getName() { + return "notification"; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/NotificationGroup.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/NotificationGroup.java index c670cdf059e..1c9d0d7bb98 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/NotificationGroup.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/NotificationGroup.java @@ -18,12 +18,12 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; - +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -31,37 +31,64 @@ * An object that models the <notification-group> element. */ @ThreadSafe -public final class NotificationGroup { +public final class NotificationGroup extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "notification-group"; + private final String xPath; private final String name; private final Notification notification; private final List notifyList; - NotificationGroup(Element notificationGroupElement) throws ServiceConfigException { + NotificationGroup(Element notificationGroupElement, String xPathParent) throws ServiceConfigException { String name = notificationGroupElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - Element notification = UtilXml.firstChildElement(notificationGroupElement, "notification"); + xPath = xPathParent.concat("/notification-group[@name='" + name + "']"); + Notification notification = config.getObjectSubElement(xPath, notificationGroupElement, Notification.class); + if (notification == null) { + throw new ServiceConfigException(" element is missing"); + } + this.notification = notification; + List notifyList = config.getSubElementsAsListEntries(xPath + "/notification", + notificationGroupElement, Notify.class); + if (notifyList.size() < 2) { + throw new ServiceConfigException(" element(s) missing"); + } + this.notifyList = Collections.unmodifiableList(notifyList); + } + + NotificationGroup(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); + } + this.name = name; + Notification notification = config.getObjectSubElement(xPath, null, Notification.class); if (notification == null) { throw new ServiceConfigException(" element is missing"); } - this.notification = new Notification(notification); - List notifyElementList = UtilXml.childElementList(notificationGroupElement, "notify"); - if (notifyElementList.size() < 2) { + this.notification = notification; + List notifyList = config.getSubElementsAsListEntries(xPath, null, Notify.class); + if (notifyList.size() < 2) { throw new ServiceConfigException(" element(s) missing"); } else { - List notifyList = new ArrayList<>(notifyElementList.size()); - for (Element notifyElement : notifyElementList) { - notifyList.add(new Notify(notifyElement)); - } this.notifyList = Collections.unmodifiableList(notifyList); } } - public String getName() { - return name; + public static NotificationGroup loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new NotificationGroup(element, xPathParent); + } + + public static NotificationGroup loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new NotificationGroup(configMap, xPath); } public Notification getNotification() { @@ -69,6 +96,12 @@ public Notification getNotification() { } public List getNotifyList() { - return this.notifyList; + return notifyList; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notify.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notify.java index a2aa6ccc494..b9803434542 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notify.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Notify.java @@ -18,8 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -27,22 +29,43 @@ * An object that models the <notify> element. */ @ThreadSafe -public final class Notify { +public final class Notify extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "notify"; + private final String xPath; private final String content; private final String type; - Notify(Element notifyElement) throws ServiceConfigException { + Notify(Element notifyElement, String xPathParent) throws ServiceConfigException { + xPath = xPathParent; String type = notifyElement.getAttribute("type").intern(); if (type.isEmpty()) { throw new ServiceConfigException(" element type attribute is empty"); } this.type = type; - String content = UtilXml.elementValue(notifyElement); - if (content == null) { - content = ""; + content = notifyElement.getTextContent(); + } + + Notify(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String type = getNameFromXPath(xPath); + if (type.isEmpty()) { + throw new ServiceConfigException(" element type attribute is empty"); } - this.content = content; + this.type = type; + content = config.getValue(configObject, "/@content"); + } + + public static Notify loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new Notify(element, xPathParent); + } + + public static Notify loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new Notify(configMap, xPath); } public String getContent() { @@ -53,4 +76,8 @@ public String getType() { return type; } + @Override + public String getName() { + return type; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Parameter.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Parameter.java index b3fe7eff2c9..aeb0ab4bffa 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Parameter.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Parameter.java @@ -18,7 +18,11 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; + +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,29 +30,60 @@ * An object that models the <parameter> element. */ @ThreadSafe -public final class Parameter { +public final class Parameter extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "parameter"; + private final String xPath; private final String name; private final String value; - Parameter(Element parameterElement) throws ServiceConfigException { + Parameter(Element parameterElement, String xPathParent) throws ServiceConfigException { String name = parameterElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - String value = parameterElement.getAttribute("value").intern(); + xPath = xPathParent.concat("/engine[@name='" + name + "']"); + String value = config.getValue(xPath.concat("/@value")); if (value.isEmpty()) { throw new ServiceConfigException(" element value attribute is empty"); } this.value = value; } - public String getName() { - return name; + Parameter(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); + } + this.name = name; + String value = config.getValue(configObject, "/@value"); + if (value.isEmpty()) { + throw new ServiceConfigException(" element value attribute is empty"); + } + this.value = value; + } + + public static Parameter loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new Parameter(element, xPathParent); + } + + public static Parameter loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new Parameter(configMap, xPath); } public String getValue() { return value; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ResourceLoader.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ResourceLoader.java index 1468ed21a91..14580c0007d 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ResourceLoader.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ResourceLoader.java @@ -18,7 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,34 +29,61 @@ * An object that models the <resource-loader> element. */ @ThreadSafe -public final class ResourceLoader { +public final class ResourceLoader extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "resource-loader"; + private final String xPath; private final String className; private final String name; private final String prefix; private final String prependEnv; - ResourceLoader(Element resourceLoaderElement) throws ServiceConfigException { + ResourceLoader(Element resourceLoaderElement, String xPathParent) throws ServiceConfigException { String name = resourceLoaderElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - String className = resourceLoaderElement.getAttribute("class").intern(); + xPath = xPathParent.concat("/resource-loader[@name='" + name + "']"); + String className = config.getValue(xPath.concat("/@class")); if (className.isEmpty()) { throw new ServiceConfigException(" element class attribute is empty"); } this.className = className; - this.prependEnv = resourceLoaderElement.getAttribute("prepend-env").intern(); - this.prefix = resourceLoaderElement.getAttribute("prefix").intern(); + this.prependEnv = config.getValue(xPath.concat("/@prepend-env")); + this.prefix = resourceLoaderElement.getAttribute(xPath.concat("/@prefix")); } - public String getClassName() { - return className; + ResourceLoader(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); + } + this.name = name; + String className = config.getValue(configObject, "/@class"); + if (className.isEmpty()) { + throw new ServiceConfigException(" element class attribute is empty"); + } + this.className = className; + this.prependEnv = config.getValue(configObject, "/@prepend-env"); + this.prefix = config.getValue(configObject, "/@prefix"); } - public String getName() { - return name; + public static ResourceLoader loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new ResourceLoader(element, xPathParent); + } + + public static ResourceLoader loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new ResourceLoader(configMap, xPath); + } + + public String getClassName() { + return className; } public String getPrefix() { @@ -63,4 +93,10 @@ public String getPrefix() { public String getPrependEnv() { return prependEnv; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/RunFromPool.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/RunFromPool.java index 2c709cf21a5..296b97286b2 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/RunFromPool.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/RunFromPool.java @@ -18,7 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,19 +29,48 @@ * An object that models the <run-from-pool> element. */ @ThreadSafe -public final class RunFromPool { +public final class RunFromPool extends AbstractConfigElement { + public static final String ELEMENT_NAME = "run-from-pool"; + private final String xPath; private final String name; - RunFromPool(Element runFromPoolElement) throws ServiceConfigException { + RunFromPool(Element runFromPoolElement, String xPathParent) throws ServiceConfigException { String name = runFromPoolElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; + xPath = xPathParent.concat("/run-from-pool[@name='" + name + "']"); } + RunFromPool(Map configObject, String xPath) throws ServiceConfigException { + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); + } + this.name = name; + this.xPath = xPath; + } + + public static RunFromPool loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new RunFromPool(element, xPathParent); + } + + public static RunFromPool loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new RunFromPool(configMap, xPath); + } + + @Override + public boolean allowMultipleSources() { + return false; + } + + @Override public String getName() { return name; } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Server.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Server.java index 94bb716d162..8c09aba2a27 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Server.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/Server.java @@ -18,7 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,7 +29,11 @@ * An object that models the <server> element. */ @ThreadSafe -public final class Server { +public final class Server extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "server"; + private final String xPath; private final String clientId; private final String jndiName; @@ -38,32 +45,72 @@ public final class Server { private final String type; private final String username; - Server(Element serverElement) throws ServiceConfigException { + Server(Element serverElement, String xPathParent) throws ServiceConfigException { String jndiServerName = serverElement.getAttribute("jndi-server-name").intern(); if (jndiServerName.isEmpty()) { throw new ServiceConfigException(" element jndi-server-name attribute is empty"); } this.jndiServerName = jndiServerName; - String jndiName = serverElement.getAttribute("jndi-name").intern(); + xPath = xPathParent.concat("/server[@jndi-server-name='" + jndiServerName + "']"); + String jndiName = config.getValue(xPath.concat("/@jndi-name")); if (jndiName.isEmpty()) { throw new ServiceConfigException(" element jndi-name attribute is empty"); } this.jndiName = jndiName; - String topicQueue = serverElement.getAttribute("topic-queue").intern(); + String topicQueue = config.getValue(xPath.concat("/@topic-queue")); if (topicQueue.isEmpty()) { throw new ServiceConfigException(" element topic-queue attribute is empty"); } this.topicQueue = topicQueue; - String type = serverElement.getAttribute("type").intern(); + String type = config.getValue(xPath.concat("/@type")); if (type.isEmpty()) { throw new ServiceConfigException(" element type attribute is empty"); } this.type = type; - this.username = serverElement.getAttribute("username").intern(); - this.password = serverElement.getAttribute("password").intern(); - this.clientId = serverElement.getAttribute("client-id").intern(); - this.listen = "true".equals(serverElement.getAttribute("listen")); - this.listenerClass = serverElement.getAttribute("listener-class").intern(); + this.username = config.getValue(xPath.concat("/@username")); + this.password = config.getValue(xPath.concat("/@password")); + this.clientId = config.getValue(xPath.concat("/@client-id")); + this.listen = "true".equals(config.getValue(xPath.concat("/@listen"))); + this.listenerClass = config.getValue(xPath.concat("/@listener-class")); + } + + Server(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String jndiServerName = getNameFromXPath(xPath); + if (jndiServerName.isEmpty()) { + throw new ServiceConfigException(" element jndi-server-name attribute is empty"); + } + this.jndiServerName = jndiServerName; + String jndiName = config.getValue(configObject, "/@jndi-name"); + if (jndiName.isEmpty()) { + throw new ServiceConfigException(" element jndi-name attribute is empty"); + } + this.jndiName = jndiName; + String topicQueue = config.getValue(configObject, "/@topic-queue"); + if (topicQueue.isEmpty()) { + throw new ServiceConfigException(" element topic-queue attribute is empty"); + } + this.topicQueue = topicQueue; + String type = config.getValue(configObject, "/@type"); + if (type.isEmpty()) { + throw new ServiceConfigException(" element type attribute is empty"); + } + this.type = type; + this.username = config.getValue(configObject, "/@username"); + this.password = config.getValue(configObject, "/@password"); + this.clientId = config.getValue(configObject, "/@client-id"); + this.listen = "true".equals(config.getValue(configObject, "/@listen")); + this.listenerClass = config.getValue(configObject, "/@listener-class"); + } + + public static Server loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new Server(element, xPathParent); + } + + public static Server loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new Server(configMap, xPath); } public String getClientId() { @@ -102,4 +149,8 @@ public String getUsername() { return username; } + @Override + public String getName() { + return jndiServerName; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfig.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfig.java index 09a89103722..7e0f0996c9a 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfig.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfig.java @@ -20,12 +20,9 @@ import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -35,14 +32,11 @@ @ThreadSafe public final class ServiceConfig { + private static final String X_PATH = "/service-config"; + public static ServiceConfig create(Element serviceConfigElement) throws ServiceConfigException { - Map serviceEngineMap = new HashMap<>(); - List engineElementList = UtilXml.childElementList(serviceConfigElement, "service-engine"); - for (Element engineElement : engineElementList) { - ServiceEngine engineModel = new ServiceEngine(engineElement); - serviceEngineMap.put(engineModel.getName(), engineModel); - } - return new ServiceConfig(serviceEngineMap); + return new ServiceConfig(ServiceConfigGetter.getInstance() + .getSubElementsAsMapValues(X_PATH, serviceConfigElement, ServiceEngine.class)); } private final Map serviceEngineMap; @@ -52,12 +46,10 @@ private ServiceConfig(Map serviceEngineMap) { } public Collection getServiceEngines() { - return this.serviceEngineMap.values(); + return serviceEngineMap.values(); } public ServiceEngine getServiceEngine(String name) { - return this.serviceEngineMap.get(name); + return serviceEngineMap.get(name); } - - } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfigFactory.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfigFactory.java new file mode 100644 index 00000000000..5f3dc8f974f --- /dev/null +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfigFactory.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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.service.config.model; + +import org.apache.ofbiz.base.config.GenericConfigException; +import org.apache.ofbiz.base.config.XmlFileReader; +import org.w3c.dom.Element; + +public class ServiceConfigFactory { + private static ServiceConfig instance = null; + + private static final String SERVICE_ENGINE_XML_FILENAME = "serviceengine.xml"; + + public static ServiceConfig createInstance() throws GenericConfigException { + Element serviceConfigElement = XmlFileReader.read(SERVICE_ENGINE_XML_FILENAME); + + instance = ServiceConfig.create(serviceConfigElement); + return instance; + } + + public static ServiceConfig getInstance() throws GenericConfigException { + if (instance == null) { + instance = createInstance(); + } + return instance; + } + +} diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfigGetter.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfigGetter.java new file mode 100644 index 00000000000..9cf8567153b --- /dev/null +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceConfigGetter.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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.service.config.model; + +import org.apache.ofbiz.base.config.AbstractXmlConfigGetter; +import org.apache.ofbiz.base.lang.ThreadSafe; + +@ThreadSafe +public final class ServiceConfigGetter extends AbstractXmlConfigGetter { + public static final String SERVICE_ENGINE_XML_FILENAME = "serviceengine.xml"; + private static final ServiceConfigGetter INSTANCE = initInstance(); + + public ServiceConfigGetter() { + super(SERVICE_ENGINE_XML_FILENAME, "/service-config"); + } + + public static ServiceConfigGetter getInstance() { + return INSTANCE; + } + + public static ServiceConfigGetter initInstance() { + synchronized (ServiceConfigGetter.class) { + return new ServiceConfigGetter(); + } + } + + public static String getServiceEngineXmlFileName() { + return SERVICE_ENGINE_XML_FILENAME; + } +} diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEcas.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEcas.java index a00aa747dd0..d0d2140ece0 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEcas.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEcas.java @@ -18,30 +18,63 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <service-ecas> element. */ @ThreadSafe -public final class ServiceEcas { +public final class ServiceEcas extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "service-ecas"; + private final String xPath; private final String loader; private final String location; - ServiceEcas(Element serviceEcasElement) throws ServiceConfigException { - String loader = serviceEcasElement.getAttribute("loader").intern(); + ServiceEcas(Element serviceEcasElement, String xPathParent) throws ServiceConfigException { + String location = serviceEcasElement.getAttribute("location").intern(); + if (location.isEmpty()) { + throw new ServiceConfigException(" element location attribute is empty"); + } + this.location = location; + xPath = xPathParent.concat("/service-ecas[@location='" + location + "']"); + String loader = config.getValue(xPath.concat("/@loader")); if (loader.isEmpty()) { throw new ServiceConfigException(" element loader attribute is empty"); } this.loader = loader; - String location = serviceEcasElement.getAttribute("location").intern(); + } + + ServiceEcas(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String location = getNameFromXPath(xPath); if (location.isEmpty()) { throw new ServiceConfigException(" element location attribute is empty"); } this.location = location; + String loader = config.getValue(configObject, "/@loader"); + if (loader.isEmpty()) { + throw new ServiceConfigException(" element loader attribute is empty"); + } + this.loader = loader; + } + + public static ServiceEcas loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceEcas(element, xPathParent); + } + + public static ServiceEcas loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceEcas(configMap, xPath); } public String getLoader() { @@ -51,4 +84,9 @@ public String getLoader() { public String getLocation() { return location; } + + @Override + public String getName() { + return location; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEngine.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEngine.java index 392e0572db4..41459d7360b 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEngine.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceEngine.java @@ -18,15 +18,13 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; - +import org.apache.ofbiz.base.config.AbstractConfigElement; +import org.apache.ofbiz.base.config.ConfigHelper; import org.apache.ofbiz.base.lang.ThreadSafe; -import org.apache.ofbiz.base.start.Start; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -34,7 +32,11 @@ * An object that models the <service-engine> element. */ @ThreadSafe -public final class ServiceEngine { +public final class ServiceEngine extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "service-engine"; + private final String xPath; private final Authorization authorization; private final List engines; @@ -50,124 +52,115 @@ public final class ServiceEngine { private final List startupServices; private final ThreadPool threadPool; - ServiceEngine(Element engineElement) throws ServiceConfigException { + ServiceEngine(Element engineElement, String xPathParent) throws ServiceConfigException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); String name = engineElement.getAttribute("name").intern(); - if (name.isEmpty()) { + if (name.isEmpty() && checkStructure) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - Element authElement = UtilXml.firstChildElement(engineElement, "authorization"); - if (authElement == null) { + xPath = xPathParent.concat("/service-engine[@name='" + name + "']"); + Authorization auth = config.getObjectSubElement(xPath, engineElement, Authorization.class); + if (auth == null && checkStructure) { throw new ServiceConfigException(" element is missing"); } - this.authorization = new Authorization(authElement); - Element poolElement = UtilXml.firstChildElement(engineElement, "thread-pool"); - if (poolElement == null) { + this.authorization = auth; + ThreadPool pool = config.getObjectSubElement(xPath, engineElement, ThreadPool.class); + if (pool == null && checkStructure) { throw new ServiceConfigException(" element is missing"); } - this.threadPool = new ThreadPool(poolElement); - List engineElementList = UtilXml.childElementList(engineElement, "engine"); - if (engineElementList.isEmpty()) { - this.engines = Collections.emptyList(); + this.threadPool = pool; + List engineList = config.getSubElementsAsListEntries(xPath, engineElement, Engine.class); + engines = Collections.unmodifiableList(engineList); + if (engineList.isEmpty()) { this.engineMap = Collections.emptyMap(); } else { - List engines = new ArrayList<>(engineElementList.size()); - Map engineMap = new HashMap<>(); - for (Element childEngineElement : engineElementList) { - Engine engine = new Engine(childEngineElement); - engines.add(engine); - engineMap.put(engine.getName(), engine); - } - this.engines = Collections.unmodifiableList(engines); - this.engineMap = Collections.unmodifiableMap(engineMap); - } - List serviceLocationElementList = UtilXml.childElementList(engineElement, "service-location"); - if (serviceLocationElementList.isEmpty()) { - this.serviceLocations = Collections.emptyList(); - } else { - List serviceLocations = new ArrayList<>(serviceLocationElementList.size()); - for (Element serviceLocationElement : serviceLocationElementList) { - String location = serviceLocationElement.getAttribute("location").intern(); - if (location.contains("localhost") && Start.getInstance().getConfig().getPortOffset() != 0) { - String s = location.substring(location.lastIndexOf(":") + 1); - Integer locationPort = Integer.valueOf(s.substring(0, s.indexOf("/"))); - Integer port = locationPort + Start.getInstance().getConfig().getPortOffset(); - location = location.replace(locationPort.toString(), port.toString()); - } - serviceLocations.add(new ServiceLocation(serviceLocationElement, location)); - } - this.serviceLocations = Collections.unmodifiableList(serviceLocations); - } - List notificationGroupElementList = UtilXml.childElementList(engineElement, "notification-group"); - if (notificationGroupElementList.isEmpty()) { - this.notificationGroups = Collections.emptyList(); - } else { - List notificationGroups = new ArrayList<>(notificationGroupElementList.size()); - for (Element notificationGroupElement : notificationGroupElementList) { - notificationGroups.add(new NotificationGroup(notificationGroupElement)); - } - this.notificationGroups = Collections.unmodifiableList(notificationGroups); + this.engineMap = config.getSubElementsAsMapValues(xPath, engineElement, Engine.class); } - List startupServiceElementList = UtilXml.childElementList(engineElement, "startup-service"); - if (startupServiceElementList.isEmpty()) { - this.startupServices = Collections.emptyList(); - } else { - List startupServices = new ArrayList<>(startupServiceElementList.size()); - for (Element startupServiceElement : startupServiceElementList) { - startupServices.add(new StartupService(startupServiceElement)); - } - this.startupServices = Collections.unmodifiableList(startupServices); - } - List resourceLoaderElementList = UtilXml.childElementList(engineElement, "resource-loader"); - if (resourceLoaderElementList.isEmpty()) { - this.resourceLoaders = Collections.emptyList(); - } else { - List resourceLoaders = new ArrayList<>(resourceLoaderElementList.size()); - for (Element resourceLoaderElement : resourceLoaderElementList) { - resourceLoaders.add(new ResourceLoader(resourceLoaderElement)); - } - this.resourceLoaders = Collections.unmodifiableList(resourceLoaders); - } - List globalServicesElementList = UtilXml.childElementList(engineElement, "global-services"); - if (globalServicesElementList.isEmpty()) { - this.globalServices = Collections.emptyList(); - } else { - List globalServices = new ArrayList<>(globalServicesElementList.size()); - for (Element globalServicesElement : globalServicesElementList) { - globalServices.add(new GlobalServices(globalServicesElement)); - } - this.globalServices = Collections.unmodifiableList(globalServices); + List serviceLocationList = config.getSubElementsAsListEntries(xPath, + engineElement, ServiceLocation.class); + serviceLocations = Collections.unmodifiableList(serviceLocationList); + List notificationGroupList = config.getSubElementsAsListEntries(xPath, + engineElement, NotificationGroup.class); + notificationGroups = Collections.unmodifiableList(notificationGroupList); + List startupServiceList = config.getSubElementsAsListEntries(xPath, + engineElement, StartupService.class); + startupServices = Collections.unmodifiableList(startupServiceList); + List resourceLoaderList = config.getSubElementsAsListEntries(xPath, + engineElement, ResourceLoader.class); + resourceLoaders = Collections.unmodifiableList(resourceLoaderList); + List globalServicesList = config.getSubElementsAsListEntries(xPath, + engineElement, GlobalServices.class); + globalServices = Collections.unmodifiableList(globalServicesList); + List serviceGroupsList = config.getSubElementsAsListEntries(xPath, + engineElement, ServiceGroups.class); + serviceGroups = Collections.unmodifiableList(serviceGroupsList); + List serviceEcasList = config.getSubElementsAsListEntries(xPath, + engineElement, ServiceEcas.class); + serviceEcas = Collections.unmodifiableList(serviceEcasList); + List jmsServiceList = config.getSubElementsAsListEntries(xPath, + engineElement, JmsService.class); + jmsServices = Collections.unmodifiableList(jmsServiceList); + } + + ServiceEngine(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); } - List serviceGroupsElementList = UtilXml.childElementList(engineElement, "service-groups"); - if (serviceGroupsElementList.isEmpty()) { - this.serviceGroups = Collections.emptyList(); - } else { - List serviceGroups = new ArrayList<>(serviceGroupsElementList.size()); - for (Element serviceGroupsElement : serviceGroupsElementList) { - serviceGroups.add(new ServiceGroups(serviceGroupsElement)); - } - this.serviceGroups = Collections.unmodifiableList(serviceGroups); + this.name = name; + Authorization auth = config.getObjectSubElement(xPath, null, Authorization.class); + if (auth == null) { + throw new ServiceConfigException(" element is missing"); } - List serviceEcasElementList = UtilXml.childElementList(engineElement, "service-ecas"); - if (serviceEcasElementList.isEmpty()) { - this.serviceEcas = Collections.emptyList(); - } else { - List serviceEcas = new ArrayList<>(serviceEcasElementList.size()); - for (Element serviceEcasElement : serviceEcasElementList) { - serviceEcas.add(new ServiceEcas(serviceEcasElement)); - } - this.serviceEcas = Collections.unmodifiableList(serviceEcas); + this.authorization = auth; + ThreadPool pool = config.getObjectSubElement(xPath, null, ThreadPool.class); + if (pool == null) { + throw new ServiceConfigException(" element is missing"); } - List jmsServiceElementList = UtilXml.childElementList(engineElement, "jms-service"); - if (jmsServiceElementList.isEmpty()) { - this.jmsServices = Collections.emptyList(); + this.threadPool = pool; + List engineList = config.getSubElementsAsListEntries(xPath, null, Engine.class); + engines = Collections.unmodifiableList(engineList); + if (engineList.isEmpty()) { + this.engineMap = Collections.emptyMap(); } else { - List jmsServices = new ArrayList<>(jmsServiceElementList.size()); - for (Element jmsServiceElement : jmsServiceElementList) { - jmsServices.add(new JmsService(jmsServiceElement)); - } - this.jmsServices = Collections.unmodifiableList(jmsServices); + this.engineMap = config.getSubElementsAsMapValues(xPath, null, Engine.class); } + List serviceLocationList = config.getSubElementsAsListEntries(xPath, + null, ServiceLocation.class); + serviceLocations = Collections.unmodifiableList(serviceLocationList); + List notificationGroupList = config.getSubElementsAsListEntries(xPath, + null, NotificationGroup.class); + notificationGroups = Collections.unmodifiableList(notificationGroupList); + List startupServiceList = config.getSubElementsAsListEntries(xPath, + null, StartupService.class); + startupServices = Collections.unmodifiableList(startupServiceList); + List resourceLoaderList = config.getSubElementsAsListEntries(xPath, + null, ResourceLoader.class); + resourceLoaders = Collections.unmodifiableList(resourceLoaderList); + List globalServicesList = config.getSubElementsAsListEntries(xPath, + null, GlobalServices.class); + globalServices = Collections.unmodifiableList(globalServicesList); + List serviceGroupsList = config.getSubElementsAsListEntries(xPath, + null, ServiceGroups.class); + serviceGroups = Collections.unmodifiableList(serviceGroupsList); + List serviceEcasList = config.getSubElementsAsListEntries(xPath, + null, ServiceEcas.class); + serviceEcas = Collections.unmodifiableList(serviceEcasList); + List jmsServiceList = config.getSubElementsAsListEntries(xPath, + null, JmsService.class); + jmsServices = Collections.unmodifiableList(jmsServiceList); + } + + public static ServiceEngine loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceEngine(element, xPathParent); + } + + public static ServiceEngine loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceEngine(configMap, xPath); } public Authorization getAuthorization() { @@ -179,11 +172,11 @@ public Engine getEngine(String engineName) { } public List getEngines() { - return this.engines; + return engines; } public List getGlobalServices() { - return this.globalServices; + return globalServices; } public JmsService getJmsServiceByName(String name) { @@ -199,34 +192,37 @@ public List getJmsServices() { return this.jmsServices; } - public String getName() { - return name; - } - public List getNotificationGroups() { - return this.notificationGroups; + return notificationGroups; } public List getResourceLoaders() { - return this.resourceLoaders; + return resourceLoaders; } public List getServiceEcas() { - return this.serviceEcas; + return serviceEcas; } + public List getServiceGroups() { - return this.serviceGroups; + return serviceGroups; } public List getServiceLocations() { - return this.serviceLocations; + return serviceLocations; } public List getStartupServices() { - return this.startupServices; + return startupServices; } public ThreadPool getThreadPool() { return threadPool; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceGroups.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceGroups.java index 8df58d5d864..17412f67847 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceGroups.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceGroups.java @@ -18,7 +18,10 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,22 +29,51 @@ * An object that models the <service-groups> element. */ @ThreadSafe -public final class ServiceGroups { +public final class ServiceGroups extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "service-groups"; + private final String xPath; private final String loader; private final String location; - ServiceGroups(Element serviceGroupsElement) throws ServiceConfigException { - String loader = serviceGroupsElement.getAttribute("loader").intern(); + ServiceGroups(Element serviceGroupsElement, String xPathParent) throws ServiceConfigException { + String location = serviceGroupsElement.getAttribute("location").intern(); + if (location.isEmpty()) { + throw new ServiceConfigException(" element location attribute is empty"); + } + this.location = location; + xPath = xPathParent.concat("/service-groups[@location='" + location + "']"); + String loader = config.getValue(xPath.concat("/@loader")); if (loader.isEmpty()) { throw new ServiceConfigException(" element loader attribute is empty"); } this.loader = loader; - String location = serviceGroupsElement.getAttribute("location").intern(); + } + + ServiceGroups(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String location = getNameFromXPath(xPath); if (location.isEmpty()) { throw new ServiceConfigException(" element location attribute is empty"); } this.location = location; + String loader = config.getValue(configObject, "/@loader"); + if (loader.isEmpty()) { + throw new ServiceConfigException(" element loader attribute is empty"); + } + this.loader = loader; + } + + public static ServiceGroups loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceGroups(element, xPathParent); + } + + public static ServiceGroups loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceGroups(configMap, xPath); } public String getLoader() { @@ -51,4 +83,9 @@ public String getLoader() { public String getLocation() { return location; } + + @Override + public String getName() { + return location; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceLocation.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceLocation.java index a52757d08ea..b74160f18f5 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceLocation.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ServiceLocation.java @@ -18,7 +18,13 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import java.util.Map; + +import org.apache.ofbiz.base.config.AbstractConfigElement; +import org.apache.ofbiz.base.config.ConfigHelper; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.base.start.Start; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -26,27 +32,71 @@ * An object that models the <service-location> element. */ @ThreadSafe -public final class ServiceLocation { +public final class ServiceLocation extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "service-location"; + private final String xPath; private final String location; private final String name; - ServiceLocation(Element serviceLocationElement, String location) throws ServiceConfigException { + ServiceLocation(Element serviceLocationElement, String xPathParent) throws ServiceConfigException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); String name = serviceLocationElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; + xPath = xPathParent.concat("/service-location[@name='" + name + "']"); + String location = config.getValue(xPath.concat("/@location")); + if (location.isEmpty() && checkStructure) { + throw new ServiceConfigException(" element location attribute is empty"); + } + if (location.contains("localhost") && Start.getInstance().getConfig().getPortOffset() != 0) { + String s = location.substring(location.lastIndexOf(":") + 1); + int locationPort = Integer.parseInt(s.substring(0, s.indexOf("/"))); + int port = locationPort + Start.getInstance().getConfig().getPortOffset(); + location = location.replace(Integer.toString(locationPort), Integer.toString(port)); + } + this.location = location; + } + + ServiceLocation(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); + } + this.name = name; + String location = config.getValue(configObject, "/@location"); if (location.isEmpty()) { throw new ServiceConfigException(" element location attribute is empty"); } + if (location.contains("localhost") && Start.getInstance().getConfig().getPortOffset() != 0) { + String s = location.substring(location.lastIndexOf(":") + 1); + int locationPort = Integer.parseInt(s.substring(0, s.indexOf("/"))); + int port = locationPort + Start.getInstance().getConfig().getPortOffset(); + location = location.replace(Integer.toString(locationPort), Integer.toString(port)); + } this.location = location; } + public static ServiceLocation loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceLocation(element, xPathParent); + } + + public static ServiceLocation loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new ServiceLocation(configMap, xPath); + } + public String getLocation() { return location; } + @Override public String getName() { return name; } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/StartupService.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/StartupService.java index db3352a75b7..f041a10f12a 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/StartupService.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/StartupService.java @@ -18,44 +18,61 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; +import org.apache.ofbiz.base.config.AbstractConfigElement; import org.apache.ofbiz.base.lang.ThreadSafe; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; +import java.util.Map; + /** * An object that models the <startup-service> element. */ @ThreadSafe -public final class StartupService { +public final class StartupService extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "startup-service"; + private final String xPath; private final String name; private final String runInPool; private final String runtimeDataId; private final int runtimeDelay; - StartupService(Element startupServiceElement) throws ServiceConfigException { + StartupService(Element startupServiceElement, String xPathParent) throws ServiceConfigException { String name = startupServiceElement.getAttribute("name").intern(); if (name.isEmpty()) { throw new ServiceConfigException(" element name attribute is empty"); } this.name = name; - String runtimeDataId = startupServiceElement.getAttribute("runtime-data-id").intern(); - this.runtimeDataId = runtimeDataId.isEmpty() ? null : runtimeDataId; - String runtimeDelay = startupServiceElement.getAttribute("runtime-delay").intern(); - if (runtimeDelay.isEmpty()) { - this.runtimeDelay = 0; - } else { - try { - this.runtimeDelay = Integer.parseInt(runtimeDelay); - } catch (Exception e) { - throw new ServiceConfigException(" element runtime-delay attribute value is invalid"); - } + xPath = xPathParent.concat("/startup-service[@name='" + name + "']"); + runtimeDataId = config.getValue(xPath.concat("/@runtime-data-id"), null, String.class); + runtimeDelay = config.getValue(xPath.concat("/@runtime-delay"), 0, Integer.class); + this.runInPool = config.getValue(xPath.concat("/@run-in-pool")); + } + + StartupService(Map configObject, String xPath) throws ServiceConfigException { + this.xPath = xPath; + String name = getNameFromXPath(xPath); + if (name.isEmpty()) { + throw new ServiceConfigException(" element name attribute is empty"); } - this.runInPool = startupServiceElement.getAttribute("run-in-pool").intern(); + this.name = name; + runtimeDataId = config.getValue(configObject, "/@runtime-data-id", null, String.class); + runtimeDelay = config.getValue(configObject, "/@runtime-delay", 0, Integer.class); + this.runInPool = config.getValue(configObject, "/@run-in-pool"); } - public String getName() { - return name; + public static StartupService loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new StartupService(element, xPathParent); + } + + public static StartupService loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new StartupService(configMap, xPath); } public String getRunInPool() { @@ -69,4 +86,10 @@ public String getRuntimeDataId() { public int getRuntimeDelay() { return runtimeDelay; } + + @Override + public String getName() { + return name; + } + } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ThreadPool.java b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ThreadPool.java index da93086e281..0042ea400e1 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ThreadPool.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/config/model/ThreadPool.java @@ -18,13 +18,14 @@ *******************************************************************************/ package org.apache.ofbiz.service.config.model; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Map; +import org.apache.ofbiz.base.config.AbstractConfigElement; +import org.apache.ofbiz.base.config.ConfigHelper; import org.apache.ofbiz.base.lang.ThreadSafe; import org.apache.ofbiz.base.util.Debug; -import org.apache.ofbiz.base.util.UtilXml; +import org.apache.ofbiz.entity.GenericEntityConfException; import org.apache.ofbiz.service.config.ServiceConfigException; import org.w3c.dom.Element; @@ -32,7 +33,11 @@ * An object that models the <thread-pool> element. */ @ThreadSafe -public final class ThreadPool { +public final class ThreadPool extends AbstractConfigElement { + + private final ServiceConfigGetter config = ServiceConfigGetter.getInstance(); + public static final String ELEMENT_NAME = "thread-pool"; + private final String xPath; private static final String MODULE = ThreadPool.class.getName(); @@ -62,13 +67,151 @@ public final class ThreadPool { private final int leaseValidationMillis; private final int leaseExpiryMillis; - ThreadPool(Element poolElement) throws ServiceConfigException, NumberFormatException { - String sendToPool = poolElement.getAttribute("send-to-pool").intern(); + ThreadPool(Element poolElement, String xPathParent) throws ServiceConfigException, NumberFormatException { + boolean checkStructure = ConfigHelper.checkStrictXmlStructure(); + xPath = xPathParent.concat("/thread-pool"); + String sendToPool = config.getValue(xPath.concat("/@send-to-pool")); + if (sendToPool.isEmpty() && checkStructure) { + throw new ServiceConfigException(" element send-to-pool attribute is empty"); + } + this.sendToPool = sendToPool; + String purgeJobDays = config.getValue(xPath.concat("/@purge-job-days")); + if (purgeJobDays.isEmpty()) { + this.purgeJobDays = PURGE_JOBS_DAYS; + } else { + try { + this.purgeJobDays = Integer.parseInt(purgeJobDays); + if (this.purgeJobDays < 0 && checkStructure) { + throw new ServiceConfigException(" element purge-job-days attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + throw new ServiceConfigException(" element purge-job-days attribute value is invalid"); + } + } + String failedRetryMin = config.getValue(xPath.concat("/@failed-retry-min")); + if (failedRetryMin.isEmpty()) { + this.failedRetryMin = FAILED_RETRY_MIN; + } else { + try { + this.failedRetryMin = Integer.parseInt(failedRetryMin); + if (this.failedRetryMin < 0) { + throw new ServiceConfigException(" element failed-retry-min attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element failed-retry-min attribute value is invalid"); + } + } + String ttl = config.getValue(xPath.concat("/@ttl")); + if (ttl.isEmpty()) { + this.ttl = THREAD_TTL; + } else { + try { + this.ttl = Integer.parseInt(ttl); + if (this.ttl < 0) { + throw new ServiceConfigException(" element ttl attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element ttl attribute value is invalid"); + } + } + String jobs = config.getValue(xPath.concat("/@jobs")); + if (ttl.isEmpty()) { + this.jobs = QUEUE_SIZE; + } else { + try { + this.jobs = Integer.parseInt(jobs); + if (this.jobs < 1 && checkStructure) { + throw new ServiceConfigException(" element jobs attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element jobs attribute value is invalid"); + } + } + String minThreads = config.getValue(xPath.concat("/@min-threads")); + if (minThreads.isEmpty()) { + this.minThreads = MIN_THREADS; + } else { + try { + this.minThreads = Integer.parseInt(minThreads); + if (this.minThreads < 1 && checkStructure) { + throw new ServiceConfigException(" element min-threads attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element min-threads attribute value is invalid"); + } + } + String maxThreads = config.getValue(xPath.concat("/@max-threads")); + if (maxThreads.isEmpty()) { + this.maxThreads = MAX_THREADS; + } else { + try { + this.maxThreads = Integer.parseInt(maxThreads); + if (this.maxThreads < this.minThreads) { + throw new ServiceConfigException(" element max-threads attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element max-threads attribute value is invalid"); + } + } + this.pollEnabled = "true".equals(config.getValue(xPath.concat("/@poll-enabled"))); + String pollDbMillis = config.getValue(xPath.concat("/@poll-db-millis")); + if (pollDbMillis.isEmpty()) { + this.pollDbMillis = POLL_WAIT; + } else { + try { + this.pollDbMillis = Integer.parseInt(pollDbMillis); + if (this.pollDbMillis < 0) { + throw new ServiceConfigException(" element poll-db-millis attribute value is invalid"); + } + } catch (NumberFormatException | ServiceConfigException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element poll-db-millis attribute value is invalid"); + } + } + this.runFromPools = config.getSubElementsAsListEntries(xPath, poolElement, RunFromPool.class); + + String leaseRefreshMillis = config.getValue(xPath.concat("/@lease-refresh-millis")); + try { + this.leaseRefreshMillis = leaseRefreshMillis.isEmpty() + ? LEASE_REFRESH_MILLIS + : Integer.parseInt(leaseRefreshMillis); + } catch (NumberFormatException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element lease-refresh-millis attribute value is invalid"); + } + try { + String leaseValidationMillis = config.getValue(xPath.concat("/@lease-validation-millis")); + this.leaseValidationMillis = leaseValidationMillis.isEmpty() + ? LEASE_VALIDATION_MILLIS + : Integer.parseInt(leaseValidationMillis); + } catch (NumberFormatException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element lease-validation-millis attribute value is invalid"); + } + try { + String leaseExpiryMillis = config.getValue(xPath.concat("/@lease-expiry-millis")); + this.leaseExpiryMillis = leaseExpiryMillis.isEmpty() + ? LEASE_EXPIRY_MILLIS + : Integer.parseInt(leaseExpiryMillis); + } catch (NumberFormatException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element lease-expiry-millis attribute value is invalid"); + } + } + + ThreadPool(Map configObject, String xPath) throws ServiceConfigException, NumberFormatException { + this.xPath = xPath; + String sendToPool = config.getValue(configObject, "/@send-to-pool"); if (sendToPool.isEmpty()) { throw new ServiceConfigException(" element send-to-pool attribute is empty"); } this.sendToPool = sendToPool; - String purgeJobDays = poolElement.getAttribute("purge-job-days").intern(); + String purgeJobDays = config.getValue(configObject, "/@purge-job-days"); if (purgeJobDays.isEmpty()) { this.purgeJobDays = PURGE_JOBS_DAYS; } else { @@ -81,7 +224,7 @@ public final class ThreadPool { throw new ServiceConfigException(" element purge-job-days attribute value is invalid"); } } - String failedRetryMin = poolElement.getAttribute("failed-retry-min").intern(); + String failedRetryMin = config.getValue(configObject, "/@failed-retry-min"); if (failedRetryMin.isEmpty()) { this.failedRetryMin = FAILED_RETRY_MIN; } else { @@ -95,7 +238,7 @@ public final class ThreadPool { throw new ServiceConfigException(" element failed-retry-min attribute value is invalid"); } } - String ttl = poolElement.getAttribute("ttl").intern(); + String ttl = config.getValue(configObject, "/@ttl"); if (ttl.isEmpty()) { this.ttl = THREAD_TTL; } else { @@ -109,7 +252,7 @@ public final class ThreadPool { throw new ServiceConfigException(" element ttl attribute value is invalid"); } } - String jobs = poolElement.getAttribute("jobs").intern(); + String jobs = config.getValue(configObject, "/@jobs"); if (ttl.isEmpty()) { this.jobs = QUEUE_SIZE; } else { @@ -123,7 +266,7 @@ public final class ThreadPool { throw new ServiceConfigException(" element jobs attribute value is invalid"); } } - String minThreads = poolElement.getAttribute("min-threads").intern(); + String minThreads = config.getValue(configObject, "/@min-threads"); if (minThreads.isEmpty()) { this.minThreads = MIN_THREADS; } else { @@ -137,7 +280,7 @@ public final class ThreadPool { throw new ServiceConfigException(" element min-threads attribute value is invalid"); } } - String maxThreads = poolElement.getAttribute("max-threads").intern(); + String maxThreads = config.getValue(configObject, "/@max-threads"); if (maxThreads.isEmpty()) { this.maxThreads = MAX_THREADS; } else { @@ -151,8 +294,8 @@ public final class ThreadPool { throw new ServiceConfigException(" element max-threads attribute value is invalid"); } } - this.pollEnabled = !"false".equals(poolElement.getAttribute("poll-enabled")); - String pollDbMillis = poolElement.getAttribute("poll-db-millis").intern(); + this.pollEnabled = "true".equals(config.getValue(configObject, "/@poll-enabled")); + String pollDbMillis = config.getValue(configObject, "/@poll-db-millis"); if (pollDbMillis.isEmpty()) { this.pollDbMillis = POLL_WAIT; } else { @@ -166,22 +309,45 @@ public final class ThreadPool { throw new ServiceConfigException(" element poll-db-millis attribute value is invalid"); } } - List runFromPoolElementList = UtilXml.childElementList(poolElement, "run-from-pool"); - if (runFromPoolElementList.isEmpty()) { - this.runFromPools = Collections.emptyList(); - } else { - List runFromPools = new ArrayList<>(runFromPoolElementList.size()); - for (Element runFromPoolElement : runFromPoolElementList) { - runFromPools.add(new RunFromPool(runFromPoolElement)); - } - this.runFromPools = Collections.unmodifiableList(runFromPools); + this.runFromPools = config.getSubElementsAsListEntries(xPath, null, RunFromPool.class); + + String leaseRefreshMillis = config.getValue(xPath.concat("/@lease-refresh-millis")); + try { + this.leaseRefreshMillis = leaseRefreshMillis.isEmpty() + ? LEASE_REFRESH_MILLIS + : Integer.parseInt(leaseRefreshMillis); + } catch (NumberFormatException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element lease-refresh-millis attribute value is invalid"); + } + try { + String leaseValidationMillis = config.getValue(xPath.concat("/@lease-validation-millis")); + this.leaseValidationMillis = leaseValidationMillis.isEmpty() + ? LEASE_VALIDATION_MILLIS + : Integer.parseInt(leaseValidationMillis); + } catch (NumberFormatException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element lease-validation-millis attribute value is invalid"); + } + try { + String leaseExpiryMillis = config.getValue(xPath.concat("/@lease-expiry-millis")); + this.leaseExpiryMillis = leaseExpiryMillis.isEmpty() + ? LEASE_EXPIRY_MILLIS + : Integer.parseInt(leaseExpiryMillis); + } catch (NumberFormatException e) { + Debug.logError(e, MODULE); + throw new ServiceConfigException(" element lease-expiry-millis attribute value is invalid"); } - String leaseRefreshMillis = poolElement.getAttribute("lease-refresh-millis").intern(); - this.leaseRefreshMillis = leaseRefreshMillis.isEmpty() ? LEASE_REFRESH_MILLIS : Integer.parseInt(leaseRefreshMillis); - String leaseValidationMillis = poolElement.getAttribute("lease-validation-millis").intern(); - this.leaseValidationMillis = leaseValidationMillis.isEmpty() ? LEASE_VALIDATION_MILLIS : Integer.parseInt(leaseValidationMillis); - String leaseExpiryMillis = poolElement.getAttribute("lease-expiry-millis").intern(); - this.leaseExpiryMillis = leaseExpiryMillis.isEmpty() ? LEASE_EXPIRY_MILLIS : Integer.parseInt(leaseExpiryMillis); + } + + public static ThreadPool loadFromXml(Element element, String xPathParent) + throws GenericEntityConfException, ServiceConfigException { + return new ThreadPool(element, xPathParent); + } + + public static ThreadPool loadFromConfig(Map configMap, String xPath) + throws GenericEntityConfException, ServiceConfigException { + return new ThreadPool(configMap, xPath); } public int getFailedRetryMin() { @@ -213,7 +379,7 @@ public int getPurgeJobDays() { } public List getRunFromPools() { - return this.runFromPools; + return runFromPools; } public String getSendToPool() { @@ -235,4 +401,9 @@ public int getLeaseValidationMillis() { public int getLeaseExpiryMillis() { return leaseExpiryMillis; } + + @Override + public String getName() { + return "thread-pool"; + } } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/eca/ServiceEcaUtil.java b/framework/service/src/main/java/org/apache/ofbiz/service/eca/ServiceEcaUtil.java index 757724cf97a..c35bdcccfee 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/eca/ServiceEcaUtil.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/eca/ServiceEcaUtil.java @@ -40,6 +40,7 @@ import org.apache.ofbiz.service.DispatchContext; import org.apache.ofbiz.service.GenericServiceException; import org.apache.ofbiz.service.config.ServiceConfigUtil; +import org.apache.ofbiz.service.config.model.ServiceConfigGetter; import org.apache.ofbiz.service.config.model.ServiceEcas; import org.w3c.dom.Element; @@ -77,7 +78,7 @@ public static void readConfig() { throw new RuntimeException(e.getMessage()); } for (ServiceEcas serviceEcas : serviceEcasList) { - ResourceHandler handler = new MainResourceHandler(ServiceConfigUtil.getServiceEngineXmlFileName(), serviceEcas.getLoader(), + ResourceHandler handler = new MainResourceHandler(ServiceConfigGetter.getServiceEngineXmlFileName(), serviceEcas.getLoader(), serviceEcas.getLocation()); futures.add(ExecutionPool.GLOBAL_FORK_JOIN.submit(createEcaLoaderCallable(handler))); } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/group/ServiceGroupReader.java b/framework/service/src/main/java/org/apache/ofbiz/service/group/ServiceGroupReader.java index 7c0eee26e06..ec32abc2151 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/group/ServiceGroupReader.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/group/ServiceGroupReader.java @@ -29,6 +29,7 @@ import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.service.config.ServiceConfigUtil; +import org.apache.ofbiz.service.config.model.ServiceConfigGetter; import org.apache.ofbiz.service.config.model.ServiceGroups; import org.w3c.dom.Element; @@ -54,7 +55,7 @@ public static void readConfig() { throw new RuntimeException(e.getMessage()); } for (ServiceGroups serviceGroup : serviceGroupsList) { - ResourceHandler handler = new MainResourceHandler(ServiceConfigUtil.getServiceEngineXmlFileName(), serviceGroup.getLoader(), + ResourceHandler handler = new MainResourceHandler(ServiceConfigGetter.getServiceEngineXmlFileName(), serviceGroup.getLoader(), serviceGroup.getLocation()); addGroupDefinitions(handler); } diff --git a/framework/service/src/main/java/org/apache/ofbiz/service/job/JobManager.java b/framework/service/src/main/java/org/apache/ofbiz/service/job/JobManager.java index 3d80a45ecc6..081a2e02465 100644 --- a/framework/service/src/main/java/org/apache/ofbiz/service/job/JobManager.java +++ b/framework/service/src/main/java/org/apache/ofbiz/service/job/JobManager.java @@ -31,13 +31,12 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.ofbiz.service.tracker.JobTracker; +import org.apache.ofbiz.base.config.ConfigurationFactory; import org.apache.ofbiz.base.config.GenericConfigException; import org.apache.ofbiz.base.util.Assert; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilDateTime; import org.apache.ofbiz.base.util.UtilMisc; -import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.GenericEntityException; @@ -59,6 +58,7 @@ import org.apache.ofbiz.service.config.ServiceConfigUtil; import org.apache.ofbiz.service.config.model.RunFromPool; import org.apache.ofbiz.service.config.model.ThreadPool; +import org.apache.ofbiz.service.tracker.JobTracker; import org.apache.ofbiz.service.tracker.JobTrackerFactory; /** @@ -74,7 +74,7 @@ public final class JobManager { private static final String MODULE = JobManager.class.getName(); - public static final String INSTANCE_ID = UtilProperties.getPropertyValue("general", "unique.instanceId", "ofbiz0"); + public static final String INSTANCE_ID = ConfigurationFactory.getInstance().getValue("general", "unique.instanceId", "ofbiz0"); private static final ConcurrentHashMap REG_MANAGERS = new ConcurrentHashMap<>(); private static boolean isShutDown = false; @@ -117,7 +117,7 @@ public static void shutDown() { private final Delegator delegator; private boolean crashedJobsReloaded = false; - private JobManager(Delegator delegator) { + public JobManager(Delegator delegator) { this.delegator = delegator; } @@ -641,7 +641,7 @@ public void schedule(String jobName, String poolName, String serviceName, String if (UtilValidate.isEmpty(jobName)) { jobName = Long.toString((new Date().getTime())); } - Map jFields = UtilMisc.toMap("jobName", jobName, "runTime", new java.sql.Timestamp(startTime), + Map jFields = UtilMisc.toMap("jobName", jobName, "runTime", new Timestamp(startTime), "serviceName", serviceName, "statusId", "SERVICE_PENDING", "recurrenceInfoId", infoId, "runtimeDataId", dataId, "priority", JobPriority.NORMAL); // set the pool ID diff --git a/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/BaseServiceConfigReaderTest.groovy b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/BaseServiceConfigReaderTest.groovy new file mode 100644 index 00000000000..f98b86348e4 --- /dev/null +++ b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/BaseServiceConfigReaderTest.groovy @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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.service.config.test + +import static org.mockito.Mockito.any + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import org.apache.ofbiz.base.config.ConfigHelper +import org.apache.ofbiz.base.config.ConfigurationInterface +import org.apache.ofbiz.base.config.ConfigurationFactory +import org.apache.ofbiz.base.config.XmlFileReader +import org.apache.ofbiz.base.util.UtilXml +import org.apache.ofbiz.service.config.model.ServiceConfigFactory +import org.apache.ofbiz.service.config.model.ServiceConfigGetter +import org.apache.ofbiz.service.config.model.ServiceEngine +import org.junit.jupiter.api.AfterEach +import org.mockito.MockedStatic +import org.mockito.Mockito +import org.w3c.dom.Document + +class BaseServiceConfigReaderTest { + + private static final String XML_HEADER = ''' + + ''' + private static final String XML_FOOTER = '' + private MockedStatic mockXmlReader + private MockedStatic mockHelper + private MockedStatic mockConfig + private MockedStatic mockConfigGetter + + @AfterEach + void closeMock() { + mockXmlReader?.close() + mockConfigGetter?.close() + mockHelper?.close() + mockConfig?.close() + } + + private static String prepareXmlContent(String xmlContent) { + return XML_HEADER + xmlContent + XML_FOOTER + } + + private static Document readXmlContent(String xmlContent) { + return UtilXml.readXmlDocument(prepareXmlContent(xmlContent), false) + } + + private void initHoconConfig(String hoconContent) { + Config mockedConfig = hoconContent + ? ConfigFactory.parseString( + """{"serviceengine": {"service-config": {"service-engine": {"test": {${hoconContent}}}}}}""") + : ConfigFactory.empty() + mockConfig = Mockito.mockStatic(ConfigFactory) + mockConfig.when(ConfigFactory.load((String) any())) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory::load) + .thenReturn(mockedConfig) + mockConfig.when(ConfigFactory::empty) + .thenReturn(mockedConfig) + } + + private void initXmlConfig(String xmlContent) { + mockHelper = Mockito.mockStatic(ConfigHelper) + mockHelper.when(ConfigHelper::checkStrictXmlStructure) + .thenReturn(Boolean.FALSE) + Document xmlDoc = readXmlContent(xmlContent) + mockXmlReader = Mockito.mockStatic(XmlFileReader) + mockXmlReader.when(XmlFileReader.read(any())) + .thenReturn(xmlDoc.getDocumentElement()) + } + + protected ServiceEngine getConfig(String xmlContent, String hoconContent, boolean override = true) { + initXmlConfig(xmlContent) + initHoconConfig(hoconContent) + ConfigurationInterface configuration = ConfigurationFactory.resetAndGet() // creation of configuration is enough + configuration.clearCache() + configuration.setUseOverrideValue(override) + + mockConfigGetter = Mockito.mockStatic(ServiceConfigGetter) + mockConfigGetter.when(ServiceConfigGetter::getInstance) + .thenReturn(new ServiceConfigGetter()) + return ServiceConfigFactory.createInstance().getServiceEngine('test') + } + +} diff --git a/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderAuthorization.groovy b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderAuthorization.groovy new file mode 100644 index 00000000000..dc989f46675 --- /dev/null +++ b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderAuthorization.groovy @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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.service.config.test + +import org.apache.ofbiz.service.config.model.ServiceEngine +import org.junit.jupiter.api.Test + +class ConfigReaderAuthorization extends BaseServiceConfigReaderTest { + + @Test + void testAuthorizationFromXml() { + ServiceEngine engine = getConfig(''' + ''', '') + assert engine.getAuthorization().getServiceName() == 'serviceNameTest' + } + + @Test + void testAuthorizationWithOverride() { + ServiceEngine engine = getConfig(''' + ''', '"authorization": { "service-name": "serviceNameTest-over" }') + assert engine.getAuthorization().getServiceName() == 'serviceNameTest-over' + } + +} diff --git a/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderEngine.groovy b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderEngine.groovy new file mode 100644 index 00000000000..c5e16461a30 --- /dev/null +++ b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderEngine.groovy @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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.service.config.test + +import org.apache.ofbiz.service.config.model.ServiceEngine +import org.junit.jupiter.api.Test + +class ConfigReaderEngine extends BaseServiceConfigReaderTest { + + @Test + void testEngineFromXml() { + ServiceEngine engine = getConfig(''' + ''', '') + assert engine.getEngines().size() == 1 + assert engine.getEngine('engine-test')?.getClassName() == 'org.apache.ofbiz.service.engine.EntityTest' + } + + @Test + void testEngineWithOverride() { + ServiceEngine engine = getConfig(''' + ''', + '''"engine": { + "engine-test" : { "class": "org.apache.ofbiz.service.engine.EntityTestOver" } + }''') + assert engine.getEngines().size() == 1 + assert engine.getEngine('engine-test')?.getClassName() == 'org.apache.ofbiz.service.engine.EntityTestOver' + } + + @Test + void testEngineWithAddByOverride() { + ServiceEngine engine = getConfig(''' + ''', + '''"engine": { + "engine-test-add" : { "class": "org.apache.ofbiz.service.engine.EntityTestOver" } + }''') + assert engine.getEngines().size() == 2 + assert engine.getEngine('engine-test')?.getClassName() == 'org.apache.ofbiz.service.engine.EntityTest' + assert engine.getEngine('engine-test-add')?.getClassName() == 'org.apache.ofbiz.service.engine.EntityTestOver' + } + +} diff --git a/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderNotificationGroup.groovy b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderNotificationGroup.groovy new file mode 100644 index 00000000000..c485d9386f1 --- /dev/null +++ b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderNotificationGroup.groovy @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.service.config.test + +import org.apache.ofbiz.service.config.model.ServiceEngine +import org.junit.jupiter.api.Test + +class ConfigReaderNotificationGroup extends BaseServiceConfigReaderTest { + + @Test + void testNotificationGroupFromXml() { + ServiceEngine engine = getConfig(''' + + + from@test.com + to@test.com + cc@test.com + bcc@test.com + ''', '') + List notificationGroups = engine.getNotificationGroups() + assert notificationGroups?.size() == 1 + notificationGroups.first().with { + assert getNotification()?.getSubject() == 'Subject Test' + assert getNotification()?.getService() == 'service-test' + assert getNotification()?.getScreen() == 'screen-test' + List notifies = getNotifyList() + assert notifies.size() == 4 + ['to', 'from', 'cc', 'bcc'].each { type -> + assert notifies.find { it.getType() == type }?.getContent() == "$type@test.com" + } + } + } + + @Test + void testNotificationGroupFromOverride() { + ServiceEngine engine = getConfig(''' + + + from@test.com + to@test.com + cc@test.com + bcc@test.com + ''', ''' + "notification-group": { + "test": { + "subject": "Subject Over" + "service": "service-over" + "screen": "screen-over" + "notify": {"type": "to"} + } }''') + List notificationGroups = engine.getNotificationGroups() + assert notificationGroups?.size() == 1 + /* FIXME + notificationGroups.first().with { + assert getNotification()?.getSubject() == 'Subject Over' + assert getNotification()?.getService() == 'service-over' + assert getNotification()?.getScreen() == 'screen-over' + } + */ + } + +} diff --git a/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderServiceLocations.groovy b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderServiceLocations.groovy new file mode 100644 index 00000000000..0b9167af94e --- /dev/null +++ b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderServiceLocations.groovy @@ -0,0 +1,62 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *******************************************************************************/ +package org.apache.ofbiz.service.config.test + +import org.apache.ofbiz.service.config.model.ServiceEngine +import org.junit.jupiter.api.Test + +class ConfigReaderServiceLocations extends BaseServiceConfigReaderTest { + + @Test + void testServiceLocationFromXml() { + ServiceEngine engine = getConfig(''' + ''', '') + assert engine.getServiceLocations().size() == 1 + engine.getServiceLocations().first().with { + assert getName() == 'test' + assert getLocation() == 'location-test' + } + } + + @Test + void testServiceLocationWithOverride() { + ServiceEngine engine = getConfig(''' + ''', + '''"service-location": { "test": { "location": "location-over" } }''') + assert engine.getServiceLocations().size() == 1 + engine.getServiceLocations().first().with { + assert getName() == 'test' + assert getLocation() == 'location-over' + } + } + + @Test + void testServiceLocationWithAddOverride() { + ServiceEngine engine = getConfig(''' + ''', + '''"service-location": { "test-add": { "location": "location-over" } }''') + assert engine.getServiceLocations().size() == 2 + assert engine.getServiceLocations().find { it.getName() == 'test' } + ?.getLocation() == 'location-test' + assert engine.getServiceLocations().find { it.getName() == 'test-add' } + ?.getLocation() == 'location-over' + } + +} + diff --git a/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderThreadPool.groovy b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderThreadPool.groovy new file mode 100644 index 00000000000..c1b59ce3efd --- /dev/null +++ b/framework/service/src/test/groovy/org/apache/ofbiz/service/config/test/ConfigReaderThreadPool.groovy @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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.service.config.test + +import org.apache.ofbiz.service.config.model.ServiceEngine +import org.junit.jupiter.api.Test + +class ConfigReaderThreadPool extends BaseServiceConfigReaderTest { + + @Test + void testThreadPoolFromXml() { + ServiceEngine engine = getConfig(''' + + + + ''', '') + engine.getThreadPool().with { + assert getSendToPool() == 'pool-test' + assert getPurgeJobDays() == 10 + assert getFailedRetryMin() == 11 + assert getTtl() == 12 + assert getJobs() == 13 + assert getMinThreads() == 14 + assert getMaxThreads() == 15 + assert getPollEnabled() + assert getPollDbMillis() == 16 + assert getLeaseRefreshMillis() == 17 + assert getLeaseValidationMillis() == 18 + assert getLeaseExpiryMillis() == 19 + List runFroms = getRunFromPools() + assert runFroms.size() == 2 + assert runFroms.find { it.getName() == 'pool-test1' } + assert runFroms.find { it.getName() == 'pool-test2' } + } + } + + @Test + void testThreadPoolWithOverride() { + ServiceEngine engine = getConfig(''' + + + + ''', '''"thread-pool": { + "send-to-pool": "pool-over", + "purge-job-days": "910", + "failed-retry-min": "911", + "ttl": "912", + "jobs": "913", + "min-threads": "914", + "max-threads": "915", + "poll-enabled": "false", + "poll-db-millis": "916" + "lease-refresh-millis"="917" + "lease-validation-millis"="918" + "lease-expiry-millis"="919" + "run-from-pool": [{ "name": "pool-over" }] + }''') + engine.getThreadPool().with { + assert getSendToPool() == 'pool-over' + assert getPurgeJobDays() == 910 + assert getFailedRetryMin() == 911 + assert getTtl() == 912 + assert getJobs() == 913 + assert getMinThreads() == 914 + assert getMaxThreads() == 915 + assert !getPollEnabled() + assert getPollDbMillis() == 916 + assert getLeaseRefreshMillis() == 917 + assert getLeaseValidationMillis() == 918 + assert getLeaseExpiryMillis() == 919 + + List runFroms = getRunFromPools() + assert runFroms.size() == 1 + assert runFroms.find { it.getName() == 'pool-over' } + } + } + +} diff --git a/framework/start/src/main/java/org/apache/ofbiz/base/start/Config.java b/framework/start/src/main/java/org/apache/ofbiz/base/start/Config.java index 315b4ae0f7e..821a4380577 100644 --- a/framework/start/src/main/java/org/apache/ofbiz/base/start/Config.java +++ b/framework/start/src/main/java/org/apache/ofbiz/base/start/Config.java @@ -48,6 +48,7 @@ public final class Config { private final Path logDir; private final boolean shutdownAfterLoad; private final boolean useShutdownHook; + private final boolean configOverloadEnabled; public Path getLogDir() { return logDir; @@ -65,6 +66,10 @@ public boolean isUseShutdownHook() { return useShutdownHook; } + public boolean isConfigOverloadEnable() { + return configOverloadEnabled; + } + public InetAddress getAdminAddress() { return adminAddress; } @@ -100,6 +105,7 @@ public Path getOfbizHome() { logDir = getAbsolutePath(props, "ofbiz.log.dir", DEFAULT_LOG_DIRECTORY, ofbizHome); shutdownAfterLoad = "true".equalsIgnoreCase(getProperty(props, "ofbiz.auto.shutdown", "false")); useShutdownHook = "true".equalsIgnoreCase(getProperty(props, "ofbiz.enable.hook", "true")); + configOverloadEnabled = "true".equalsIgnoreCase(getProperty(props, "ofbiz.config.overload.enable", "false")); System.out.println("Set OFBIZ_HOME to - " + ofbizHome); @@ -147,9 +153,8 @@ private static String determineOfbizPropertiesFileName(List ofbi } else if (ofbizCommands.stream().anyMatch( option -> option.getName().equals(StartupCommandUtil.StartupOption.TEST.getName()))) { return "test.properties"; - } else { - return "start.properties"; } + return "start.properties"; } private static int getPortOffsetValue(List ofbizCommands, String defaultOffset) throws StartupException { diff --git a/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupCommandUtil.java b/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupCommandUtil.java index 22025caebb4..4e401e670de 100644 --- a/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupCommandUtil.java +++ b/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupCommandUtil.java @@ -65,6 +65,7 @@ public enum StartupOption { SHUTDOWN("shutdown"), START("start"), STATUS("status"), + CONFIG("configuration"), TEST("test"); private String name; @@ -158,6 +159,18 @@ public String getName() { .optionalArg(true) .argName("key=value") .get(); + private static final Option CONFIG = Option.builder("c") + .longOpt(StartupOption.CONFIG.getName()) + .desc("Runs ofbiz with dedicated configuration files to override default ofbiz configuration: " + + System.lineSeparator() + + "-c file=myConf1.conf,myConf2.conf" + + System.lineSeparator() + + "-c enable=true") + .numberOfArgs(2) + .valueSeparator('=') + .optionalArg(true) + .argName("key=value") + .get(); static List parseOfbizCommands(final String[] args) throws StartupException { CommandLine commandLine = null; @@ -209,6 +222,7 @@ private static Options getOfbizStartupOptions() { ofbizCommandOptions.addOption(SHUTDOWN); ofbizCommandOptions.addOption(START); ofbizCommandOptions.addOption(STATUS); + ofbizCommandOptions.addOption(CONFIG); ofbizCommandOptions.addOption(TEST); Options options = new Options(); diff --git a/framework/start/src/main/resources/org/apache/ofbiz/base/start/start.properties b/framework/start/src/main/resources/org/apache/ofbiz/base/start/start.properties index 7cc61948094..67722595627 100644 --- a/framework/start/src/main/resources/org/apache/ofbiz/base/start/start.properties +++ b/framework/start/src/main/resources/org/apache/ofbiz/base/start/start.properties @@ -34,6 +34,16 @@ ofbiz.start.loaders=main # Default is framework/base/config/ofbiz-containers.xml #ofbiz.container.config= +# --- Enable config overload, Default is true. +# If you want to disable the config system overload put it to false. +# By default, ofbiz will grab all config file present on component conf directory +# To potential override some configuration or add someone. +#ofbiz.config.overload.enable=true + +# --- Enable xml configuration file check. Default is true. +# If you want to disable xml configuration verification. Only useful for test. +#ofbiz.config.xml.check.strict=true + # --- Network host, port and key used by the AdminClient to communicate # with AdminServer for shutting down OFBiz or inquiring on status # Default ofbiz.admin.host 127.0.0.1 diff --git a/framework/testtools/src/main/java/org/apache/ofbiz/testtools/GroovyScriptAssert.java b/framework/testtools/src/main/java/org/apache/ofbiz/testtools/GroovyScriptAssert.java index b95d1274d06..ffea0d410d6 100644 --- a/framework/testtools/src/main/java/org/apache/ofbiz/testtools/GroovyScriptAssert.java +++ b/framework/testtools/src/main/java/org/apache/ofbiz/testtools/GroovyScriptAssert.java @@ -18,7 +18,7 @@ *******************************************************************************/ package org.apache.ofbiz.testtools; -import groovy.test.GroovyAssert; +import groovy.test.GroovyTestCase; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.security.Security; import org.apache.ofbiz.service.LocalDispatcher; @@ -26,7 +26,7 @@ /** * This test case engine allow writing test in groovy script that do not need compilation. */ -public class GroovyScriptAssert extends GroovyAssert { +public class GroovyScriptAssert extends GroovyTestCase { private Delegator delegator; private LocalDispatcher dispatcher;