You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by jl...@apache.org on 2018/09/05 16:23:04 UTC

svn commit: r1840139 - in /ofbiz/ofbiz-framework/trunk/framework/base: dtd/ofbiz-component.xsd src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java

Author: jleroux
Date: Wed Sep  5 16:23:04 2018
New Revision: 1840139

URL: http://svn.apache.org/viewvc?rev=1840139&view=rev
Log:
Implemented: Implement Depends on support for Component Loading.
(OFBIZ-10368)

Components are loaded in the order they are found (i.e. alphabetic or creation 
date). 

Here is a feature which supports in ofbiz-component to create a series of dependency for component loading, before loading a particular component.
 
Here we can define dependency for a component like Component ebaystore is dependent on Component ebay (even multiple dependencies for a single component is also supported). We can declare it in this fashion in ofbiz-component.xml file.
 

<ofbiz-component name="ebaystore" enabled="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-component.xsd">
<depends-on component-name="ebay"/>

 
Now we can declare dependency and if any anomaly is found loading of components will stop.  

Thanks:

Modified:
    ofbiz/ofbiz-framework/trunk/framework/base/dtd/ofbiz-component.xsd
    ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
    ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java

Modified: ofbiz/ofbiz-framework/trunk/framework/base/dtd/ofbiz-component.xsd
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/dtd/ofbiz-component.xsd?rev=1840139&r1=1840138&r2=1840139&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/base/dtd/ofbiz-component.xsd (original)
+++ ofbiz/ofbiz-framework/trunk/framework/base/dtd/ofbiz-component.xsd Wed Sep  5 16:23:04 2018
@@ -43,21 +43,26 @@ under the License.
             <xs:attributeGroup ref="attlist.depends-on">
                 <xs:annotation>
                     <xs:documentation>
-                        This is yet unimplemented. It's not a trivial implementation because at the moment components are loaded
-                        in the order they are found (i.e. alphabetic or creation date).
-                        While this feature would require them all to be located first and then reordered and loaded.
-                        So, for now, simply follow below guidelines.
+                        Alter the way components are loaded. 
                         
+                        By default components are loaded in the order they are found (i.e. alphabetic or creation date).
                         For the plugins, all components will be loaded after the OFBiz components, including, in this order, those in:
-                        framework
-                        themes
-                        applications
+                            framework
+                            themes
+                            applications
+                        The plugins Auto-Loading feature also loads all components in the order they are found (i.e. alphabetic or creation date).
+                         
+                        Using depends-on allows to create a series of dependencies for component loading, before loading a particular component.
+                        For instance we can define dependency for a component like component ebaystore is dependent on component ebay 
+                        (even multiple dependencies for a single component is also supported). 
                         
-                        The plugins Auto-Loading feature loads all components in the order they are found (i.e. alphabetic or creation date).
-                        
-                        If you need a specific loading order of these components then you need to disable the Auto-Loading feature 
-                        by creating a component-load.xml file in the plugins  directory and use the load-component tag to load 
-                        your components in the order you want (just use the component-load.xml file in the application folder as a template).
+                        We can declare it in this fashion in ofbiz-component.xml file (beware ebaystore is disabled by default)
+                        <![CDATA[
+                        <ofbiz-component name="ebaystore" enabled="true"
+                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-component.xsd">
+                        <depends-on component-name="ebay"/>
+                        ]]>
                     </xs:documentation>
                 </xs:annotation>
             </xs:attributeGroup>

