strolch/agent/src/main/java/li/strolch/agent/impl/ComponentDependencyAnalyzer...

175 lines
6.7 KiB
Java

/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed 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 li.strolch.agent.impl;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.runtime.configuration.ComponentConfiguration;
import li.strolch.runtime.configuration.StrolchConfiguration;
import li.strolch.runtime.configuration.StrolchConfigurationException;
import li.strolch.utils.dbc.DBC;
import li.strolch.utils.helper.StringHelper;
public class ComponentDependencyAnalyzer {
private static final Logger logger = LoggerFactory.getLogger(ComponentDependencyAnalyzer.class);
private StrolchConfiguration strolchConfiguration;
private Map<String, ComponentController> controllerMap;
public ComponentDependencyAnalyzer(StrolchConfiguration strolchConfiguration,
Map<String, ComponentController> controllerMap) {
this.strolchConfiguration = strolchConfiguration;
this.controllerMap = controllerMap;
}
public Set<ComponentController> findRootUpstreamComponents() {
Set<ComponentController> controllers = new HashSet<>();
for (ComponentController controller : this.controllerMap.values()) {
if (!controller.hasUpstreamDependencies())
controllers.add(controller);
}
return controllers;
}
public Set<ComponentController> findRootDownstreamComponents() {
Set<ComponentController> controllers = new HashSet<>();
for (ComponentController controller : this.controllerMap.values()) {
if (!controller.hasDownstreamDependencies())
controllers.add(controller);
}
return controllers;
}
private Set<ComponentController> collectAllUpstreamDependencies(ComponentController controller) {
Set<ComponentController> upstreamDependencies = new HashSet<>(controller.getUpstreamDependencies());
for (ComponentController upstream : controller.getUpstreamDependencies()) {
upstreamDependencies.addAll(collectAllUpstreamDependencies(upstream));
}
return upstreamDependencies;
}
public Set<ComponentController> collectDirectUpstreamDependencies(Set<ComponentController> controllers) {
// assert no upstream is in this list
for (ComponentController controller : controllers) {
Set<ComponentController> upstreamDependencies = collectAllUpstreamDependencies(controller);
for (ComponentController upstream : upstreamDependencies) {
DBC.INTERIM.assertFalse("Upstream " + upstream.getName() + " is one of the input controllers!",
controllers.contains(upstream));
}
}
Set<ComponentController> directUpstreamDependencies = new HashSet<>();
// collect all direct upstream dependencies
for (ComponentController controller : controllers) {
Set<ComponentController> upstreamDependencies = controller.getUpstreamDependencies();
directUpstreamDependencies.addAll(upstreamDependencies);
}
// prune any controllers which are an upstream dependency of any of these dependencies
Set<ComponentController> tmp = new HashSet<>(directUpstreamDependencies);
for (ComponentController controller : tmp) {
Set<ComponentController> upstreamDeps = collectAllUpstreamDependencies(controller);
directUpstreamDependencies.removeAll(upstreamDeps);
}
return directUpstreamDependencies;
}
private Set<ComponentController> collectAllDownstreamDependencies(ComponentController controller) {
Set<ComponentController> downstreamDependencies = new HashSet<>(controller.getDownstreamDependencies());
for (ComponentController downstream : controller.getDownstreamDependencies()) {
downstreamDependencies.addAll(collectAllDownstreamDependencies(downstream));
}
return downstreamDependencies;
}
public Set<ComponentController> collectDirectDownstreamDependencies(Set<ComponentController> controllers) {
// assert no downstream is in this list
for (ComponentController controller : controllers) {
Set<ComponentController> downstreamDependencies = collectAllUpstreamDependencies(controller);
for (ComponentController downstream : downstreamDependencies) {
DBC.INTERIM.assertFalse("Downstream " + downstream.getName() + " is one of the input controllers!",
controllers.contains(downstream));
}
}
Set<ComponentController> directDownstreamDependencies = new HashSet<>();
// collect all direct downstream dependencies
for (ComponentController controller : controllers) {
Set<ComponentController> downstreamDependencies = controller.getDownstreamDependencies();
directDownstreamDependencies.addAll(downstreamDependencies);
}
// prune any controllers which are a downstream dependency of any of these dependencies
Set<ComponentController> tmp = new HashSet<>(directDownstreamDependencies);
for (ComponentController controller : tmp) {
Set<ComponentController> downstreamDeps = collectAllDownstreamDependencies(controller);
directDownstreamDependencies.removeAll(downstreamDeps);
}
return directDownstreamDependencies;
}
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 dependencyName : dependencies) {
ComponentController dependency = this.controllerMap.get(dependencyName);
if (dependency == null) {
String msg = "Component {0} is missing required dependency {1}"; //$NON-NLS-1$
msg = MessageFormat.format(msg, name, dependencyName);
throw new StrolchConfigurationException(msg);
}
controller.addUpstreamDependency(dependency);
}
}
logDependencies(1, findRootUpstreamComponents());
}
/**
* @param components
*/
private void logDependencies(int depth, Set<ComponentController> components) {
if (depth == 1) {
logger.info("Dependency tree:"); //$NON-NLS-1$
}
String inset = StringHelper.normalizeLength("", depth * 2, false, ' '); //$NON-NLS-1$
for (ComponentController controller : components) {
logger.info(inset + controller.getComponent().getName() + ": "
+ controller.getComponent().getClass().getName());
logDependencies(depth + 1, controller.getDownstreamDependencies());
}
}
}