From 602f60b900354f685b9d1085d08dd26258b68c4d Mon Sep 17 00:00:00 2001 From: Florian Bossert Date: Mon, 20 Nov 2023 16:43:28 +0100 Subject: [PATCH] Ensure services are executed after their requirements --- .../rules/engine/ServiceConfiguration.java | 53 ++++++---- .../rules/workflow/RuleEngineJob.java | 96 ++++++++++++------- 2 files changed, 96 insertions(+), 53 deletions(-) diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/engine/ServiceConfiguration.java b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/engine/ServiceConfiguration.java index 92e25c43..0dde5c03 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/engine/ServiceConfiguration.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.engine/src/org/palladiosimulator/somox/analyzer/rules/engine/ServiceConfiguration.java @@ -1,8 +1,10 @@ package org.palladiosimulator.somox.analyzer.rules.engine; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -106,30 +108,49 @@ public Set getSelected() { return Collections.unmodifiableSet(selectedServices); } - public List getSelectedOrdered() { - List orderedServices = new LinkedList<>(); - Queue selectedServices = new ArrayDeque<>(getSelected()); - List dependingServices = new LinkedList<>(); - while (!selectedServices.isEmpty()) { - T candidate = selectedServices.poll(); - if (isNotDependingOnAny(candidate, selectedServices) && isNotDependingOnAny(candidate, dependingServices)) { - orderedServices.add(candidate); - - selectedServices.addAll(dependingServices); - dependingServices.clear(); + public Queue> getExecutionOrder() { + List> executionOrder = new ArrayList<>(); + Queue remainingServices = new ArrayDeque<>(getSelected()); + List requiringServices = new LinkedList<>(); + while (!remainingServices.isEmpty()) { + T candidate = remainingServices.poll(); + if (isRequiringAny(candidate, remainingServices) || isRequiringAny(candidate, requiringServices)) { + requiringServices.add(candidate); } else { - dependingServices.add(candidate); + addAfterRequirements(candidate, executionOrder); + + remainingServices.addAll(requiringServices); + requiringServices.clear(); } } - if (!dependingServices.isEmpty()) { + if (!requiringServices.isEmpty()) { throw new IllegalStateException("Dependency cycle in services, no possible execution order."); } - return orderedServices; + return new ArrayDeque<>(executionOrder); } - private boolean isNotDependingOnAny(T service, Collection services) { + private void addAfterRequirements(T service, List> executionOrder) { + if (executionOrder.isEmpty() || isRequiringAny(service, executionOrder.get(executionOrder.size() - 1))) { + Collection newStep = new ArrayList<>(); + newStep.add(service); + executionOrder.add(newStep); + return; + } + + Collection earliestCandidate = executionOrder.get(executionOrder.size() - 1); + for (int i = executionOrder.size() - 2; i >= 0; i--) { + Collection currentStep = executionOrder.get(i); + if (isRequiringAny(service, currentStep)) { + break; + } + earliestCandidate = currentStep; + } + earliestCandidate.add(service); + } + + private boolean isRequiringAny(T service, Collection services) { Set dependencies = service.getRequiredServices(); - return !services.stream().map(Service::getID).anyMatch(dependencies::contains); + return services.stream().map(Service::getID).anyMatch(dependencies::contains); } public Collection getAvailable() { diff --git a/bundles/org.palladiosimulator.somox.analyzer.rules.main/src/org/palladiosimulator/somox/analyzer/rules/workflow/RuleEngineJob.java b/bundles/org.palladiosimulator.somox.analyzer.rules.main/src/org/palladiosimulator/somox/analyzer/rules/workflow/RuleEngineJob.java index 3d09137a..08c8687e 100644 --- a/bundles/org.palladiosimulator.somox.analyzer.rules.main/src/org/palladiosimulator/somox/analyzer/rules/workflow/RuleEngineJob.java +++ b/bundles/org.palladiosimulator.somox.analyzer.rules.main/src/org/palladiosimulator/somox/analyzer/rules/workflow/RuleEngineJob.java @@ -1,5 +1,9 @@ package org.palladiosimulator.somox.analyzer.rules.workflow; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import org.palladiosimulator.somox.analyzer.rules.blackboard.RuleEngineBlackboard; import org.palladiosimulator.somox.analyzer.rules.configuration.RuleEngineBlackboardKeys; import org.palladiosimulator.somox.analyzer.rules.engine.Rule; @@ -18,15 +22,15 @@ public class RuleEngineJob extends AbstractExtendableJob { public RuleEngineJob(RuleEngineConfiguration configuration) { super.setBlackboard(new RuleEngineBlackboard()); - super.add(createDiscoverersJob(configuration)); + super.addAll(createDiscovererJobs(configuration)); - super.add(createRulesJob(configuration)); + super.addAll(createRuleJobs(configuration)); - super.add(createBuildRulesJob(configuration)); + super.addAll(createBuildRulesJob(configuration)); super.add(new RuleEngineBlackboardInteractingJob(configuration, getBlackboard())); - super.add(createAnalystsJob(configuration)); + super.addAll(createAnalystJobs(configuration)); // Generate service effect specifications based on AST nodes and merge them into repository super.add( @@ -57,57 +61,75 @@ public RuleEngineJob(RuleEngineConfiguration configuration) { super.add(new PlantUmlJob(configuration, getBlackboard())); } - private ParallelJob createRulesJob(RuleEngineConfiguration configuration) { - ParallelJob parentJob = new ParallelJob(); - - for (Rule rule : configuration.getConfig(Rule.class) - .getSelectedOrdered()) { - // Assume only build rules depend on build rules. - if (!rule.isBuildRule()) { + private List createRuleJobs(RuleEngineConfiguration configuration) { + List jobs = new ArrayList<>(); + + for (Collection step : configuration.getConfig(Rule.class).getExecutionOrder()) { + ParallelJob parentJob = new ParallelJob(); + for (Rule rule : step) { + // Assume only build rules depend on build rules. + if (rule.isBuildRule()) { + continue; + } IBlackboardInteractingJob ruleJob = rule.create(configuration, myBlackboard); parentJob.add(ruleJob); logger.info("Adding rule job \"" + ruleJob.getName() + "\""); } + jobs.add(parentJob); } - return parentJob; + return jobs; } - private ParallelJob createBuildRulesJob(RuleEngineConfiguration configuration) { - ParallelJob parentJob = new ParallelJob(); - - for (Rule rule : configuration.getConfig(Rule.class) - .getSelectedOrdered()) { - if (rule.isBuildRule()) { + private List createBuildRulesJob(RuleEngineConfiguration configuration) { + List jobs = new ArrayList<>(); + + for (Collection step : configuration.getConfig(Rule.class).getExecutionOrder()) { + ParallelJob parentJob = new ParallelJob(); + for (Rule rule : step) { + // Assume only build rules depend on build rules. + if (!rule.isBuildRule()) { + continue; + } IBlackboardInteractingJob ruleJob = rule.create(configuration, myBlackboard); parentJob.add(ruleJob); logger.info("Adding build rule job \"" + ruleJob.getName() + "\""); } + jobs.add(parentJob); } - - return parentJob; + + return jobs; } - private ParallelJob createDiscoverersJob(RuleEngineConfiguration configuration) { - ParallelJob parentJob = new ParallelJob(); - for (Discoverer discoverer : configuration.getConfig(Discoverer.class) - .getSelected()) { - IBlackboardInteractingJob discovererJob = discoverer.create(configuration, - myBlackboard); - parentJob.add(discovererJob); - logger.info("Adding discoverer job \"" + discovererJob.getName() + "\""); + private List createDiscovererJobs(RuleEngineConfiguration configuration) { + List jobs = new ArrayList<>(); + + for (Collection step : configuration.getConfig(Discoverer.class).getExecutionOrder()) { + ParallelJob parentJob = new ParallelJob(); + for (Discoverer discoverer : step) { + IBlackboardInteractingJob discovererJob = discoverer.create(configuration, myBlackboard); + parentJob.add(discovererJob); + logger.info("Adding discoverer job \"" + discovererJob.getName() + "\""); + } + jobs.add(parentJob); } - return parentJob; + + return jobs; } - private ParallelJob createAnalystsJob(RuleEngineConfiguration configuration) { - ParallelJob parentJob = new ParallelJob(); - for (Analyst analyst : configuration.getConfig(Analyst.class) - .getSelected()) { - IBlackboardInteractingJob analystJob = analyst.create(configuration, myBlackboard); - parentJob.add(analystJob); - logger.info("Adding analyst job \"" + analystJob.getName() + "\""); + private List createAnalystJobs(RuleEngineConfiguration configuration) { + List jobs = new ArrayList<>(); + + for (Collection step : configuration.getConfig(Analyst.class).getExecutionOrder()) { + ParallelJob parentJob = new ParallelJob(); + for (Analyst analyst : step) { + IBlackboardInteractingJob analystJob = analyst.create(configuration, myBlackboard); + parentJob.add(analystJob); + logger.info("Adding analyst job \"" + analystJob.getName() + "\""); + } + jobs.add(parentJob); } - return parentJob; + + return jobs; } }