Modified: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java?rev=1840139&r1=1840138&r2=1840139&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java Wed Sep  5 16:23:04 2018
@@ -340,6 +340,7 @@ public final class ComponentConfig {
     private final boolean enabled;
     private final Map<String, ResourceLoaderInfo> resourceLoaderInfos;
     private final List<ClasspathInfo> classpathInfos;
+    private final List<DependsOnInfo> dependsOnInfos;
     private final List<EntityResourceInfo> entityResourceInfos;
     private final List<ServiceResourceInfo> serviceResourceInfos;
     private final List<TestSuiteInfo> testSuiteInfos;
@@ -390,6 +391,19 @@ public final class ComponentConfig {
         } else {
             this.resourceLoaderInfos = Collections.emptyMap();
         }
+
+        childElements = UtilXml.childElementList(ofbizComponentElement, "depends-on");
+        if (!childElements.isEmpty()) {
+            List<DependsOnInfo> dependsOnList = new ArrayList<>(childElements.size());
+            for (Element curElement : childElements) {
+                DependsOnInfo dependsOnInfo = new DependsOnInfo(this, curElement);
+                dependsOnList.add(dependsOnInfo);
+            }
+            this.dependsOnInfos = Collections.unmodifiableList(dependsOnList);
+        } else {
+            this.dependsOnInfos = Collections.emptyList();
+        }
+
         // classpath - classpathInfos
         childElements = UtilXml.childElementList(ofbizComponentElement, "classpath");
         if (!childElements.isEmpty()) {
@@ -529,6 +543,10 @@ public final class ComponentConfig {
         return this.globalName;
     }
 
+    public List<DependsOnInfo> getDependsOn() {
+        return this.dependsOnInfos;
+    }
+
     public List<KeystoreInfo> getKeystoreInfos() {
         return this.keystoreInfos;
     }
@@ -677,6 +695,15 @@ public final class ComponentConfig {
         }
     }
 
+    public static final class DependsOnInfo extends ResourceInfo {
+        public final String componentName;
+
+        private DependsOnInfo(ComponentConfig componentConfig, Element element) {
+            super(componentConfig, element);
+            this.componentName = element.getAttribute("component-name");
+        }
+    }
+
     /**
      * An object that models the <code>&lt;keystore&gt;</code> element.
      *

Modified: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java?rev=1840139&r1=1840138&r2=1840139&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java Wed Sep  5 16:23:04 2018
@@ -25,9 +25,14 @@ import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Arrays;
+
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
-
+import java.util.stream.Collectors;
 import org.apache.ofbiz.base.component.ComponentConfig;
 import org.apache.ofbiz.base.component.ComponentException;
 import org.apache.ofbiz.base.component.ComponentLoaderConfig;
@@ -36,6 +41,7 @@ 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.UtilValidate;
 
 /**
  * ComponentContainer - StartupContainer implementation for Components
@@ -54,6 +60,7 @@ public class ComponentContainer implemen
     private String name;
     private final AtomicBoolean loaded = new AtomicBoolean(false);
     private final List<Classpath> componentsClassPath = new ArrayList<>();
+    private static Map<String, List<ComponentConfig.DependsOnInfo>> toBeLoadedComponents = new HashMap<>();
 
     @Override
     public void init(List<StartupCommand> ofbizCommands, String name, String configFile) throws ContainerException {
@@ -110,8 +117,10 @@ public class ComponentContainer implemen
      * @param parentPath the parent path of what is being loaded
      * @param def the component or directory loader definition
      * @throws IOException
+     * @throws ContainerException
+     * @throws ComponentException
      */
-    private void loadComponentFromConfig(String parentPath, ComponentLoaderConfig.ComponentDef def) throws IOException {
+    private void loadComponentFromConfig(String parentPath, ComponentLoaderConfig.ComponentDef def) throws IOException, ContainerException, ComponentException {
         String location = def.location.startsWith("/") ? def.location : parentPath + "/" + def.location;
 
         if (def.type.equals(ComponentLoaderConfig.ComponentType.COMPONENT_DIRECTORY)) {
@@ -130,8 +139,10 @@ public class ComponentContainer implemen
      *
      * @param directoryName the name of component directory to load
      * @throws IOException
+     * @throws ContainerException
+     * @throws ComponentException
      */
-    private void loadComponentDirectory(String directoryName) throws IOException {
+    private void loadComponentDirectory(String directoryName) throws IOException, ContainerException, ComponentException {
         Debug.logInfo("Auto-Loading component directory : [" + directoryName + "]", module);
 
         File directoryPath = FileUtil.getFile(directoryName);
@@ -156,8 +167,9 @@ public class ComponentContainer implemen
      * @param directoryPath the absolute path of the directory
      * @param componentLoadFile the name of the load file (i.e. component-load.xml)
      * @throws IOException
+     * @throws ContainerException
      */
-    private void loadComponentsInDirectoryUsingLoadFile(File directoryPath, File componentLoadFile) throws IOException {
+    private void loadComponentsInDirectoryUsingLoadFile(File directoryPath, File componentLoadFile) throws IOException, ContainerException {
         URL configUrl = null;
         try {
             configUrl = componentLoadFile.toURI().toURL();
@@ -179,9 +191,11 @@ public class ComponentContainer implemen
      *
      * @param directoryPath the absolute path of the directory
      * @throws IOException
+     * @throws ComponentException
      */
-    private void loadComponentsInDirectory(File directoryPath) throws IOException {
+    private void loadComponentsInDirectory(File directoryPath) throws IOException, ComponentException {
         String[] sortedComponentNames = directoryPath.list();
+        List<ComponentConfig> componentConfigs = new ArrayList<>();
         if (sortedComponentNames == null) {
             throw new IllegalArgumentException("sortedComponentNames is null, directory path is invalid " + directoryPath.getPath());
         }
@@ -194,8 +208,46 @@ public class ComponentContainer implemen
 
             if (componentPath.isDirectory() && !componentName.startsWith(".") && configFile.exists()) {
                 ComponentConfig config = retrieveComponentConfig(null, componentLocation);
-                if (config != null) {
-                    loadComponent(config);
+                componentConfigs.add(config);
+            }
+        }
+        for (ComponentConfig componentConfig : componentConfigs) {
+            if (componentConfig != null) {
+                loadComponent(componentConfig);
+            }
+        }
+        loadComponentWithDependency();
+    }
+
+    /**
+     * Checks dependency for unloaded components and add them into
+     * componentsClassPath
+     *
+     * @throws IOException
+     * @throws ComponentException
+     */
+    private void loadComponentWithDependency() throws IOException, ComponentException {
+        while (true) {
+            if (UtilValidate.isEmpty(toBeLoadedComponents)) {
+                return;
+            } else {
+                for (Map.Entry<String, List<ComponentConfig.DependsOnInfo>> entries : toBeLoadedComponents.entrySet()) {
+                    ComponentConfig config = retrieveComponentConfig(entries.getKey(), null);
+                    if (config.enabled()) {
+                        List<ComponentConfig.DependsOnInfo> dependencyList = checkDependencyForComponent(config);
+                        if (UtilValidate.isNotEmpty(dependencyList)) {
+                            toBeLoadedComponents.replace(config.getComponentName(), dependencyList);
+                            String msg = "Not loading component [" + config.getComponentName() + "] because it's dependent Component is not loaded [ " + dependencyList + "]";
+                            Debug.logInfo(msg, module);
+                        }
+                        if (UtilValidate.isEmpty(dependencyList)) {
+                            componentsClassPath.add(buildClasspathFromComponentConfig(config));
+                            toBeLoadedComponents.replace(config.getComponentName(), dependencyList);
+                            Debug.logInfo("Added class path for component : [" + config.getComponentName() + "]", module);
+                        }
+                    } else {
+                        Debug.logInfo("Not loading component [" + config.getComponentName() + "] because it's disabled", module);
+                    }
                 }
             }
         }
@@ -227,15 +279,52 @@ public class ComponentContainer implemen
      *
      * @param config the component configuration
      * @throws IOException
+     * @throws ComponentException
      */
-    private void loadComponent(ComponentConfig config) throws IOException {
+    private void loadComponent(ComponentConfig config) throws IOException, ComponentException {
         if (config.enabled()) {
-            Classpath classpath = buildClasspathFromComponentConfig(config);
-            componentsClassPath.add(classpath);
-            Debug.logInfo("Added class path for component : [" + config.getComponentName() + "]", module);
+            List<ComponentConfig.DependsOnInfo> dependencyList = checkDependencyForComponent(config);
+            if (UtilValidate.isEmpty(dependencyList)) {
+                componentsClassPath.add(buildClasspathFromComponentConfig(config));
+                Debug.logInfo("Added class path for component : [" + config.getComponentName() + "]", module);
+            }
         } else {
-            Debug.logInfo("Not loading component [" + config.getComponentName() + "] because it is disabled", module);
+            Debug.logInfo("Not loading component [" + config.getComponentName() + "] because it's disabled", module);
+        }
+    }
+
+    /**
+     * Check for components loaded and Removes loaded components dependency
+     * from list of unloaded components
+     *
+     * @param config the component configuration
+     * @throws IOException
+     * @throws ComponentException
+     *
+     */
+    private List<ComponentConfig.DependsOnInfo> checkDependencyForComponent(ComponentConfig config) throws IOException, ComponentException {
+        List<ComponentConfig.DependsOnInfo> dependencyList = new ArrayList<>(config.getDependsOn());
+        if (UtilValidate.isNotEmpty(dependencyList)) {
+            Set<ComponentConfig.DependsOnInfo> resolvedDependencyList = new HashSet<>();
+            for (ComponentConfig.DependsOnInfo dependency : dependencyList) {
+                Debug.logInfo("Component : " + config.getComponentName() + " is Dependent on  " + dependency.componentName, module);
+                ComponentConfig componentConfig = ComponentConfig.getComponentConfig(String.valueOf(dependency.componentName));
+                Classpath dependentComponentClasspath = buildClasspathFromComponentConfig(componentConfig);
+                componentsClassPath.forEach(componentClassPath -> {
+                    if (Arrays.equals(componentClassPath.toString().split(":"), dependentComponentClasspath.toString().split(":"))) {
+                        resolvedDependencyList.add(dependency);
+                    }
+                });
+            }
+            resolvedDependencyList.forEach(resolvedDependency -> Debug.logInfo("Resolved : " + resolvedDependency.componentName + " Dependency for Component " + config.getComponentName(), module));
+            dependencyList.removeAll(resolvedDependencyList);
+            if (UtilValidate.isEmpty(dependencyList)) {
+                toBeLoadedComponents.remove(config.getComponentName());
+            } else {
+                toBeLoadedComponents.put(config.getComponentName(), dependencyList);
+            }
         }
+        return dependencyList;
     }
 
     /**