Add winery source code
[vfc/nfvo/wfengine.git] / winery / org.eclipse.winery.repository / src / main / java / org / eclipse / winery / repository / resources / _support / collections / EntityCollectionResource.java
1 /*******************************************************************************
2  * Copyright (c) 2012-2014 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._support.collections;
13
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import javax.ws.rs.Consumes;
18 import javax.ws.rs.GET;
19 import javax.ws.rs.POST;
20 import javax.ws.rs.Path;
21 import javax.ws.rs.PathParam;
22 import javax.ws.rs.Produces;
23 import javax.ws.rs.QueryParam;
24 import javax.ws.rs.core.HttpHeaders;
25 import javax.ws.rs.core.MediaType;
26 import javax.ws.rs.core.Response;
27 import javax.ws.rs.core.Response.Status;
28
29 import org.eclipse.winery.common.Util;
30 import org.eclipse.winery.repository.datatypes.select2.Select2DataItem;
31 import org.eclipse.winery.repository.resources._support.IPersistable;
32 import org.restdoc.annotations.RestDoc;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 import com.sun.jersey.api.NotFoundException;
37 import com.sun.jersey.api.view.Viewable;
38
39 /**
40  * Class managing a list of entities. It is intended to manage subresources,
41  * which are stored in a list. Either all entities have a unique key given by
42  * the TOSCA specification (subclass EntityWithIdCollectionResource) or a unique
43  * key is generated (subclass EntityWithoutIdCollectionResource)
44  * 
45  * @param <EntityResourceT> the resource modeling the entity
46  * @param <EntityT> the entity type of single items in the list
47  */
48 public abstract class EntityCollectionResource<EntityResourceT extends EntityResource<EntityT>, EntityT> implements IIdDetermination<EntityT> {
49         
50         private static final Logger logger = LoggerFactory.getLogger(EntityCollectionResource.class);
51         
52         protected final List<EntityT> list;
53         
54         protected final IPersistable res;
55         
56         protected final Class<EntityT> entityTClazz;
57         
58         protected final Class<EntityResourceT> entityResourceTClazz;
59         
60         
61         /**
62          * @param clazz the class of EntityT. Required as it is not possible to call
63          *            new EntityT (see http://stackoverflow.com/a/1090488/873282)
64          * @param list the list of entities contained in this resource. Has to be
65          *            typed <Object> as not all TOSCA elements in the specification
66          *            inherit from TExtensibleElements
67          * @param res the main resource the list is belonging to. Required for
68          *            persistence.
69          */
70         public EntityCollectionResource(Class<EntityResourceT> entityResourceTClazz, Class<EntityT> entityTClazz, List<EntityT> list, IPersistable res) {
71                 this.entityResourceTClazz = entityResourceTClazz;
72                 this.entityTClazz = entityTClazz;
73                 this.list = list;
74                 this.res = res;
75         }
76         
77         /**
78          * Returns a list of ids of all entities nested here
79          */
80         @GET
81         @Produces(MediaType.APPLICATION_JSON)
82         public Object getListOfAllEntityIds(@QueryParam("select2") String select2) {
83                 if (select2 == null) {
84                         return this.getListOfAllEntityIdsAsList();
85                 } else {
86                         // return data ready for consumption by select2
87                         List<Select2DataItem> res = new ArrayList<Select2DataItem>(this.list.size());
88                         for (EntityT o : this.list) {
89                                 String id = this.getId(o);
90                                 Select2DataItem di = new Select2DataItem(id, id);
91                                 res.add(di);
92                         }
93                         return res;
94                 }
95         }
96         
97         public List<String> getListOfAllEntityIdsAsList() {
98                 List<String> res = new ArrayList<String>(this.list.size());
99                 for (EntityT o : this.list) {
100                         // We assume that different Object serializations *always* have different hashCodes
101                         res.add(this.getId(o));
102                 }
103                 return res;
104         }
105         
106         /**
107          * Required by reqandcapdefs.jsp
108          */
109         public List<EntityResourceT> getAllEntityResources() {
110                 List<String> listOfAllSubResources = this.getListOfAllEntityIdsAsList();
111                 List<EntityResourceT> res = new ArrayList<EntityResourceT>(listOfAllSubResources.size());
112                 for (String id : listOfAllSubResources) {
113                         res.add(this.getEntityResourceFromDecodedId(id));
114                 }
115                 return res;
116         }
117         
118         public EntityResourceT getEntityResourceFromDecodedId(String id) {
119                 EntityT entity = null;
120                 int idx = -1;
121                 for (EntityT c : this.list) {
122                         idx++;
123                         String cId = this.getId(c);
124                         if (cId.equals(id)) {
125                                 entity = c;
126                                 break;
127                         }
128                 }
129                 if (entity == null) {
130                         throw new NotFoundException();
131                 } else {
132                         return this.getEntityResourceInstance(entity, idx);
133                 }
134         }
135         
136         @Path("{id}/")
137         public EntityResourceT getEntityResource(@PathParam("id") String id) {
138                 if (id == null) {
139                         throw new IllegalArgumentException("id has to be given");
140                 }
141                 id = Util.URLdecode(id);
142                 return this.getEntityResourceFromDecodedId(id);
143         }
144         
145         /**
146          * @param entity the entity to create a resource for
147          * @param idx the index in the list
148          * @return the resource managing the given entity
149          */
150         protected abstract EntityResourceT getEntityResourceInstance(EntityT entity, int idx);
151         
152         @GET
153         @Produces(MediaType.TEXT_HTML)
154         @RestDoc(methodDescription = "@return the HTML fragment (DIV-container) to be embedded in the 'Interface' part of nodetype.js ")
155         public Response getHTMLAsResponse() {
156                 Viewable viewable = this.getHTML();
157                 return Response.ok().header(HttpHeaders.VARY, HttpHeaders.ACCEPT).entity(viewable).build();
158         }
159         
160         /**
161          * called by getHTMLAsResponse
162          */
163         public abstract Viewable getHTML();
164         
165         /**
166          * Adds a new entity
167          * 
168          * In case the element already exists, we return "CONFLICT"
169          */
170         @POST
171         @Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
172         public Response addNewElement(EntityT entity) {
173                 if (entity == null) {
174                         return Response.status(Status.BAD_REQUEST).entity("a valid XML/JSON element has to be posted").build();
175                 }
176                 if (this.alreadyContains(entity)) {
177                         // we do not replace the element, but replace it
178                         return Response.status(Status.CONFLICT).build();
179                 }
180                 this.list.add(entity);
181                 return CollectionsHelper.persist(this.res, this, entity);
182         }
183         
184         @Override
185         public abstract String getId(EntityT entity);
186         
187         /**
188          * Checks for containment of e in the list. <code>equals</code> is not used
189          * as most EntityT do not offer a valid implementation
190          * 
191          * @return true if list already contains e.
192          */
193         public boolean alreadyContains(EntityT e) {
194                 String id = this.getId(e);
195                 for (EntityT el : this.list) {
196                         if (this.getId(el).equals(id)) {
197                                 // break loop
198                                 // we found an equal list item
199                                 return true;
200                         }
201                 }
202                 // all items checked: nothing equal contained
203                 return false;
204         }
205         
206 }