765b07a92560b2af65f4b814b55b2ff468f24518
[vfc/nfvo/wfengine.git] / winery / org.eclipse.winery.repository / src / main / java / org / eclipse / winery / repository / export / TOSCAExportUtil.java
1 /*******************************************************************************
2  * Copyright (c) 2012-2013 University of Stuttgart.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * and the Apache License 2.0 which both accompany this distribution,
6  * and are available at http://www.eclipse.org/legal/epl-v10.html
7  * and http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Contributors:
10  *     Kálmán Képes - initial API and implementation and/or initial documentation
11  *     Oliver Kopp - adapted to new storage model and to TOSCA v1.0
12  *******************************************************************************/
13 package org.eclipse.winery.repository.export;
14
15 import java.io.IOException;
16 import java.io.OutputStream;
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.InvocationTargetException;
19 import java.net.URI;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.SortedSet;
28
29 import javax.xml.XMLConstants;
30 import javax.xml.bind.JAXBException;
31 import javax.xml.bind.Marshaller;
32 import javax.xml.namespace.QName;
33
34 import org.apache.commons.lang3.StringUtils;
35 import org.eclipse.winery.common.ModelUtilities;
36 import org.eclipse.winery.common.RepositoryFileReference;
37 import org.eclipse.winery.common.Util;
38 import org.eclipse.winery.common.constants.QNames;
39 import org.eclipse.winery.common.ids.definitions.ArtifactTemplateId;
40 import org.eclipse.winery.common.ids.definitions.ArtifactTypeId;
41 import org.eclipse.winery.common.ids.definitions.CapabilityTypeId;
42 import org.eclipse.winery.common.ids.definitions.EntityTypeId;
43 import org.eclipse.winery.common.ids.definitions.NodeTypeId;
44 import org.eclipse.winery.common.ids.definitions.NodeTypeImplementationId;
45 import org.eclipse.winery.common.ids.definitions.PolicyTemplateId;
46 import org.eclipse.winery.common.ids.definitions.PolicyTypeId;
47 import org.eclipse.winery.common.ids.definitions.RelationshipTypeId;
48 import org.eclipse.winery.common.ids.definitions.RelationshipTypeImplementationId;
49 import org.eclipse.winery.common.ids.definitions.RequirementTypeId;
50 import org.eclipse.winery.common.ids.definitions.ServiceTemplateId;
51 import org.eclipse.winery.common.ids.definitions.TOSCAComponentId;
52 import org.eclipse.winery.common.ids.definitions.TopologyGraphElementEntityTypeId;
53 import org.eclipse.winery.common.ids.definitions.imports.GenericImportId;
54 import org.eclipse.winery.common.ids.elements.PlanId;
55 import org.eclipse.winery.common.ids.elements.PlansId;
56 import org.eclipse.winery.common.propertydefinitionkv.WinerysPropertiesDefinition;
57 import org.eclipse.winery.model.tosca.Definitions;
58 import org.eclipse.winery.model.tosca.TBoundaryDefinitions;
59 import org.eclipse.winery.model.tosca.TBoundaryDefinitions.Policies;
60 import org.eclipse.winery.model.tosca.TCapability;
61 import org.eclipse.winery.model.tosca.TCapabilityDefinition;
62 import org.eclipse.winery.model.tosca.TDeploymentArtifact;
63 import org.eclipse.winery.model.tosca.TDeploymentArtifacts;
64 import org.eclipse.winery.model.tosca.TEntityTemplate;
65 import org.eclipse.winery.model.tosca.TEntityType;
66 import org.eclipse.winery.model.tosca.TEntityType.PropertiesDefinition;
67 import org.eclipse.winery.model.tosca.TImplementationArtifact;
68 import org.eclipse.winery.model.tosca.TImplementationArtifacts;
69 import org.eclipse.winery.model.tosca.TImport;
70 import org.eclipse.winery.model.tosca.TNodeTemplate;
71 import org.eclipse.winery.model.tosca.TNodeTemplate.Capabilities;
72 import org.eclipse.winery.model.tosca.TNodeTemplate.Requirements;
73 import org.eclipse.winery.model.tosca.TNodeType;
74 import org.eclipse.winery.model.tosca.TNodeType.CapabilityDefinitions;
75 import org.eclipse.winery.model.tosca.TNodeType.RequirementDefinitions;
76 import org.eclipse.winery.model.tosca.TPolicy;
77 import org.eclipse.winery.model.tosca.TRelationshipTemplate;
78 import org.eclipse.winery.model.tosca.TRelationshipType;
79 import org.eclipse.winery.model.tosca.TRelationshipType.ValidSource;
80 import org.eclipse.winery.model.tosca.TRelationshipType.ValidTarget;
81 import org.eclipse.winery.model.tosca.TRequirement;
82 import org.eclipse.winery.model.tosca.TRequirementDefinition;
83 import org.eclipse.winery.repository.JAXBSupport;
84 import org.eclipse.winery.repository.Utils;
85 import org.eclipse.winery.repository.backend.BackendUtils;
86 import org.eclipse.winery.repository.backend.Repository;
87 import org.eclipse.winery.repository.backend.constants.Filename;
88 import org.eclipse.winery.repository.datatypes.ids.elements.ArtifactTemplateDirectoryId;
89 import org.eclipse.winery.repository.datatypes.ids.elements.VisualAppearanceId;
90 import org.eclipse.winery.repository.resources.AbstractComponentInstanceResource;
91 import org.eclipse.winery.repository.resources.AbstractComponentInstanceResourceWithNameDerivedFromAbstractFinal;
92 import org.eclipse.winery.repository.resources.AbstractComponentsResource;
93 import org.eclipse.winery.repository.resources.EntityTypeResource;
94 import org.eclipse.winery.repository.resources.entitytemplates.artifacttemplates.ArtifactTemplateResource;
95 import org.eclipse.winery.repository.resources.entitytemplates.policytemplates.PolicyTemplateResource;
96 import org.eclipse.winery.repository.resources.entitytypeimplementations.nodetypeimplementations.NodeTypeImplementationResource;
97 import org.eclipse.winery.repository.resources.entitytypeimplementations.relationshiptypeimplementations.RelationshipTypeImplementationResource;
98 import org.eclipse.winery.repository.resources.entitytypes.nodetypes.NodeTypeResource;
99 import org.eclipse.winery.repository.resources.entitytypes.relationshiptypes.RelationshipTypeResource;
100 import org.eclipse.winery.repository.resources.entitytypes.requirementtypes.RequirementTypeResource;
101 import org.eclipse.winery.repository.resources.servicetemplates.ServiceTemplateResource;
102 import org.slf4j.ext.XLogger;
103 import org.slf4j.ext.XLoggerFactory;
104 import org.w3c.dom.Document;
105
106 public class TOSCAExportUtil {
107         
108         private static final XLogger logger = XLoggerFactory.getXLogger(TOSCAExportUtil.class);
109         
110         /*
111          * these two are GLOBAL VARIABLES leading to the fact that this class has to
112          * be constructed for each export
113          */
114         
115         // collects the references to be put in the CSAR and the assigned path in
116         // the CSAR MANIFEST
117         // this allows to use other paths in the CSAR than on the local storage
118         private Map<RepositoryFileReference, String> referencesToPathInCSARMap = null;
119         
120         /**
121          * Currently a very simple approach to configure the export
122          */
123         private Map<String, Object> exportConfiguration;
124         
125         
126         public enum ExportProperties {
127                 INCLUDEXYCOORDINATES, REPOSITORY_URI
128         };
129         
130         
131         /**
132          * Writes the <em>complete</em> tosca xml into the given outputstream
133          * 
134          * @param id the id of the TOSCA component instance to export
135          * @param out outputstream to write to
136          * @param addRelatedComponents true: all referenced components
137          *            (artifactTemplates, artifactTypes, ...) are added, false: only
138          *            the XML belonging to the id is exported. If XML types are
139          *            generated by Winery (e.g., the properties XSD for node types),
140          *            these XML types are also exported
141          * @param exportConfiguration the configuration map for the export. Uses
142          * @param exportedState exportedState object to modify. ExportProperties
143          *            provides the possible keys
144          * @return a collection of TOSCAcomponentIds referenced by the given
145          *         component
146          * @throws JAXBException
147          */
148         public Collection<TOSCAComponentId> exportTOSCA(TOSCAComponentId id, OutputStream out, Map<String, Object> exportConfiguration) throws IOException, JAXBException {
149                 this.exportConfiguration = exportConfiguration;
150                 this.initializeExport();
151                 return this.writeDefinitionsElement(id, out);
152         }
153         
154         private void initializeExport() {
155                 this.setDefaultExportConfiguration();
156                 // quick hack to avoid NPE
157                 if (this.referencesToPathInCSARMap == null) {
158                         this.referencesToPathInCSARMap = new HashMap<>();
159                 }
160         }
161         
162         /**
163          * Quick hack to set defaults. Typically, a configuration builder or similar
164          * is used
165          */
166         private void setDefaultExportConfiguration() {
167                 this.checkConfig(ExportProperties.INCLUDEXYCOORDINATES, Boolean.FALSE);
168         }
169         
170         private void checkConfig(ExportProperties propKey, Boolean bo) {
171                 if (!this.exportConfiguration.containsKey(propKey.toString())) {
172                         this.exportConfiguration.put(propKey.toString(), bo);
173                 }
174         }
175         
176         /**
177          * Writes the <em>complete</em> TOSCA XML into the given outputstream.
178          * Additionally, a the artifactMap is filled to enable the CSAR exporter to
179          * create necessary entries in TOSCA-Meta and to add them to the CSAR itself
180          * 
181          * @param id the component instance to export
182          * @param out outputstream to write to
183          * @param exportConfiguration Configures the exporter
184          * @param referencesToPathInCSARMap collects the references to export. It is
185          *            updated during the export
186          * @return a collection of TOSCAcomponentIds referenced by the given
187          *         component
188          * @throws JAXBException
189          */
190         protected Collection<TOSCAComponentId> exportTOSCA(TOSCAComponentId id, OutputStream out, Map<RepositoryFileReference, String> referencesToPathInCSARMap, Map<String, Object> exportConfiguration) throws IOException, JAXBException {
191                 this.referencesToPathInCSARMap = referencesToPathInCSARMap;
192                 return this.exportTOSCA(id, out, exportConfiguration);
193         }
194         
195         /**
196          * Called when the entry resource is definitions backed
197          * 
198          * @throws JAXBException
199          */
200         private void writeDefinitionsElement(Definitions entryDefinitions, OutputStream out) throws JAXBException {
201                 Marshaller m = JAXBSupport.createMarshaller(true);
202                 m.marshal(entryDefinitions, out);
203         }
204         
205         /**
206          * Writes the Definitions belonging to the given TOSCA component to the
207          * outputstream
208          * 
209          * @return a collection of TOSCAcomponentIds referenced by the given
210          *         component
211          * 
212          * @throws IOException
213          * @throws JAXBException
214          * @throws IllegalStateException if tcId does not exist
215          */
216         private Collection<TOSCAComponentId> writeDefinitionsElement(TOSCAComponentId tcId, OutputStream out) throws IOException, JAXBException {
217                 if (!Repository.INSTANCE.exists(tcId)) {
218                         String error = "Component instance " + tcId.toString() + " does not exist.";
219                         TOSCAExportUtil.logger.error(error);
220                         throw new IllegalStateException(error);
221                 }
222                 
223                 AbstractComponentInstanceResource res = AbstractComponentsResource.getComponentInstaceResource(tcId);
224                 Definitions entryDefinitions = res.getDefinitions();
225                 
226                 // BEGIN: Definitions modification
227                 // the "imports" collection contains the imports of Definitions, not of other definitions
228                 // the other definitions are stored in entryDefinitions.getImport()
229                 // we modify the internal definitions object directly. It is not written back to the storage. Therefore, we do not need to clone it
230                 
231                 // the imports (pointing to not-definitions (xsd, wsdl, ...)) already have a correct relative URL. (quick hack)
232                 URI uri = (URI) this.exportConfiguration.get(TOSCAExportUtil.ExportProperties.REPOSITORY_URI.toString());
233                 if (uri != null) {
234                         // we are in the plain-XML mode, the URLs of the imports have to be adjusted
235                         for (TImport i : entryDefinitions.getImport()) {
236                                 String loc = i.getLocation();
237                                 assert (loc.startsWith("../"));
238                                 loc = loc.substring(3);
239                                 loc = uri + loc;
240                                 // now the location is an absolute URL
241                                 i.setLocation(loc);
242                         }
243                 }
244                 
245                 // files of imports have to be added to the CSAR, too
246                 for (TImport i : entryDefinitions.getImport()) {
247                         String loc = i.getLocation();
248                         if (Util.isRelativeURI(loc)) {
249                                 // locally stored, add to CSAR
250                                 GenericImportId iid = new GenericImportId(i);
251                                 String fileName = Util.getLastURIPart(loc);
252                                 fileName = Util.URLdecode(fileName);
253                                 RepositoryFileReference ref = new RepositoryFileReference(iid, fileName);
254                                 this.putRefAsReferencedItemInCSAR(ref);
255                         }
256                 }
257                 
258                 // adjust imports: add imports of definitions to it
259                 Collection<TOSCAComponentId> referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds(tcId);
260                 Collection<TImport> imports = new ArrayList<>();
261                 for (TOSCAComponentId id : referencedTOSCAComponentIds) {
262                         this.addToImports(id, imports);
263                 }
264                 entryDefinitions.getImport().addAll(imports);
265                 
266                 if (res.getElement() instanceof TEntityType) {
267                         // we have an entity type with a possible properties definition
268                         EntityTypeResource entityTypeRes = (EntityTypeResource) res;
269                         WinerysPropertiesDefinition wpd = ModelUtilities.getWinerysPropertiesDefinition(entityTypeRes.getEntityType());
270                         if (wpd != null) {
271                                 if (wpd.getIsDerivedFromXSD() == null) {
272                                         // Write WPD only to file if it exists and is NOT derived from an XSD (which may happen during import)
273                                         
274                                         String wrapperElementNamespace = wpd.getNamespace();
275                                         String wrapperElementLocalName = wpd.getElementName();
276                                         
277                                         // BEGIN: add import and put into CSAR
278                                         
279                                         TImport imp = new TImport();
280                                         entryDefinitions.getImport().add(imp);
281                                         
282                                         // fill known import values
283                                         imp.setImportType(XMLConstants.W3C_XML_SCHEMA_NS_URI);
284                                         imp.setNamespace(wrapperElementNamespace);
285                                         // add "winerysPropertiesDefinition" flag to import tag to support
286                                         Map<QName, String> otherAttributes = imp.getOtherAttributes();
287                                         otherAttributes.put(QNames.QNAME_WINERYS_PROPERTIES_DEFINITION_ATTRIBUTE, "true");
288                                         
289                                         // Determine location
290                                         String loc = BackendUtils.getImportLocationForWinerysPropertiesDefinitionXSD((EntityTypeId) tcId, uri, wrapperElementLocalName);
291                                         if (uri == null) {
292                                                 TOSCAExportUtil.logger.trace("CSAR Export mode. Putting XSD into CSAR");
293                                                 // CSAR Export mode
294                                                 // XSD has to be put into the CSAR
295                                                 Document document = ModelUtilities.getWinerysPropertiesDefinitionXSDAsDocument(wpd);
296                                                 
297                                                 // loc in import is URLencoded, loc on filesystem isn't
298                                                 String locInCSAR = Util.URLdecode(loc);
299                                                 // furthermore, the path has to start from the root of the CSAR; currently, it starts from Definitions/
300                                                 locInCSAR = locInCSAR.substring(3);
301                                                 TOSCAExportUtil.logger.trace("Location in CSAR: {}", locInCSAR);
302                                                 this.referencesToPathInCSARMap.put(new DummyRepositoryFileReferenceForGeneratedXSD(document), locInCSAR);
303                                         }
304                                         imp.setLocation(loc);
305                                         
306                                         // END: add import and put into CSAR
307                                         
308                                         // BEGIN: generate TOSCA conforming PropertiesDefinition
309                                         
310                                         TEntityType entityType = entityTypeRes.getEntityType();
311                                         PropertiesDefinition propertiesDefinition = new PropertiesDefinition();
312                                         propertiesDefinition.setType(new QName(wrapperElementNamespace, wrapperElementLocalName));
313                                         entityType.setPropertiesDefinition(propertiesDefinition);
314                                         
315                                         // END: generate TOSCA conforming PropertiesDefinition
316                                 } else {
317                                         // otherwise WPD exists, but is derived from XSD
318                                         // we DO NOT have to remove the winery properties definition from the output to allow "debugging" of the CSAR
319                                 }
320                         }
321                 }
322                 
323                 // END: Definitions modification
324                 
325                 this.writeDefinitionsElement(entryDefinitions, out);
326                 
327                 return referencedTOSCAComponentIds;
328         }
329         
330         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(EntityTypeId id) {
331                 return this.getReferencedTOSCAComponentIdOfParentForAnAbstractComponentsWithTypeReferenceResource(id);
332         }
333         
334         /**
335          * There is now equivalent id class for
336          * AbstractComponentInstanceResourceWithNameDerivedFromAbstractFinal,
337          * therefore we take the super type and hope that the caller knows what he
338          * does.
339          */
340         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIdOfParentForAnAbstractComponentsWithTypeReferenceResource(TOSCAComponentId id) {
341                 AbstractComponentInstanceResourceWithNameDerivedFromAbstractFinal res = (AbstractComponentInstanceResourceWithNameDerivedFromAbstractFinal) AbstractComponentsResource.getComponentInstaceResource(id);
342                 String derivedFrom = res.getInheritanceManagement().getDerivedFrom();
343                 if (StringUtils.isEmpty(derivedFrom)) {
344                         return Collections.emptySet();
345                 } else {
346                         // Instantiate an id with the same class as the current id
347                         TOSCAComponentId parentId;
348                         QName qname = QName.valueOf(derivedFrom);
349                         
350                         Constructor<? extends TOSCAComponentId> constructor;
351                         try {
352                                 constructor = id.getClass().getConstructor(QName.class);
353                         } catch (NoSuchMethodException | SecurityException e1) {
354                                 throw new IllegalStateException("Could get constructor to instantiate parent id", e1);
355                         }
356                         try {
357                                 parentId = constructor.newInstance(qname);
358                         } catch (InstantiationException | IllegalAccessException
359                                         | IllegalArgumentException | InvocationTargetException e) {
360                                 throw new IllegalStateException("Could not instantiate id for parent", e);
361                         }
362                         
363                         Collection<TOSCAComponentId> result = new ArrayList<>(1);
364                         result.add(parentId);
365                         return result;
366                 }
367         }
368         
369         /**
370          * This method is intended to be used by exportTOSCA. However,
371          * org.eclipse.winery.repository.client requires an XML representation of a
372          * component instances without a surrounding definitions element.
373          * 
374          * We name this method differently to prevent wrong calling due to
375          * inheritance
376          * 
377          * @param definitionsElement the parent XML element
378          */
379         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(TOSCAComponentId id) {
380                 Collection<TOSCAComponentId> referencedTOSCAComponentIds;
381                 
382                 // first of all, handle the concrete elements
383                 if (id instanceof ServiceTemplateId) {
384                         referencedTOSCAComponentIds = this.prepareForExport((ServiceTemplateId) id);
385                 } else if (id instanceof NodeTypeId) {
386                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((NodeTypeId) id);
387                 } else if (id instanceof NodeTypeImplementationId) {
388                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((NodeTypeImplementationId) id);
389                 } else if (id instanceof RelationshipTypeId) {
390                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((RelationshipTypeId) id);
391                 } else if (id instanceof RelationshipTypeImplementationId) {
392                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((RelationshipTypeImplementationId) id);
393                 } else if (id instanceof RequirementTypeId) {
394                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((RequirementTypeId) id);
395                 } else if (id instanceof CapabilityTypeId) {
396                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((CapabilityTypeId) id);
397                 } else if (id instanceof ArtifactTypeId) {
398                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((ArtifactTypeId) id);
399                 } else if (id instanceof ArtifactTemplateId) {
400                         referencedTOSCAComponentIds = this.prepareForExport((ArtifactTemplateId) id);
401                 } else if (id instanceof PolicyTypeId) {
402                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((PolicyTypeId) id);
403                 } else if (id instanceof PolicyTemplateId) {
404                         referencedTOSCAComponentIds = this.getReferencedTOSCAComponentIds((PolicyTemplateId) id);
405                 } else if (id instanceof GenericImportId) {
406                         // in case of imports, there are no other ids referenced
407                         referencedTOSCAComponentIds = Collections.emptyList();
408                 } else {
409                         throw new IllegalStateException("Unhandled id class " + id.getClass());
410                 }
411                 
412                 // Then, handle the super classes, which support inheritance
413                 // Currently, it is EntityType and EntityTypeImplementation only
414                 // Since the latter does not exist in the TOSCA MetaModel, we just handle EntityType here
415                 if (id instanceof EntityTypeId) {
416                         Collection<TOSCAComponentId> additionalRefs = this.getReferencedTOSCAComponentIds((EntityTypeId) id);
417                         // the original referenceTOSCAComponentIds could be unmodifiable
418                         // we just create a new one...
419                         referencedTOSCAComponentIds = new ArrayList<>(referencedTOSCAComponentIds);
420                         // ...and add the new reference
421                         referencedTOSCAComponentIds.addAll(additionalRefs);
422                 }
423                 
424                 return referencedTOSCAComponentIds;
425         }
426         
427         /**
428          * Adds the given id as import to the given imports collection
429          */
430         private void addToImports(TOSCAComponentId id, Collection<TImport> imports) {
431                 TImport imp = new TImport();
432                 imp.setImportType(org.eclipse.winery.common.constants.Namespaces.TOSCA_NAMESPACE);
433                 imp.setNamespace(id.getNamespace().getDecoded());
434                 URI uri = (URI) this.exportConfiguration.get(TOSCAExportUtil.ExportProperties.REPOSITORY_URI.toString());
435                 if (uri == null) {
436                         // self-contained mode
437                         // all Definitions are contained in "Definitions" directory, therefore, we provide the filename only
438                         // references are resolved relatively from a definitions element (COS01, line 425)
439                         String fn = CSARExporter.getDefinitionsFileName(id);
440                         fn = Util.URLencode(fn);
441                         imp.setLocation(fn);
442                 } else {
443                         String path = Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(id));
444                         path = path + "?definitions";
445                         URI absoluteURI = uri.resolve(path);
446                         imp.setLocation(absoluteURI.toString());
447                 }
448                 imports.add(imp);
449                 
450                 // FIXME: Currently the depended elements (such as the artifact templates linked to a node type implementation) are gathered by the corresponding "addXY" method.
451                 // Reason: the corresponding TDefinitions element is *not* updated if a related element is added/removed.
452                 // That means: The imports are not changed.
453                 // The current issue is that TOSCA allows imports of Definitions only and the repository has the concrete elements as main structure
454                 // Although during save the import can be updated (by fetching the associated resource and get the definitions of it),
455                 // The concrete definitions cannot be determined without
456                 //  a) having a complete index of all definitions in the repository
457                 //  b) crawling through the *complete* repository
458                 // Possibly the current solution, just lazily adding all dependent elements is the better solution.
459         }
460         
461         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(NodeTypeImplementationId id) {
462                 // We have to use a HashSet to ensure that no duplicate ids are added
463                 // There may be multiple DAs/IAs referencing the same type
464                 Collection<TOSCAComponentId> ids = new HashSet<>();
465                 
466                 NodeTypeImplementationResource res = new NodeTypeImplementationResource(id);
467                 
468                 // DAs
469                 TDeploymentArtifacts deploymentArtifacts = res.getNTI().getDeploymentArtifacts();
470                 if (deploymentArtifacts != null) {
471                         for (TDeploymentArtifact da : deploymentArtifacts.getDeploymentArtifact()) {
472                                 QName qname;
473                                 if ((qname = da.getArtifactRef()) != null) {
474                                         ids.add(new ArtifactTemplateId(qname));
475                                 }
476                                 ids.add(new ArtifactTypeId(da.getArtifactType()));
477                         }
478                 }
479                 
480                 // IAs
481                 TImplementationArtifacts implementationArtifacts = res.getNTI().getImplementationArtifacts();
482                 if (implementationArtifacts != null) {
483                         for (TImplementationArtifact ia : implementationArtifacts.getImplementationArtifact()) {
484                                 QName qname;
485                                 if ((qname = ia.getArtifactRef()) != null) {
486                                         ids.add(new ArtifactTemplateId(qname));
487                                 }
488                                 ids.add(new ArtifactTypeId(ia.getArtifactType()));
489                         }
490                 }
491                 
492                 // inheritance
493                 ids.addAll(this.getReferencedTOSCAComponentIdOfParentForAnAbstractComponentsWithTypeReferenceResource(id));
494                 
495                 return ids;
496         }
497         
498         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(RelationshipTypeImplementationId id) {
499                 // We have to use a HashSet to ensure that no duplicate ids are added
500                 // There may be multiple IAs referencing the same type
501                 Collection<TOSCAComponentId> ids = new HashSet<>();
502                 
503                 RelationshipTypeImplementationResource res = new RelationshipTypeImplementationResource(id);
504                 
505                 // IAs
506                 for (TImplementationArtifact ia : res.getRTI().getImplementationArtifacts().getImplementationArtifact()) {
507                         QName qname;
508                         if ((qname = ia.getArtifactRef()) != null) {
509                                 ids.add(new ArtifactTemplateId(qname));
510                         }
511                         ids.add(new ArtifactTypeId(ia.getArtifactType()));
512                 }
513                 
514                 // inheritance
515                 ids.addAll(this.getReferencedTOSCAComponentIdOfParentForAnAbstractComponentsWithTypeReferenceResource(id));
516                 
517                 return ids;
518         }
519         
520         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(RequirementTypeId id) {
521                 Collection<TOSCAComponentId> ids = new ArrayList<>(1);
522                 
523                 RequirementTypeResource res = new RequirementTypeResource(id);
524                 QName requiredCapabilityType = res.getRequirementType().getRequiredCapabilityType();
525                 if (requiredCapabilityType != null) {
526                         CapabilityTypeId capId = new CapabilityTypeId(requiredCapabilityType);
527                         ids.add(capId);
528                 }
529                 return ids;
530         }
531         
532         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(CapabilityTypeId id) {
533                 return Collections.emptyList();
534         }
535         
536         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(PolicyTypeId id) {
537                 return Collections.emptyList();
538         }
539         
540         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(PolicyTemplateId id) {
541                 Collection<TOSCAComponentId> ids = new ArrayList<>();
542                 PolicyTemplateResource res = new PolicyTemplateResource(id);
543                 ids.add(new PolicyTypeId(res.getType()));
544                 return ids;
545         }
546         
547         /**
548          * Synchronizes the plan model references and returns the referenced TOSCA
549          * Component Ids.
550          */
551         private Collection<TOSCAComponentId> prepareForExport(ServiceTemplateId id) {
552                 // We have to use a HashSet to ensure that no duplicate ids are added
553                 // E.g., there may be multiple relationship templates having the same type
554                 Collection<TOSCAComponentId> ids = new HashSet<>();
555                 ServiceTemplateResource res = new ServiceTemplateResource(id);
556                 
557                 // ensure that the plans stored locally are the same ones as stored in the definitions
558                 res.synchronizeReferences();
559                 
560                 // add all plans as reference in the CSAR
561                 // the data model is consistent with the repository
562                 // we crawl through the repository to as putRefAsReferencedItemInCSAR expects a repository file reference
563                 PlansId plansContainerId = new PlansId(id);
564                 SortedSet<PlanId> nestedPlans = Repository.INSTANCE.getNestedIds(plansContainerId, PlanId.class);
565                 for (PlanId planId : nestedPlans) {
566                         SortedSet<RepositoryFileReference> containedFiles = Repository.INSTANCE.getContainedFiles(planId);
567                         // even if we currently support only one file in the directory, we just add everything
568                         for (RepositoryFileReference ref : containedFiles) {
569                                 this.putRefAsReferencedItemInCSAR(ref);
570                         }
571                 }
572                 
573                 // add included things to export queue
574                 
575                 TBoundaryDefinitions boundaryDefs;
576                 if ((boundaryDefs = res.getServiceTemplate().getBoundaryDefinitions()) != null) {
577                         Policies policies = boundaryDefs.getPolicies();
578                         if (policies != null) {
579                                 for (TPolicy policy : policies.getPolicy()) {
580                                         PolicyTypeId policyTypeId = new PolicyTypeId(policy.getPolicyType());
581                                         ids.add(policyTypeId);
582                                 }
583                         }
584                         
585                         // reqs and caps don't have to be exported here as they are references to existing reqs/caps (of nested node templates)
586                 }
587                 
588                 if (res.getServiceTemplate().getTopologyTemplate() != null) {
589                         for (TEntityTemplate entityTemplate : res.getServiceTemplate().getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
590                                 QName qname = entityTemplate.getType();
591                                 if (entityTemplate instanceof TNodeTemplate) {
592                                         ids.add(new NodeTypeId(qname));
593                                         TNodeTemplate n = (TNodeTemplate) entityTemplate;
594                                         
595                                         // crawl through deployment artifacts
596                                         TDeploymentArtifacts deploymentArtifacts = n.getDeploymentArtifacts();
597                                         if (deploymentArtifacts != null) {
598                                                 List<TDeploymentArtifact> das = deploymentArtifacts.getDeploymentArtifact();
599                                                 for (TDeploymentArtifact da : das) {
600                                                         ids.add(new ArtifactTypeId(da.getArtifactType()));
601                                                         if ((qname = da.getArtifactRef()) != null) {
602                                                                 ids.add(new ArtifactTemplateId(qname));
603                                                         }
604                                                 }
605                                         }
606                                         
607                                         // crawl through reqs/caps
608                                         Requirements requirements = n.getRequirements();
609                                         if (requirements != null) {
610                                                 for (TRequirement req : requirements.getRequirement()) {
611                                                         QName type = req.getType();
612                                                         RequirementTypeId rtId = new RequirementTypeId(type);
613                                                         ids.add(rtId);
614                                                 }
615                                         }
616                                         Capabilities capabilities = n.getCapabilities();
617                                         if (capabilities != null) {
618                                                 for (TCapability cap : capabilities.getCapability()) {
619                                                         QName type = cap.getType();
620                                                         CapabilityTypeId ctId = new CapabilityTypeId(type);
621                                                         ids.add(ctId);
622                                                 }
623                                         }
624                                         
625                                         // crawl through policies
626                                         org.eclipse.winery.model.tosca.TNodeTemplate.Policies policies = n.getPolicies();
627                                         if (policies != null) {
628                                                 for (TPolicy pol : policies.getPolicy()) {
629                                                         QName type = pol.getPolicyType();
630                                                         PolicyTypeId ctId = new PolicyTypeId(type);
631                                                         ids.add(ctId);
632                                                 }
633                                         }
634                                 } else {
635                                         assert (entityTemplate instanceof TRelationshipTemplate);
636                                         ids.add(new RelationshipTypeId(qname));
637                                 }
638                         }
639                 }
640                 
641                 return ids;
642         }
643         
644         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(ArtifactTypeId id) {
645                 // no recursive crawling needed
646                 return Collections.emptyList();
647         }
648         
649         /**
650          * Determines the referenced TOSCA Component Ids and also updates the
651          * references in the Artifact Template
652          * 
653          * @return a collection of referenced TOCSA Component Ids
654          */
655         private Collection<TOSCAComponentId> prepareForExport(ArtifactTemplateId id) {
656                 Collection<TOSCAComponentId> ids = new ArrayList<>();
657                 
658                 ArtifactTemplateResource res = new ArtifactTemplateResource(id);
659                 
660                 // "Export" type
661                 QName type = res.getType();
662                 if (type == null) {
663                         throw new IllegalStateException("Type is null for " + id.toString());
664                 }
665                 ids.add(new ArtifactTypeId(type));
666                 
667                 // Export files
668                 
669                 // This method is called BEFORE the concrete definitions element is written.
670                 // Therefore, we adapt the content of the attached files to the really existing files
671                 res.synchronizeReferences();
672                 
673                 ArtifactTemplateDirectoryId fileDir = new ArtifactTemplateDirectoryId(id);
674                 SortedSet<RepositoryFileReference> files = Repository.INSTANCE.getContainedFiles(fileDir);
675                 for (RepositoryFileReference ref : files) {
676                         // Even if writing a TOSCA only (!this.writingCSAR),
677                         // we put the virtual path in the TOSCA
678                         // Reason: Winery is mostly used as a service and local storage
679                         // reference to not make sense
680                         // The old implementation had absolutePath.toUri().toString();
681                         // there, but this does not work when using a cloud blob store.
682                         
683                         this.putRefAsReferencedItemInCSAR(ref);
684                 }
685                 
686                 return ids;
687         }
688         
689         /**
690          * Puts the given reference as item in the CSAR
691          * 
692          * Thereby, it uses the global variable referencesToPathInCSARMap
693          */
694         private void putRefAsReferencedItemInCSAR(RepositoryFileReference ref) {
695                 // Determine path
696                 String path = BackendUtils.getPathInsideRepo(ref);
697                 
698                 // put mapping reference to path into global map
699                 // the path is the same as put in "synchronizeReferences"
700                 this.referencesToPathInCSARMap.put(ref, path);
701         }
702         
703         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(RelationshipTypeId id) {
704                 Collection<TOSCAComponentId> ids = new ArrayList<>();
705                 
706                 // add all implementations
707                 Collection<RelationshipTypeImplementationId> allTypeImplementations = BackendUtils.getAllElementsRelatedWithATypeAttribute(RelationshipTypeImplementationId.class, id.getQName());
708                 for (RelationshipTypeImplementationId ntiId : allTypeImplementations) {
709                         ids.add(ntiId);
710                 }
711                 
712                 RelationshipTypeResource res = new RelationshipTypeResource(id);
713                 TRelationshipType relationshipType = (TRelationshipType) res.getElement();
714                 
715                 ValidSource validSource = relationshipType.getValidSource();
716                 if (validSource != null) {
717                         QName typeRef = validSource.getTypeRef();
718                         // can be a node type or a requirement type
719                         
720                         // similar code as for valid target (difference: req/cap)
721                         NodeTypeId ntId = new NodeTypeId(typeRef);
722                         if (Repository.INSTANCE.exists(ntId)) {
723                                 ids.add(ntId);
724                         } else {
725                                 RequirementTypeId rtId = new RequirementTypeId(typeRef);
726                                 ids.add(rtId);
727                         }
728                 }
729                 
730                 ValidTarget validTarget = relationshipType.getValidTarget();
731                 if (validTarget != null) {
732                         QName typeRef = validTarget.getTypeRef();
733                         // can be a node type or a capability type
734                         
735                         // similar code as for valid target (difference: req/cap)
736                         NodeTypeId ntId = new NodeTypeId(typeRef);
737                         if (Repository.INSTANCE.exists(ntId)) {
738                                 ids.add(ntId);
739                         } else {
740                                 CapabilityTypeId capId = new CapabilityTypeId(typeRef);
741                                 ids.add(capId);
742                         }
743                 }
744                 
745                 this.addVisualAppearanceToCSAR(id);
746                 
747                 return ids;
748         }
749         
750         private Collection<TOSCAComponentId> getReferencedTOSCAComponentIds(NodeTypeId id) {
751                 Collection<TOSCAComponentId> ids = new ArrayList<>();
752                 Collection<NodeTypeImplementationId> allNodeTypeImplementations = BackendUtils.getAllElementsRelatedWithATypeAttribute(NodeTypeImplementationId.class, id.getQName());
753                 for (NodeTypeImplementationId ntiId : allNodeTypeImplementations) {
754                         ids.add(ntiId);
755                 }
756                 
757                 NodeTypeResource res = new NodeTypeResource(id);
758                 TNodeType nodeType = (TNodeType) res.getElement();
759                 
760                 // add all referenced requirement types
761                 RequirementDefinitions reqDefsContainer = nodeType.getRequirementDefinitions();
762                 if (reqDefsContainer != null) {
763                         List<TRequirementDefinition> reqDefs = reqDefsContainer.getRequirementDefinition();
764                         for (TRequirementDefinition reqDef : reqDefs) {
765                                 RequirementTypeId reqTypeId = new RequirementTypeId(reqDef.getRequirementType());
766                                 ids.add(reqTypeId);
767                         }
768                 }
769                 
770                 // add all referenced capability types
771                 CapabilityDefinitions capDefsContainer = nodeType.getCapabilityDefinitions();
772                 if (capDefsContainer != null) {
773                         List<TCapabilityDefinition> capDefs = capDefsContainer.getCapabilityDefinition();
774                         for (TCapabilityDefinition capDef : capDefs) {
775                                 CapabilityTypeId capTypeId = new CapabilityTypeId(capDef.getCapabilityType());
776                                 ids.add(capTypeId);
777                         }
778                 }
779                 
780                 this.addVisualAppearanceToCSAR(id);
781                 
782                 return ids;
783         }
784         
785         private void addVisualAppearanceToCSAR(TopologyGraphElementEntityTypeId id) {
786                 VisualAppearanceId visId = new VisualAppearanceId(id);
787                 if (Repository.INSTANCE.exists(visId)) {
788                         // we do NOT check for the id, but simply check for bigIcon.png (only exists in NodeType) and smallIcon.png (exists in NodeType and RelationshipType)
789                         
790                         RepositoryFileReference ref = new RepositoryFileReference(visId, Filename.FILENAME_BIG_ICON);
791                         if (Repository.INSTANCE.exists(ref)) {
792                                 this.referencesToPathInCSARMap.put(ref, BackendUtils.getPathInsideRepo(ref));
793                         }
794                         
795                         ref = new RepositoryFileReference(visId, Filename.FILENAME_SMALL_ICON);
796                         if (Repository.INSTANCE.exists(ref)) {
797                                 this.referencesToPathInCSARMap.put(ref, BackendUtils.getPathInsideRepo(ref));
798                         }
799                 }
800         }
801         
802 }