Merge "Fix build errors in autorelease full clean build"
[vfc/nfvo/wfengine.git] / winery / org.eclipse.winery.repository / src / main / java / org / eclipse / winery / repository / resources / servicetemplates / ServiceTemplateResource.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  *     Oliver Kopp - initial API and implementation
11  *******************************************************************************/
12 package org.eclipse.winery.repository.resources.servicetemplates;
13
14 import java.io.IOException;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Set;
19 import java.util.SortedSet;
20
21 import javax.ws.rs.DELETE;
22 import javax.ws.rs.GET;
23 import javax.ws.rs.Path;
24 import javax.ws.rs.Produces;
25 import javax.ws.rs.core.MediaType;
26 import javax.ws.rs.core.Response;
27 import javax.ws.rs.core.Response.Status;
28 import javax.xml.namespace.QName;
29
30 import org.eclipse.winery.common.RepositoryFileReference;
31 import org.eclipse.winery.common.ids.XMLId;
32 import org.eclipse.winery.common.ids.definitions.ServiceTemplateId;
33 import org.eclipse.winery.common.ids.elements.PlanId;
34 import org.eclipse.winery.common.ids.elements.PlansId;
35 import org.eclipse.winery.model.tosca.TBoundaryDefinitions;
36 import org.eclipse.winery.model.tosca.TExtensibleElements;
37 import org.eclipse.winery.model.tosca.TPlan;
38 import org.eclipse.winery.model.tosca.TPlan.PlanModelReference;
39 import org.eclipse.winery.model.tosca.TPlans;
40 import org.eclipse.winery.model.tosca.TServiceTemplate;
41 import org.eclipse.winery.model.tosca.TTopologyTemplate;
42 import org.eclipse.winery.repository.Utils;
43 import org.eclipse.winery.repository.backend.BackendUtils;
44 import org.eclipse.winery.repository.backend.Repository;
45 import org.eclipse.winery.repository.resources.AbstractComponentInstanceWithReferencesResource;
46 import org.eclipse.winery.repository.resources.IHasName;
47 import org.eclipse.winery.repository.resources.servicetemplates.boundarydefinitions.BoundaryDefinitionsResource;
48 import org.eclipse.winery.repository.resources.servicetemplates.plans.PlansResource;
49 import org.eclipse.winery.repository.resources.servicetemplates.selfserviceportal.SelfServicePortalResource;
50 import org.eclipse.winery.repository.resources.servicetemplates.topologytemplates.TopologyTemplateResource;
51 import org.restdoc.annotations.RestDoc;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class ServiceTemplateResource extends AbstractComponentInstanceWithReferencesResource implements IHasName {
56         
57         private static final Logger logger = LoggerFactory.getLogger(ServiceTemplateResource.class);
58         
59         
60         public ServiceTemplateResource(ServiceTemplateId id) {
61                 super(id);
62         }
63         
64         /** sub-resources **/
65         
66         @Path("topologytemplate/")
67         public TopologyTemplateResource getTopologyTemplateResource() {
68                 if (this.getServiceTemplate().getTopologyTemplate() == null) {
69                         // the main service template resource exists
70                         // default topology template: empty template
71                         // This eases the JSPs etc. and is valid as a non-existant topology template is equal to an empty one
72                         this.getServiceTemplate().setTopologyTemplate(new TTopologyTemplate());
73                 }
74                 return new TopologyTemplateResource(this);
75         }
76         
77         @Path("plans/")
78         public PlansResource getPlansResource() {
79                 TPlans plans = this.getServiceTemplate().getPlans();
80                 if (plans == null) {
81                         plans = new TPlans();
82                         this.getServiceTemplate().setPlans(plans);
83                 }
84                 return new PlansResource(plans.getPlan(), this);
85         }
86         
87         @Path("selfserviceportal/")
88         public SelfServicePortalResource getSelfServicePortalResource() {
89                 return new SelfServicePortalResource(this);
90         }
91         
92         @Path("boundarydefinitions/")
93         public BoundaryDefinitionsResource getBoundaryDefinitionsResource() {
94                 TBoundaryDefinitions boundaryDefinitions = this.getServiceTemplate().getBoundaryDefinitions();
95                 if (boundaryDefinitions == null) {
96                         boundaryDefinitions = new TBoundaryDefinitions();
97                         this.getServiceTemplate().setBoundaryDefinitions(boundaryDefinitions);
98                 }
99                 return new BoundaryDefinitionsResource(this, boundaryDefinitions);
100         }
101         
102         @Override
103         public String getName() {
104                 String name = this.getServiceTemplate().getName();
105                 if (name == null) {
106                         // place default
107                         name = this.getId().getXmlId().getDecoded();
108                 }
109                 return name;
110         }
111         
112         @Override
113         public Response setName(String name) {
114                 this.getServiceTemplate().setName(name);
115                 return BackendUtils.persist(this);
116         }
117         
118         // @formatter:off
119         @GET
120         @RestDoc(methodDescription="Returns the associated node type, which can be substituted by this service template.<br />" +
121         "@return a QName of the form {namespace}localName is returned.")
122         @Path("substitutableNodeType")
123         @Produces(MediaType.TEXT_PLAIN)
124         // @formatter:on
125         public Response getSubstitutableNodeTypeAsResponse() {
126                 QName qname = this.getServiceTemplate().getSubstitutableNodeType();
127                 if (qname == null) {
128                         return Response.status(Status.NOT_FOUND).build();
129                 } else {
130                         return Response.ok(qname.toString()).build();
131                 }
132         }
133         
134         /**
135          * 
136          * @return null if there is no substitutable node type
137          */
138         public QName getSubstitutableNodeType() {
139                 return this.getServiceTemplate().getSubstitutableNodeType();
140         }
141         
142         @DELETE
143         @RestDoc(methodDescription = "Removes the association to substitutable node type")
144         @Path("substitutableNodeType")
145         public Response deleteSubstitutableNodeType() {
146                 this.getServiceTemplate().setSubstitutableNodeType(null);
147                 BackendUtils.persist(this);
148                 return Response.noContent().build();
149         }
150         
151         public TServiceTemplate getServiceTemplate() {
152                 return (TServiceTemplate) this.getElement();
153         }
154         
155         @Override
156         protected TExtensibleElements createNewElement() {
157                 return new TServiceTemplate();
158         }
159         
160         @Override
161         protected void copyIdToFields() {
162                 this.getServiceTemplate().setId(this.getId().getXmlId().getDecoded());
163                 this.getServiceTemplate().setTargetNamespace(this.getId().getNamespace().getDecoded());
164         }
165         
166         /**
167          * Synchronizes the known plans with the data in the XML. When there is a
168          * stored file, but no known entry in the XML, we guess "BPEL" as language
169          * and "build plan" as type.
170          * 
171          * @throws IOException
172          */
173         @Override
174         public void synchronizeReferences() {
175                 // locally stored plans
176                 TPlans plans = this.getServiceTemplate().getPlans();
177                 
178                 // plans stored in the repository
179                 PlansId plansContainerId = new PlansId((ServiceTemplateId) this.getId());
180                 SortedSet<PlanId> nestedPlans = Repository.INSTANCE.getNestedIds(plansContainerId, PlanId.class);
181                 
182                 Set<PlanId> plansToAdd = new HashSet<PlanId>();
183                 plansToAdd.addAll(nestedPlans);
184                 
185                 if (nestedPlans.isEmpty()) {
186                         if (plans == null) {
187                                 // data on the file system equals the data -> no plans
188                                 return;
189                         } else {
190                                 // we have to check for equality later
191                         }
192                 }
193                 
194                 if (plans == null) {
195                         plans = new TPlans();
196                         this.getServiceTemplate().setPlans(plans);
197                 }
198                 
199                 for (Iterator<TPlan> iterator = plans.getPlan().iterator(); iterator.hasNext();) {
200                         TPlan plan = iterator.next();
201                         if (plan.getPlanModel() != null) {
202                                 // in case, a plan is directly contained in a Model element, we do not need to do anything
203                                 continue;
204                         }
205                         PlanModelReference planModelReference = plan.getPlanModelReference();
206                         if ((planModelReference = plan.getPlanModelReference()) != null) {
207                                 String ref = planModelReference.getReference();
208                                 if ((ref == null) || ref.startsWith("../")) {
209                                         // references to local plans start with "../"
210                                         // special case (due to errors in the importer): empty PlanModelReference field
211                                         if (plan.getId() == null) {
212                                                 // invalid plan entry: no id.
213                                                 // we remove the entry
214                                                 iterator.remove();
215                                                 continue;
216                                         }
217                                         PlanId planId = new PlanId(plansContainerId, new XMLId(plan.getId(), false));
218                                         if (nestedPlans.contains(planId)) {
219                                                 // everything allright
220                                                 // we do NOT need to add the plan on the HDD to the XML
221                                                 plansToAdd.remove(planId);
222                                         } else {
223                                                 // no local storage for the plan, we remove it from the XML
224                                                 iterator.remove();
225                                         }
226                                 }
227                         }
228                 }
229                 
230                 // add all plans locally stored, but not contained in the XML, as plan element to the plans of the service template.
231                 List<TPlan> thePlans = plans.getPlan();
232                 for (PlanId planId : plansToAdd) {
233                         SortedSet<RepositoryFileReference> files = Repository.INSTANCE.getContainedFiles(planId);
234                         if (files.size() != 1) {
235                                 throw new IllegalStateException("Currently, only one file per plan is supported.");
236                         }
237                         RepositoryFileReference ref = files.iterator().next();
238                         
239                         TPlan plan = new TPlan();
240                         plan.setId(planId.getXmlId().getDecoded());
241                         plan.setName(planId.getXmlId().getDecoded());
242                         plan.setPlanType(org.eclipse.winery.repository.Constants.TOSCA_PLANTYPE_BUILD_PLAN);
243                         plan.setPlanLanguage(org.eclipse.winery.common.constants.Namespaces.URI_BPEL20_EXECUTABLE);
244                         
245                         // create a PlanModelReferenceElement pointing to that file
246                         String path = Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(ref));
247                         // path is relative from the definitions element
248                         path = "../" + path;
249                         PlanModelReference pref = new PlanModelReference();
250                         pref.setReference(path);
251                         
252                         plan.setPlanModelReference(pref);
253                         thePlans.add(plan);
254                 }
255                 
256                 try {
257                         this.persist();
258                 } catch (IOException e) {
259                         throw new IllegalStateException("Could not persist resource", e);
260                 }
261                 return;
262         }
263 }