diff --git a/src/main/java/li/strolch/runtime/component/ComponentContainer.java b/src/main/java/li/strolch/runtime/component/ComponentContainer.java index fd74d304d..87073a68f 100644 --- a/src/main/java/li/strolch/runtime/component/ComponentContainer.java +++ b/src/main/java/li/strolch/runtime/component/ComponentContainer.java @@ -6,7 +6,6 @@ import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -23,6 +22,7 @@ public class ComponentContainer { private static final Logger logger = LoggerFactory.getLogger(ComponentContainer.class); private Map, StrolchComponent> componentMap; + private Map controllerMap; private ComponentDependencyAnalyzer dependencyAnalyzer; @@ -46,26 +46,26 @@ public class ComponentContainer { StrolchConfiguration strolchConfiguration = ConfigurationParser.parseConfiguration(path); - Map, StrolchComponent> componentByApiMap = new HashMap<>(); - Map componentByNameMap = new HashMap<>(); + Map, StrolchComponent> componentMap = new HashMap<>(); + Map controllerMap = new HashMap<>(); Set componentNames = strolchConfiguration.getComponentNames(); for (String componentName : componentNames) { ComponentConfiguration componentConfiguration = strolchConfiguration .getComponentConfiguration(componentName); - initializeComponent(componentByApiMap, componentByNameMap, componentConfiguration); + initializeComponent(componentMap, controllerMap, componentConfiguration); } - this.dependencyAnalyzer = new ComponentDependencyAnalyzer(strolchConfiguration, componentByNameMap); - this.dependencyAnalyzer.assertHasNoCyclicDependency(); + this.dependencyAnalyzer = new ComponentDependencyAnalyzer(strolchConfiguration, controllerMap); + this.dependencyAnalyzer.setupDependencies(); - this.componentMap = componentByApiMap; + this.componentMap = componentMap; this.strolchConfiguration = strolchConfiguration; msg = "Strolch Container setup with {0} components."; //$NON-NLS-1$ logger.info(MessageFormat.format(msg, this.componentMap.size())); } - private void initializeComponent(Map, StrolchComponent> componentByApiMap, - Map componentByNameMap, ComponentConfiguration componentConfiguration) { + private void initializeComponent(Map, StrolchComponent> componentMap, + Map controllerMap, ComponentConfiguration componentConfiguration) { String componentName = componentConfiguration.getName(); try { @@ -92,8 +92,8 @@ public class ComponentContainer { String.class); StrolchComponent strolchComponent = constructor.newInstance(this, componentName); - componentByApiMap.put(apiClass, strolchComponent); - componentByNameMap.put(componentName, strolchComponent); + componentMap.put(apiClass, strolchComponent); + controllerMap.put(componentName, new ComponentController(strolchComponent)); String msg = "Initialized component {0} with API {1} and impl {2}."; //$NON-NLS-1$ logger.info(MessageFormat.format(msg, componentName, api, impl)); @@ -108,7 +108,7 @@ public class ComponentContainer { public void start(Set components) { - Set allDownstreamDependencies = findDirectDownstreamDependencies(components); + // TODO Set allDownstreamDependencies = findDirectDownstreamDependencies(components); // initialize each component for (StrolchComponent component : components) { @@ -116,12 +116,12 @@ public class ComponentContainer { } // initialize downstream dependencies - start(allDownstreamDependencies); + // TODO start(allDownstreamDependencies); } public void stop(Set components) { - Set allDownstreamDependencies = findDirectUpstreamDependencies(components); + // TODO Set allDownstreamDependencies = findDirectUpstreamDependencies(components); // initialize each component for (StrolchComponent component : components) { @@ -129,12 +129,12 @@ public class ComponentContainer { } // initialize downstream dependencies - stop(allDownstreamDependencies); + // TODO stop(allDownstreamDependencies); } public void destroy(Set components) { - Set allUpstreamDependencies = findDirectUpstreamDependencies(components); + Set allUpstreamDependencies = this.dependencyAnalyzer.findRootUpstreamComponents(); // initialize each component for (StrolchComponent component : components) { @@ -142,40 +142,7 @@ public class ComponentContainer { } // initialize downstream dependencies - destroy(allUpstreamDependencies); - } - - private Set findDirectDownstreamDependencies(Set components) { - Set allDownstreamDependencies = new HashSet<>(); - - // find the direct downstream dependencies - for (StrolchComponent component : components) { - - // find this component's downstream dependencies - Set downstreamDependencies = this.dependencyAnalyzer - .findDownstreamDependencies(component); - - // add all of these downstream dependencies - allDownstreamDependencies.addAll(downstreamDependencies); - } - - // make sure that in these downstream dependencies - // none of these dependencies depend on each other - Set tmpDependencies = new HashSet<>(allDownstreamDependencies); - for (StrolchComponent component : tmpDependencies) { - - Set upstreamDependencies = this.dependencyAnalyzer.findUpstreamDependencies(component); - allDownstreamDependencies.removeAll(upstreamDependencies); - } - return allDownstreamDependencies; - } - - private Set findDirectUpstreamDependencies(Set components) { - Set allUpstreamDependencies = new HashSet<>(); - - // TODO - - return allUpstreamDependencies; + // TODO destroy(allUpstreamDependencies); } public void initialize() { diff --git a/src/main/java/li/strolch/runtime/component/ComponentDependencyAnalyzer.java b/src/main/java/li/strolch/runtime/component/ComponentDependencyAnalyzer.java index 443baac20..31e51b2a7 100644 --- a/src/main/java/li/strolch/runtime/component/ComponentDependencyAnalyzer.java +++ b/src/main/java/li/strolch/runtime/component/ComponentDependencyAnalyzer.java @@ -1,146 +1,93 @@ package li.strolch.runtime.component; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; -import java.util.List; +import java.util.Iterator; import java.util.Map; import java.util.Set; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.configuration.StrolchConfiguration; -import li.strolch.runtime.configuration.StrolchConfigurationException; public class ComponentDependencyAnalyzer { private StrolchConfiguration strolchConfiguration; - private Map componentMap; + private Map controllerMap; public ComponentDependencyAnalyzer(StrolchConfiguration strolchConfiguration, - Map componentMap) { + Map controllerMap) { this.strolchConfiguration = strolchConfiguration; - this.componentMap = componentMap; + this.controllerMap = controllerMap; } - public Set findAllRootComponents() { - - Set rootComponents = new HashSet<>(); - - Set componentNames = this.strolchConfiguration.getComponentNames(); - for (String componentName : componentNames) { - ComponentConfiguration componentConfiguration = this.strolchConfiguration - .getComponentConfiguration(componentName); - if (componentConfiguration.getDependencies().isEmpty()) { - StrolchComponent strolchComponent = this.componentMap.get(componentName); - rootComponents.add(strolchComponent); - } + public Set findRootUpstreamComponents() { + Set controllers = new HashSet<>(); + for (ComponentController controller : this.controllerMap.values()) { + if (!controller.hasUpstreamDependencies()) + controllers.add(controller); } - return rootComponents; + return controllers; } - public Set findAllLeafComponents() { - - Set leafComponents = new HashSet<>(); - - Set componentNames = this.strolchConfiguration.getComponentNames(); - for (String componentName : componentNames) { - - boolean noDownStreamDependency = true; - for (String possibleDependency : componentNames) { - if (componentName.equals(possibleDependency)) - continue; - - ComponentConfiguration dependency = this.strolchConfiguration - .getComponentConfiguration(possibleDependency); - if (dependency.getDependencies().contains(componentName)) - noDownStreamDependency = false; - } - - if (noDownStreamDependency) { - StrolchComponent strolchComponent = this.componentMap.get(componentName); - leafComponents.add(strolchComponent); - } + public Set findRootDownstreamComponents() { + Set controllers = new HashSet<>(); + for (ComponentController controller : this.controllerMap.values()) { + if (!controller.hasDownstreamDependencies()) + controllers.add(controller); } - return leafComponents; + return controllers; } - public Set findUpstreamDependencies(StrolchComponent component) { + public Set findDirectUpstreamDependencies(Set controllers) { - String componentName = component.getName(); - ComponentConfiguration componentConfiguration = this.strolchConfiguration - .getComponentConfiguration(componentName); + Set directUpstreamDependencies = new HashSet<>(); - Set dependencyNames = componentConfiguration.getDependencies(); - if (dependencyNames.isEmpty()) - return Collections.emptySet(); - - Set dependencies = new HashSet<>(dependencyNames.size()); - for (String dependencyName : dependencyNames) { - StrolchComponent dependency = this.componentMap.get(dependencyName); - if (dependency == null) { - String msg = "The dependency {0} for component {0} does not exist!"; //$NON-NLS-1$ - msg = MessageFormat.format(msg, dependencyName, componentName); - throw new StrolchConfigurationException(msg); - } - - dependencies.add(dependency); + // collect all direct upstream dependencies + for (ComponentController controller : controllers) { + Set upstreamDependencies = controller.getUpstreamDependencies(); + directUpstreamDependencies.addAll(upstreamDependencies); } + + // assert no dependency in list which was from source + //directUpstreamDependencies. - return dependencies; - } + // prune dependencies which are a dependency of any of these dependencies + for (ComponentController controller : controllers) { + Set upstreamDependencies = controller.getUpstreamDependencies(); - public Set findDownstreamDependencies(StrolchComponent component) { + for (ComponentController upstream : upstreamDependencies) { - String componentName = component.getName(); - Set dependencies = new HashSet<>(); + Iterator iter = directUpstreamDependencies.iterator(); + while (iter.hasNext()) { + ComponentController possibleTransitiveDependency = iter.next(); + if (upstream.hasUpstreamDependency(possibleTransitiveDependency)) + continue; - Set componentNames = this.strolchConfiguration.getComponentNames(); - for (String name : componentNames) { - ComponentConfiguration configuration = this.strolchConfiguration.getComponentConfiguration(name); - if (configuration.getDependencies().contains(componentName)) - dependencies.add(this.componentMap.get(name)); - } - - return dependencies; - } - - public void assertHasNoCyclicDependency() { - - Set componentNames = this.strolchConfiguration.getComponentNames(); - for (String componentName : componentNames) { - ComponentConfiguration componentConfiguration = this.strolchConfiguration - .getComponentConfiguration(componentName); - List cyclicDependencies = new ArrayList<>(); - findCyclicDependency(cyclicDependencies, componentName, componentConfiguration.getDependencies()); - if (!cyclicDependencies.isEmpty()) { - String msg = "Found a cyclic dependency for component {0}: {1}"; //$NON-NLS-1$ - StringBuilder sb = new StringBuilder(componentName); - for (String dep : cyclicDependencies) { - sb.append(" -> "); //$NON-NLS-1$ - sb.append(dep); + if (upstream.hasTransitiveUpstreamDependency(possibleTransitiveDependency)) + iter.remove(); } - msg = MessageFormat.format(msg, componentName, sb.toString()); - throw new StrolchConfigurationException(msg); + } + } + + return directUpstreamDependencies; + } + + public Set findDirectDownstreamDependencies(ComponentController component) { + + Set controllers = new HashSet<>(); + + return controllers; + } + + public void setupDependencies() { + for (ComponentController controller : this.controllerMap.values()) { + String name = controller.getComponent().getName(); + ComponentConfiguration configuration = this.strolchConfiguration.getComponentConfiguration(name); + Set dependencies = configuration.getDependencies(); + for (String dependency : dependencies) { + } } } - - private void findCyclicDependency(List cyclicDependencies, String componentName, Set dependencies) { - - for (String dependency : dependencies) { - if (componentName.equals(dependency)) { - cyclicDependencies.add(dependency); - return; - } - - ComponentConfiguration dependencyConfiguration = this.strolchConfiguration - .getComponentConfiguration(dependency); - Set nextDependencies = dependencyConfiguration.getDependencies(); - findCyclicDependency(cyclicDependencies, componentName, nextDependencies); - } - } - } diff --git a/src/test/java/li/strolch/runtime/ControllerDependencyTest.java b/src/test/java/li/strolch/runtime/ControllerDependencyTest.java index 1be9d2d78..0028b3b9a 100644 --- a/src/test/java/li/strolch/runtime/ControllerDependencyTest.java +++ b/src/test/java/li/strolch/runtime/ControllerDependencyTest.java @@ -3,12 +3,23 @@ package li.strolch.runtime; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import li.strolch.runtime.component.ComponentContainer; import li.strolch.runtime.component.ComponentController; +import li.strolch.runtime.component.ComponentDependencyAnalyzer; import li.strolch.runtime.component.StrolchComponent; +import li.strolch.runtime.configuration.ConfigurationParser; +import li.strolch.runtime.configuration.StrolchConfiguration; import li.strolch.runtime.configuration.StrolchConfigurationException; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -52,6 +63,9 @@ public class ControllerDependencyTest { private StrolchComponent com3; private ComponentController con3; + private StrolchConfiguration strolchConfiguration; + private Map controllerMap; + @Before public void setupModel() { @@ -87,6 +101,10 @@ public class ControllerDependencyTest { this.con3.addUpstreamDependency(this.con8); this.con3.addUpstreamDependency(this.con10); + + File rootPathF = new File("src/test/resources/configtest"); + this.strolchConfiguration = ConfigurationParser.parseConfiguration(rootPathF); + this.controllerMap = new HashMap<>(); } private void assertModel() { @@ -212,6 +230,39 @@ public class ControllerDependencyTest { this.con11.addUpstreamDependency(this.con8); } + @Test + public void shouldNotBreakModel4() { + assertModel(); + this.con8.addUpstreamDependency(this.con11); + } + + // 11 + // ^ ^ ^ + // 7 9 < 8 + // + // (7, 8) => (11) + + @Test + @Ignore + public void shouldCollectUpstreamDependencies() { + assertModel(); + + ComponentDependencyAnalyzer dependencyAnalyzer = new ComponentDependencyAnalyzer(this.strolchConfiguration, + this.controllerMap); + + this.con8.addUpstreamDependency(this.con11); + + Set controllers = new HashSet<>(); + controllers.add(this.con7); + controllers.add(this.con8); + + Set directUpstreamDependencies = dependencyAnalyzer + .findDirectUpstreamDependencies(controllers); + + assertEquals(1, directUpstreamDependencies.size()); + assertTrue(directUpstreamDependencies.contains(this.con11)); + } + @Test public void shouldAddDepedencies() {