[Devel] busy implementing a component directed acyclic dependency tree

This is needed to start/stop in the right order
This commit is contained in:
Robert von Burg 2013-11-06 22:34:00 +01:00
parent af34d81df8
commit 3a44a1407e
3 changed files with 124 additions and 159 deletions

View File

@ -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() {

View File

@ -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);
}
}
}

View File

@ -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() {