[ofbiz-framework] branch trunk updated: Reverted: "Fixed: Remove dependency management from ‘ComponentContainer’" (OFBIZ-11275)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[ofbiz-framework] branch trunk updated: Reverted: "Fixed: Remove dependency management from ‘ComponentContainer’" (OFBIZ-11275)

mthl
This is an automated email from the ASF dual-hosted git repository.

mthl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a521de9  Reverted: "Fixed: Remove dependency management from ‘ComponentContainer’" (OFBIZ-11275)
a521de9 is described below

commit a521de9ad8a0b5a0f9ceadab348c46d7961ff89a
Author: Mathieu Lirzin <[hidden email]>
AuthorDate: Sat Nov 9 00:50:56 2019 +0100

    Reverted: "Fixed: Remove dependency management from ‘ComponentContainer’"
    (OFBIZ-11275)
   
    This reverts commit 3d3533cf5e04b303e5d7a4b0f178c54f10ddf620.
   
    ‘./gradlew cleanAll loadAll’ do not work anymore.
---
 build.gradle                                       |   2 +-
 .../base/component/AlreadyLoadedException.java     |  44 ++++++++
 .../ofbiz/base/component/ComponentConfig.java      |  61 +----------
 .../ofbiz/base/container/ComponentContainer.java   | 120 +++++++++++++++++++--
 .../base/container/ComponentContainerTest.java     |   2 +
 5 files changed, 160 insertions(+), 69 deletions(-)

