/******************************************************************************* * Copyright (c) 2012-2013 University of Stuttgart. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and the Apache License 2.0 which both accompany this distribution, * and are available at http://www.eclipse.org/legal/epl-v10.html * and http://www.apache.org/licenses/LICENSE-2.0 * * Contributors: * Oliver Kopp - initial API and implementation *******************************************************************************/ package org.eclipse.winery.repository.resources.servicetemplates; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.SortedSet; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.xml.namespace.QName; import org.eclipse.winery.common.RepositoryFileReference; import org.eclipse.winery.common.ids.XMLId; import org.eclipse.winery.common.ids.definitions.ServiceTemplateId; import org.eclipse.winery.common.ids.elements.PlanId; import org.eclipse.winery.common.ids.elements.PlansId; import org.eclipse.winery.model.tosca.TBoundaryDefinitions; import org.eclipse.winery.model.tosca.TExtensibleElements; import org.eclipse.winery.model.tosca.TPlan; import org.eclipse.winery.model.tosca.TPlan.PlanModelReference; import org.eclipse.winery.model.tosca.TPlans; import org.eclipse.winery.model.tosca.TServiceTemplate; import org.eclipse.winery.model.tosca.TTopologyTemplate; import org.eclipse.winery.repository.Utils; import org.eclipse.winery.repository.backend.BackendUtils; import org.eclipse.winery.repository.backend.Repository; import org.eclipse.winery.repository.resources.AbstractComponentInstanceWithReferencesResource; import org.eclipse.winery.repository.resources.IHasName; import org.eclipse.winery.repository.resources.servicetemplates.boundarydefinitions.BoundaryDefinitionsResource; import org.eclipse.winery.repository.resources.servicetemplates.plans.PlansResource; import org.eclipse.winery.repository.resources.servicetemplates.selfserviceportal.SelfServicePortalResource; import org.eclipse.winery.repository.resources.servicetemplates.topologytemplates.TopologyTemplateResource; import org.restdoc.annotations.RestDoc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServiceTemplateResource extends AbstractComponentInstanceWithReferencesResource implements IHasName { private static final Logger logger = LoggerFactory.getLogger(ServiceTemplateResource.class); public ServiceTemplateResource(ServiceTemplateId id) { super(id); } /** sub-resources **/ @Path("topologytemplate/") public TopologyTemplateResource getTopologyTemplateResource() { if (this.getServiceTemplate().getTopologyTemplate() == null) { // the main service template resource exists // default topology template: empty template // This eases the JSPs etc. and is valid as a non-existant topology template is equal to an empty one this.getServiceTemplate().setTopologyTemplate(new TTopologyTemplate()); } return new TopologyTemplateResource(this); } @Path("plans/") public PlansResource getPlansResource() { TPlans plans = this.getServiceTemplate().getPlans(); if (plans == null) { plans = new TPlans(); this.getServiceTemplate().setPlans(plans); } return new PlansResource(plans.getPlan(), this); } @Path("selfserviceportal/") public SelfServicePortalResource getSelfServicePortalResource() { return new SelfServicePortalResource(this); } @Path("boundarydefinitions/") public BoundaryDefinitionsResource getBoundaryDefinitionsResource() { TBoundaryDefinitions boundaryDefinitions = this.getServiceTemplate().getBoundaryDefinitions(); if (boundaryDefinitions == null) { boundaryDefinitions = new TBoundaryDefinitions(); this.getServiceTemplate().setBoundaryDefinitions(boundaryDefinitions); } return new BoundaryDefinitionsResource(this, boundaryDefinitions); } @Override public String getName() { String name = this.getServiceTemplate().getName(); if (name == null) { // place default name = this.getId().getXmlId().getDecoded(); } return name; } @Override public Response setName(String name) { this.getServiceTemplate().setName(name); return BackendUtils.persist(this); } // @formatter:off @GET @RestDoc(methodDescription="Returns the associated node type, which can be substituted by this service template.
" + "@return a QName of the form {namespace}localName is returned.") @Path("substitutableNodeType") @Produces(MediaType.TEXT_PLAIN) // @formatter:on public Response getSubstitutableNodeTypeAsResponse() { QName qname = this.getServiceTemplate().getSubstitutableNodeType(); if (qname == null) { return Response.status(Status.NOT_FOUND).build(); } else { return Response.ok(qname.toString()).build(); } } /** * * @return null if there is no substitutable node type */ public QName getSubstitutableNodeType() { return this.getServiceTemplate().getSubstitutableNodeType(); } @DELETE @RestDoc(methodDescription = "Removes the association to substitutable node type") @Path("substitutableNodeType") public Response deleteSubstitutableNodeType() { this.getServiceTemplate().setSubstitutableNodeType(null); BackendUtils.persist(this); return Response.noContent().build(); } public TServiceTemplate getServiceTemplate() { return (TServiceTemplate) this.getElement(); } @Override protected TExtensibleElements createNewElement() { return new TServiceTemplate(); } @Override protected void copyIdToFields() { this.getServiceTemplate().setId(this.getId().getXmlId().getDecoded()); this.getServiceTemplate().setTargetNamespace(this.getId().getNamespace().getDecoded()); } /** * Synchronizes the known plans with the data in the XML. When there is a * stored file, but no known entry in the XML, we guess "BPEL" as language * and "build plan" as type. * * @throws IOException */ @Override public void synchronizeReferences() { // locally stored plans TPlans plans = this.getServiceTemplate().getPlans(); // plans stored in the repository PlansId plansContainerId = new PlansId((ServiceTemplateId) this.getId()); SortedSet nestedPlans = Repository.INSTANCE.getNestedIds(plansContainerId, PlanId.class); Set plansToAdd = new HashSet(); plansToAdd.addAll(nestedPlans); if (nestedPlans.isEmpty()) { if (plans == null) { // data on the file system equals the data -> no plans return; } else { // we have to check for equality later } } if (plans == null) { plans = new TPlans(); this.getServiceTemplate().setPlans(plans); } for (Iterator iterator = plans.getPlan().iterator(); iterator.hasNext();) { TPlan plan = iterator.next(); if (plan.getPlanModel() != null) { // in case, a plan is directly contained in a Model element, we do not need to do anything continue; } PlanModelReference planModelReference = plan.getPlanModelReference(); if ((planModelReference = plan.getPlanModelReference()) != null) { String ref = planModelReference.getReference(); if ((ref == null) || ref.startsWith("../")) { // references to local plans start with "../" // special case (due to errors in the importer): empty PlanModelReference field if (plan.getId() == null) { // invalid plan entry: no id. // we remove the entry iterator.remove(); continue; } PlanId planId = new PlanId(plansContainerId, new XMLId(plan.getId(), false)); if (nestedPlans.contains(planId)) { // everything allright // we do NOT need to add the plan on the HDD to the XML plansToAdd.remove(planId); } else { // no local storage for the plan, we remove it from the XML iterator.remove(); } } } } // add all plans locally stored, but not contained in the XML, as plan element to the plans of the service template. List thePlans = plans.getPlan(); for (PlanId planId : plansToAdd) { SortedSet files = Repository.INSTANCE.getContainedFiles(planId); if (files.size() != 1) { throw new IllegalStateException("Currently, only one file per plan is supported."); } RepositoryFileReference ref = files.iterator().next(); TPlan plan = new TPlan(); plan.setId(planId.getXmlId().getDecoded()); plan.setName(planId.getXmlId().getDecoded()); plan.setPlanType(org.eclipse.winery.repository.Constants.TOSCA_PLANTYPE_BUILD_PLAN); plan.setPlanLanguage(org.eclipse.winery.common.constants.Namespaces.URI_BPEL20_EXECUTABLE); // create a PlanModelReferenceElement pointing to that file String path = Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(ref)); // path is relative from the definitions element path = "../" + path; PlanModelReference pref = new PlanModelReference(); pref.setReference(path); plan.setPlanModelReference(pref); thePlans.add(plan); } try { this.persist(); } catch (IOException e) { throw new IllegalStateException("Could not persist resource", e); } return; } }