diff --git a/bundles/org.palladiosimulator.retriever.extraction.discoverers/plugin.xml b/bundles/org.palladiosimulator.retriever.extraction.discoverers/plugin.xml index 71c3e256..1df443ee 100644 --- a/bundles/org.palladiosimulator.retriever.extraction.discoverers/plugin.xml +++ b/bundles/org.palladiosimulator.retriever.extraction.discoverers/plugin.xml @@ -57,5 +57,13 @@ class="org.palladiosimulator.retriever.extraction.discoverers.EcmaScriptDiscoverer"> + + + + diff --git a/bundles/org.palladiosimulator.retriever.extraction.discoverers/src/org/palladiosimulator/retriever/extraction/discoverers/DockerfileDiscoverer.java b/bundles/org.palladiosimulator.retriever.extraction.discoverers/src/org/palladiosimulator/retriever/extraction/discoverers/DockerfileDiscoverer.java new file mode 100644 index 00000000..6ed08c3d --- /dev/null +++ b/bundles/org.palladiosimulator.retriever.extraction.discoverers/src/org/palladiosimulator/retriever/extraction/discoverers/DockerfileDiscoverer.java @@ -0,0 +1,69 @@ +package org.palladiosimulator.retriever.extraction.discoverers; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.CommonPlugin; +import org.palladiosimulator.retriever.services.Discoverer; +import org.palladiosimulator.retriever.services.RetrieverConfiguration; +import org.palladiosimulator.retriever.services.blackboard.RetrieverBlackboard; + +import de.uka.ipd.sdq.workflow.jobs.AbstractBlackboardInteractingJob; +import de.uka.ipd.sdq.workflow.jobs.CleanupFailedException; +import de.uka.ipd.sdq.workflow.jobs.IBlackboardInteractingJob; +import de.uka.ipd.sdq.workflow.jobs.JobFailedException; +import de.uka.ipd.sdq.workflow.jobs.UserCanceledException; + +public class DockerfileDiscoverer implements Discoverer { + + private static final String DISCOVERER_ID = "org.palladiosimulator.retriever.extraction.discoverers.docker"; + + @Override + public IBlackboardInteractingJob create(final RetrieverConfiguration configuration, + final RetrieverBlackboard blackboard) { + return new AbstractBlackboardInteractingJob<>() { + + @Override + public void cleanup(final IProgressMonitor monitor) throws CleanupFailedException { + } + + @Override + public void execute(final IProgressMonitor monitor) throws JobFailedException, UserCanceledException { + final Path root = Paths.get(CommonPlugin.asLocalURI(configuration.getInputFolder()) + .devicePath()); + this.setBlackboard(Objects.requireNonNull(blackboard)); + final Map dockerfiles = new HashMap<>(); + Discoverer.find(root, "Dockerfile", this.logger) + .forEach(p -> dockerfiles.put(p, null)); + this.getBlackboard() + .putDiscoveredFiles(DISCOVERER_ID, dockerfiles); + } + + @Override + public String getName() { + return "Dockerfile Discoverer Job"; + } + }; + } + + @Override + public Set getConfigurationKeys() { + return Collections.emptySet(); + } + + @Override + public String getID() { + return DISCOVERER_ID; + } + + @Override + public String getName() { + return "Dockerfile Discoverer"; + } +} diff --git a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/Component.java b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/Component.java index a45035f8..e44e390a 100644 --- a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/Component.java +++ b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/Component.java @@ -15,12 +15,14 @@ public class Component { private final CompUnitOrName compUnitOrName; private final Requirements requirements; private final Provisions provisions; + private final Optional separatingIdentifier; - public Component(final CompUnitOrName compUnitOrName, final Requirements requirements, - final Provisions provisions) { + public Component(final CompUnitOrName compUnitOrName, final Requirements requirements, final Provisions provisions, + final Optional separatingIdentifier) { this.compUnitOrName = compUnitOrName; this.requirements = requirements; this.provisions = provisions; + this.separatingIdentifier = separatingIdentifier; } public Requirements requirements() { @@ -43,6 +45,10 @@ public CompUnitOrName identifier() { return this.compUnitOrName; } + public Optional separatingIdentifier() { + return this.separatingIdentifier; + } + @Override public int hashCode() { return Objects.hash(this.compUnitOrName, this.provisions, this.requirements); diff --git a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/ComponentBuilder.java b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/ComponentBuilder.java index 41fa93d9..98d3f4d7 100644 --- a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/ComponentBuilder.java +++ b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/ComponentBuilder.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.Objects; +import java.util.Optional; /** * Used to build {@code Component}s. @@ -13,11 +14,13 @@ public class ComponentBuilder { private final CompUnitOrName compUnitOrName; private final RequirementsBuilder requirements; private final ProvisionsBuilder provisions; + private Optional separatingIdentifier; public ComponentBuilder(final CompUnitOrName compUnitOrName) { this.compUnitOrName = compUnitOrName; this.requirements = new RequirementsBuilder(); this.provisions = new ProvisionsBuilder(); + this.separatingIdentifier = Optional.empty(); } public CompUnitOrName identifier() { @@ -32,10 +35,14 @@ public ProvisionsBuilder provisions() { return this.provisions; } + public void setSeparatingIdentifier(final String separatingIdentifier) { + this.separatingIdentifier = Optional.of(separatingIdentifier); + } + public Component create(final Collection allDependencies, final Collection visibleProvisions) { return new Component(this.compUnitOrName, this.requirements.create(allDependencies, visibleProvisions), - this.provisions.create(allDependencies)); + this.provisions.create(allDependencies), this.separatingIdentifier); } @Override diff --git a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/CompositeBuilder.java b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/CompositeBuilder.java index cf14eea0..fcc47ceb 100644 --- a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/CompositeBuilder.java +++ b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/CompositeBuilder.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; @@ -215,6 +216,8 @@ private static List findRequiringComponents(final Set !compositeProvisions.containsEntire(x)) .forEach(provisions::add); + final Optional separatingIdentifier = providingComponent.separatingIdentifier(); + final List traversedOperations = new ArrayList<>(); while (!provisions.isEmpty()) { final OperationInterface provision = provisions.pop(); @@ -222,6 +225,10 @@ private static List findRequiringComponents(final Set x.requirements() .containsPartOf(provision)) .filter(x -> !providingComponent.equals(x)) + .filter(x -> x.separatingIdentifier() + .isEmpty() || separatingIdentifier.isEmpty() + || x.separatingIdentifier() + .equals(separatingIdentifier)) .collect(Collectors.toSet()); // Skip this provision if no unit requires it. @@ -250,6 +257,8 @@ private static List findProvidingComponents(final Set !compositeProvisions.containsEntire(x)) .forEach(requirements::add); + final Optional separatingIdentifier = requiringComponent.separatingIdentifier(); + final List traversedOperations = new ArrayList<>(); while (!requirements.isEmpty()) { final OperationInterface requirement = requirements.pop(); @@ -257,6 +266,10 @@ private static List findProvidingComponents(final Set x.provisions() .containsPartOf(requirement)) .filter(x -> !requiringComponent.equals(x)) + .filter(x -> x.separatingIdentifier() + .isEmpty() || separatingIdentifier.isEmpty() + || x.separatingIdentifier() + .equals(separatingIdentifier)) .collect(Collectors.toSet()); // Skip this requirement if no unit provides it. diff --git a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/PCMDetectionResult.java b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/PCMDetectionResult.java index 0cd38553..208c3f8c 100644 --- a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/PCMDetectionResult.java +++ b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/commonalities/PCMDetectionResult.java @@ -4,6 +4,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -93,19 +94,16 @@ private static Set createCompositeComponents(final Set fre final Set redundantComposites = new HashSet<>(); final Set remainingComposites = new HashSet<>(); - for (int i = 0; i < allComposites.size(); ++i) { - final Composite subject = allComposites.get(i); - final long subsetCount = allComposites.subList(i + 1, allComposites.size()) - .stream() + for (final Composite subject : allComposites) { + final Optional other = allComposites.stream() + .filter(x -> !subject.equals(x)) .filter(x -> !redundantComposites.contains(x)) - .filter(x -> subject.isSubsetOf(x) || x.isSubsetOf(subject)) - .count(); + .filter(x -> subject.isSubsetOf(x)) + .findFirst(); - // Any composite is guaranteed to be the subset of at least one composite in the - // list, namely itself. If it is the subset of any composites other than itself, it is - // redundant. - if (subsetCount > 0) { + if (other.isPresent()) { redundantComposites.add(subject); + continue; } else { // TODO: Is there any merging necessary, like adapting the redundant composite's // requirements to its peer? @@ -113,7 +111,46 @@ private static Set createCompositeComponents(final Set fre } } + // Remove composite components contained in multiple other composites, according to a + // conservative heuristic. + // TODO: A comprehensive solution would require e.g. graph traversal and tie-breaking in + // cycles. + + final Set collectivelyContainedComposites = new HashSet<>(); + for (final Composite subject : remainingComposites) { + final Set allOtherParts = remainingComposites.stream() + .filter(x -> !subject.equals(x)) + .map(Composite::parts) + .flatMap(Set::stream) + .collect(Collectors.toSet()); + final boolean isContainedInOthers = subject.parts() + .stream() + .allMatch(allOtherParts::contains); + if (isContainedInOthers) { + collectivelyContainedComposites.add(subject); + } + } + + final Set actuallyContainedComposites = new HashSet<>(); + for (final Composite subject : collectivelyContainedComposites) { + final Set allOtherParts = remainingComposites.stream() + .filter(x -> !subject.equals(x)) + .filter(x -> !collectivelyContainedComposites.contains(x)) + .map(Composite::parts) + .flatMap(Set::stream) + .collect(Collectors.toSet()); + final boolean isContainedInOthers = subject.parts() + .stream() + .allMatch(allOtherParts::contains); + if (isContainedInOthers) { + actuallyContainedComposites.add(subject); + } + } + + remainingComposites.removeAll(actuallyContainedComposites); + return remainingComposites; + } private static Set collectVisibleProvisions(final Set components, diff --git a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/engine/PCMDetector.java b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/engine/PCMDetector.java index 1002d9b3..50b71bce 100644 --- a/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/engine/PCMDetector.java +++ b/bundles/org.palladiosimulator.retriever.extraction/src/org/palladiosimulator/retriever/extraction/engine/PCMDetector.java @@ -2,6 +2,8 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -14,6 +16,7 @@ import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclaration; @@ -44,6 +47,9 @@ public class PCMDetector { private final Map composites = new ConcurrentHashMap<>(); private final ProvisionsBuilder compositeProvisions = new ProvisionsBuilder(); private final RequirementsBuilder compositeRequirements = new RequirementsBuilder(); + private final Map> weakComponents = new ConcurrentHashMap<>(); + private final Map separatingIdentifiers = new ConcurrentHashMap<>(); + private final Set blacklist = new HashSet<>(); private static String getFullUnitName(final CompUnitOrName unit) { // TODO this is potentially problematic, maybe restructure @@ -73,14 +79,13 @@ private static String getFullUnitName(final CompUnitOrName unit) { public void detectComponent(final CompUnitOrName unit) { if (!unit.isUnit()) { - this.components.put(unit, new ComponentBuilder(unit)); + tryAddComponent(unit); return; } for (final Object type : unit.compilationUnit() .get() .types()) { - if (type instanceof TypeDeclaration) { - this.components.put(unit, new ComponentBuilder(unit)); + if (type instanceof TypeDeclaration && tryAddComponent(unit)) { final ITypeBinding binding = ((TypeDeclaration) type).resolveBinding(); this.detectProvidedInterfaceWeakly(unit, binding); } @@ -93,8 +98,8 @@ public void detectRequiredInterface(final CompUnitOrName unit, final InterfaceNa private void detectRequiredInterface(final CompUnitOrName unit, final InterfaceName interfaceName, final boolean compositeRequired) { - if (this.components.get(unit) == null) { - this.components.put(unit, new ComponentBuilder(unit)); + if (!tryAddComponent(unit)) { + return; } final EntireInterface iface = new EntireInterface(interfaceName); this.detectRequiredInterface(unit, compositeRequired, false, iface); @@ -110,8 +115,8 @@ public void detectRequiredInterfaceWeakly(final CompUnitOrName unit, final Field private void detectRequiredInterface(final CompUnitOrName unit, final FieldDeclaration field, final boolean compositeRequired, final boolean detectWeakly) { - if (this.components.get(unit) == null) { - this.components.put(unit, new ComponentBuilder(unit)); + if (!tryAddComponent(unit)) { + return; } @SuppressWarnings("unchecked") final List ifaces = ((List) field.fragments()).stream() @@ -123,9 +128,23 @@ private void detectRequiredInterface(final CompUnitOrName unit, final FieldDecla this.detectRequired(unit, compositeRequired, detectWeakly, ifaces); } + public void detectCompositeRequiredInterfaceWeakly(final CompUnitOrName unit, final MethodInvocation invocation) { + final IMethodBinding method = invocation.resolveMethodBinding(); + if (method == null) { + return; + } + final ITypeBinding type = method.getDeclaringClass(); + if (!tryAddComponent(unit)) { + return; + } + final EntireInterface iface = new EntireInterface(type, + new JavaInterfaceName(NameConverter.toPCMIdentifier(type))); + this.detectRequiredInterface(unit, true, true, iface); + } + public void detectRequiredInterface(final CompUnitOrName unit, final SingleVariableDeclaration parameter) { - if (this.components.get(unit) == null) { - this.components.put(unit, new ComponentBuilder(unit)); + if (!tryAddComponent(unit)) { + return; } final IVariableBinding parameterBinding = parameter.resolveBinding(); if (parameterBinding == null) { @@ -224,8 +243,8 @@ public void detectProvidedOperation(final CompUnitOrName unit, final IMethodBind private void detectProvidedOperation(final CompUnitOrName unit, final IMethodBinding method, final OperationName name, final boolean compositeProvided, final boolean detectWeakly) { - if (this.components.get(unit) == null) { - this.components.put(unit, new ComponentBuilder(unit)); + if (!tryAddComponent(unit)) { + return; } final OperationInterface provision = new Operation(method, name); this.detectProvidedInterface(unit, provision, compositeProvided, detectWeakly); @@ -259,15 +278,63 @@ private void detectProvidedInterface(final CompUnitOrName unit, final OperationI } } - public void detectPartOfComposite(final CompUnitOrName unit, final String compositeName) { + public void detectSeparatingIdentifier(final CompUnitOrName unit, final String separatingIdentifier) { if (this.components.get(unit) == null) { - this.components.put(unit, new ComponentBuilder(unit)); + this.separatingIdentifiers.put(unit, separatingIdentifier); + } else { + this.components.get(unit) + .setSeparatingIdentifier(separatingIdentifier); + } + } + + public void detectPartOfComposite(final CompUnitOrName unit, final String compositeName) { + if (!tryAddComponent(unit)) { + return; } if (!this.composites.containsKey(compositeName)) { this.composites.put(compositeName, new CompositeBuilder(compositeName)); } this.composites.get(compositeName) .addPart(this.components.get(unit)); + + // Setting the separating identifier is sufficient if the component is part of a composite + if (separatingIdentifiers.containsKey(unit)) { + this.components.get(unit) + .setSeparatingIdentifier(separatingIdentifiers.get(unit)); + } + + // Realize weak composite components that this component is part of + if (this.weakComponents.containsKey(unit)) { + for (String weakCompositeName : weakComponents.get(unit)) { + if (!this.composites.containsKey(weakCompositeName)) { + this.composites.put(weakCompositeName, new CompositeBuilder(weakCompositeName)); + } + this.composites.get(weakCompositeName) + .addPart(this.components.get(unit)); + } + } + } + + // Weak composite components only get created if at least one of their components is part of + // another composite. This allows for e.g. composite components based on build files that + // do not require direct dependencies between their parts. + public void detectPartOfWeakComposite(CompUnitOrName unit, String compositeName) { + if (!this.weakComponents.containsKey(unit)) { + this.weakComponents.put(unit, new ArrayList<>()); + } + this.weakComponents.get(unit) + .add(compositeName); + + final boolean isPartOfStrongComposite = this.composites.values() + .stream() + .anyMatch(x -> x.hasPart(unit)); + if (isPartOfStrongComposite) { + if (!this.composites.containsKey(compositeName)) { + this.composites.put(compositeName, new CompositeBuilder(compositeName)); + } + this.composites.get(compositeName) + .addPart(this.components.get(unit)); + } } public void detectCompositeRequiredInterface(final CompUnitOrName unit, final InterfaceName interfaceName) { @@ -302,4 +369,40 @@ public String toString() { }); return sb.toString(); } + + public boolean isPartOfComposite(CompUnitOrName name) { + for (CompositeBuilder composite : this.composites.values()) { + if (composite.hasPart(name)) { + return true; + } + } + return false; + } + + public void addToBlacklist(String string) { + this.blacklist.add(string); + + // Clean up already added but now blacklisted components + List toDelete = new LinkedList<>(); + for (CompUnitOrName unit : this.components.keySet()) { + if (unit.name() + .equals(string)) { + toDelete.add(unit); + } + } + for (CompUnitOrName unit : toDelete) { + this.components.remove(unit); + } + } + + private boolean tryAddComponent(CompUnitOrName unit) { + if (this.components.get(unit) != null) { + return true; + } + if (this.blacklist.contains(unit.name())) { + return false; + } + this.components.put(unit, new ComponentBuilder(unit)); + return true; + } }