diff --git a/build.gradle b/build.gradle
index 0395b8c..6c8ac6c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -309,7 +309,7 @@ checkstyle {
     // the sum of errors that were present before introducing the
     // ‘checkstyle’ tool present in the framework and in the official
     // plugins.
-    tasks.checkstyleMain.maxErrors = 37776
+    tasks.checkstyleMain.maxErrors = 37779
     // Currently there are a lot of errors so we need to temporarily
     // hide them to avoid polluting the terminal output.
     showViolations = false
diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/component/AlreadyLoadedException.java b/framework/base/src/main/java/org/apache/ofbiz/base/component/AlreadyLoadedException.java
new file mode 100644
index 0000000..4485ccf
--- /dev/null
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/component/AlreadyLoadedException.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.base.component;
+
+/**
+ * Component Already Loaded Exception
+ *
+ */
+@SuppressWarnings("serial")
+public class AlreadyLoadedException extends ComponentException {
+
+    public AlreadyLoadedException() {
+        super();
+    }
+
+    public AlreadyLoadedException(String str) {
+        super(str);
+    }
+
+    public AlreadyLoadedException(Throwable nested) {
+        super(nested);
+    }
+
+    public AlreadyLoadedException(String str, Throwable nested) {
+        super(str, nested);
+    }
+}
+
diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java b/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
index c1a1ada..4a39885 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
@@ -18,9 +18,6 @@
  *******************************************************************************/
 package org.apache.ofbiz.base.component;
 
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toMap;
-
 import java.io.InputStream;
 import java.net.URL;
 import java.nio.file.Files;
@@ -43,7 +40,6 @@ import org.apache.ofbiz.base.container.ContainerConfig;
 import org.apache.ofbiz.base.location.FlexibleLocation;
 import org.apache.ofbiz.base.util.Assert;
 import org.apache.ofbiz.base.util.Debug;
-import org.apache.ofbiz.base.util.Digraph;
 import org.apache.ofbiz.base.util.KeyStoreUtil;
 import org.apache.ofbiz.base.util.UtilURL;
 import org.apache.ofbiz.base.util.UtilValidate;
@@ -95,16 +91,6 @@ public final class ComponentConfig {
         return componentConfigCache.values();
     }
 
-    /**
-     * Sorts the cached component configurations.
-     *
-     * @throws ComponentException when a component configuration contains an invalid dependency
-     *         or if the dependency graph contains a cycle.
-     */
-    public static void sortDependencies() throws ComponentException {
-        componentConfigCache.sort();
-    }
-
     public static Stream<ComponentConfig> components() {
         return componentConfigCache.values().stream();
     }
@@ -695,8 +681,6 @@ public final class ComponentConfig {
         private final Map<String, ComponentConfig> componentConfigs = new LinkedHashMap<>();
         // Root location mapped to global name.
         private final Map<String, String> componentLocations = new HashMap<>();
-        /** Sorted component configurations (based on depends-on declaration if ofbiz-component.xml). */
-        private List<ComponentConfig> componentConfigsSorted;
 
         private synchronized ComponentConfig fromGlobalName(String globalName) {
             return componentConfigs.get(globalName);
@@ -717,49 +701,8 @@ public final class ComponentConfig {
             return componentConfigs.put(globalName, config);
         }
 
-        /**
-         * Provides a sequence of cached component configurations.
-         *
-         * <p>the order of the sequence is arbitrary unless the {@link #sort())} has been invoked beforehand.
-         *
-         * @return the list of cached component configurations
-         */
-        synchronized List<ComponentConfig> values() {
-            // When the configurations have not been sorted use arbitrary order.
-            List<ComponentConfig> res = (componentConfigsSorted == null)
-                    ? new ArrayList<>(componentConfigs.values())
-                    : componentConfigsSorted;
-            return Collections.unmodifiableList(res);
-        }
-
-        /**
-         * Provides the stored dependencies as component configurations.
-         *
-         * <p>Handle invalid dependencies by creating dummy component configurations.
-         *
-         * @param the component configuration containing dependencies
-         * @return a collection of component configurations dependencies.
-         */
-        private Collection<ComponentConfig> componentConfigDeps(ComponentConfig cc) {
-            return cc.getDependsOn().stream()
-                    .map(dep -> {
-                        ComponentConfig storedCc = componentConfigs.get(dep);
-                        return (storedCc != null) ? storedCc : new Builder().globalName(dep).create();
-                    })
-                    .collect(Collectors.toList());
-        }
-
-        /**
-         * Sorts the component configurations based on their “depends-on” XML elements.
-         */
-        synchronized void sort() throws ComponentException {
-            Map<ComponentConfig, Collection<ComponentConfig>> graphSpec = componentConfigs.values().stream()
-                    .collect(toMap(identity(), this::componentConfigDeps));
-            try {
-                componentConfigsSorted = new Digraph<>(graphSpec).sort();
-            } catch (IllegalArgumentException | IllegalStateException e) {
-                throw new ComponentException("failed to initialize components", e);
-            }
+        synchronized Collection<ComponentConfig> values() {
+            return Collections.unmodifiableList(new ArrayList<>(componentConfigs.values()));
         }
     }
 
diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java b/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java
index 31f6e60..3f14707 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/container/ComponentContainer.java
@@ -26,7 +26,13 @@ import java.net.URLClassLoader;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -38,6 +44,7 @@ import org.apache.ofbiz.base.component.ComponentLoaderConfig.ComponentDef;
 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.UtilValidate;
 
 /**
  * ComponentContainer - StartupContainer implementation for Components
@@ -55,6 +62,9 @@ public class ComponentContainer implements Container {
 
     private String name;
     private final AtomicBoolean loaded = new AtomicBoolean(false);
+    /** The set of ready components in their inverse dependency order. */
+    private final LinkedHashSet<ComponentConfig> readyComponents = new LinkedHashSet<>();
+    private static Map<String, List<String>> toBeLoadedComponents = new ConcurrentHashMap<>();
 
     @Override
     public void init(List<StartupCommand> ofbizCommands, String name, String configFile) throws ContainerException {
@@ -79,12 +89,11 @@ public class ComponentContainer implements Container {
             for (ComponentDef def: ComponentLoaderConfig.getRootComponents()) {
                 loadComponent(ofbizHome, def);
             }
-            ComponentConfig.sortDependencies();
         } catch (IOException | ComponentException e) {
             throw new ContainerException(e);
         }
         String fmt = "Added class path for component : [%s]";
-        List<Classpath> componentsClassPath = ComponentConfig.components()
+        List<Classpath> componentsClassPath = readyComponents.stream()
                 .peek(cmpnt -> Debug.logInfo(String.format(fmt, cmpnt.getComponentName()), module))
                 .map(cmpnt -> buildClasspathFromComponentConfig(cmpnt))
                 .collect(Collectors.toList());
@@ -125,15 +134,19 @@ public class ComponentContainer implements Container {
      * @param dir  the location where the component should be loaded
      * @param component  a single component or a component directory definition
      * @throws IOException when component directory loading fails.
+     * @throws ComponentException when retrieving component configuration files fails.
      */
-    private void loadComponent(Path dir, ComponentDef component) throws IOException {
+    private void loadComponent(Path dir, ComponentDef component) throws IOException, ComponentException {
         Path location = component.location.isAbsolute() ? component.location : dir.resolve(component.location);
         switch (component.type) {
         case COMPONENT_DIRECTORY:
             loadComponentDirectory(location);
             break;
         case SINGLE_COMPONENT:
-            retrieveComponentConfig(null, location);
+            ComponentConfig config = retrieveComponentConfig(null, location);
+            if (config != null) {
+                loadSingleComponent(config);
+            }
             break;
         }
     }
@@ -144,8 +157,9 @@ public class ComponentContainer implements Container {
      *
      * @param directoryName the name of component directory to load
      * @throws IOException
+     * @throws ComponentException
      */
-    private void loadComponentDirectory(Path directoryName) throws IOException {
+    private void loadComponentDirectory(Path directoryName) throws IOException, ComponentException {
         Debug.logInfo("Auto-Loading component directory : [" + directoryName + "]", module);
         if (Files.exists(directoryName) && Files.isDirectory(directoryName)) {
             Path componentLoad = directoryName.resolve(ComponentLoaderConfig.COMPONENT_LOAD_XML_FILENAME);
@@ -191,15 +205,56 @@ public class ComponentContainer implements Container {
      * for loading purposes
      *
      * @param directoryPath a valid absolute path of a component directory
-     * @throws IOException if an I/O error occurs when opening the directory
+     * @throws IOException
+     * @throws ComponentException
      */
-    private static void loadComponentsInDirectory(Path directoryPath) throws IOException {
+    private void loadComponentsInDirectory(Path directoryPath) throws IOException, ComponentException {
         try (Stream<Path> paths = Files.list(directoryPath)) {
-            paths.sorted()
+            List<ComponentConfig> componentConfigs = paths.sorted()
                     .map(cmpnt -> directoryPath.resolve(cmpnt).toAbsolutePath().normalize())
                     .filter(Files::isDirectory)
                     .filter(dir -> Files.exists(dir.resolve(ComponentConfig.OFBIZ_COMPONENT_XML_FILENAME)))
-                    .forEach(componentDir -> retrieveComponentConfig(null, componentDir));
+                    .map(componentDir -> retrieveComponentConfig(null, componentDir))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+
+            for (ComponentConfig cmpnt : componentConfigs) {
+                loadSingleComponent(cmpnt);
+            }
+        }
+        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<String>> entries : toBeLoadedComponents.entrySet()) {
+                    ComponentConfig config = retrieveComponentConfig(entries.getKey(), null);
+                    if (config.enabled()) {
+                        List<String> 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)) {
+                            readyComponents.add(config);
+                            toBeLoadedComponents.replace(config.getComponentName(), dependencyList);
+                        }
+                    } else {
+                        Debug.logInfo("Not loading component [" + config.getComponentName() + "] because it's disabled", module);
+                    }
+                }
+            }
         }
     }
 
@@ -224,6 +279,53 @@ public class ComponentContainer implements Container {
     }
 
     /**
+     * Load a single component by adding all its classpath entries to
+     * the list of classpaths to be loaded
+     *
+     * @param config the component configuration
+     * @throws ComponentException
+     */
+    private void loadSingleComponent(ComponentConfig config) throws ComponentException {
+        if (config.enabled()) {
+            List<String> dependencyList = checkDependencyForComponent(config);
+            if (UtilValidate.isEmpty(dependencyList)) {
+                readyComponents.add(config);
+            }
+        } else {
+            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 ComponentException
+     */
+    private List<String> checkDependencyForComponent(ComponentConfig config) throws ComponentException {
+        List<String> dependencyList = new ArrayList<>(config.getDependsOn());
+        if (UtilValidate.isNotEmpty(dependencyList)) {
+            Set<String> resolvedDependencyList = new HashSet<>();
+            for (String dependency : dependencyList) {
+                Debug.logInfo("Component : " + config.getComponentName() + " is Dependent on  " + dependency, module);
+                ComponentConfig componentConfig = ComponentConfig.getComponentConfig(String.valueOf(dependency));
+                if (readyComponents.contains(componentConfig)) {
+                    resolvedDependencyList.add(dependency);
+                }
+            }
+            resolvedDependencyList.forEach(resolvedDependency -> Debug.logInfo("Resolved : " + resolvedDependency + " 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;
+    }
+
+    /**
      * Constructs a {@code Classpath} object for a specific component definition.
      *
      * @param config  the component configuration
diff --git a/framework/base/src/test/java/org/apache/ofbiz/base/container/ComponentContainerTest.java b/framework/base/src/test/java/org/apache/ofbiz/base/container/ComponentContainerTest.java
index 57be05a..358f0bc 100644
--- a/framework/base/src/test/java/org/apache/ofbiz/base/container/ComponentContainerTest.java
+++ b/framework/base/src/test/java/org/apache/ofbiz/base/container/ComponentContainerTest.java
@@ -35,6 +35,7 @@ import org.apache.ofbiz.base.component.ComponentConfig;
 import org.apache.ofbiz.base.start.StartupException;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 
@@ -60,6 +61,7 @@ public class ComponentContainerTest {
         }
     }
 
+    @Ignore("Dependency declarations do not impact the components order")
     @Test
     public void testCheckDependencyForComponent() throws ContainerException, StartupException, MalformedURLException {
         ComponentContainer containerObj = new ComponentContainer();