[Devel] busy implementing a component directed acyclic dependency tree
This is needed to start/stop in the right order
This commit is contained in:
parent
af34d81df8
commit
3a44a1407e
|
@ -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<Class<?>, StrolchComponent> componentMap;
|
||||
private Map<String, ComponentController> controllerMap;
|
||||
|
||||
private ComponentDependencyAnalyzer dependencyAnalyzer;
|
||||
|
||||
|
@ -46,26 +46,26 @@ public class ComponentContainer {
|
|||
|
||||
StrolchConfiguration strolchConfiguration = ConfigurationParser.parseConfiguration(path);
|
||||
|
||||
Map<Class<?>, StrolchComponent> componentByApiMap = new HashMap<>();
|
||||
Map<String, StrolchComponent> componentByNameMap = new HashMap<>();
|
||||
Map<Class<?>, StrolchComponent> componentMap = new HashMap<>();
|
||||
Map<String, ComponentController> controllerMap = new HashMap<>();
|
||||
Set<String> 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<Class<?>, StrolchComponent> componentByApiMap,
|
||||
Map<String, StrolchComponent> componentByNameMap, ComponentConfiguration componentConfiguration) {
|
||||
private void initializeComponent(Map<Class<?>, StrolchComponent> componentMap,
|
||||
Map<String, ComponentController> 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<StrolchComponent> components) {
|
||||
|
||||
Set<StrolchComponent> allDownstreamDependencies = findDirectDownstreamDependencies(components);
|
||||
// TODO Set<StrolchComponent> 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<StrolchComponent> components) {
|
||||
|
||||
Set<StrolchComponent> allDownstreamDependencies = findDirectUpstreamDependencies(components);
|
||||
// TODO Set<StrolchComponent> 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<StrolchComponent> components) {
|
||||
|
||||
Set<StrolchComponent> allUpstreamDependencies = findDirectUpstreamDependencies(components);
|
||||
Set<ComponentController> 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<StrolchComponent> findDirectDownstreamDependencies(Set<StrolchComponent> components) {
|
||||
Set<StrolchComponent> allDownstreamDependencies = new HashSet<>();
|
||||
|
||||
// find the direct downstream dependencies
|
||||
for (StrolchComponent component : components) {
|
||||
|
||||
// find this component's downstream dependencies
|
||||
Set<StrolchComponent> 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<StrolchComponent> tmpDependencies = new HashSet<>(allDownstreamDependencies);
|
||||
for (StrolchComponent component : tmpDependencies) {
|
||||
|
||||
Set<StrolchComponent> upstreamDependencies = this.dependencyAnalyzer.findUpstreamDependencies(component);
|
||||
allDownstreamDependencies.removeAll(upstreamDependencies);
|
||||
}
|
||||
return allDownstreamDependencies;
|
||||
}
|
||||
|
||||
private Set<StrolchComponent> findDirectUpstreamDependencies(Set<StrolchComponent> components) {
|
||||
Set<StrolchComponent> allUpstreamDependencies = new HashSet<>();
|
||||
|
||||
// TODO
|
||||
|
||||
return allUpstreamDependencies;
|
||||
// TODO destroy(allUpstreamDependencies);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
|
|
|
@ -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<String, StrolchComponent> componentMap;
|
||||
private Map<String, ComponentController> controllerMap;
|
||||
|
||||
public ComponentDependencyAnalyzer(StrolchConfiguration strolchConfiguration,
|
||||
Map<String, StrolchComponent> componentMap) {
|
||||
Map<String, ComponentController> controllerMap) {
|
||||
this.strolchConfiguration = strolchConfiguration;
|
||||
this.componentMap = componentMap;
|
||||
this.controllerMap = controllerMap;
|
||||
}
|
||||
|
||||
public Set<StrolchComponent> findAllRootComponents() {
|
||||
|
||||
Set<StrolchComponent> rootComponents = new HashSet<>();
|
||||
|
||||
Set<String> 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<ComponentController> findRootUpstreamComponents() {
|
||||
Set<ComponentController> controllers = new HashSet<>();
|
||||
for (ComponentController controller : this.controllerMap.values()) {
|
||||
if (!controller.hasUpstreamDependencies())
|
||||
controllers.add(controller);
|
||||
}
|
||||
|
||||
return rootComponents;
|
||||
return controllers;
|
||||
}
|
||||
|
||||
public Set<StrolchComponent> findAllLeafComponents() {
|
||||
|
||||
Set<StrolchComponent> leafComponents = new HashSet<>();
|
||||
|
||||
Set<String> 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<ComponentController> findRootDownstreamComponents() {
|
||||
Set<ComponentController> controllers = new HashSet<>();
|
||||
for (ComponentController controller : this.controllerMap.values()) {
|
||||
if (!controller.hasDownstreamDependencies())
|
||||
controllers.add(controller);
|
||||
}
|
||||
|
||||
return leafComponents;
|
||||
return controllers;
|
||||
}
|
||||
|
||||
public Set<StrolchComponent> findUpstreamDependencies(StrolchComponent component) {
|
||||
public Set<ComponentController> findDirectUpstreamDependencies(Set<ComponentController> controllers) {
|
||||
|
||||
String componentName = component.getName();
|
||||
ComponentConfiguration componentConfiguration = this.strolchConfiguration
|
||||
.getComponentConfiguration(componentName);
|
||||
Set<ComponentController> directUpstreamDependencies = new HashSet<>();
|
||||
|
||||
Set<String> dependencyNames = componentConfiguration.getDependencies();
|
||||
if (dependencyNames.isEmpty())
|
||||
return Collections.emptySet();
|
||||
|
||||
Set<StrolchComponent> 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<ComponentController> 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<ComponentController> upstreamDependencies = controller.getUpstreamDependencies();
|
||||
|
||||
public Set<StrolchComponent> findDownstreamDependencies(StrolchComponent component) {
|
||||
for (ComponentController upstream : upstreamDependencies) {
|
||||
|
||||
String componentName = component.getName();
|
||||
Set<StrolchComponent> dependencies = new HashSet<>();
|
||||
Iterator<ComponentController> iter = directUpstreamDependencies.iterator();
|
||||
while (iter.hasNext()) {
|
||||
ComponentController possibleTransitiveDependency = iter.next();
|
||||
if (upstream.hasUpstreamDependency(possibleTransitiveDependency))
|
||||
continue;
|
||||
|
||||
Set<String> 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<String> componentNames = this.strolchConfiguration.getComponentNames();
|
||||
for (String componentName : componentNames) {
|
||||
ComponentConfiguration componentConfiguration = this.strolchConfiguration
|
||||
.getComponentConfiguration(componentName);
|
||||
List<String> 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<ComponentController> findDirectDownstreamDependencies(ComponentController component) {
|
||||
|
||||
Set<ComponentController> 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<String> dependencies = configuration.getDependencies();
|
||||
for (String dependency : dependencies) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findCyclicDependency(List<String> cyclicDependencies, String componentName, Set<String> dependencies) {
|
||||
|
||||
for (String dependency : dependencies) {
|
||||
if (componentName.equals(dependency)) {
|
||||
cyclicDependencies.add(dependency);
|
||||
return;
|
||||
}
|
||||
|
||||
ComponentConfiguration dependencyConfiguration = this.strolchConfiguration
|
||||
.getComponentConfiguration(dependency);
|
||||
Set<String> nextDependencies = dependencyConfiguration.getDependencies();
|
||||
findCyclicDependency(cyclicDependencies, componentName, nextDependencies);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<String, ComponentController> 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<ComponentController> controllers = new HashSet<>();
|
||||
controllers.add(this.con7);
|
||||
controllers.add(this.con8);
|
||||
|
||||
Set<ComponentController> directUpstreamDependencies = dependencyAnalyzer
|
||||
.findDirectUpstreamDependencies(controllers);
|
||||
|
||||
assertEquals(1, directUpstreamDependencies.size());
|
||||
assertTrue(directUpstreamDependencies.contains(this.con11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddDepedencies() {
|
||||
|
||||
|
|
Loading…
Reference in New Issue