diff options
Diffstat (limited to 'winery/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/resources/servicetemplates/plans/PlansResource.java')
-rw-r--r-- | winery/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/resources/servicetemplates/plans/PlansResource.java | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/winery/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/resources/servicetemplates/plans/PlansResource.java b/winery/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/resources/servicetemplates/plans/PlansResource.java new file mode 100644 index 0000000..13a8420 --- /dev/null +++ b/winery/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/resources/servicetemplates/plans/PlansResource.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2012-2015 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.plans; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +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 org.apache.commons.lang3.StringUtils; +import org.eclipse.winery.common.RepositoryFileReference; +import org.eclipse.winery.common.Util; +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.TPlan; +import org.eclipse.winery.model.tosca.TPlan.PlanModelReference; +import org.eclipse.winery.repository.Constants; +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._support.collections.withid.EntityWithIdCollectionResource; +import org.eclipse.winery.repository.resources.admin.types.PlanLanguagesManager; +import org.eclipse.winery.repository.resources.admin.types.PlanTypesManager; +import org.eclipse.winery.repository.resources.servicetemplates.ServiceTemplateResource; +import org.restdoc.annotations.RestDoc; +import org.restdoc.annotations.RestDocParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonGenerator; +import com.sun.jersey.api.view.Viewable; +import com.sun.jersey.core.header.FormDataContentDisposition; +import com.sun.jersey.multipart.FormDataBodyPart; +import com.sun.jersey.multipart.FormDataParam; + +/** + * Presents the plans nested in one Service Template + */ +public class PlansResource extends EntityWithIdCollectionResource<PlanResource, TPlan> { + + private static final Logger logger = LoggerFactory.getLogger(PlansResource.class); + + + public PlansResource(List<TPlan> plans, ServiceTemplateResource res) { + super(PlanResource.class, TPlan.class, plans, res); + } + + @Override + public Viewable getHTML() { + return new Viewable("/jsp/servicetemplates/plans/plans.jsp", new PlansResourceData(this.list)); + } + + @POST + @RestDoc(methodDescription = "<p>Linked plans are currently not supported. Existing plans with the same id are overwritten</p> <p>@return JSON with .tableData: Array with row data for dataTable</p>") + @Consumes({MediaType.MULTIPART_FORM_DATA}) + @Produces(MediaType.APPLICATION_JSON) + // the supertype consumes JSON and XML at org.eclipse.winery.repository.resources._support.collections.EntityCollectionResource.addNewElement(EntityT) + // @formatter:off + public Response onPost( + @FormDataParam("planName") String name, + @FormDataParam("planType") String type, + @FormDataParam("planLanguage") @RestDocParam(description = "the plan language (e..g, BPMN or BPEL). Full URL.") String language, + @FormDataParam("file") @RestDocParam(description="(optional in the case of BPMN4TOSCA) file containing the plan.") InputStream uploadedInputStream, + @FormDataParam("file") FormDataContentDisposition fileDetail, + @FormDataParam("file") FormDataBodyPart body + ) { + // @formatter:on + if (StringUtils.isEmpty(name)) { + return Response.status(Status.BAD_REQUEST).entity("planName must be given").build(); + } + if (StringUtils.isEmpty(type)) { + return Response.status(Status.BAD_REQUEST).entity("planType must be given").build(); + } + if (StringUtils.isEmpty(language)) { + return Response.status(Status.BAD_REQUEST).entity("planLanguage must be given").build(); + } + + boolean bpmn4toscaMode = org.eclipse.winery.common.constants.Namespaces.URI_BPMN4TOSCA_20.equals(language); + if (!bpmn4toscaMode) { + if (uploadedInputStream == null) { + return Response.status(Status.BAD_REQUEST).entity("file must be given").build(); + } + } + + // A plan carries both a name and an ID + // To be user-friendly, we create the ID based on the name + // the drawback is, that we do not allow two plans with the same name + // during creation, but allow renaming plans to the same name (as we do + // not allow ID renaming) + String xmlId = Utils.createXMLidAsString(name); + + // BEGIN: Store plan file + + // Determine Id + PlansId plansId = new PlansId((ServiceTemplateId) ((ServiceTemplateResource) this.res).getId()); + PlanId planId = new PlanId(plansId, new XMLId(xmlId, false)); + // Ensure overwriting + if (Repository.INSTANCE.exists(planId)) { + try { + Repository.INSTANCE.forceDelete(planId); + // Quick hack to remove the deleted plan from the plans element + ((ServiceTemplateResource) this.res).synchronizeReferences(); + } catch (IOException e) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); + } + } + + String fileName; + if (bpmn4toscaMode) { + fileName = xmlId + Constants.SUFFIX_BPMN4TOSCA; + RepositoryFileReference ref = new RepositoryFileReference(planId, fileName); + try { + Repository.INSTANCE.putContentToFile(ref, "{}", MediaType.APPLICATION_JSON_TYPE); + } catch (IOException e1) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not create empty plan. " + e1.getMessage()).build(); + } + } else { + // We use the filename also as local file name. Alternatively, we could use the xml id + // With URL encoding, this should not be an issue + fileName = Util.URLencode(fileDetail.getFileName()); + + // Really store it + RepositoryFileReference ref = new RepositoryFileReference(planId, fileName); + try { + Repository.INSTANCE.putContentToFile(ref, uploadedInputStream, body.getMediaType()); + } catch (IOException e1) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not store plan. " + e1.getMessage()).build(); + } + } + // END: Store plan file + + TPlan plan = new TPlan(); + plan.setId(xmlId); + plan.setName(name); + plan.setPlanType(type); + plan.setPlanLanguage(language); + PlansResource.setPlanModelReference(plan, planId, fileName); + this.list.add(plan); + + // prepare result + JsonFactory jsonFactory = new JsonFactory(); + StringWriter sw = new StringWriter(); + try { + JsonGenerator jGenerator = jsonFactory.createGenerator(sw); + jGenerator.writeStartObject(); + jGenerator.writeFieldName("tableData"); + jGenerator.writeStartArray(); + jGenerator.writeString(xmlId); + jGenerator.writeString(""); // precondition + jGenerator.writeString(name); + jGenerator.writeString(PlanTypesManager.INSTANCE.getShortName(type)); + jGenerator.writeString(PlanLanguagesManager.INSTANCE.getShortName(language)); + jGenerator.writeEndArray(); + jGenerator.writeEndObject(); + jGenerator.close(); + sw.close(); + } catch (JsonGenerationException e) { + PlansResource.logger.error(e.getMessage(), e); + return Response.serverError().build(); + } catch (IOException e) { + PlansResource.logger.error(e.getMessage(), e); + return Response.serverError().build(); + } + + Response res = BackendUtils.persist(this.res); + if (res.getStatus() == 204) { + // everything OK, return created + return Response.created(Utils.createURI(Util.URLencode(xmlId))).entity(sw.toString()).build(); + } else { + return res; + } + } + + static void setPlanModelReference(TPlan plan, PlanId planId, String fileName) { + PlanModelReference pref = new PlanModelReference(); + // Set path relative to Definitions/ path inside CSAR. + pref.setReference("../" + Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(planId)) + fileName); + plan.setPlanModelReference(pref); + } + + @Override + public String getId(TPlan plan) { + return plan.getId(); + } +} |