Add winery source code
[vfc/nfvo/wfengine.git] / winery / org.eclipse.winery.repository / src / main / java / org / eclipse / winery / repository / backend / BackendUtils.java
1 /*******************************************************************************
2  * Copyright (c) 2012-2013,2015 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  *     Oliver Kopp - initial API and implementation
11  *******************************************************************************/
12 package org.eclipse.winery.repository.backend;
13
14 import java.io.ByteArrayInputStream;
15 import java.io.ByteArrayOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.InputStreamReader;
19 import java.io.Reader;
20 import java.io.UnsupportedEncodingException;
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.net.URI;
24 import java.nio.file.attribute.FileTime;
25 import java.text.ParseException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.SortedSet;
35
36 import javax.ws.rs.core.HttpHeaders;
37 import javax.ws.rs.core.MediaType;
38 import javax.ws.rs.core.Response;
39 import javax.ws.rs.core.Response.ResponseBuilder;
40 import javax.ws.rs.core.Response.Status;
41 import javax.xml.XMLConstants;
42 import javax.xml.bind.JAXBException;
43 import javax.xml.bind.Marshaller;
44 import javax.xml.namespace.QName;
45
46 import org.apache.commons.configuration.Configuration;
47 import org.apache.commons.lang3.StringUtils;
48 import org.apache.commons.lang3.time.DateUtils;
49 import org.apache.xerces.impl.dv.XSSimpleType;
50 import org.apache.xerces.impl.xs.XSImplementationImpl;
51 import org.apache.xerces.xs.XSComplexTypeDefinition;
52 import org.apache.xerces.xs.XSElementDeclaration;
53 import org.apache.xerces.xs.XSImplementation;
54 import org.apache.xerces.xs.XSLoader;
55 import org.apache.xerces.xs.XSModel;
56 import org.apache.xerces.xs.XSModelGroup;
57 import org.apache.xerces.xs.XSObjectList;
58 import org.apache.xerces.xs.XSParticle;
59 import org.apache.xerces.xs.XSTerm;
60 import org.apache.xerces.xs.XSTypeDefinition;
61 import org.eclipse.winery.common.ModelUtilities;
62 import org.eclipse.winery.common.RepositoryFileReference;
63 import org.eclipse.winery.common.Util;
64 import org.eclipse.winery.common.ids.GenericId;
65 import org.eclipse.winery.common.ids.IdUtil;
66 import org.eclipse.winery.common.ids.Namespace;
67 import org.eclipse.winery.common.ids.definitions.EntityTypeId;
68 import org.eclipse.winery.common.ids.definitions.NodeTypeImplementationId;
69 import org.eclipse.winery.common.ids.definitions.TOSCAComponentId;
70 import org.eclipse.winery.common.ids.definitions.imports.GenericImportId;
71 import org.eclipse.winery.common.ids.elements.PlansId;
72 import org.eclipse.winery.common.ids.elements.TOSCAElementId;
73 import org.eclipse.winery.common.propertydefinitionkv.PropertyDefinitionKV;
74 import org.eclipse.winery.common.propertydefinitionkv.PropertyDefinitionKVList;
75 import org.eclipse.winery.common.propertydefinitionkv.WinerysPropertiesDefinition;
76 import org.eclipse.winery.model.tosca.Definitions;
77 import org.eclipse.winery.model.tosca.ObjectFactory;
78 import org.eclipse.winery.model.tosca.TDeploymentArtifact;
79 import org.eclipse.winery.model.tosca.TDeploymentArtifacts;
80 import org.eclipse.winery.model.tosca.TEntityTemplate;
81 import org.eclipse.winery.model.tosca.TEntityType;
82 import org.eclipse.winery.model.tosca.TEntityType.PropertiesDefinition;
83 import org.eclipse.winery.model.tosca.TExtensibleElements;
84 import org.eclipse.winery.model.tosca.TImplementationArtifacts;
85 import org.eclipse.winery.model.tosca.TImplementationArtifacts.ImplementationArtifact;
86 import org.eclipse.winery.model.tosca.TNodeTemplate;
87 import org.eclipse.winery.model.tosca.TServiceTemplate;
88 import org.eclipse.winery.model.tosca.TTopologyTemplate;
89 import org.eclipse.winery.repository.Constants;
90 import org.eclipse.winery.repository.JAXBSupport;
91 import org.eclipse.winery.repository.Utils;
92 import org.eclipse.winery.repository.backend.constants.Filename;
93 import org.eclipse.winery.repository.datatypes.ids.admin.AdminId;
94 import org.eclipse.winery.repository.datatypes.ids.elements.VisualAppearanceId;
95 import org.eclipse.winery.repository.resources.AbstractComponentsResource;
96 import org.eclipse.winery.repository.resources.IHasTypeReference;
97 import org.eclipse.winery.repository.resources._support.IPersistable;
98 import org.eclipse.winery.repository.resources.admin.NamespacesResource;
99 import org.eclipse.winery.repository.resources.entitytypeimplementations.nodetypeimplementations.NodeTypeImplementationResource;
100 import org.eclipse.winery.repository.resources.entitytypes.TopologyGraphElementEntityTypeResource;
101 import org.eclipse.winery.repository.resources.imports.xsdimports.XSDImportsResource;
102 import org.slf4j.Logger;
103 import org.slf4j.LoggerFactory;
104 import org.w3c.dom.ls.LSInput;
105
106 import com.sun.jersey.core.header.ContentDisposition;
107
108 /**
109  * Contains generic utility functions for the Backend
110  * 
111  * Contains everything that is useful for our ids etc. Does <em>not</em> contain
112  * anything that has to do with resources
113  */
114 public class BackendUtils {
115         
116         private static final Logger logger = LoggerFactory.getLogger(BackendUtils.class);
117         
118         
119         /**
120          * Deletes given file/dir and returns appropriate response code
121          */
122         public static Response delete(GenericId id) {
123                 if (!Repository.INSTANCE.exists(id)) {
124                         return Response.status(Status.NOT_FOUND).build();
125                 }
126                 try {
127                         Repository.INSTANCE.forceDelete(id);
128                 } catch (IOException e) {
129                         BackendUtils.logger.error(e.getMessage(), e);
130                         return Response.serverError().entity(e.getMessage()).build();
131                 }
132                 return Response.noContent().build();
133         }
134         
135         /**
136          * Deletes given file and returns appropriate response code
137          */
138         public static Response delete(RepositoryFileReference ref) {
139                 if (!Repository.INSTANCE.exists(ref)) {
140                         return Response.status(Status.NOT_FOUND).build();
141                 }
142                 try {
143                         Repository.INSTANCE.forceDelete(ref);
144                 } catch (IOException e) {
145                         BackendUtils.logger.error(e.getMessage(), e);
146                         return Response.serverError().entity(e.getMessage()).build();
147                 }
148                 return Response.ok().build();
149         }
150         
151         /**
152          * Generates given TOSCA element and returns appropriate response code <br  />
153          * 
154          * In the case of an existing resource, the other possible return code is
155          * 302. This code has no Status constant, therefore we use Status.CONFLICT,
156          * which is also possible.
157          * 
158          * @return <ul>
159          *         <li>
160          *         <ul>
161          *         <li>Status.CREATED (201) if the resource has been created,</li>
162          *         <li>Status.CONFLICT if the resource already exists,</li>
163          *         <li>Status.INTERNAL_SERVER_ERROR (500) if something went wrong</li>
164          *         </ul>
165          *         </li>
166          *         <li>URI: the absolute URI of the newly created resource</li>
167          *         </ul>
168          */
169         public static ResourceCreationResult create(GenericId id) {
170                 ResourceCreationResult res = new ResourceCreationResult();
171                 if (Repository.INSTANCE.exists(id)) {
172                         // res.setStatus(302);
173                         res.setStatus(Status.CONFLICT);
174                 } else {
175                         if (Repository.INSTANCE.flagAsExisting(id)) {
176                                 res.setStatus(Status.CREATED);
177                                 // @formatter:off
178                                 // This method is a generic method
179                                 // We cannot return an "absolute" URL as the URL is always
180                                 // relative to the caller
181                                 // Does not work: String path = Prefs.INSTANCE.getResourcePath()
182                                 // + "/" +
183                                 // Utils.getURLforPathInsideRepo(id.getPathInsideRepo());
184                                 // We distinguish between two cases: TOSCAcomponentId and
185                                 // TOSCAelementId
186                                 // @formatter:on
187                                 String path;
188                                 if (id instanceof TOSCAComponentId) {
189                                         // here, we return namespace + id, as it is only possible to
190                                         // post on the TOSCA component*s* resource to create an
191                                         // instance of a TOSCA component
192                                         TOSCAComponentId tcId = (TOSCAComponentId) id;
193                                         path = tcId.getNamespace().getEncoded() + "/" + tcId.getXmlId().getEncoded() + "/";
194                                 } else {
195                                         assert (id instanceof TOSCAElementId);
196                                         // We just return the id as we assume that only the parent
197                                         // of this id may create sub elements
198                                         path = id.getXmlId().getEncoded() + "/";
199                                 }
200                                 // we have to encode it twice to get correct URIs
201                                 path = Utils.getURLforPathInsideRepo(path);
202                                 URI uri = Utils.createURI(path);
203                                 res.setUri(uri);
204                                 res.setId(id);
205                         } else {
206                                 res.setStatus(Status.INTERNAL_SERVER_ERROR);
207                         }
208                 }
209                 return res;
210         }
211         
212         /**
213          * 
214          * Sends the file if modified and "not modified" if not modified future work
215          * may put each file with a unique id in a separate folder in tomcat * use
216          * that static URL for each file * if file is modified, URL of file changes
217          * * -> client always fetches correct file
218          * 
219          * additionally "Vary: Accept" header is added (enables caching of the
220          * response)
221          * 
222          * method header for calling method public <br />
223          * <code>Response getXY(@HeaderParam("If-Modified-Since") String modified) {...}</code>
224          * 
225          * 
226          * @param ref references the file to be send
227          * @param modified - HeaderField "If-Modified-Since" - may be "null"
228          * @return Response to be sent to the client
229          */
230         public static Response returnRepoPath(RepositoryFileReference ref, String modified) {
231                 return BackendUtils.returnRefAsResponseBuilder(ref, modified).build();
232         }
233         
234         /**
235          * @return true if given fileDate is newer then the modified date (or
236          *         modified is null)
237          */
238         public static boolean isFileNewerThanModifiedDate(long millis, String modified) {
239                 if (modified == null) {
240                         return true;
241                 }
242                 
243                 Date modifiedDate = null;
244                 
245                 assert (Locale.getDefault() == Locale.ENGLISH);
246                 try {
247                         modifiedDate = DateUtils.parseDate(modified, org.apache.http.impl.cookie.DateUtils.DEFAULT_PATTERNS);
248                 } catch (ParseException e) {
249                         BackendUtils.logger.error(e.getMessage(), e);
250                 }
251                 
252                 if (modifiedDate != null) {
253                         // modifiedDate does not carry milliseconds, but fileDate does
254                         // therefore we have to do a range-based comparison
255                         if ((millis - modifiedDate.getTime()) < DateUtils.MILLIS_PER_SECOND) {
256                                 return false;
257                         }
258                 }
259                 
260                 return true;
261         }
262         
263         /**
264          * This is not repository specific, but we leave it close to the only caller
265          * 
266          * If the passed ref is newer than the modified date (or the modified date
267          * is null), an OK response with an inputstream pointing to the path is
268          * returned
269          */
270         private static ResponseBuilder returnRefAsResponseBuilder(RepositoryFileReference ref, String modified) {
271                 if (!Repository.INSTANCE.exists(ref)) {
272                         return Response.status(Status.NOT_FOUND);
273                 }
274                 
275                 FileTime lastModified;
276                 try {
277                         lastModified = Repository.INSTANCE.getLastModifiedTime(ref);
278                 } catch (IOException e1) {
279                         BackendUtils.logger.debug("Could not get lastModifiedTime", e1);
280                         return Response.serverError();
281                 }
282                 
283                 // do we really need to send the file or can send "not modified"?
284                 if (!BackendUtils.isFileNewerThanModifiedDate(lastModified.toMillis(), modified)) {
285                         return Response.status(Status.NOT_MODIFIED);
286                 }
287                 
288                 ResponseBuilder res;
289                 try {
290                         res = Response.ok(Repository.INSTANCE.newInputStream(ref));
291                 } catch (IOException e) {
292                         BackendUtils.logger.debug("Could not open input stream", e);
293                         return Response.serverError();
294                 }
295                 res = res.lastModified(new Date(lastModified.toMillis()));
296                 // vary:accept header is always set to be safe
297                 res = res.header(HttpHeaders.VARY, HttpHeaders.ACCEPT);
298                 // determine and set MIME content type
299                 try {
300                         res = res.header(HttpHeaders.CONTENT_TYPE, Repository.INSTANCE.getMimeType(ref));
301                 } catch (IOException e) {
302                         BackendUtils.logger.debug("Could not determine mime type", e);
303                         return Response.serverError();
304                 }
305                 // set filename
306                 ContentDisposition contentDisposition = ContentDisposition.type("attachment").fileName(ref.getFileName()).modificationDate(new Date(lastModified.toMillis())).build();
307                 res.header("Content-Disposition", contentDisposition);
308                 return res;
309         }
310         
311         /**
312          * Updates the given property in the given configuration. Currently always
313          * returns "no content", because the underlying class does not report any
314          * errors during updating. <br />
315          * 
316          * If null or "" is passed as value, the property is cleared
317          * 
318          * @return Status.NO_CONTENT
319          */
320         public static Response updateProperty(Configuration configuration, String property, String val) {
321                 if (StringUtils.isBlank(val)) {
322                         configuration.clearProperty(property);
323                 } else {
324                         configuration.setProperty(property, val);
325                 }
326                 return Response.noContent().build();
327         }
328         
329         /**
330          * Persists the resource and returns appropriate response
331          */
332         public static Response persist(IPersistable res) {
333                 Response r;
334                 try {
335                         res.persist();
336                 } catch (IOException e) {
337                         BackendUtils.logger.debug("Could not persist resource", e);
338                         r = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
339                         return r;
340                 }
341                 r = Response.noContent().build();
342                 return r;
343         }
344         
345         /**
346          * Writes data to file. Replaces the file's content with the given content.
347          * The file does not need to exist
348          * 
349          * @param ref Reference to the File to write to (overwrite)
350          * @param content the data to write
351          * @return a JAX-RS Response containing the result. NOCONTENT if successful,
352          *         InternalSeverError otherwise
353          */
354         public static Response putContentToFile(RepositoryFileReference ref, String content, MediaType mediaType) {
355                 try {
356                         Repository.INSTANCE.putContentToFile(ref, content, mediaType);
357                 } catch (IOException e) {
358                         BackendUtils.logger.error(e.getMessage(), e);
359                         return Response.serverError().entity(e.getMessage()).build();
360                 }
361                 return Response.noContent().build();
362         }
363         
364         public static Response putContentToFile(RepositoryFileReference ref, InputStream inputStream, MediaType mediaType) {
365                 try {
366                         Repository.INSTANCE.putContentToFile(ref, inputStream, mediaType);
367                 } catch (IOException e) {
368                         BackendUtils.logger.error(e.getMessage(), e);
369                         return Response.serverError().entity(e.getMessage()).build();
370                 }
371                 return Response.noContent().build();
372         }
373         
374         public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, String qnameStr) {
375                 QName qname = QName.valueOf(qnameStr);
376                 return BackendUtils.getTOSCAcomponentId(idClass, qname.getNamespaceURI(), qname.getLocalPart(), false);
377         }
378         
379         public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, QName qname) {
380                 // we got two implementation possibilities: one is to directly use the
381                 // QName constructor,
382                 // the other is to use a namespace, localname, urlencoded constructor
383                 // we opt for the latter one, which forces the latter constructor to
384                 // exist at all ids
385                 return BackendUtils.getTOSCAcomponentId(idClass, qname.getNamespaceURI(), qname.getLocalPart(), false);
386         }
387         
388         public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, String namespace, String id, boolean URLencoded) {
389                 Constructor<T> constructor;
390                 try {
391                         constructor = idClass.getConstructor(String.class, String.class, boolean.class);
392                 } catch (NoSuchMethodException | SecurityException e) {
393                         BackendUtils.logger.error("Could not get constructor for id " + idClass.getName(), e);
394                         throw new IllegalStateException(e);
395                 }
396                 T tcId;
397                 try {
398                         tcId = constructor.newInstance(namespace, id, URLencoded);
399                 } catch (InstantiationException | IllegalAccessException
400                                 | IllegalArgumentException | InvocationTargetException e) {
401                         BackendUtils.logger.error("Could not create id instance", e);
402                         throw new IllegalStateException(e);
403                 }
404                 return tcId;
405         }
406         
407         /**
408          * @param id the id to determine the namespace of the parent for
409          * @return the namespace of the first TOSCAcomponentId found in the ID
410          *         hierarchy
411          */
412         public static Namespace getNamespace(TOSCAElementId id) {
413                 GenericId parent = id.getParent();
414                 while (!(parent instanceof TOSCAComponentId)) {
415                         parent = parent.getParent();
416                 }
417                 return ((TOSCAComponentId) parent).getNamespace();
418         }
419         
420         public static String getName(TOSCAComponentId instanceId) {
421                 // TODO: Here is a performance issue as we don't use caching or a database
422                 // Bad, but without performance loss: Use "text = instanceId.getXmlId().getDecoded();"
423                 TExtensibleElements instanceElement = AbstractComponentsResource.getComponentInstaceResource(instanceId).getElement();
424                 return ModelUtilities.getNameWithIdFallBack(instanceElement);
425         }
426         
427 /**
428          * Do <em>not</em> use this for creating URLs. Use
429          *
430          * {@link org.eclipse.winery.repository.Utils.getURLforPathInsideRepo(String)}
431          *
432          * or
433          *
434          * {@link org.eclipse.winery.repository.Utils.getAbsoluteURL(GenericId)
435          * instead.
436          *
437          * @return the path starting from the root element to the current element.
438          *         Separated by "/", URLencoded, but <b>not</b> double encoded. With
439          *         trailing slash if sub-resources can exist
440          * @throws IllegalStateException if id is of an unknown subclass of id
441          */
442         public static String getPathInsideRepo(GenericId id) {
443                 if (id == null) {
444                         throw new NullPointerException("id is null");
445                 }
446                 
447                 // for creating paths see also org.eclipse.winery.repository.Utils.getIntermediateLocationStringForType(String, String)
448                 // and org.eclipse.winery.common.Util.getRootPathFragment(Class<? extends TOSCAcomponentId>)
449                 if (id instanceof AdminId) {
450                         return "admin/" + id.getXmlId().getEncoded() + "/";
451                 } else if (id instanceof GenericImportId) {
452                         GenericImportId i = (GenericImportId) id;
453                         String res = "imports/";
454                         res = res + Util.URLencode(i.getType()) + "/";
455                         res = res + i.getNamespace().getEncoded() + "/";
456                         res = res + i.getXmlId().getEncoded() + "/";
457                         return res;
458                 } else if (id instanceof TOSCAComponentId) {
459                         return IdUtil.getPathFragment(id);
460                 } else if (id instanceof TOSCAElementId) {
461                         // we cannot reuse IdUtil.getPathFragment(id) as this TOSCAelementId
462                         // might be nested in an AdminId
463                         return BackendUtils.getPathInsideRepo(id.getParent()) + id.getXmlId().getEncoded() + "/";
464                 } else {
465                         throw new IllegalStateException("Unknown subclass of GenericId " + id.getClass());
466                 }
467         }
468         
469 /**
470          * Do <em>not</em> use this for creating URLs. Use
471          *
472          * {@link org.eclipse.winery.repository.Utils.getURLforPathInsideRepo(String)}
473          *
474          * or
475          *
476          * {@link org.eclipse.winery.repository.Utils.getAbsoluteURL(GenericId)
477          * instead.
478          *
479          * @return the path starting from the root element to the current element.
480          *         Separated by "/", parent URLencoded. Without trailing slash.
481          */
482         public static String getPathInsideRepo(RepositoryFileReference ref) {
483                 return BackendUtils.getPathInsideRepo(ref.getParent()) + ref.getFileName();
484         }
485         
486         /**
487          * Returns the reference to the definitions XML storing the TOSCA for the
488          * given id
489          * 
490          * @param id the id to lookup
491          * @return the reference
492          */
493         public static RepositoryFileReference getRefOfDefinitions(TOSCAComponentId id) {
494                 String name = Util.getTypeForComponentId(id.getClass());
495                 name = name + Constants.SUFFIX_TOSCA_DEFINITIONS;
496                 RepositoryFileReference ref = new RepositoryFileReference(id, name);
497                 return ref;
498         }
499         
500         /**
501          * Returns the reference to the properties file storing the TOSCA
502          * information for the given id
503          * 
504          * @param id the id to lookup
505          * @return the reference
506          */
507         public static RepositoryFileReference getRefOfConfiguration(GenericId id) {
508                 String name;
509                 // Hack to determine file name
510                 if (id instanceof TOSCAComponentId) {
511                         name = Util.getTypeForComponentId(((TOSCAComponentId) id).getClass());
512                         name = name + Constants.SUFFIX_PROPERTIES;
513                 } else if (id instanceof AdminId) {
514                         name = Utils.getTypeForAdminId(((AdminId) id).getClass());
515                         name = name + Constants.SUFFIX_PROPERTIES;
516                 } else {
517                         assert (id instanceof TOSCAElementId);
518                         TOSCAElementId tId = (TOSCAElementId) id;
519                         if (tId instanceof PlansId) {
520                                 name = Filename.FILENAME_PROPERTIES_PLANCONTAINER;
521                         } else if (tId instanceof VisualAppearanceId) {
522                                 // quick hack for special name here
523                                 name = Filename.FILENAME_PROPERTIES_VISUALAPPEARANCE;
524                         } else {
525                                 name = Util.getTypeForElementId(tId.getClass()) + Constants.SUFFIX_PROPERTIES;
526                         }
527                 }
528                 
529                 RepositoryFileReference ref = new RepositoryFileReference(id, name);
530                 return ref;
531         }
532         
533         /**
534          * @param qNameOfTheType the QName of the type, where all TOSCAComponentIds,
535          *            where the associated element points to the type
536          * @param clazz the Id class of the entities to discover
537          */
538         public static <X extends TOSCAComponentId> Collection<X> getAllElementsRelatedWithATypeAttribute(Class<X> clazz, QName qNameOfTheType) {
539                 // we do not use any database system,
540                 // therefore we have to crawl through each node type implementation by ourselves
541                 SortedSet<X> allIds = Repository.INSTANCE.getAllTOSCAComponentIds(clazz);
542                 Collection<X> res = new HashSet<>();
543                 for (X id : allIds) {
544                         IHasTypeReference resource;
545                         try {
546                                 resource = (IHasTypeReference) AbstractComponentsResource.getComponentInstaceResource(id);
547                         } catch (ClassCastException e) {
548                                 String error = "Requested following the type, but the component instance does not implmenet IHasTypeReference";
549                                 BackendUtils.logger.error(error);
550                                 throw new IllegalStateException(error);
551                         }
552                         // The resource may have been freshly initialized due to existence of a directory
553                         // then it has no node type assigned leading to ntiRes.getType() being null
554                         // we ignore this error here
555                         if (qNameOfTheType.equals(resource.getType())) {
556                                 // the component instance is an implementation of the associated node type
557                                 res.add(id);
558                         }
559                 }
560                 return res;
561         }
562         
563         /**
564          * Returns a list of the topology template nested in the given service
565          * template
566          */
567         public static List<TNodeTemplate> getAllNestedNodeTemplates(TServiceTemplate serviceTemplate) {
568                 List<TNodeTemplate> l = new ArrayList<TNodeTemplate>();
569                 TTopologyTemplate topologyTemplate = serviceTemplate.getTopologyTemplate();
570                 if (topologyTemplate == null) {
571                         return Collections.emptyList();
572                 }
573                 for (TEntityTemplate t : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
574                         if (t instanceof TNodeTemplate) {
575                                 l.add((TNodeTemplate) t);
576                         }
577                 }
578                 return l;
579         }
580         
581         private static Collection<QName> getAllReferencedArtifactTemplates(TDeploymentArtifacts tDeploymentArtifacts) {
582                 if (tDeploymentArtifacts == null) {
583                         return Collections.emptyList();
584                 }
585                 List<TDeploymentArtifact> deploymentArtifacts = tDeploymentArtifacts.getDeploymentArtifact();
586                 if (deploymentArtifacts == null) {
587                         return Collections.emptyList();
588                 }
589                 Collection<QName> res = new ArrayList<>();
590                 for (TDeploymentArtifact da : deploymentArtifacts) {
591                         QName artifactRef = da.getArtifactRef();
592                         if (artifactRef != null) {
593                                 res.add(artifactRef);
594                         }
595                 }
596                 return res;
597         }
598         
599         private static Collection<QName> getAllReferencedArtifactTemplates(TImplementationArtifacts tImplementationArtifacts) {
600                 if (tImplementationArtifacts == null) {
601                         return Collections.emptyList();
602                 }
603                 List<ImplementationArtifact> implementationArtifacts = tImplementationArtifacts.getImplementationArtifact();
604                 if (implementationArtifacts == null) {
605                         return Collections.emptyList();
606                 }
607                 Collection<QName> res = new ArrayList<>();
608                 for (ImplementationArtifact ia : implementationArtifacts) {
609                         QName artifactRef = ia.getArtifactRef();
610                         if (artifactRef != null) {
611                                 res.add(artifactRef);
612                         }
613                 }
614                 return res;
615         }
616         
617         public static Collection<QName> getArtifactTemplatesOfReferencedDeploymentArtifacts(TNodeTemplate nodeTemplate) {
618                 List<QName> l = new ArrayList<QName>();
619                 
620                 // DAs may be assigned directly to a node template
621                 Collection<QName> allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(nodeTemplate.getDeploymentArtifacts());
622                 l.addAll(allReferencedArtifactTemplates);
623                 
624                 // DAs may be assigned via node type implementations
625                 QName nodeTypeQName = nodeTemplate.getType();
626                 Collection<NodeTypeImplementationId> allNodeTypeImplementations = BackendUtils.getAllElementsRelatedWithATypeAttribute(NodeTypeImplementationId.class, nodeTypeQName);
627                 for (NodeTypeImplementationId nodeTypeImplementationId : allNodeTypeImplementations) {
628                         NodeTypeImplementationResource ntiRes = new NodeTypeImplementationResource(nodeTypeImplementationId);
629                         allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(ntiRes.getNTI().getDeploymentArtifacts());
630                         l.addAll(allReferencedArtifactTemplates);
631                 }
632                 
633                 return l;
634         }
635         
636         public static Collection<QName> getArtifactTemplatesOfReferencedImplementationArtifacts(TNodeTemplate nodeTemplate) {
637                 List<QName> l = new ArrayList<QName>();
638                 
639                 // IAs may be assigned via node type implementations
640                 QName nodeTypeQName = nodeTemplate.getType();
641                 Collection<NodeTypeImplementationId> allNodeTypeImplementations = BackendUtils.getAllElementsRelatedWithATypeAttribute(NodeTypeImplementationId.class, nodeTypeQName);
642                 for (NodeTypeImplementationId nodeTypeImplementationId : allNodeTypeImplementations) {
643                         NodeTypeImplementationResource ntiRes = new NodeTypeImplementationResource(nodeTypeImplementationId);
644                         Collection<QName> allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(ntiRes.getNTI().getImplementationArtifacts());
645                         l.addAll(allReferencedArtifactTemplates);
646                 }
647                 
648                 return l;
649         }
650         
651         /**
652          * Creates a new TDefintions element wrapping a TOSCA Component instance.
653          * The namespace of the tosca component is used as namespace and
654          * {@code winery-defs-for-} concatenated with the (unique) ns prefix and
655          * idOfContainedElement is used as id
656          * 
657          * @param toscAcomponentId the id of the element the wrapper is used for
658          * 
659          * @return a definitions element prepared for wrapping a TOSCA component
660          *         instance
661          */
662         public static Definitions createWrapperDefinitions(TOSCAComponentId tcId) {
663                 ObjectFactory of = new ObjectFactory();
664                 Definitions defs = of.createDefinitions();
665                 
666                 // set target namespace
667                 // an internal namespace is not possible
668                 //   a) tPolicyTemplate and tArtfactTemplate do NOT support the "targetNamespace" attribute
669                 //   b) the imports statement would look bad as it always imported the artificial namespace
670                 defs.setTargetNamespace(tcId.getNamespace().getDecoded());
671                 
672                 // set a unique id to create a valid definitions element
673                 // we do not use UUID to be more human readable and deterministic (for debugging)
674                 String prefix = NamespacesResource.getPrefix(tcId.getNamespace());
675                 String elId = tcId.getXmlId().getDecoded();
676                 String id = "winery-defs-for_" + prefix + "-" + elId;
677                 defs.setId(id);
678                 
679                 return defs;
680         }
681         
682         /**
683          * @throws IOException if content could not be updated in the repository
684          * @throws IllegalStateException if an JAXBException occurred. This should
685          *             never happen.
686          */
687         public static void persist(Object o, RepositoryFileReference ref, MediaType mediaType) throws IOException {
688                 // We assume that the object is not too large
689                 // Otherwise, http://io-tools.googlecode.com/svn/www/easystream/apidocs/index.html should be used
690                 ByteArrayOutputStream out = new ByteArrayOutputStream();
691                 Marshaller m;
692                 try {
693                         m = JAXBSupport.createMarshaller(true);
694                         m.marshal(o, out);
695                 } catch (JAXBException e) {
696                         BackendUtils.logger.error("Could not put content to file", e);
697                         throw new IllegalStateException(e);
698                 }
699                 byte[] data = out.toByteArray();
700                 ByteArrayInputStream in = new ByteArrayInputStream(data);
701                 // this may throw an IOExcpetion. We propagate this exception.
702                 Repository.INSTANCE.putContentToFile(ref, in, mediaType);
703         }
704         
705         /**
706          * Updates the color if the color is not yet existent
707          * 
708          * @param name the name of the component. Used as basis for a generated
709          *            color
710          * @param qname the QName of the color attribute
711          * @param otherAttributes the plain "XML" attributes. They are used to check
712          * @param res
713          */
714         public static String getColorAndSetDefaultIfNotExisting(String name, QName qname, Map<QName, String> otherAttributes, TopologyGraphElementEntityTypeResource res) {
715                 String colorStr = otherAttributes.get(qname);
716                 if (colorStr == null) {
717                         colorStr = Util.getColor(name);
718                         otherAttributes.put(qname, colorStr);
719                         BackendUtils.persist(res);
720                 }
721                 return colorStr;
722         }
723         
724         /**
725          * 
726          * @param tcId The element type id to get the location for
727          * @param uri uri to use if in XML export mode, null if in CSAR export mode
728          * @param wrapperElementLocalName the local name of the wrapper element
729          * @return
730          */
731         public static String getImportLocationForWinerysPropertiesDefinitionXSD(EntityTypeId tcId, URI uri, String wrapperElementLocalName) {
732                 String loc = BackendUtils.getPathInsideRepo(tcId);
733                 loc = loc + "propertiesdefinition/";
734                 loc = Utils.getURLforPathInsideRepo(loc);
735                 if (uri == null) {
736                         loc = loc + wrapperElementLocalName + ".xsd";
737                         // for the import later, we need "../" in front
738                         loc = "../" + loc;
739                 } else {
740                         loc = uri + loc + "xsd";
741                 }
742                 return loc;
743         }
744         
745         /**
746          * @param ref the file to read from
747          */
748         public static XSModel getXSModel(final RepositoryFileReference ref) {
749                 if (ref == null) {
750                         return null;
751                 }
752                 final InputStream is;
753                 try {
754                         is = Repository.INSTANCE.newInputStream(ref);
755                 } catch (IOException e) {
756                         BackendUtils.logger.debug("Could not create input stream", e);
757                         return null;
758                 }
759                 
760                 // we rely on xerces to parse the XSD
761                 // idea based on http://stackoverflow.com/a/5165177/873282
762                 XSImplementation impl = new XSImplementationImpl();
763                 XSLoader schemaLoader = impl.createXSLoader(null);
764                 
765                 // minimal LSInput implementation sufficient for XSLoader in Oracle's JRE7
766                 LSInput input = new LSInput() {
767                         
768                         @Override
769                         public void setSystemId(String systemId) {
770                         }
771                         
772                         @Override
773                         public void setStringData(String stringData) {
774                         }
775                         
776                         @Override
777                         public void setPublicId(String publicId) {
778                         }
779                         
780                         @Override
781                         public void setEncoding(String encoding) {
782                         }
783                         
784                         @Override
785                         public void setCharacterStream(Reader characterStream) {
786                         }
787                         
788                         @Override
789                         public void setCertifiedText(boolean certifiedText) {
790                         }
791                         
792                         @Override
793                         public void setByteStream(InputStream byteStream) {
794                         }
795                         
796                         @Override
797                         public void setBaseURI(String baseURI) {
798                         }
799                         
800                         @Override
801                         public String getSystemId() {
802                                 return null;
803                         }
804                         
805                         @Override
806                         public String getStringData() {
807                                 return null;
808                         }
809                         
810                         @Override
811                         public String getPublicId() {
812                                 return BackendUtils.getPathInsideRepo(ref);
813                         }
814                         
815                         @Override
816                         public String getEncoding() {
817                                 return "UTF-8";
818                         }
819                         
820                         @Override
821                         public Reader getCharacterStream() {
822                                 try {
823                                         return new InputStreamReader(is, "UTF-8");
824                                 } catch (UnsupportedEncodingException e) {
825                                         System.out.println("exeption");
826                                         throw new IllegalStateException("UTF-8 is unkown", e);
827                                 }
828                         }
829                         
830                         @Override
831                         public boolean getCertifiedText() {
832                                 return false;
833                         }
834                         
835                         @Override
836                         public InputStream getByteStream() {
837                                 return null;
838                         }
839                         
840                         @Override
841                         public String getBaseURI() {
842                                 return null;
843                         }
844                 };
845                 XSModel model = schemaLoader.load(input);
846                 return model;
847         }
848         
849         /**
850          * Derives Winery's Properties Definition from an existing properties
851          * definition
852          * 
853          * @param ci the entity type to try to modify the WPDs
854          * @param errors the list to add errors to
855          */
856         public static void deriveWPD(TEntityType ci, List<String> errors) {
857                 BackendUtils.logger.trace("deriveWPD");
858                 PropertiesDefinition propertiesDefinition = ci.getPropertiesDefinition();
859                 QName element = propertiesDefinition.getElement();
860                 if (element == null) {
861                         BackendUtils.logger.debug("only works for an element definition, not for types");
862                 } else {
863                         BackendUtils.logger.debug("Looking for the definition of {" + element.getNamespaceURI() + "}" + element.getLocalPart());
864                         // fetch the XSD defining the element
865                         XSDImportsResource importsRes = new XSDImportsResource();
866                         Map<String, RepositoryFileReference> mapFromLocalNameToXSD = importsRes.getMapFromLocalNameToXSD(element.getNamespaceURI(), false);
867                         RepositoryFileReference ref = mapFromLocalNameToXSD.get(element.getLocalPart());
868                         if (ref == null) {
869                                 String msg = "XSD not found for " + element.getNamespaceURI() + " / " + element.getLocalPart();
870                                 BackendUtils.logger.debug(msg);
871                                 errors.add(msg);
872                                 return;
873                         }
874                         
875                         XSModel xsModel = BackendUtils.getXSModel(ref);
876                         XSElementDeclaration elementDeclaration = xsModel.getElementDeclaration(element.getLocalPart(), element.getNamespaceURI());
877                         if (elementDeclaration == null) {
878                                 String msg = "XSD model claimed to contain declaration for {" + element.getNamespaceURI() + "}" + element.getLocalPart() + ", but it did not.";
879                                 BackendUtils.logger.debug(msg);
880                                 errors.add(msg);
881                                 return;
882                         }
883                         
884                         // go through the XSD definition and
885                         XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition();
886                         if (typeDefinition instanceof XSComplexTypeDefinition) {
887                                 XSComplexTypeDefinition cTypeDefinition = (XSComplexTypeDefinition) typeDefinition;
888                                 XSParticle particle = cTypeDefinition.getParticle();
889                                 if (particle == null) {
890                                         BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Complex type does not contain particles");
891                                 } else {
892                                         XSTerm term = particle.getTerm();
893                                         if (term instanceof XSModelGroup) {
894                                                 XSModelGroup modelGroup = (XSModelGroup) term;
895                                                 if (modelGroup.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) {
896                                                         XSObjectList particles = modelGroup.getParticles();
897                                                         int len = particles.getLength();
898                                                         boolean everyThingIsASimpleType = true;
899                                                         PropertyDefinitionKVList list = new PropertyDefinitionKVList();
900                                                         if (len != 0) {
901                                                                 for (int i = 0; i < len; i++) {
902                                                                         XSParticle innerParticle = (XSParticle) particles.item(i);
903                                                                         XSTerm innerTerm = innerParticle.getTerm();
904                                                                         if (innerTerm instanceof XSElementDeclaration) {
905                                                                                 XSElementDeclaration innerElementDeclaration = (XSElementDeclaration) innerTerm;
906                                                                                 String name = innerElementDeclaration.getName();
907                                                                                 XSTypeDefinition innerTypeDefinition = innerElementDeclaration.getTypeDefinition();
908                                                                                 if (innerTypeDefinition instanceof XSSimpleType) {
909                                                                                         XSSimpleType xsSimpleType = (XSSimpleType) innerTypeDefinition;
910                                                                                         String typeNS = xsSimpleType.getNamespace();
911                                                                                         String typeName = xsSimpleType.getName();
912                                                                                         if (typeNS.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
913                                                                                                 PropertyDefinitionKV def = new PropertyDefinitionKV();
914                                                                                                 def.setKey(name);
915                                                                                                 // convention at WPD: use "xsd" as prefix for XML Schema Definition
916                                                                                                 def.setType("xsd:" + typeName);
917                                                                                                 list.add(def);
918                                                                                         } else {
919                                                                                                 everyThingIsASimpleType = false;
920                                                                                                 break;
921                                                                                         }
922                                                                                 } else {
923                                                                                         everyThingIsASimpleType = false;
924                                                                                         break;
925                                                                                 }
926                                                                         } else {
927                                                                                 everyThingIsASimpleType = false;
928                                                                                 break;
929                                                                         }
930                                                                 }
931                                                         }
932                                                         if (everyThingIsASimpleType) {
933                                                                 // everything went allright, we can add a WPD
934                                                                 WinerysPropertiesDefinition wpd = new WinerysPropertiesDefinition();
935                                                                 wpd.setIsDerivedFromXSD(Boolean.TRUE);
936                                                                 wpd.setElementName(element.getLocalPart());
937                                                                 wpd.setNamespace(element.getNamespaceURI());
938                                                                 wpd.setPropertyDefinitionKVList(list);
939                                                                 ModelUtilities.replaceWinerysPropertiesDefinition(ci, wpd);
940                                                                 BackendUtils.logger.debug("Successfully generated WPD");
941                                                         } else {
942                                                                 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Not all types in the sequence are simple types");
943                                                         }
944                                                 } else {
945                                                         BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Model group is not a sequence");
946                                                 }
947                                         } else {
948                                                 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Not a model group");
949                                         }
950                                 }
951                         } else {
952                                 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: No Complex Type Definition");
953                         }
954                 }
955         }
956         
957         /**
958          * Returns all components available of the given id type
959          * 
960          * Similar functionality as {@link
961          * org.eclipse.winery.repository.backend.IGenericRepository.
962          * getAllTOSCAComponentIds(Class<T>)}, but it crawls through the repository
963          * 
964          * This method is required as we do not use a database.
965          * 
966          * @param idClass class of the Ids to search for
967          * @return empty set if no ids are available
968          */
969         public <T extends TOSCAElementId> SortedSet<T> getAllTOSCAElementIds(Class<T> idClass) {
970                 throw new IllegalStateException("Not yet implemented");
971                 
972                 /*
973                  Implementation idea:
974                    * switch of instance of idClass
975                    * nodetemplate / relationshiptemplate -> fetch all service templates -> crawl through topology -> add all to res
976                    * req/cap do as above, but inspect nodetemplate
977                    * (other special handlings; check spec where each type can be linked from)
978                  */
979         }
980         
981         /**
982          * Converts the given collection of TOSCA Component Ids to a collection of
983          * QNames by using the getQName() method.
984          * 
985          * This is required for QNameChooser.tag
986          */
987         public static Collection<QName> convertTOSCAComponentIdCollectionToQNameCollection(Collection<? extends TOSCAComponentId> col) {
988                 Collection<QName> res = new ArrayList<>();
989                 for (TOSCAComponentId id : col) {
990                         res.add(id.getQName());
991                 }
992                 return res;
993         }
994         
995 }