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
10 * Oliver Kopp - initial API and implementation
11 *******************************************************************************/
12 package org.eclipse.winery.repository.backend;
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;
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;
34 import java.util.SortedSet;
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;
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;
106 import com.sun.jersey.core.header.ContentDisposition;
109 * Contains generic utility functions for the Backend
111 * Contains everything that is useful for our ids etc. Does <em>not</em> contain
112 * anything that has to do with resources
114 public class BackendUtils {
116 private static final Logger logger = LoggerFactory.getLogger(BackendUtils.class);
120 * Deletes given file/dir and returns appropriate response code
122 public static Response delete(GenericId id) {
123 if (!Repository.INSTANCE.exists(id)) {
124 return Response.status(Status.NOT_FOUND).build();
127 Repository.INSTANCE.forceDelete(id);
128 } catch (IOException e) {
129 BackendUtils.logger.error(e.getMessage(), e);
130 return Response.serverError().entity(e.getMessage()).build();
132 return Response.noContent().build();
136 * Deletes given file and returns appropriate response code
138 public static Response delete(RepositoryFileReference ref) {
139 if (!Repository.INSTANCE.exists(ref)) {
140 return Response.status(Status.NOT_FOUND).build();
143 Repository.INSTANCE.forceDelete(ref);
144 } catch (IOException e) {
145 BackendUtils.logger.error(e.getMessage(), e);
146 return Response.serverError().entity(e.getMessage()).build();
148 return Response.ok().build();
152 * Generates given TOSCA element and returns appropriate response code <br />
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.
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>
166 * <li>URI: the absolute URI of the newly created resource</li>
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);
175 if (Repository.INSTANCE.flagAsExisting(id)) {
176 res.setStatus(Status.CREATED);
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()
183 // Utils.getURLforPathInsideRepo(id.getPathInsideRepo());
184 // We distinguish between two cases: TOSCAcomponentId and
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() + "/";
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() + "/";
200 // we have to encode it twice to get correct URIs
201 path = Utils.getURLforPathInsideRepo(path);
202 URI uri = Utils.createURI(path);
206 res.setStatus(Status.INTERNAL_SERVER_ERROR);
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
219 * additionally "Vary: Accept" header is added (enables caching of the
222 * method header for calling method public <br />
223 * <code>Response getXY(@HeaderParam("If-Modified-Since") String modified) {...}</code>
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
230 public static Response returnRepoPath(RepositoryFileReference ref, String modified) {
231 return BackendUtils.returnRefAsResponseBuilder(ref, modified).build();
235 * @return true if given fileDate is newer then the modified date (or
238 public static boolean isFileNewerThanModifiedDate(long millis, String modified) {
239 if (modified == null) {
243 Date modifiedDate = null;
245 assert (Locale.getDefault() == Locale.ENGLISH);
247 modifiedDate = DateUtils.parseDate(modified, org.apache.http.impl.cookie.DateUtils.DEFAULT_PATTERNS);
248 } catch (ParseException e) {
249 BackendUtils.logger.error(e.getMessage(), e);
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) {
264 * This is not repository specific, but we leave it close to the only caller
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
270 private static ResponseBuilder returnRefAsResponseBuilder(RepositoryFileReference ref, String modified) {
271 if (!Repository.INSTANCE.exists(ref)) {
272 return Response.status(Status.NOT_FOUND);
275 FileTime lastModified;
277 lastModified = Repository.INSTANCE.getLastModifiedTime(ref);
278 } catch (IOException e1) {
279 BackendUtils.logger.debug("Could not get lastModifiedTime", e1);
280 return Response.serverError();
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);
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();
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
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();
306 ContentDisposition contentDisposition = ContentDisposition.type("attachment").fileName(ref.getFileName()).modificationDate(new Date(lastModified.toMillis())).build();
307 res.header("Content-Disposition", contentDisposition);
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 />
316 * If null or "" is passed as value, the property is cleared
318 * @return Status.NO_CONTENT
320 public static Response updateProperty(Configuration configuration, String property, String val) {
321 if (StringUtils.isBlank(val)) {
322 configuration.clearProperty(property);
324 configuration.setProperty(property, val);
326 return Response.noContent().build();
330 * Persists the resource and returns appropriate response
332 public static Response persist(IPersistable res) {
336 } catch (IOException e) {
337 BackendUtils.logger.debug("Could not persist resource", e);
338 r = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
341 r = Response.noContent().build();
346 * Writes data to file. Replaces the file's content with the given content.
347 * The file does not need to exist
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
354 public static Response putContentToFile(RepositoryFileReference ref, String content, MediaType mediaType) {
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();
361 return Response.noContent().build();
364 public static Response putContentToFile(RepositoryFileReference ref, InputStream inputStream, MediaType mediaType) {
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();
371 return Response.noContent().build();
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);
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
385 return BackendUtils.getTOSCAcomponentId(idClass, qname.getNamespaceURI(), qname.getLocalPart(), false);
388 public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, String namespace, String id, boolean URLencoded) {
389 Constructor<T> constructor;
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);
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);
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
412 public static Namespace getNamespace(TOSCAElementId id) {
413 GenericId parent = id.getParent();
414 while (!(parent instanceof TOSCAComponentId)) {
415 parent = parent.getParent();
417 return ((TOSCAComponentId) parent).getNamespace();
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);
428 * Do <em>not</em> use this for creating URLs. Use
430 * {@link org.eclipse.winery.repository.Utils.getURLforPathInsideRepo(String)}
434 * {@link org.eclipse.winery.repository.Utils.getAbsoluteURL(GenericId)
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
442 public static String getPathInsideRepo(GenericId id) {
444 throw new NullPointerException("id is null");
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() + "/";
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() + "/";
465 throw new IllegalStateException("Unknown subclass of GenericId " + id.getClass());
470 * Do <em>not</em> use this for creating URLs. Use
472 * {@link org.eclipse.winery.repository.Utils.getURLforPathInsideRepo(String)}
476 * {@link org.eclipse.winery.repository.Utils.getAbsoluteURL(GenericId)
479 * @return the path starting from the root element to the current element.
480 * Separated by "/", parent URLencoded. Without trailing slash.
482 public static String getPathInsideRepo(RepositoryFileReference ref) {
483 return BackendUtils.getPathInsideRepo(ref.getParent()) + ref.getFileName();
487 * Returns the reference to the definitions XML storing the TOSCA for the
490 * @param id the id to lookup
491 * @return the reference
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);
501 * Returns the reference to the properties file storing the TOSCA
502 * information for the given id
504 * @param id the id to lookup
505 * @return the reference
507 public static RepositoryFileReference getRefOfConfiguration(GenericId id) {
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;
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;
525 name = Util.getTypeForElementId(tId.getClass()) + Constants.SUFFIX_PROPERTIES;
529 RepositoryFileReference ref = new RepositoryFileReference(id, name);
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
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;
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);
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
564 * Returns a list of the topology template nested in the given service
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();
573 for (TEntityTemplate t : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
574 if (t instanceof TNodeTemplate) {
575 l.add((TNodeTemplate) t);
581 private static Collection<QName> getAllReferencedArtifactTemplates(TDeploymentArtifacts tDeploymentArtifacts) {
582 if (tDeploymentArtifacts == null) {
583 return Collections.emptyList();
585 List<TDeploymentArtifact> deploymentArtifacts = tDeploymentArtifacts.getDeploymentArtifact();
586 if (deploymentArtifacts == null) {
587 return Collections.emptyList();
589 Collection<QName> res = new ArrayList<>();
590 for (TDeploymentArtifact da : deploymentArtifacts) {
591 QName artifactRef = da.getArtifactRef();
592 if (artifactRef != null) {
593 res.add(artifactRef);
599 private static Collection<QName> getAllReferencedArtifactTemplates(TImplementationArtifacts tImplementationArtifacts) {
600 if (tImplementationArtifacts == null) {
601 return Collections.emptyList();
603 List<ImplementationArtifact> implementationArtifacts = tImplementationArtifacts.getImplementationArtifact();
604 if (implementationArtifacts == null) {
605 return Collections.emptyList();
607 Collection<QName> res = new ArrayList<>();
608 for (ImplementationArtifact ia : implementationArtifacts) {
609 QName artifactRef = ia.getArtifactRef();
610 if (artifactRef != null) {
611 res.add(artifactRef);
617 public static Collection<QName> getArtifactTemplatesOfReferencedDeploymentArtifacts(TNodeTemplate nodeTemplate) {
618 List<QName> l = new ArrayList<QName>();
620 // DAs may be assigned directly to a node template
621 Collection<QName> allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(nodeTemplate.getDeploymentArtifacts());
622 l.addAll(allReferencedArtifactTemplates);
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);
636 public static Collection<QName> getArtifactTemplatesOfReferencedImplementationArtifacts(TNodeTemplate nodeTemplate) {
637 List<QName> l = new ArrayList<QName>();
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);
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
657 * @param toscAcomponentId the id of the element the wrapper is used for
659 * @return a definitions element prepared for wrapping a TOSCA component
662 public static Definitions createWrapperDefinitions(TOSCAComponentId tcId) {
663 ObjectFactory of = new ObjectFactory();
664 Definitions defs = of.createDefinitions();
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());
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;
683 * @throws IOException if content could not be updated in the repository
684 * @throws IllegalStateException if an JAXBException occurred. This should
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();
693 m = JAXBSupport.createMarshaller(true);
695 } catch (JAXBException e) {
696 BackendUtils.logger.error("Could not put content to file", e);
697 throw new IllegalStateException(e);
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);
706 * Updates the color if the color is not yet existent
708 * @param name the name of the component. Used as basis for a generated
710 * @param qname the QName of the color attribute
711 * @param otherAttributes the plain "XML" attributes. They are used to check
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);
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
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);
736 loc = loc + wrapperElementLocalName + ".xsd";
737 // for the import later, we need "../" in front
740 loc = uri + loc + "xsd";
746 * @param ref the file to read from
748 public static XSModel getXSModel(final RepositoryFileReference ref) {
752 final InputStream is;
754 is = Repository.INSTANCE.newInputStream(ref);
755 } catch (IOException e) {
756 BackendUtils.logger.debug("Could not create input stream", e);
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);
765 // minimal LSInput implementation sufficient for XSLoader in Oracle's JRE7
766 LSInput input = new LSInput() {
769 public void setSystemId(String systemId) {
773 public void setStringData(String stringData) {
777 public void setPublicId(String publicId) {
781 public void setEncoding(String encoding) {
785 public void setCharacterStream(Reader characterStream) {
789 public void setCertifiedText(boolean certifiedText) {
793 public void setByteStream(InputStream byteStream) {
797 public void setBaseURI(String baseURI) {
801 public String getSystemId() {
806 public String getStringData() {
811 public String getPublicId() {
812 return BackendUtils.getPathInsideRepo(ref);
816 public String getEncoding() {
821 public Reader getCharacterStream() {
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);
831 public boolean getCertifiedText() {
836 public InputStream getByteStream() {
841 public String getBaseURI() {
845 XSModel model = schemaLoader.load(input);
850 * Derives Winery's Properties Definition from an existing properties
853 * @param ci the entity type to try to modify the WPDs
854 * @param errors the list to add errors to
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");
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());
869 String msg = "XSD not found for " + element.getNamespaceURI() + " / " + element.getLocalPart();
870 BackendUtils.logger.debug(msg);
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);
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");
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();
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();
915 // convention at WPD: use "xsd" as prefix for XML Schema Definition
916 def.setType("xsd:" + typeName);
919 everyThingIsASimpleType = false;
923 everyThingIsASimpleType = false;
927 everyThingIsASimpleType = false;
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");
942 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Not all types in the sequence are simple types");
945 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Model group is not a sequence");
948 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Not a model group");
952 BackendUtils.logger.debug("XSD does not follow the requirements put by winery: No Complex Type Definition");
958 * Returns all components available of the given id type
960 * Similar functionality as {@link
961 * org.eclipse.winery.repository.backend.IGenericRepository.
962 * getAllTOSCAComponentIds(Class<T>)}, but it crawls through the repository
964 * This method is required as we do not use a database.
966 * @param idClass class of the Ids to search for
967 * @return empty set if no ids are available
969 public <T extends TOSCAElementId> SortedSet<T> getAllTOSCAElementIds(Class<T> idClass) {
970 throw new IllegalStateException("Not yet implemented");
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)
982 * Converts the given collection of TOSCA Component Ids to a collection of
983 * QNames by using the getQName() method.
985 * This is required for QNameChooser.tag
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());