diff options
Diffstat (limited to 'winery/org.eclipse.winery.topologymodeler/src/main/java')
24 files changed, 2865 insertions, 0 deletions
diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/WineryUtil.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/WineryUtil.java new file mode 100644 index 0000000..2dc7e27 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/WineryUtil.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation + *******************************************************************************/ +package org.eclipse.winery.topologymodeler; + +import java.util.List; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.eclipse.winery.common.interfaces.QNameWithName; + +public class WineryUtil { + + /** + * LocalName is the ID of the element, whereas Name is the speaking name + */ + public static class LocalNameNamePair implements Comparable<LocalNameNamePair> { + + String localName; + String name; + + + public LocalNameNamePair(String localName, String name) { + this.localName = localName; + this.name = name; + } + + public String getLocalName() { + return this.localName; + } + + public String getName() { + return this.name; + } + + /** + * Ordering according to name + */ + @Override + public int compareTo(LocalNameNamePair otherPair) { + return this.name.compareTo(otherPair.name); + } + + @Override + public int hashCode() { + return this.localName.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof LocalNameNamePair) { + return this.localName.equals(((LocalNameNamePair) o).getLocalName()); + } else { + return false; + } + } + } + + + public static SortedMap<String, SortedSet<LocalNameNamePair>> convertQNameWithNameListToNamespaceToLocalNameNamePairList(List<QNameWithName> list) { + if (list == null) { + throw new IllegalArgumentException("list may not be null"); + } + SortedMap<String, SortedSet<LocalNameNamePair>> res = new TreeMap<>(); + for (QNameWithName qnameWithName : list) { + SortedSet<LocalNameNamePair> localNameNamePairSet = res.get(qnameWithName.qname.getNamespaceURI()); + if (localNameNamePairSet == null) { + localNameNamePairSet = new TreeSet<>(); + res.put(qnameWithName.qname.getNamespaceURI(), localNameNamePairSet); + } + LocalNameNamePair pair = new LocalNameNamePair(qnameWithName.qname.getLocalPart(), qnameWithName.name); + localNameNamePairSet.add(pair); + } + return res; + } + +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/DeferredAnalyzer.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/DeferredAnalyzer.java new file mode 100644 index 0000000..c4db9bb --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/DeferredAnalyzer.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Constants; + +/** + * This class contains a method to analyze a TOSCA {@link TTopologyTemplate} for + * the occurrence of "Deferred"-{@link TRelationshipTemplate}s. + * + * A "Deferred"-{@link TRelationshipTemplate} serves as place holder for any number of Node or Relationship + * Templates. + */ +public class DeferredAnalyzer { + + /** + * Iterates over all {@link TRelationshipTemplate} and checks if its type is "deferred". + * + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return a list of found deferred {@link TRelationshipTemplate}s + */ + public static List<TRelationshipTemplate> analyzeDeferredRelations(TOSCAAnalyzer toscaAnalyzer) { + + List<TRelationshipTemplate> foundDeferredRelations = new ArrayList<TRelationshipTemplate>(); + + for (TRelationshipTemplate relationshipTemplate : toscaAnalyzer.getRelationshipTemplates()) { + if (relationshipTemplate.getType() != null && relationshipTemplate.getType().getLocalPart().equals(Constants.DEFERRED_QNAME.getLocalPart()) && + relationshipTemplate.getType().getNamespaceURI().equals(Constants.DEFERRED_QNAME.getNamespaceURI())) { + + // TODO: This step has to be done until the "Provisioning-API" + // is implemented. The Deferred RelationshipTemplate can only be + // completed if Requirements exist at the source template. + TNodeTemplate source = (TNodeTemplate) relationshipTemplate.getSourceElement().getRef(); + + if (source.getRequirements() != null) { + foundDeferredRelations.add(relationshipTemplate); + } + } + } + return foundDeferredRelations; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/PlaceHolderAnalyzer.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/PlaceHolderAnalyzer.java new file mode 100644 index 0000000..ddf0e91 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/PlaceHolderAnalyzer.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Constants; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Utils; + +/** + * This class analyzes the occurrence of place holders in a topology and writes them to an {@link ArrayList}. + */ +public class PlaceHolderAnalyzer { + + /** + * This method searches {@link TNodeTemplate}s that are derived from the abstract "PlaceHolder" type and adds them to a list. + * + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return the found place holders of the topology as a list. + */ + public static List<TNodeTemplate> analyzePlaceHolders(TOSCAAnalyzer toscaAnalyzer) { + + List<TNodeTemplate> foundPlaceHolders = new ArrayList<TNodeTemplate>(); + + // Check the type of the NodeTemplates, write them to a list if the type is derived from the common place holder type. + for (TNodeTemplate nodeTemplate : toscaAnalyzer.getNodeTemplates()) { + + TNodeType nodeType = Utils.getNodeTypeForId(toscaAnalyzer.getNodeTypes(), nodeTemplate.getType()); + + if (nodeType != null && nodeType.getDerivedFrom() != null && nodeType.getDerivedFrom().getTypeRef().getLocalPart().equals(Constants.PLACE_HOLDER_QNAME.getLocalPart()) && + nodeType.getDerivedFrom().getTypeRef().getNamespaceURI().equals(Constants.PLACE_HOLDER_QNAME.getNamespaceURI())) { + foundPlaceHolders.add(nodeTemplate); + } + } + return foundPlaceHolders; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/RequirementAnalyzer.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/RequirementAnalyzer.java new file mode 100644 index 0000000..71f3961 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/RequirementAnalyzer.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.winery.model.tosca.TCapability; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TRequirementDefinition; +import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Utils; + +/** + * This class analyzes the occurrence of TOSCA {@link TRequirement}s in a {@link TTopologyTemplate} and checks whether they + * are already fulfilled or not. + */ +public class RequirementAnalyzer { + + /** + * This method checks if {@link TNodeTemplate}s contain {@link TRequirement}s and adds them to a {@link Map}. + * + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return a map containing {@link TNodeTemplate}s and their {@link TRequirement}s + */ + public static Map<TRequirement, TNodeTemplate> analyzeRequirements(TOSCAAnalyzer toscaAnalyzer) { + + // map containing entries for a Requirement and its corresponding NodeTemplate + Map<TRequirement, TNodeTemplate> unfulfilledRequirements = new HashMap<TRequirement, TNodeTemplate>(); + + for (TNodeTemplate nodeTemplate : toscaAnalyzer.getNodeTemplates()) { + + List<TRequirement> requirements = new ArrayList<>(); + + TNodeType nodeType = Utils.getNodeTypeForId(toscaAnalyzer.getNodeTypes(), nodeTemplate.getType()); + + if (nodeType.getRequirementDefinitions() != null && !nodeType.getRequirementDefinitions().getRequirementDefinition().isEmpty()) { + + List<TRequirementDefinition> requirementDefinitions = nodeType.getRequirementDefinitions().getRequirementDefinition(); + + // check the requirements of the type of the used NodeTemplate + for (TRequirementDefinition requirementDefinition: requirementDefinitions) { + TRequirement requirement = new TRequirement(); + requirement.setType(requirementDefinition.getRequirementType()); + requirement.setName(requirementDefinition.getName()); + requirement.setId(Utils.createRandomID()); + } + } + + if (nodeTemplate.getRequirements() != null && !nodeTemplate.getRequirements().getRequirement().isEmpty()) { + requirements.addAll(nodeTemplate.getRequirements().getRequirement()); + } + + if (!requirements.isEmpty()) { + // list containing the RelationshipTemplates connecting to the NodeTemplate + List<TRelationshipTemplate> connectors = new ArrayList<TRelationshipTemplate>(); + + // add the connected RelationshipTemplates + for (TRelationshipTemplate connector : toscaAnalyzer.getRelationshipTemplates()) { + if (connector.getSourceElement().getRef().equals(nodeTemplate)) { + connectors.add(connector); + } + } + + // add requirements of unconnected NodeTemplates to the map because they can't be fulfilled + if (connectors.size() == 0) { + for (TRequirement requirement : requirements) { + unfulfilledRequirements.put(requirement, nodeTemplate); + } + } else { + boolean fulfilled = false; + + // check if one of the connected NodeTemplates already fulfill the requirement + for (TRequirement requirement : requirements) { + for (TRelationshipTemplate connector : connectors) { + TNodeTemplate connectedNodeTemplate = (TNodeTemplate) connector.getTargetElement().getRef(); + if (connectedNodeTemplate.getCapabilities() != null) { + for (TCapability capa : connectedNodeTemplate.getCapabilities().getCapability()) { + for (TRequirementType reqType : toscaAnalyzer.getRequirementTypes()) { + if (requirement.getType().getLocalPart().equals(reqType.getName())) { + if (reqType.getRequiredCapabilityType().getLocalPart().equals(capa.getType().getLocalPart()) + && reqType.getRequiredCapabilityType().getNamespaceURI().equals(capa.getType().getNamespaceURI())) { + fulfilled = true; + } + } + } + } + } + } + if (!fulfilled) { + unfulfilledRequirements.put(requirement, nodeTemplate); + } + } + } + } + } + return unfulfilledRequirements; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/TOSCAAnalyzer.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/TOSCAAnalyzer.java new file mode 100644 index 0000000..45131a7 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/TOSCAAnalyzer.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; + +/** + * This class contains several methods to analyze the content of a TOSCA {@link TTopologyTemplate} and to fill a data model + * with the analyzed information. This class serves the access to all types and templates of a topology. + */ +public class TOSCAAnalyzer { + + // lists containing the elements of a topology + List<TNodeTemplate> nodeTemplates = new ArrayList<TNodeTemplate>(); + List<TRelationshipTemplate> relationshipTemplates = new ArrayList<TRelationshipTemplate>(); + List<TRequirement> requirements = new ArrayList<TRequirement>(); + + List<TNodeType> nodeTypes; + List<TRelationshipType> relationshipTypes; + List<TRequirementType> requirementTypes; + + /** + * This method analyzes the TOSCA {@link TTopologyTemplate} for {@link TNodeTemplate}s, {@link TRelationshipTemplate}s + * and existing {@link TRequirement}s and adds them to a list. + * + * @param topology + * the TOSCA {@link TTopologyTemplate} + */ + public void analyzeTOSCATopology(TTopologyTemplate topology) { + + // fill the data model with content of the topology + List<TEntityTemplate> templateNodes = topology.getNodeTemplateOrRelationshipTemplate(); + + for (TEntityTemplate entityTemplate : templateNodes) { + if (entityTemplate instanceof TNodeTemplate) { + // add the node templates and their requirements to the data model + nodeTemplates.add((TNodeTemplate) entityTemplate); + if (((TNodeTemplate) entityTemplate).getRequirements() != null) { + requirements.addAll(((TNodeTemplate) entityTemplate).getRequirements().getRequirement()); + } + } else if (entityTemplate instanceof TRelationshipTemplate) { + // add RelationshipTemplates + relationshipTemplates.add((TRelationshipTemplate) entityTemplate); + } + } + } + + /** + * Setter for the types received from the Winery repository. + * + * @param nodeTypeXMLStrings + * a list of {@link TNodeType}s from the Winery repository + * @param relationshipTypeXMLStrings + * a list of {@link TRelationshipType}s from the Winery repository + * @param requirementTypeList + * a list of {@link TRequirementType}s from the Winery repository + */ + public void setTypes(List<TNodeType> nodeTypes, List<TRelationshipType> relationshipTypes, List<TRequirementType> requirementTypes) { + this.nodeTypes = nodeTypes; + this.relationshipTypes = relationshipTypes; + this.requirementTypes = requirementTypes; + } + + /** + * Returns the {@link TNodeTemplate}s of the topology. + * + * @return the {@link TNodeTemplate}s as a list + */ + public List<TNodeTemplate> getNodeTemplates() { + return nodeTemplates; + } + + /** + * Returns the {@link TRelationshipTemplate}s of the topology. + * + * @return the {@link TRelationshipTemplate}s as a list + */ + public List<TRelationshipTemplate> getRelationshipTemplates() { + return relationshipTemplates; + } + + /** + * Returns the {@link TRequirement}s of the topology. + * + * @return the {@link TRequirement}s as a list + */ + public List<TRequirement> getRequirements() { + return requirements; + } + + /** + * Returns the {@link TRelationshipType}s of the topology. + * + * @return the {@link TRelationshipType}s as a list + */ + public List<TRelationshipType> getRelationshipTypes() { + return relationshipTypes; + } + + /** + * Returns the {@link TNodeType}s of the topology. + * + * @return the {@link TNodeType}s as a list + */ + public List<TNodeType> getNodeTypes() { + return nodeTypes; + } + + /** + * Returns the {@link TRequirementType}s of the topology. + * + * @return the {@link TRequirementType}s as a list + */ + public List<TRequirementType> getRequirementTypes() { + return requirementTypes; + } + + /** + * Clears all the templates from the data model before the analysis of a topology is restarted. + */ + public void clear() { + nodeTemplates.clear(); + relationshipTemplates.clear(); + requirements.clear(); + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/package-info.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/package-info.java new file mode 100644 index 0000000..5333b5e --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/analyzer/package-info.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +/** + * This package contains classes and methods to analyze a TOSCA {@link org.opentosca.model.tosca.TTopologyTemplate} for its content. + */ +package org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer; + diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/Constants.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/Constants.java new file mode 100644 index 0000000..e45c11a --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/Constants.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; + +import javax.xml.namespace.QName; + +import org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.CompletionInterface; + +/** + * This class contains several constants used by the completion add-on. + */ +public class Constants { + + /** + * Constant for the QName of the "deferred" type. + */ + public static final QName DEFERRED_QNAME = new QName("http://www.opentosca.org", "deferred"); + + /** + * Constant for the QName of the "PlaceHolder" type. + */ + public static final QName PLACE_HOLDER_QNAME = new QName("http://www.opentosca.org", "PlaceHolder"); + + /** + * Contains possible types of expandable place holders. + */ + public enum PlaceHolders { + WEBSERVER, DATABASE, OPERATINGSYSTEM, CLOUDPROVIDER; + + /** + * Overwritten toString() method to return formatted strings. + */ + public String toString() { + + switch (this) { + case WEBSERVER: + return "Webserver"; + case DATABASE: + return "Database"; + case OPERATINGSYSTEM: + return "OperatingSystem"; + case CLOUDPROVIDER: + return "CloudProvider"; + default: + return null; + } + } + } + + /** + * The messages returned by the {@link CompletionInterface} + */ + public enum CompletionMessages { + TOPOLOGYCOMPLETE, USERINTERACTION, STEPBYSTEP, SUCCESS, USERTOPOLOGYSELECTION, FAILURE; + + /** + * Overwritten toString() method to return formatted strings. + */ + public String toString() { + + switch (this) { + case TOPOLOGYCOMPLETE: + return "topologyComplete"; + case USERINTERACTION: + return "userInteraction"; + case STEPBYSTEP: + return "stepByStep"; + case SUCCESS: + return "success"; + case USERTOPOLOGYSELECTION: + return "userTopologySelection"; + case FAILURE: + return "failure"; + default: + return null; + } + } + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/JAXBHelper.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/JAXBHelper.java new file mode 100644 index 0000000..84a3e28 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/JAXBHelper.java @@ -0,0 +1,374 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import org.eclipse.winery.common.ModelUtilities; +import org.eclipse.winery.common.Util; +import org.eclipse.winery.model.tosca.Definitions; +import org.eclipse.winery.model.tosca.TDefinitions; +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipTemplate.SourceElement; +import org.eclipse.winery.model.tosca.TServiceTemplate; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * This class contains methods for marshalling and unmarshalling a topology XML string via JAXB. + * + */ +public class JAXBHelper { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JAXBHelper.class.getName()); + + /** + * This constant is used in the buildXML method which add coordinates to Node Templates so they + * are arranged properly in the Winery topology modeler. + * + * The x coordinate is constant because it is assumed that a stack of NodeTemplates is displayed. + */ + private static final String NODETEMPLATE_X_COORDINATE = "500"; + + /** + * This method creates an JAXB Unmarshaller used by the methods contained in this class. + * + * @return the JAXB unmarshaller object + * + * @throws JAXBException + * this exception can occur when the JAXBContext is created + */ + private static Unmarshaller createUnmarshaller() throws JAXBException { + // initiate JaxB context + JAXBContext context; + context = JAXBContext.newInstance(Definitions.class); + + return context.createUnmarshaller(); + } + + /** + * This method returns a {@link TTopologyTemplate} given as XML string as JaxBObject. + * + * @param xmlString + * the {@link TTopologyTemplate} to be unmarshalled + * + * @return the unmarshalled {@link TTopologyTemplate} + */ + public static TTopologyTemplate getTopologyAsJaxBObject(String xmlString) { + try { + + logger.info("Getting Definitions Document..."); + + StringReader reader = new StringReader(xmlString); + + // unmarshall the XML string + Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); + TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); + + logger.info("Unmarshalling successful! "); + + return serviceTemplate.getTopologyTemplate(); + + } catch (JAXBException e) { + logger.error(e.getLocalizedMessage()); + } + return null; + } + + /** + * This method returns {@link TRelationshipTemplate}s as a JaxBObject. + * + * @param xmlString + * the {@link TRelationshipTemplate} to be unmarshalled + * + * @return the unmarshalled {@link TRelationshipTemplate} + */ + public static List<TRelationshipTemplate> getRelationshipTemplatesAsJaxBObject(String xmlString) { + try { + StringReader reader = new StringReader(xmlString); + + // unmarshall + Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); + TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); + + List<TRelationshipTemplate> foundRTs = new ArrayList<>(); + for (TEntityTemplate entity : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) { + if (entity instanceof TRelationshipTemplate) { + foundRTs.add((TRelationshipTemplate) entity); + } + } + + return foundRTs; + + } catch (JAXBException e) { + logger.error(e.getLocalizedMessage()); + } + return null; + + } + + /** + * Turns XML Strings into {@link TEntityTemplate} objects using JaxB. + * + * @param xmlString + * the XMLString to be parsed + * @return the parsed XMLString as {@link TEntityTemplate} + */ + public static List<TEntityTemplate> getEntityTemplatesAsJaxBObject(String xmlString) { + try { + StringReader reader = new StringReader(xmlString); + + Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); + TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); + + return serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate(); + + } catch (JAXBException e) { + logger.error(e.getLocalizedMessage()); + } + return null; + + } + + /** + * Converts any object of the TOSCA data model to a JaxBObject. + * + * @param xmlString + * the {@link Definitions} object to be converted + * + * @return the unmarshalled {@link Definitions} object + */ + public static Definitions getXasJaxBObject(String xmlString) { + try { + StringReader reader = new StringReader(xmlString); + Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); + + return jaxBDefinitions; + + } catch (JAXBException e) { + logger.error(e.getLocalizedMessage()); + } + return null; + + } + + /** + * This method adds a selection of {@link TNodeTemplate}- and {@link TRelationshipTemplate}-XML-Strings to a {@link TTopologyTemplate}-XML-String using JAXB. + * After the templates have been added, the {@link TTopologyTemplate} object is re-marshalled to an XML-String. + * + * This method is called by the selectionHandler.jsp after several Node or RelationshipTemplates have been chosen in a dialog. + * + * @param topology + * the topology as XML string + * @param allTemplateChoicesAsXML + * all possible template choices as TOSCA-XML strings containing the complete templates + * @param selectedNodeTemplatesAsJSON + * the names of the selected NodeTemplates as JSONArray + * @param selectedRelationshipTemplatesAsJSON + * the names of the selected RelationshipTemplates as JSONArray + * + * @return the complete topology XML string + */ + public static String addTemplatesToTopology(String topology, String allTemplateChoicesAsXML, String selectedNodeTemplatesAsJSON, String selectedRelationshipTemplatesAsJSON) { + try { + + // initialization code for the jackson types used to convert JSON string arrays to a java.util.List + ObjectMapper mapper = new ObjectMapper(); + TypeFactory factory = mapper.getTypeFactory(); + + // convert the JSON array containing the names of the selected RelationshipTemplates to a java.util.List + List<String> selectedRelationshipTemplates = mapper.readValue(selectedRelationshipTemplatesAsJSON, factory.constructCollectionType(List.class, String.class)); + + // convert the topology and the choices to objects using JAXB + TTopologyTemplate topologyTemplate = getTopologyAsJaxBObject(topology); + List<TEntityTemplate> allTemplateChoices = getEntityTemplatesAsJaxBObject(allTemplateChoicesAsXML); + + // this distinction of cases is necessary because it is possible that only RelationshipTemplates have been selected + if (selectedNodeTemplatesAsJSON != null) { + + // convert the JSON string array containing the names of the selected NodeTemplates to a java.util.List + List<String> selectedNodeTemplates = mapper.readValue(selectedNodeTemplatesAsJSON, factory.constructCollectionType(List.class, String.class)); + + // search the selected NodeTemplate in the List of all choices by its name to receive its object which will ne added to the topology + for (String nodeTemplateName : selectedNodeTemplates) { + for (TEntityTemplate choice : allTemplateChoices) { + if (choice instanceof TNodeTemplate) { + TNodeTemplate nodeTemplate = (TNodeTemplate) choice; + // matching a name is usually unsafe because the uniqueness cannot be assured, + // however similar names are not possible at this location due to the implementation of the selection dialogs + if (nodeTemplateName.equals(nodeTemplate.getName())) { + // add the selected NodeTemplate to the topology + topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(nodeTemplate); + + // due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement. + // Re-add it to avoid errors. + for (TEntityTemplate entity: topologyTemplate.getNodeTemplateOrRelationshipTemplate()) { + if (entity instanceof TRelationshipTemplate) { + TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) entity; + if (relationshipTemplate.getSourceElement().getRef() == null) { + // connect to the added NodeTemplate + SourceElement sourceElement = new SourceElement(); + sourceElement.setRef(nodeTemplate); + relationshipTemplate.setSourceElement(sourceElement); + } + } + } + } + } + } + } + + // now search and add the selected RelationshipTemplate object connecting to the inserted NodeTemplate + for (String relationshipTemplateName : selectedRelationshipTemplates) { + for (TEntityTemplate toBeAdded : allTemplateChoices) { + if (toBeAdded instanceof TRelationshipTemplate) { + TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) toBeAdded; + if (relationshipTemplateName.equals(relationshipTemplate.getName())) { + topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(relationshipTemplate); + } + } + } + } + + } else { + // in this case only Relationship Templates have been selected + List<TRelationshipTemplate> allRelationshipTemplateChoices = JAXBHelper.getRelationshipTemplatesAsJaxBObject(allTemplateChoicesAsXML); + + // add the target Node Template to the topology which is unique due to the implementation of the selection dialog + topologyTemplate.getNodeTemplateOrRelationshipTemplate().add((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)).getTargetElement().getRef()); + + // search the JAXB object of the selected RelationshipTemplate and add it to the topology + for (String relationshipTemplateName : selectedRelationshipTemplates) { + for (TRelationshipTemplate choice : allRelationshipTemplateChoices) { + if (relationshipTemplateName.equals(choice.getName())) { + topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(choice); + } + } + } + + for (TEntityTemplate entityTemplate : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) { + if (entityTemplate instanceof TRelationshipTemplate) { + TRelationshipTemplate relationship = (TRelationshipTemplate) entityTemplate; + + // due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement. + // Re-add it to avoid errors. + if (relationship.getSourceElement().getRef() == null) { + relationship.getSourceElement().setRef((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)).getTargetElement().getRef()); + } + } + } + } + + // re-convert the topology from a JAXB object to an XML string and return it + Definitions definitions = new Definitions(); + TServiceTemplate st = new TServiceTemplate(); + st.setTopologyTemplate(topologyTemplate); + definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().add(st); + JAXBContext context = JAXBContext.newInstance(Definitions.class); + Marshaller m = context.createMarshaller(); + StringWriter stringWriter = new StringWriter(); + + m.marshal(definitions, stringWriter); + + return stringWriter.toString(); + + } catch (JAXBException | IOException e) { + logger.error(e.getLocalizedMessage()); + } + + return null; + } + + + /** + * Marshalls a JAXB object of the TOSCA model to an XML string. + * + * @param clazz + * the class of the object + * @param obj + * the object to be marshalled + * + * @return + */ + public static String getXMLAsString(@SuppressWarnings("rawtypes") Class clazz, Object obj) { + try { + @SuppressWarnings("rawtypes") + JAXBElement rootElement = Util.getJAXBElement(clazz, obj); + JAXBContext context = JAXBContext.newInstance(TDefinitions.class); + Marshaller m; + + m = context.createMarshaller(); + + StringWriter w = new StringWriter(); + m.marshal(rootElement, w); + String res = w.toString(); + + return res; + } catch (JAXBException e) { + logger.error(e.getLocalizedMessage()); + } + return null; + } + + /** + * This methods alters the XML with JAXB so it can be imported in Winery. This is necessary because Winery needs additional information for the position of the templates in the + * Winery-Modeler-UI. + * + * This code is adapted from the org.eclipse.winery.repository.Utils.getXMLAsString() method. + * + * @param topology + * the {@link TTopologyTemplate} to be altered + * + * @return the altered {@link TTopologyTemplate} + */ + public static TTopologyTemplate buildXML(TTopologyTemplate topology) { + + // the coordinate of the NodeTemplate in Winery. Begin 100 pixel from the top to improve arrangement. + int yCoordinates = 100; + + for (TEntityTemplate template : topology.getNodeTemplateOrRelationshipTemplate()) { + // add node templates + if (template instanceof TNodeTemplate) { + + TNodeTemplate nodeTemplate = (TNodeTemplate) template; + + // remove the Requirements tag if necessary + if (nodeTemplate.getRequirements() != null && nodeTemplate.getRequirements().getRequirement() == null) { + nodeTemplate.setRequirements(null); + } + + ModelUtilities.setLeft(nodeTemplate, NODETEMPLATE_X_COORDINATE); + ModelUtilities.setTop(nodeTemplate, Integer.toString(yCoordinates)); + + yCoordinates += 150; + } + } + + return topology; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/NodeTemplateConnector.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/NodeTemplateConnector.java new file mode 100644 index 0000000..f1d4b6b --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/NodeTemplateConnector.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.winery.model.tosca.TCapability; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; + +/** + * This class searches a {@link TRelationshipType} which is able to connect two given {@link TNodeTemplate}s. + * + */ +public class NodeTemplateConnector { + + /** + * Searches a compatible {@link TRelationshipType} to connect two {@link TNodeTemplate}s. + * + * @param source + * the source {@link TNodeTemplate} + * @param target + * the target {@link TNodeTemplate} + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * @param requirement + * the {@link TRequirement} of the source {@link TNodeTemplate} + * + * @return a list of suitable {@link TRelationshipType}s + */ + public static List<TRelationshipType> findRelationshipType(TNodeTemplate source, TNodeTemplate target, TOSCAAnalyzer toscaAnalyzer, TRequirement requirement) { + + List<TRelationshipType> suitableRelationshipTypes = new ArrayList<TRelationshipType>(); + List<TRelationshipType> allRelationshipTypes = toscaAnalyzer.getRelationshipTypes(); + + // in case the connection to a placeholder is searched, no requirement exists + if (requirement != null) { + + List<TCapability> capabilities = target.getCapabilities().getCapability(); + + // check if a RelationshipType can connect a requirement of the source NodeTemplate to a capability of the target NodeTemplate + for (TRelationshipType relationshipType : allRelationshipTypes) { + if (relationshipType.getValidSource() != null && relationshipType.getValidTarget() != null) { + for (TCapability capability : capabilities) { + if ((relationshipType.getValidSource().getTypeRef().equals(requirement.getType()) && relationshipType.getValidTarget().getTypeRef().equals(capability.getType()))) { + suitableRelationshipTypes.add(relationshipType); + } + } + } + } + } + + // to extend the selection check if a RelationshipType can connect the type of the source NodeTemplate to the type of the target NodeTemplate + for (TRelationshipType rt : allRelationshipTypes) { + if (rt.getValidSource() != null && rt.getValidTarget() != null) { + if ((rt.getValidSource().getTypeRef().equals(source.getType()) && rt.getValidTarget().getTypeRef().equals(target.getType()))) { + suitableRelationshipTypes.add(rt); + } + } + } + + // in case no suitable relationship type could be found, search for generic types without the optional ValidSource / ValidTarget elements. + if (suitableRelationshipTypes.isEmpty()) { + for (TRelationshipType rt : allRelationshipTypes) { + if (rt.getValidSource() == null && rt.getValidTarget() == null) { + suitableRelationshipTypes.add(rt); + } + } + } + + return suitableRelationshipTypes; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/RESTHelper.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/RESTHelper.java new file mode 100644 index 0000000..789d529 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/RESTHelper.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; + + +import org.eclipse.winery.common.Util; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.slf4j.LoggerFactory; + +/** + * This class contains helper methods to call the REST API and PUT/POST information to it. + */ +public class RESTHelper { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(RESTHelper.class.getName()); + + /** + * This method uses a REST call to save the completed {@link TTopologyTemplate} to the repository. + * + * @param topology + * the {@link TTopologyTemplate} to be saved + * @param topologyTemplateURL + * the URL the {@link TTopologyTemplate} is saved to + * @param overwriteTopology + * whether the topology is overwritten or a new topology shall be created + * @param topologyName + * the name of the newly created topology to build the URL if a new topology shall be created + * @param topologyNamespace + * the name space of the newly created topology to build the URL if a new topology shall be created + * @param repositoryURL + * the URL to the repository to build the URL if a new topology shall be created + */ + public static void saveCompleteTopology(TTopologyTemplate topology, String topologyTemplateURL, boolean overwriteTopology, String topologyName, String topologyNamespace, String repositoryURL) { + try { + + URL url = null; + + if (overwriteTopology) { + url = new URL(topologyTemplateURL); + } else { + // this is necessary to avoid encoding issues + topologyNamespace = Util.DoubleURLencode(topologyNamespace); + // build the URL with the repositoryURL, the topology namespace and the topology name + url = new URL(repositoryURL + "/servicetemplates/" + topologyNamespace + "/" + topologyName + "/topologytemplate/"); + + logger.info("The URL the topology is saved to: " + url); + } + + // using SSL + System.setProperty("javax.net.ssl.trustStore", "jssecacerts.cert"); + + // configure message + HttpURLConnection urlConn; + urlConn = (HttpURLConnection) url.openConnection(); + + logger.info("Sending HTTP request..."); + + urlConn.setDoOutput(true); + urlConn.setRequestMethod("PUT"); + urlConn.setRequestProperty("Content-type", "text/xml"); + OutputStreamWriter out = new OutputStreamWriter(urlConn.getOutputStream()); + + // build the XML string to be saved + TTopologyTemplate outputTopology = JAXBHelper.buildXML(topology); + String outputString = JAXBHelper.getXMLAsString(outputTopology.getClass(), outputTopology); + + logger.info(outputString); + logger.info("Sending output to Winery."); + + out.write(outputString); + out.close(); + urlConn.getOutputStream().close(); + logger.info("Output sent, waiting for response..."); + urlConn.getInputStream(); + + logger.info("HTTP Response Code is: " + urlConn.getResponseCode()); + + } catch (IOException e) { + logger.error(e.getLocalizedMessage()); + } + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/Utils.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/Utils.java new file mode 100644 index 0000000..5f9826b --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/Utils.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.xml.namespace.QName; + +import org.eclipse.winery.model.tosca.TCapabilityDefinition; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.slf4j.LoggerFactory; + +/** + * Contains methods to match requirements and capabilities and find elements in the {@link TTopologyTemplate} + */ +public class Utils { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Utils.class.getName()); + + /** + * This method searches {@link TNodeType}s in the repository that match a requirement. + * + * @param requirement + * the requirement to be matched + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return a list of the matched {@link TNodeType}s + */ + public static List<TNodeType> matchRequirementAndCapability(TRequirement requirement, TOSCAAnalyzer toscaAnalyzer) { + + List<TNodeType> possibleNodeTypes = new ArrayList<TNodeType>(); + + // find all matching Node Types for a requirement by the "requiredCapabilityType" attribute of its type + for (TRequirementType requirementType : toscaAnalyzer.getRequirementTypes()) { + if (requirementType.getName().equals(requirement.getType().getLocalPart())) { + + QName requiredCapabilityType = requirementType.getRequiredCapabilityType(); + for (TNodeType nodeType : toscaAnalyzer.getNodeTypes()) { + if (nodeType.getCapabilityDefinitions() != null) { + for (TCapabilityDefinition cd : nodeType.getCapabilityDefinitions().getCapabilityDefinition()) { + if (cd.getCapabilityType().getLocalPart().equals(requiredCapabilityType.getLocalPart())) { + possibleNodeTypes.add(nodeType); + } + } + } + } + } + } + + return possibleNodeTypes; + } + + /** + * Generates a random {@link UUID} exclusively used by the {@link TemplateBuilder}. + * + * @return the generated {@link UUID} id + */ + public static String createRandomID() { + return UUID.randomUUID().toString(); + } + + /** + * Returns a {@link TNodeTemplate} for a given Id. + * + * @param nodeTemplates + * all the {@link TNodeTemplate} in the {@link TTopologyTemplate} + * @param id + * the id of the {@link TNodeTemplate} to be found + * + * @return the found {@link TNodeTemplate} or null if not found + */ + public static TNodeTemplate getNodeTemplateForId(List<TNodeTemplate> nodeTemplates, String id) { + + for (TNodeTemplate nt : nodeTemplates) { + + if (nt.getId().equals(id)) { + return nt; + } + } + + logger.error("No NodeTemplate with " + id + " exists"); + + return null; + + } + + /** + * Returns a {@link TNodeType} for a given Id. + * + * @param nodeTypes + * All the {@link TNodeType} in the {@link TTopologyTemplate} + * @param Id + * The id of the {@link TNodeType} to be searched + * @return the {@link TNodeType} or null if not found + */ + public static TNodeType getNodeTypeForId(List<TNodeType> nodeTypes, QName id) { + + for (TNodeType nodeType : nodeTypes) { + if (nodeType.getName().equals(id.getLocalPart()) && nodeType.getTargetNamespace().equals(id.getNamespaceURI())) { + return nodeType; + } + } + + logger.error("No NodeType with " + id + " exists"); + + // no type could be found for the given ID, this case cannot occur if the topology was modelled in the Winery Topology Modeler + return null; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/package-info.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/package-info.java new file mode 100644 index 0000000..5d42210 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/helper/package-info.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +/** + * This package contains helper classes and methods to assist the completion. + */ +package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; + diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/package-info.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/package-info.java new file mode 100644 index 0000000..19e334e --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/package-info.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +/** + * this package contains classes to complete a topology template + */ +package org.eclipse.winery.topologymodeler.addons.topologycompleter;
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/placeholderhandling/PlaceHolderHandler.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/placeholderhandling/PlaceHolderHandler.java new file mode 100644 index 0000000..a384e91 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/placeholderhandling/PlaceHolderHandler.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.placeholderhandling; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Constants; + +/** + * This class finds suitable replacement types for a place holder. + */ +public class PlaceHolderHandler { + + /** + * This method returns a suitable {@link TNodeType} to replace a given {@link TNodeTemplate} placeholder. + * A suitable Node Type to replace a placeholder is matched by its type. If the type of a NodeType equals the identifier of a placeholder + * it can be used to replace it. + * + * @param nodeTemplate + * the placeholder to be replaced + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return a list of {@link TNodeType}s to replace the placeholder + */ + public static List<TNodeType> getSuitableNodeTypes(TNodeTemplate nodeTemplate, TOSCAAnalyzer toscaAnalyzer) { + + List<TNodeType> suitableNodeTypes = new ArrayList<>(); + + // TODO: matching the name without a name space is unsafe and only works assuming that no one creates generic NodeTemplates with the same name as the place holders. + // However a NodeTemplate name does not have a name space. + if (nodeTemplate.getName().equals(Constants.PlaceHolders.WEBSERVER.toString())) { + for (TNodeType nodeType : toscaAnalyzer.getNodeTypes()) { + if (nodeType.getDerivedFrom() != null && nodeType.getDerivedFrom().getTypeRef().getLocalPart().equals(Constants.PlaceHolders.WEBSERVER.toString())) { + suitableNodeTypes.add(nodeType); + } + } + } else if (nodeTemplate.getName().equals(Constants.PlaceHolders.DATABASE.toString())) { + for (TNodeType nodeType : toscaAnalyzer.getNodeTypes()) { + if (nodeType.getDerivedFrom() != null && nodeType.getDerivedFrom().getTypeRef().getLocalPart().equals(Constants.PlaceHolders.DATABASE.toString())) { + suitableNodeTypes.add(nodeType); + } + } + } else if (nodeTemplate.getName().equals(Constants.PlaceHolders.OPERATINGSYSTEM.toString())) { + for (TNodeType nodeType : toscaAnalyzer.getNodeTypes()) { + if (nodeType.getDerivedFrom() != null && nodeType.getDerivedFrom().getTypeRef().getLocalPart().equals(Constants.PlaceHolders.OPERATINGSYSTEM.toString())) { + suitableNodeTypes.add(nodeType); + } + } + } else if (nodeTemplate.getName().equals(Constants.PlaceHolders.CLOUDPROVIDER.toString())) { + for (TNodeType nodeType : toscaAnalyzer.getNodeTypes()) { + if (nodeType.getDerivedFrom() != null && nodeType.getDerivedFrom().getTypeRef().getLocalPart().equals(Constants.PlaceHolders.CLOUDPROVIDER.toString())) { + suitableNodeTypes.add(nodeType); + } + } + } + + return suitableNodeTypes; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/CompletionInterface.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/CompletionInterface.java new file mode 100644 index 0000000..e46d3b1 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/CompletionInterface.java @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion; + +import java.util.List; +import java.util.Map; + +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.repository.client.IWineryRepositoryClient; +import org.eclipse.winery.repository.client.WineryRepositoryClientFactory; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.DeferredAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.PlaceHolderAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.RequirementAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Constants; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.JAXBHelper; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.RESTHelper; +import org.slf4j.LoggerFactory; + +/** + * This class is the entry point of the TOSCA topology completion which is called by the Winery Topology Modeler. + * It receives an incomplete {@link TTopologyTemplate} from Winery. + * The completion of the incomplete {@link TTopologyTemplate} is managed by this class. + */ +public class CompletionInterface { + + /** + * Logger for debug reasons. + */ + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CompletionInterface.class.getName()); + + /** + * This global variable is returned to the Winery Topology Modelerer via getCurrentTopology() to display intermediate results when an user interaction is necessary. + */ + private TTopologyTemplate currentTopology; + + /** + * This list contains {@link TTopologyTemplate}s to be chosen by the user when the topology solution isn't unique. + */ + private List<TTopologyTemplate> topologyTemplateChoices; + + /** + * This List contains {@link TRelationshipTemplate}s to be chosen by the user. + */ + private List<TEntityTemplate> relationshipTemplateChoices; + + /** + * This Map contains {@link TNodeTemplate}s and {@link TRelationshipTemplate}s to be chosen by the user during the step-by-step approach. + */ + private Map<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>> nodeTemplateChoices; + + /** + * String containing an error message to be displayed in Winery if necessary. + */ + private String errorMessage = ""; + + /** + * This method receives an incomplete {@link TTopologyTemplate} and the repository content from Winery. After analyzing the {@link TTopologyTemplate}, the topology is completed. This method will + * return a message after the completion whether the completion was successful, has failed or the user has to interact. + * + * @param topology + * (XMLString) the {@link TTopologyTemplate} to be completed as XMLString + * @param serviceTemplateName + * the name of the ServiceTemplate for REST calls + * @param topologyTemplateURL + * the URL where the template is saved to + * @param overwriteTopology + * determines in which way the {@link TTopologyTemplate} is saved. The current {@link TTopologyTemplate} can either be overwritten or a new topology can be created. + * @param topologyName + * the name of the {@link TTopologyTemplate} when a new {@link TTopologyTemplate} shall be created + * @param topologyNamespace + * the namespace of the {@link TTopologyTemplate} when a new {@link TTopologyTemplate} shall be created + * @param repositoryURL + * the URL to the repository to receive and write TOSCA specific information + * @param stepByStep + * whether the topology completion is processed step-by-step or not + * @param restarted + * whether the topology completion is restarted or started for the first time + * + * @return a message to Winery that contains information whether the topology is complete, the user has to interact or an error occurred. + */ + public String complete(String topology, String serviceTemplateName, String topologyTemplateURL, Boolean overwriteTopology, + String topologyName, String topologyNamespace, String repositoryURL, boolean stepByStep, boolean restarted) { + + logger.info("Starting completion..."); + + //////////////////////////////////////// + // STEP 1: Receive topology from Winery + //////////////////////////////////////// + + logger.info("Saving to: " + topologyTemplateURL); + + logger.info("The service template to be completed is: " + serviceTemplateName); + + // receive types from repository + IWineryRepositoryClient client = WineryRepositoryClientFactory.getWineryRepositoryClient(); + client.addRepository(repositoryURL); + + List<TNodeType> nodeTypeList = (List<TNodeType>) client.getAllTypes(TNodeType.class); + List<TRelationshipType> relationshipTypeList = (List<TRelationshipType>) client.getAllTypes(TRelationshipType.class); + List<TRequirementType> requirementTypeList = (List<TRequirementType>) client.getAllTypes(TRequirementType.class); + + ///////////////////////////////////// + // Step 2: Analyze topology content + ///////////////////////////////////// + + logger.info("The modelled topology as XML: " + topology); + + TTopologyTemplate topologyTemplate = JAXBHelper.getTopologyAsJaxBObject(topology); + + logger.info("Analyzing topology..."); + + // analyze the received topology + TOSCAAnalyzer toscaAnalyzer = new TOSCAAnalyzer(); + toscaAnalyzer.analyzeTOSCATopology(topologyTemplate); + toscaAnalyzer.setTypes(nodeTypeList, relationshipTypeList, requirementTypeList); + + // if the topology is already complete, a message is displayed + if (checkCompletnessOfTopology(toscaAnalyzer) && !restarted) { + return Constants.CompletionMessages.TOPOLOGYCOMPLETE.toString(); + } else { + + ///////////////////////////////////////// + // Step 3: Invoke the topology completion + ///////////////////////////////////////// + logger.info("Invoking Topology Completion..."); + + CompletionManager completionManager = new CompletionManager(toscaAnalyzer, stepByStep); + List<TTopologyTemplate> completedTopology = completionManager.manageCompletion(topologyTemplate); + + // the user has to interact by choosing a RelationshipTemplate, send message to Winery which will display a dialog + if (completionManager.getUserInteraction() && !stepByStep) { + currentTopology = completedTopology.get(0); + relationshipTemplateChoices = completionManager.getChoices(); + + return Constants.CompletionMessages.USERINTERACTION.toString(); + + } else if (completionManager.getNodeTemplateUserInteraction() && stepByStep) { + // the topology completion is processed Step-by-Step, the user has to choose Node and RelationshipTemplates to be inserted + currentTopology = completedTopology.get(0); + nodeTemplateChoices = completionManager.getTemplateChoices(); + + for (TNodeTemplate nodeTemplate : nodeTemplateChoices.keySet()) { + Map<TNodeTemplate, List<TEntityTemplate>> entityTemplates = nodeTemplateChoices.get(nodeTemplate); + + for (TNodeTemplate entity : entityTemplates.keySet()) { + for (TEntityTemplate relationshipTemplate : entityTemplates.get(entity)) { + // remove entity that has to be chosen next + if (currentTopology.getNodeTemplateOrRelationshipTemplate().contains(relationshipTemplate)) { + currentTopology.getNodeTemplateOrRelationshipTemplate().remove(relationshipTemplate); + } else if (currentTopology.getNodeTemplateOrRelationshipTemplate().contains(entity)) { + currentTopology.getNodeTemplateOrRelationshipTemplate().remove(entity); + } + } + } + } + + return Constants.CompletionMessages.STEPBYSTEP.toString(); + } + + logger.info("Completion successful!"); + + if (completedTopology.size() == 1) { + // solution is unique, save the topology + RESTHelper.saveCompleteTopology(completedTopology.get(0), topologyTemplateURL, overwriteTopology, topologyName, topologyNamespace, repositoryURL); + return Constants.CompletionMessages.SUCCESS.toString(); + } else if (completedTopology.size() > 1) { + // if there are several topology solutions, let the user choose + this.topologyTemplateChoices = completedTopology; + return Constants.CompletionMessages.USERTOPOLOGYSELECTION.toString(); + } else { + // an error occurred + errorMessage = "Error: No suitable NodeTemplate could be found for a Requirement or PlaceHolder."; + return Constants.CompletionMessages.FAILURE.toString(); + } + } + } + + /** + * This method checks if the topology is already complete. It will be called before executing the topology completion but + * only in case the topology completion isn't restarted after a user selection. + * + * @param toscaAnalyzer + * the topology to be checked + * @return whether the topology is complete or not + */ + public boolean checkCompletnessOfTopology(TOSCAAnalyzer toscaAnalyzer) { + + if (RequirementAnalyzer.analyzeRequirements(toscaAnalyzer).isEmpty() && PlaceHolderAnalyzer.analyzePlaceHolders(toscaAnalyzer).isEmpty() + && DeferredAnalyzer.analyzeDeferredRelations(toscaAnalyzer).isEmpty()) { + return true; + } else { + return false; + } + } + + /** + * Returns the current state of the completion. + * + * @return the current {@link TTopologyTemplate} + */ + public TTopologyTemplate getCurrentTopology() { + return currentTopology; + } + + /** + * Returns the choices whenever there are several possible complete {@link TTopologyTemplate}s. They will be displayed in Winery and chosen by the user. + * + * @return the possible {@link TTopologyTemplate} choices as a list. + */ + public List<TTopologyTemplate> getTopologyTemplateChoices() { + return topologyTemplateChoices; + } + + /** + * Returns the {@link TRelationshipTemplate} choices + * + * @return the {@link TRelationshipTemplate}s to be chosen + */ + public List<TEntityTemplate> getRelationshipTemplateChoices() { + return relationshipTemplateChoices; + } + + /** + * Returns several {@link TNodeTemplate} and {@link TRelationshipTemplate} choices when the user selected the step-by-step approach. + * + * @return the {@link TNodeTemplate} choices + */ + public Map<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>> getNodeTemplateChoices() { + return nodeTemplateChoices; + } + + /** + * Returns a message when an error occurred during the completion. + * + * @return the error message + */ + public String getErrorMessage() { + return errorMessage; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/CompletionManager.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/CompletionManager.java new file mode 100644 index 0000000..1f47cf5 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/CompletionManager.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.DeferredAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.PlaceHolderAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.RequirementAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer.DeferredCompleter; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer.PlaceHolderCompleter; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer.RequirementCompleter; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer.StepByStepCompleter; + +/** + * This class manages the completion of a TOSCA {@link TTopologyTemplate}. + */ +public class CompletionManager { + + private static final Logger logger = Logger.getLogger(CompletionManager.class.getName()); + + /** + * {@link TOSCAAnalyzer} object to access the JAXB data model + */ + TOSCAAnalyzer toscaAnalyzer; + + /** + * Map containing the topology solutions. + * + * The first parameter of the map is an index used to traverse the map easily. The second parameter of the solutions map + * is another map containing a possible topology solution and a boolean value that determines if the topology is complete. + * When all topologies of the solution map are complete, it will be returned to Winery. + */ + Map<Integer, Map<TTopologyTemplate, Boolean>> solutions; + + /** + * Whether a step-by-step or an one-step approach is conducted + */ + boolean stepByStep; + + /** + * Map containing {@link TNodeTemplate}s and {@link TRelationshipTemplate}s to be chosen by the user in the step-by-step approach. + */ + private Map<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>> templateChoices; + + /** + * Whether a user interaction for choosing inserted {@link TNodeTemplate}s and {@link TRelationshipTemplate}s is necessary or not. + */ + private boolean nodeTemplateUserInteraction = false; + + /** + * List containing {@link TRelationshipTemplate} to be chosen by the user. + */ + private List<TEntityTemplate> choices; + + /** + * Whether a user interaction for choosing inserted {@link TRelationshipTemplate}s is necessary or not. + */ + boolean userInteraction = false; + + /** + * The index of the topology solutions map. + */ + int index; + + /** + * The class constructor. + * + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * @param stepByStep + * whether the topology completion is processed step-by-step or not + */ + public CompletionManager(TOSCAAnalyzer toscaAnalyzer, boolean stepByStep) { + this.toscaAnalyzer = toscaAnalyzer; + this.stepByStep = stepByStep; + this.index = 0; + + // instantiate the solution map + solutions = new HashMap<Integer, Map<TTopologyTemplate, Boolean>>(); + } + + /** + * This recursive method analyzes and completes a TOSCA {@link TTopologyTemplate}. + * + * @param topology + * the TOSCA {@link TTopologyTemplate} to be completed + * + * @return the complete TOSCA {@link TTopologyTemplate} object + */ + public List<TTopologyTemplate> manageCompletion(TTopologyTemplate topology) { + + // ------------------------- + // Analyze topology template + // ------------------------- + + // the data model must be cleared before analyzing the topology + toscaAnalyzer.clear(); + + // analyze the content of the topology template + toscaAnalyzer.analyzeTOSCATopology(topology); + + // Note: The TOSCAAnalyzer object is used for the analysis to access the NodeTemplates or RelationshipTemplates directly, + // so no cast from the parent type TEntityTemplate is required + + // -------------------------------- + // Analyze unfulfilled requirements + // -------------------------------- + Map<TRequirement, TNodeTemplate> unfulfilledRequirements = RequirementAnalyzer.analyzeRequirements(toscaAnalyzer); + + // --------------------------------------- + // Analyze the occurrence of place holders + // --------------------------------------- + List<TNodeTemplate> placeHolders = PlaceHolderAnalyzer.analyzePlaceHolders(toscaAnalyzer); + + // -------------------------------------------------------- + // Analyze the occurrence of deferred RelationshipTemplates + // -------------------------------------------------------- + List<TRelationshipTemplate> deferredRelations = DeferredAnalyzer.analyzeDeferredRelations(toscaAnalyzer); + + // --------------------- + // Complete the topology + // --------------------- + + // with the step by step approach, a user interaction is always necessary. So the topology and + // the choices will be returned in every step. A combination of the step-by-step approach and a deferred topology is not + // possible because every path of the depth search can lead to a dead end. + if (stepByStep && deferredRelations.isEmpty()) { + logger.info("Completing topology step by step."); + + List<TTopologyTemplate> solutionList = new ArrayList<TTopologyTemplate>(); + + if (!unfulfilledRequirements.isEmpty() && placeHolders.isEmpty()) { + + // complete a topology containing requirements step by step using the StepByStepCompleter class + StepByStepCompleter stepByStepCompleter = new StepByStepCompleter(topology); + stepByStepCompleter.completeTopologyStepByStep(unfulfilledRequirements, toscaAnalyzer); + + // get the NodeTemplate choices for the user + templateChoices = stepByStepCompleter.getTemplateChoices(); + nodeTemplateUserInteraction = true; + + solutionList.add(topology); + + logger.info("Returning topology for user interaction"); + + return solutionList; + + } else if (unfulfilledRequirements.isEmpty() && !placeHolders.isEmpty()) { + + // complete a topology containing place holders step by step using the StepByStepCompleter class + StepByStepCompleter stepByStepCompleter = new StepByStepCompleter(topology); + TRelationshipTemplate genericRelationship = stepByStepCompleter.completeWildcardTopologyStepByStep(placeHolders, toscaAnalyzer); + + // get the NodeTemplate selection for the user to choose + templateChoices = stepByStepCompleter.getTemplateChoices(); + nodeTemplateUserInteraction = true; + + TNodeTemplate toBeRemoved = stepByStepCompleter.getPlaceHolder(); + topology.getNodeTemplateOrRelationshipTemplate().remove(toBeRemoved); + topology.getNodeTemplateOrRelationshipTemplate().remove(genericRelationship); + + solutionList.add(topology); + logger.info("Returning topology for user interaction"); + + return solutionList; + } else if (unfulfilledRequirements.isEmpty() && placeHolders.isEmpty() && deferredRelations.isEmpty()) { + + // the topology is complete, return it to Winery + + logger.info("The topology is complete."); + + nodeTemplateUserInteraction = false; + solutionList.add(topology); + return solutionList; + } + } else { + // the one-step approach is chosen or the topology contains deferred-RelationshipTemplates + if (unfulfilledRequirements.isEmpty() && placeHolders.isEmpty() && deferredRelations.isEmpty()) { + // the topology does not contain any elements that have to completed, it can be defined as complete + if (solutions.isEmpty()) { + // no topology solutions found, topology could not be completed due to missing types. + // Return an empty list, an error message will be shown in Winery. + return new ArrayList<TTopologyTemplate>(); + } else { + // this topology is complete, set its boolean value in the map to true + for (Integer i : solutions.keySet()) { + for (TTopologyTemplate t : solutions.get(i).keySet()) { + if (t.equals(topology)) { + solutions.get(i).put(topology, true); + } + } + } + + // check if the map still contains any incomplete topologies. If this is the case, the recursion will continue. + // Otherwise the solutions map is returned. + if (!solutions.values().contains(false)) { + logger.info("The topology is complete."); + List<TTopologyTemplate> sol = new ArrayList<TTopologyTemplate>(); + + for (Integer i : solutions.keySet()) { + sol.addAll(solutions.get(i).keySet()); + } + return sol; + } + } + } else if (!unfulfilledRequirements.isEmpty() && placeHolders.isEmpty() && deferredRelations.isEmpty()) { + + logger.info("The topology contains Requirements, but no Place Holders."); + + // complete a topology containing Requirements in one step using the RequirementCompleter class + RequirementCompleter requirementCompleter = new RequirementCompleter(topology); + + List<TTopologyTemplate> completeTopology = requirementCompleter.completeRequirementTopology(unfulfilledRequirements, toscaAnalyzer); + + for (TTopologyTemplate topologySolution : completeTopology) { + Map<TTopologyTemplate, Boolean> topologyMap = new HashMap<TTopologyTemplate, Boolean>(); + topologyMap.put(topologySolution, false); + solutions.put(index, topologyMap); + } + + // complete all topology solutions recursively + for (TTopologyTemplate topologySolution : completeTopology) { + manageCompletion(topologySolution); + index++; + } + + } else if (unfulfilledRequirements.isEmpty() && !placeHolders.isEmpty() || !unfulfilledRequirements.isEmpty() && !placeHolders.isEmpty()) { + + logger.info("The topology contains one or more PlaceHolders."); + + // complete a topology containing place holders in one step using the PlaceHolderCompleter class + PlaceHolderCompleter placeHolderCompleter = new PlaceHolderCompleter(topology); + + List<TTopologyTemplate> completeTopology = placeHolderCompleter.completePlaceholderTopology(placeHolders, toscaAnalyzer); + + if (placeHolderCompleter.getUserInteraction()) { + choices = placeHolderCompleter.getChoices(); + userInteraction = true; + + // user interaction is necessary to choose a inserted Relationship Template, return the topology to winery + List<TTopologyTemplate> intermediateSolutions = new ArrayList<>(); + TRelationshipTemplate toBeRemoved = null; + for (TEntityTemplate entityTemplate : topology.getNodeTemplateOrRelationshipTemplate()) { + if (entityTemplate instanceof TRelationshipTemplate) { + TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) entityTemplate; + if (relationshipTemplate.getTargetElement().getRef().equals(placeHolderCompleter.getPlaceHolder())) { + toBeRemoved = relationshipTemplate; + } + } + } + + topology.getNodeTemplateOrRelationshipTemplate().remove(toBeRemoved); + topology.getNodeTemplateOrRelationshipTemplate().remove(placeHolderCompleter.getPlaceHolder()); + + intermediateSolutions.add(topology); + return intermediateSolutions; + } + + int i = 0; + + for (TTopologyTemplate topologySolution : completeTopology) { + Map<TTopologyTemplate, Boolean> topologyMap = new HashMap<TTopologyTemplate, Boolean>(); + topologyMap.put(topologySolution, false); + solutions.put(i, topologyMap); + i++; + } + + for (TTopologyTemplate topologySolution : completeTopology) { + manageCompletion(topologySolution); + } + } else if (!deferredRelations.isEmpty()) { + + logger.info("The topology contains deferred RelationshipTemplates."); + + // complete a topology containing deferred Relationship Templates in one step using the DeferredCompleter class + DeferredCompleter deferredCompleter = new DeferredCompleter(topology); + List<TTopologyTemplate> completeTopology = deferredCompleter.completeDeferredTopology(deferredRelations.get(0), toscaAnalyzer); + + int i = 0; + for (TTopologyTemplate solutionTemplate : completeTopology) { + Map<TTopologyTemplate, Boolean> topologyMap = new HashMap<TTopologyTemplate, Boolean>(); + topologyMap.put(solutionTemplate, false); + solutions.put(i, topologyMap); + i++; + } + + for (TTopologyTemplate topologySolution : completeTopology) { + manageCompletion(topologySolution); + } + } + List<TTopologyTemplate> sol = new ArrayList<TTopologyTemplate>(); + for (Integer i : solutions.keySet()) { + sol.addAll(solutions.get(i).keySet()); + } + return sol; + } + + return new ArrayList<TTopologyTemplate>(); + } + + /** + * Returns whether an user interaction is necessary or not + * + * @return the field userInteraction + */ + public boolean getUserInteraction() { + return userInteraction; + } + + /** + * The possible {@link TRelationshipTemplate} choices + * + * @return the field choices + */ + public List<TEntityTemplate> getChoices() { + return choices; + } + + /** + * A map of {@link TNodeTemplate}s and {@link TRelationshipTemplate}s when completing a topology step by step + * + * @return the field nodeTemplateChoices + */ + public Map<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>> getTemplateChoices() { + return templateChoices; + } + + /** + * Returns whether user interaction by choosing {@link TNodeTemplate}s and {@link TRelationshipTemplate}s is necessary or not + * + * @return the field nodeTemplateUserInteraction + */ + public boolean getNodeTemplateUserInteraction() { + return nodeTemplateUserInteraction; + } + +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/DeferredCompleter.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/DeferredCompleter.java new file mode 100644 index 0000000..8bdf01e --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/DeferredCompleter.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.winery.common.ModelUtilities; +import org.eclipse.winery.model.tosca.TCapability; +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TRequirementType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.NodeTemplateConnector; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Utils; + +/** + * This class serves the completion of a topology containing Deferred {@link TRelationshipTemplate}s. + */ +public class DeferredCompleter { + + /** + * The TOSCA {@link TTopologyTemplate} document + */ + TTopologyTemplate topology; + + /** + * A Map containing the requirements removed during the algorithm and their corresponding {@link TNodeTemplate}. + */ + Map<TRequirement, TNodeTemplate> removedRequirements; + + /** + * Constructor of the class. + * + * @param topology + * the {@link TTopologyTemplate} to be completed + */ + public DeferredCompleter(TTopologyTemplate topology) { + this.topology = topology; + removedRequirements = new HashMap<TRequirement, TNodeTemplate>(); + } + + /** + * Completes a {@link TTopologyTemplate} that contains deferred {@link TRelationshipTemplate}s with a depth search algorithm. A deferred {@link TRelationshipTemplate} serves as place holder for a + * number of {@link TNodeTemplate}s and {@link TRelationshipTemplate}s. + * + * @param deferredRelation + * all found deferred {@link TRelationshipTemplate}s in the topology + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return the completed topology + */ + public List<TTopologyTemplate> completeDeferredTopology(TRelationshipTemplate deferredRelation, TOSCAAnalyzer toscaAnalyzer) { + + List<TTopologyTemplate> solutions = new ArrayList<TTopologyTemplate>(); + + TNodeTemplate source = (TNodeTemplate) deferredRelation.getSourceElement().getRef(); + TNodeTemplate target = (TNodeTemplate) deferredRelation.getTargetElement().getRef(); + + // TODO Remove this "if clause" after the Provisioning-API is implemented. At the moment Deferred RelationshipTemplates can't be completed + // without the existence of Requirements + if (source.getRequirements() != null && !source.getRequirements().getRequirement().isEmpty()) { + topology.getNodeTemplateOrRelationshipTemplate().remove(deferredRelation); + runDepthFirstSearch(source, target, new ArrayList<TEntityTemplate>(), solutions, toscaAnalyzer); + } + + /** + * Note: This code adds Requirements to NodeTemplates that has been removed during the algorithm but could not + * be used to replace the Deferred-RelationshipTemplates. If this step is not done, requirements could get lost. + * + * Therefore all removed Requirements are checked for fulfillment in the topology. If they have not been fulfilled + * they are re-added to the topology. + */ + Set<TRequirement> keySet = removedRequirements.keySet(); + + for (TTopologyTemplate topologyTemplate: solutions) { + boolean fulfilled = false; + for (TRequirement requirement: keySet) { + for (TEntityTemplate entity: topologyTemplate.getNodeTemplateOrRelationshipTemplate()) { + if (entity instanceof TNodeTemplate) { + TNodeTemplate nodeTemplate = (TNodeTemplate) entity; + if (nodeTemplate.getCapabilities() != null) { + for (TCapability capability: nodeTemplate.getCapabilities().getCapability()) { + String reqCapaType = ""; + for (TRequirementType reqType: toscaAnalyzer.getRequirementTypes()) { + if (reqType.getName().equals(requirement.getType().getLocalPart())) { + reqCapaType = reqType.getRequiredCapabilityType().getLocalPart(); + } + } + if (capability.getName().equals(reqCapaType)) { + fulfilled = true; + } + } + } + } + } + if (!fulfilled) { + for (TEntityTemplate entity: topologyTemplate.getNodeTemplateOrRelationshipTemplate()) { + if (entity.equals(removedRequirements.get(requirement))) { + TNodeTemplate foundNT = (TNodeTemplate) entity; + foundNT.getRequirements().getRequirement().add(requirement); + } + } + } + } + } + + return solutions; + } + + /** + * Runs a recursive depth search to find the path to the target NodeTemplate. + * + * @param source + * the source node of a given {@link TRelationshipTemplate} + * @param target + * the target node of a given {@link TRelationshipTemplate} + * @param path + * the current path to the target (can be incomplete) + * @param solutions + * list containing all possible solutions of the completion + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return the path to the target NodeTemplate + */ + private void runDepthFirstSearch(TNodeTemplate source, TNodeTemplate target, List<TEntityTemplate> path, List<TTopologyTemplate> solutions, TOSCAAnalyzer toscaAnalyzer) { + + List<TNodeType> matchingNodeTypes = new ArrayList<TNodeType>(); + + if (source.getRequirements() != null) { + + List<TRequirement> requirementsOfTemplate = new ArrayList<>(); + for (TRequirement requirement : source.getRequirements().getRequirement()) { + requirementsOfTemplate.add(requirement); + } + + for (TRequirement requirement : requirementsOfTemplate) { + + // save the requirement to a list to avoid losing requirements (see line 83) + TRequirement sourceRequirement = new TRequirement(); + sourceRequirement.setId(requirement.getId()); + sourceRequirement.setName(requirement.getName()); + sourceRequirement.setType(requirement.getType()); + + // Remember the removed requirements. In case a requirement + // can't be used for completing the deferred RelationshipTemplate it has to be re-added to the topology. + removedRequirements.put(sourceRequirement, source); + + // search for matching NodeTypes for the requirement + matchingNodeTypes.addAll(Utils.matchRequirementAndCapability(requirement, toscaAnalyzer)); + + // remove the requirement so it is not handled again during the algorithm + source.getRequirements().getRequirement().remove(requirement); + } + } + for (TNodeType match : matchingNodeTypes) { + + if (match.getName().equals(target.getType().getLocalPart()) && match.getTargetNamespace().equals(target.getType().getNamespaceURI())) { + // the search was successful connect the target + List<TRelationshipType> suitableRTs = NodeTemplateConnector.findRelationshipType(source, target, toscaAnalyzer, null); + + for (TRelationshipType rt : suitableRTs) { + TRelationshipTemplate relationship = ModelUtilities.instantiateRelationshipTemplate(rt, source, target); + path.add(relationship); + } + + TTopologyTemplate possiblePath = new TTopologyTemplate(); + possiblePath.getNodeTemplateOrRelationshipTemplate().addAll(topology.getNodeTemplateOrRelationshipTemplate()); + + // add the path to the topology + for (TEntityTemplate pathTemplate : path) { + possiblePath.getNodeTemplateOrRelationshipTemplate().add(pathTemplate); + } + + possiblePath.getNodeTemplateOrRelationshipTemplate().remove(target); + + // this is no good style, however the target has to be the last item in the list for a proper stack layouting + possiblePath.getNodeTemplateOrRelationshipTemplate().add(target); + solutions.add(possiblePath); + path.clear(); + } else { + + // the end of the path is not reached, add the found NodeTemplate and continue the depth search + TNodeTemplate instantiatedNodeTemplate = ModelUtilities.instantiateNodeTemplate(match); + + List<TRelationshipType> suitableRTs = NodeTemplateConnector.findRelationshipType(source, instantiatedNodeTemplate, toscaAnalyzer, null); + + for (TRelationshipType rt : suitableRTs) { + TRelationshipTemplate relationship = ModelUtilities.instantiateRelationshipTemplate(rt, source, instantiatedNodeTemplate); + path.add(relationship); + } + path.add(instantiatedNodeTemplate); + runDepthFirstSearch(instantiatedNodeTemplate, target, path, solutions, toscaAnalyzer); + + } + } + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/PlaceHolderCompleter.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/PlaceHolderCompleter.java new file mode 100644 index 0000000..e76d897 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/PlaceHolderCompleter.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.winery.common.ModelUtilities; +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.NodeTemplateConnector; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.placeholderhandling.PlaceHolderHandler; + +/** + * This class completes a {@link TTopologyTemplate} containing place holders. + */ +public class PlaceHolderCompleter { + + /** + * The {@link TTopologyTemplate} to be completed + */ + TTopologyTemplate topology; + + /** + * List containing user choices for {@link TRelationshipTemplate}s + */ + List<TEntityTemplate> choices; + + /** + * Whether an user interaction is necessary or not + */ + boolean userInteraction; + + /** + * The last inserted place holder + */ + TNodeTemplate placeHolder; + + /** + * The constructor of the class PlaceHolderCompleter. + * + * @param topology + * the {@link TTopologyTemplate} to be completed + */ + public PlaceHolderCompleter(TTopologyTemplate topology) { + this.topology = topology; + userInteraction = false; + } + + /** + * This method completes a {@link TTopologyTemplate} containing place holders. + * + * @param placeHolders + * the contained place holders + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return the complete {@link TTopologyTemplate} + */ + public List<TTopologyTemplate> completePlaceholderTopology(List<TNodeTemplate> placeHolders, TOSCAAnalyzer toscaAnalyzer) { + + List<TTopologyTemplate> solutions = new ArrayList<TTopologyTemplate>(); + + for (TNodeTemplate placeHolder : placeHolders) { + + List<TNodeType> suitableNodeTypes = PlaceHolderHandler.getSuitableNodeTypes(placeHolder, toscaAnalyzer); + + // if there are more than one solution for an inserted NodeTemplate, + // create copies of the topology. The user can choose from them after the completion. + TTopologyTemplate topologyCopy = null; + + for (TNodeType suitableNodeType : suitableNodeTypes) { + topologyCopy = new TTopologyTemplate(); + topologyCopy.getNodeTemplateOrRelationshipTemplate().addAll(topology.getNodeTemplateOrRelationshipTemplate()); + + TNodeTemplate nodeTemplate = ModelUtilities.instantiateNodeTemplate(suitableNodeType); + + List<TNodeTemplate> sourceTemplates = new ArrayList<>(); + + // contains RelationshipTemplates connecting to a place holder. + // These Templates are generic and have to be replaced with + // concrete ones. + List<TRelationshipTemplate> placeholderConnections = new ArrayList<>(); + + TRelationshipTemplate foundTarget = null; + for (TEntityTemplate entity : topology.getNodeTemplateOrRelationshipTemplate()) { + if (entity instanceof TRelationshipTemplate) { + TRelationshipTemplate rt = (TRelationshipTemplate) entity; + if (((TNodeTemplate) rt.getTargetElement().getRef()).getId().equals(placeHolder.getId())) { + TRelationshipTemplate placeHolderConnection = (TRelationshipTemplate) entity; + placeholderConnections.add(placeHolderConnection); + sourceTemplates.add((TNodeTemplate) placeHolderConnection.getSourceElement().getRef()); + } else if (((TNodeTemplate) rt.getSourceElement().getRef()).getId().equals(placeHolder.getId())) { + foundTarget = (TRelationshipTemplate) entity; + } + } + } + + // collect all possible RelationshipTemplates that can be used to connect to the placeholder + choices = new ArrayList<>(); + + for (TNodeTemplate sourceTemplate : sourceTemplates) { + // find matching RelationshipTypes to connect the Node Templates + List<TRelationshipType> suitableRTs = NodeTemplateConnector.findRelationshipType(sourceTemplate, nodeTemplate, toscaAnalyzer, null); + for (TRelationshipType rt : suitableRTs) { + TRelationshipTemplate relationship = ModelUtilities.instantiateRelationshipTemplate(rt, sourceTemplate, nodeTemplate); + choices.add(relationship); + } + } + + // set the source elements of the RelationshipTemplates connecting from the replaced placeholder to other NodeTemplates + for (TEntityTemplate entityTemplate : topologyCopy.getNodeTemplateOrRelationshipTemplate()) { + if (entityTemplate instanceof TRelationshipTemplate) { + TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) entityTemplate; + if (relationshipTemplate.equals(foundTarget)) { + foundTarget.getSourceElement().setRef(nodeTemplate); + } + } + } + + // remove the generic connections to the place holder + topologyCopy.getNodeTemplateOrRelationshipTemplate().removeAll(placeholderConnections); + + // there are more than one possible Relationship Templates to connect to the inserted NodeTemplate(s), so + // interrupt the completion and ask the user which one to insert + if (choices.size() > 1 && sourceTemplates.size() == 1) { + + choices.add(sourceTemplates.get(0)); + choices.add(nodeTemplate); + topologyCopy.getNodeTemplateOrRelationshipTemplate().remove(placeHolder); + + userInteraction = true; + this.placeHolder = placeHolder; + break; + } else if (choices.size() == 1 || sourceTemplates.size() > 1) { + // replace the place holder with an actual NodeTemplate + topologyCopy.getNodeTemplateOrRelationshipTemplate().addAll(choices); + topologyCopy.getNodeTemplateOrRelationshipTemplate().add(nodeTemplate); + topologyCopy.getNodeTemplateOrRelationshipTemplate().remove(placeHolder); + } + solutions.add(topologyCopy); + } + } + return solutions; + } + + /** + * Returns the replaced place holder. + * + * @return the place holder + */ + public TNodeTemplate getPlaceHolder() { + return placeHolder; + } + + /** + * Returns whether an user interaction is necessary or not. + * + * @return the field userInteraction + */ + public boolean getUserInteraction() { + return userInteraction; + } + + /** + * Possible Relationship Template choices. + * + * @return the field choices + */ + public List<TEntityTemplate> getChoices() { + return choices; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/RequirementCompleter.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/RequirementCompleter.java new file mode 100644 index 0000000..b2e7c75 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/RequirementCompleter.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.winery.common.ModelUtilities; +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.NodeTemplateConnector; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Utils; + +public class RequirementCompleter { + + /** + * The TOSCA {@link TTopologyTemplate} document. + */ + TTopologyTemplate topology; + + /** + * The constructor the class. + * + * @param topology + * the topology to be completed + */ + public RequirementCompleter(TTopologyTemplate topology) { + this.topology = topology; + } + + /** + * This method completes a topology containing {@link TRequirement}s in one step (without user interaction). + * + * @param unfulfilledRequirements + * all the unfulfilled requirements that has been found in the topology + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + * + * @return the complete topology + */ + public List<TTopologyTemplate> completeRequirementTopology(Map<TRequirement, TNodeTemplate> unfulfilledRequirements, TOSCAAnalyzer toscaAnalyzer) { + + List<TTopologyTemplate> solutions = new ArrayList<TTopologyTemplate>(); + + Set<TRequirement> requirements = unfulfilledRequirements.keySet(); + + TNodeTemplate instantiatedNodeTemplate = null; + + // fulfill the Requirements + for (TRequirement requirement : requirements) { + + // remove the requirement from the NodeTemplate + TNodeTemplate requirementTemplate = unfulfilledRequirements.get(requirement); + for (TEntityTemplate element : topology.getNodeTemplateOrRelationshipTemplate()) { + if (requirementTemplate.getId().equals(element.getId())) { + ((TNodeTemplate) element).getRequirements().getRequirement().remove(requirement); + } + } + + List<TNodeType> possibleNodeTypes = Utils.matchRequirementAndCapability(requirement, toscaAnalyzer); + + // create a NodeTemplate for every matching Type, insert it into the topology and create a topology copy for each possible inserted NodeTemplate + TTopologyTemplate topologyCopy = null; + for (TNodeType possibleType : possibleNodeTypes) { + + topologyCopy = new TTopologyTemplate(); + topologyCopy.getNodeTemplateOrRelationshipTemplate().addAll(topology.getNodeTemplateOrRelationshipTemplate()); + + // instantiate the template + instantiatedNodeTemplate = ModelUtilities.instantiateNodeTemplate(possibleType); + topologyCopy.getNodeTemplateOrRelationshipTemplate().add(instantiatedNodeTemplate); + + TNodeTemplate correspondingNodeTemplate = unfulfilledRequirements.get(requirement); + + // find matching RelationshipTypes, instantiate RelationshipTemplates and connect the Node Templates + List<TRelationshipType> suitableRTs = NodeTemplateConnector.findRelationshipType(correspondingNodeTemplate, instantiatedNodeTemplate, toscaAnalyzer, requirement); + for (TRelationshipType rt : suitableRTs) { + TRelationshipTemplate relationship = ModelUtilities.instantiateRelationshipTemplate(rt, correspondingNodeTemplate, instantiatedNodeTemplate); + topologyCopy.getNodeTemplateOrRelationshipTemplate().add(relationship); + } + + solutions.add(topologyCopy); + } + if (solutions.size() > 1) { + break; + } + } + return solutions; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/StepByStepCompleter.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/StepByStepCompleter.java new file mode 100644 index 0000000..b8cc745 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/StepByStepCompleter.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.winery.common.ModelUtilities; +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TRequirement; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.analyzer.TOSCAAnalyzer; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.NodeTemplateConnector; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.Utils; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.placeholderhandling.PlaceHolderHandler; + +/** + * This class handles topologies that are completed step by step + */ +public class StepByStepCompleter { + + /** + * the topology to be completed + */ + TTopologyTemplate topology; + + /** + * the Node and RelationshipTemplates chosen by the user in every step + */ + Map<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>> templateChoices; + + /** + * the last inserted place holder to be deleted + */ + private TNodeTemplate placeHolder; + + /** + * The constructor of the class. + * + * @param topology + * the {@link TTopologyTemplate} to be completed + */ + public StepByStepCompleter(TTopologyTemplate topology) { + this.topology = topology; + } + + /** + * This method is called when a topology containing {@link TRequirement}s is completed step by step. + * + * @param unfulfilledRequirements + * a list of unfulfilled requirements + * @param placeHolders + * a list of place holders to be fulfilled + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model + */ + public void completeTopologyStepByStep(Map<TRequirement, TNodeTemplate> unfulfilledRequirements, TOSCAAnalyzer toscaAnalyzer) { + + Set<TRequirement> requirements = unfulfilledRequirements.keySet(); + + TNodeTemplate nodeTemplate = null; + + for (TRequirement requirement : requirements) { + // remove the requirement from the NodeTemplate + TNodeTemplate requirementTemplate = unfulfilledRequirements.get(requirement); + for (TEntityTemplate element : topology.getNodeTemplateOrRelationshipTemplate()) { + if (requirementTemplate.getId().equals(element.getId())) { + ((TNodeTemplate) element).getRequirements().getRequirement().remove(requirement); + } + } + + List<TNodeType> possibleNodeTypes = Utils.matchRequirementAndCapability(requirement, toscaAnalyzer); + + // create a NodeTemplate for each matching node type + List<TNodeTemplate> possibleTemplates = new ArrayList<>(); + for (TNodeType possibleType : possibleNodeTypes) { + nodeTemplate = ModelUtilities.instantiateNodeTemplate(possibleType); + possibleTemplates.add(nodeTemplate); + } + + TNodeTemplate correspondingNodeTemplate = unfulfilledRequirements.get(requirement); + + Map<TNodeTemplate, List<TEntityTemplate>> entityTemplates = new HashMap<>(); + + // add all possible choices to a list and return it to the user + for (TNodeTemplate possibleTemplate : possibleTemplates) { + List<TEntityTemplate> choices = new ArrayList<TEntityTemplate>(); + List<TRelationshipType> suitableRTs = NodeTemplateConnector.findRelationshipType(correspondingNodeTemplate, possibleTemplate, toscaAnalyzer, requirement); + for (TRelationshipType rt : suitableRTs) { + TRelationshipTemplate relationship = ModelUtilities.instantiateRelationshipTemplate(rt, correspondingNodeTemplate, possibleTemplate); + choices.add(relationship); + } + entityTemplates.put(possibleTemplate, choices); + } + + templateChoices = new HashMap<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>>(); + templateChoices.put(correspondingNodeTemplate, entityTemplates); + + // let the user decide which template shall be inserted + break; + } + + } + + /** + * Completes a place holder {@link TTopologyTemplate} step by step. + * + * @param placeHolders + * the place holders of the topology + * @param toscaAnalyzer + * the {@link TOSCAAnalyzer} object to access the data model. + * @return the generic {@link TRelationshipTemplate} which connects to the place holder. + */ + public TRelationshipTemplate completeWildcardTopologyStepByStep(List<TNodeTemplate> placeHolders, TOSCAAnalyzer toscaAnalyzer) { + + // take the first place holder, the order doesn't matter in the step by step approach + TNodeTemplate placeHolder = placeHolders.get(0); + + // get suitable NodeTypes for a placeholder and instantiate NodeTemplates + List<TNodeType> suitableNodeTypes = PlaceHolderHandler.getSuitableNodeTypes(placeHolder, toscaAnalyzer); + List<TNodeTemplate> suitableNodeTemplates = new ArrayList<TNodeTemplate>(); + for (TNodeType suitableNodeType : suitableNodeTypes) { + TNodeTemplate nodeTemplate = ModelUtilities.instantiateNodeTemplate(suitableNodeType); + suitableNodeTemplates.add(nodeTemplate); + } + + /** + * map containing the choices for the user selection + */ + Map<TNodeTemplate, List<TEntityTemplate>> entityTemplates = new HashMap<>(); + + TNodeTemplate sourceTemplate = null; + + // the RelationshipTemplate connecting to the placeholder + TRelationshipTemplate connectingRelationshipTemplate = null; + + for (TEntityTemplate entity : topology.getNodeTemplateOrRelationshipTemplate()) { + if (entity instanceof TRelationshipTemplate) { + TRelationshipTemplate rt = (TRelationshipTemplate) entity; + if (((TNodeTemplate) rt.getTargetElement().getRef()).getId().equals(placeHolder.getId())) { + connectingRelationshipTemplate = (TRelationshipTemplate) entity; + sourceTemplate = (TNodeTemplate) connectingRelationshipTemplate.getSourceElement().getRef(); + } + } + } + + for (TNodeTemplate nodeTemplate : suitableNodeTemplates) { + + List<TEntityTemplate> choices = new ArrayList<>(); + + // find matching RelationshipTypes to connect the Node Templates + List<TRelationshipType> suitableRTs = NodeTemplateConnector.findRelationshipType(sourceTemplate, nodeTemplate, toscaAnalyzer, null); + for (TRelationshipType rt : suitableRTs) { + TRelationshipTemplate relationship = ModelUtilities.instantiateRelationshipTemplate(rt, sourceTemplate, nodeTemplate); + choices.add(relationship); + } + entityTemplates.put(nodeTemplate, choices); + } + + templateChoices = new HashMap<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>>(); + templateChoices.put(sourceTemplate, entityTemplates); + + this.placeHolder = placeHolder; + + return connectingRelationshipTemplate; + + } + + /** + * Returns a map containing the choices for the user selection when the topology is completed step by step. + * + * @return the field ntChoices + */ + public Map<TNodeTemplate, Map<TNodeTemplate, List<TEntityTemplate>>> getTemplateChoices() { + return templateChoices; + } + + /** + * Returns the replaced place holder to remove it from the topology. + * + * @return the place holder + */ + public TNodeTemplate getPlaceHolder() { + return placeHolder; + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/package-info.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/package-info.java new file mode 100644 index 0000000..8a097d5 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/completer/package-info.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +/** + * This package contains classes and methods to actually execute the completion for several use cases. + */ +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion.completer; + diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/package-info.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/package-info.java new file mode 100644 index 0000000..bdaf170 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/addons/topologycompleter/topologycompletion/package-info.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +/** + * This package contains classes and methods to execute and manage the completion. + */ +package org.eclipse.winery.topologymodeler.addons.topologycompleter.topologycompletion; + diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/resources/TopologyCompletionResource.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/resources/TopologyCompletionResource.java new file mode 100644 index 0000000..efc36e3 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/resources/TopologyCompletionResource.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +package org.eclipse.winery.topologymodeler.resources; + +import java.io.StringReader; + +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.eclipse.winery.model.tosca.Definitions; +import org.eclipse.winery.model.tosca.TNodeTemplate; +import org.eclipse.winery.model.tosca.TRelationshipTemplate; +import org.eclipse.winery.model.tosca.TServiceTemplate; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.JAXBHelper; +import org.eclipse.winery.topologymodeler.addons.topologycompleter.helper.RESTHelper; + +/** + * This class contains resources used for the topology completion. + * + */ +@Path("/") +public class TopologyCompletionResource { + + /** + * Adds selected {@link TNodeTemplate}s and {@link TRelationshipTemplate}s + * to a topology. + * + * @param topology + * the {@link TTopologyTemplate} as XML string + * @param allChoices + * all possible choices as XML + * @param selectedNodeTemplates + * the selected {@link TNodeTemplate}s as JSON array + * @param selectedRelationshipTemplates + * the selected {@link TRelationshipTemplate}s as JSON array + * @return the enhanced {@link TTopologyTemplate} + */ + @Path("selectionhandler/") + @GET + @Produces(MediaType.TEXT_PLAIN) + public Response handleSelection( + @QueryParam(value = "topology") String topology, + @QueryParam(value = "allChoices") String allChoices, + @QueryParam(value = "selectedNodeTemplates") String selectedNodeTemplates, + @QueryParam(value = "selectedRelationshipTemplates") String selectedRelationshipTemplates) { + return Response + .ok() + .entity(JAXBHelper.addTemplatesToTopology(topology, allChoices, + selectedNodeTemplates, selectedRelationshipTemplates)) + .build(); + } + + /** + * This resource is used to save a {@link TTopologyTemplate} to the repository. + * + * @param topology + * the topology to be saved + * @param templateURL + * the URL the {@link TTopologyTemplate} of the topology template + * @param repositoryURL + * the URL of the repository + * @param topologyName + * the name of the saved {@link TTopologyTemplate} + * @param topologyNamespace + * the namespace of the saved {@link TTopologyTemplate} + * @param overwriteTopology + * whether the {@link TTopologyTemplate} should be overwritten or not + * + * @return whether the save operation has been successful or not + */ + @Path("topologysaver/") + @POST + public Response saveTopology(@FormParam("topology") String topology, + @FormParam(value = "templateURL") String templateURL, + @FormParam(value = "repositoryURL") String repositoryURL, + @FormParam(value = "topologyName") String topologyName, + @FormParam(value = "topologyNamespace") String topologyNamespace, + @FormParam(value = "overwriteTopology") String overwriteTopology) { + try { + + boolean overwrite = Boolean.parseBoolean(overwriteTopology); + + // initiate JaxB context + JAXBContext context; + context = JAXBContext.newInstance(Definitions.class); + StringReader reader = new StringReader(topology); + + // unmarshall the topology XML string + Unmarshaller um; + + um = context.createUnmarshaller(); + + Definitions jaxBDefinitions = (Definitions) um.unmarshal(reader); + TServiceTemplate st = (TServiceTemplate) jaxBDefinitions + .getServiceTemplateOrNodeTypeOrNodeTypeImplementation() + .get(0); + TTopologyTemplate toBeSaved = st.getTopologyTemplate(); + + // depending on the selected save method (overwrite or create new) + // the save method is called + if (overwrite) { + RESTHelper.saveCompleteTopology(toBeSaved, templateURL, true, + "", "", repositoryURL); + } else { + RESTHelper.saveCompleteTopology(toBeSaved, templateURL, false, + topologyName, topologyNamespace, repositoryURL); + } + + return Response.ok().build(); + + } catch (JAXBException e) { + e.printStackTrace(); + return Response.serverError().build(); + } + } +} diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/resources/package-info.java b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/resources/package-info.java new file mode 100644 index 0000000..a06da46 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/java/org/eclipse/winery/topologymodeler/resources/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the resources of the topology modeler + */ +package org.eclipse.winery.topologymodeler.resources;
\ No newline at end of file |