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
10 * Oliver Kopp - initial API and implementation
11 *******************************************************************************/
12 package org.eclipse.winery.repository.resources;
14 import java.io.StringWriter;
15 import java.lang.reflect.Constructor;
16 import java.lang.reflect.InvocationTargetException;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.SortedSet;
21 import javax.ws.rs.Consumes;
22 import javax.ws.rs.FormParam;
23 import javax.ws.rs.GET;
24 import javax.ws.rs.POST;
25 import javax.ws.rs.Path;
26 import javax.ws.rs.PathParam;
27 import javax.ws.rs.Produces;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.Response.Status;
31 import javax.xml.namespace.QName;
33 import org.apache.commons.lang3.StringUtils;
34 import org.eclipse.winery.common.Util;
35 import org.eclipse.winery.common.ids.definitions.ArtifactTemplateId;
36 import org.eclipse.winery.common.ids.definitions.PolicyTemplateId;
37 import org.eclipse.winery.common.ids.definitions.ServiceTemplateId;
38 import org.eclipse.winery.common.ids.definitions.TOSCAComponentId;
39 import org.eclipse.winery.repository.Utils;
40 import org.eclipse.winery.repository.backend.BackendUtils;
41 import org.eclipse.winery.repository.backend.Repository;
42 import org.eclipse.winery.repository.backend.ResourceCreationResult;
43 import org.eclipse.winery.repository.resources.entitytemplates.artifacttemplates.ArtifactTemplatesResource;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import com.fasterxml.jackson.core.JsonFactory;
48 import com.fasterxml.jackson.core.JsonGenerator;
49 import com.sun.jersey.api.NotFoundException;
50 import com.sun.jersey.api.view.Viewable;
53 * Resource handling of a set of components. Each component has to provide a
54 * class to handle the set. This is required to provide the correct instances of
57 * TODO: Add generics here!
58 * {@link Utils.getComponentIdClassForComponentContainer} is then obsolete
60 public abstract class AbstractComponentsResource<R extends AbstractComponentInstanceResource> {
62 protected static final Logger logger = LoggerFactory.getLogger(AbstractComponentsResource.class);
66 @Produces(MediaType.TEXT_HTML)
67 public Response getHTML() {
68 return Response.ok().entity(new Viewable("/jsp/genericcomponentpage.jsp", new GenericComponentPageData(this.getClass()))).build();
72 * Creates a new component instance in the given namespace
74 * @param namespace plain namespace
77 protected ResourceCreationResult onPost(String namespace, String name) {
78 ResourceCreationResult res;
79 if (StringUtils.isEmpty(namespace) || StringUtils.isEmpty(name)) {
80 res = new ResourceCreationResult(Status.BAD_REQUEST);
82 String id = Utils.createXMLidAsString(name);
83 TOSCAComponentId tcId;
85 tcId = this.getTOSCAcomponentId(namespace, id, false);
86 res = this.createComponentInstance(tcId);
87 // in case the resource additionally supports a name attribute, we set the original name
88 if (res.getStatus().equals(Status.CREATED)) {
89 if ((tcId instanceof ServiceTemplateId) || (tcId instanceof ArtifactTemplateId) || (tcId instanceof PolicyTemplateId)) {
90 // these three types have an additional name (instead of a pure id)
92 IHasName resource = (IHasName) AbstractComponentsResource.getComponentInstaceResource(tcId);
93 resource.setName(name);
96 } catch (Exception e) {
97 AbstractComponentsResource.logger.debug("Could not create id instance", e);
98 res = new ResourceCreationResult(Status.INTERNAL_SERVER_ERROR);
105 * Creates a new component instance in the given namespace
107 * @param namespace plain namespace
109 * @param ignored this parameter is ignored, but necessary for
110 * {@link ArtifactTemplatesResource} to be able to accept the
111 * artifact type at a post
114 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
115 @Produces(MediaType.TEXT_PLAIN)
116 public Response onPost(@FormParam("namespace") String namespace, @FormParam("name") String name, String ignored) {
117 ResourceCreationResult res = this.onPost(namespace, name);
118 return res.getResponse();
122 * Creates a TOSCAcomponentId for the given namespace / id combination
124 * Uses reflection to create a new instance
126 protected TOSCAComponentId getTOSCAcomponentId(String namespace, String id, boolean URLencoded) throws Exception {
127 Class<? extends TOSCAComponentId> idClass = Utils.getComponentIdClassForComponentContainer(this.getClass());
128 return BackendUtils.getTOSCAcomponentId(idClass, namespace, id, URLencoded);
132 * Creates a new instance of the current component
135 * <li>Status.CREATED (201) if the resource has been created,</li>
136 * <li>Status.CONFLICT if the resource already exists,</li>
137 * <li>Status.INTERNAL_SERVER_ERROR (500) if something went wrong</li>
140 protected ResourceCreationResult createComponentInstance(TOSCAComponentId tcId) {
141 return BackendUtils.create(tcId);
144 @SuppressWarnings("unchecked")
145 private static Class<? extends AbstractComponentInstanceResource> getComponentInstanceResourceClassForType(String type) {
147 String pkg = "org.eclipse.winery.repository.resources.";
149 pkg += Utils.getIntermediateLocationStringForType(type, ".");
151 // naming convention: Instance is named after container, but without the
153 String className = pkg + "." + type + "Resource";
155 return (Class<? extends AbstractComponentInstanceResource>) Class.forName(className);
156 } catch (ClassNotFoundException e) {
157 throw new IllegalStateException("Could not find id class for component instance", e);
163 * @param namespace encoded namespace
164 * @param id encoded id
165 * @return an instance of the requested resource
167 @Path("{namespace}/{id}/")
168 public R getComponentInstaceResource(@PathParam("namespace") String namespace, @PathParam("id") String id) {
169 return this.getComponentInstaceResource(namespace, id, true);
173 * @param encoded specifies whether namespace and id are encoded
174 * @return an instance of the requested resource
176 @SuppressWarnings("unchecked")
177 public R getComponentInstaceResource(String namespace, String id, boolean encoded) {
178 TOSCAComponentId tcId;
180 tcId = this.getTOSCAcomponentId(namespace, id, encoded);
181 } catch (Exception e) {
182 throw new IllegalStateException("Could not create id instance", e);
184 return (R) AbstractComponentsResource.getComponentInstaceResource(tcId);
188 * @return an instance of the requested resource
190 public AbstractComponentInstanceResource getComponentInstaceResource(QName qname) {
191 return this.getComponentInstaceResource(qname.getNamespaceURI(), qname.getLocalPart(), false);
195 * @return an instance of the requested resource
196 * @throws NotFoundException if resource doesn't exist.
198 public static AbstractComponentInstanceResource getComponentInstaceResource(TOSCAComponentId tcId) {
199 String type = Util.getTypeForComponentId(tcId.getClass());
200 if (!Repository.INSTANCE.exists(tcId)) {
201 AbstractComponentsResource.logger.debug("TOSCA component id " + tcId.toString() + " not found");
202 throw new NotFoundException("TOSCA component id " + tcId.toString() + " not found");
204 Class<? extends AbstractComponentInstanceResource> newResource = AbstractComponentsResource.getComponentInstanceResourceClassForType(type);
205 Constructor<?>[] constructors = newResource.getConstructors();
206 assert (constructors.length == 1);
207 AbstractComponentInstanceResource newInstance;
209 newInstance = (AbstractComponentInstanceResource) constructors[0].newInstance(tcId);
210 } catch (InstantiationException | IllegalAccessException
211 | IllegalArgumentException | InvocationTargetException e) {
212 AbstractComponentsResource.logger.error("Could not instantiate sub resource " + tcId);
213 throw new IllegalStateException("Could not instantiate sub resource", e);
219 * Returns resources for all known component instances
221 * Required by topologytemplateedit.jsp
223 public Collection<AbstractComponentInstanceResource> getAll() {
224 Class<? extends TOSCAComponentId> idClass = Utils.getComponentIdClassForComponentContainer(this.getClass());
225 SortedSet<? extends TOSCAComponentId> allTOSCAcomponentIds = Repository.INSTANCE.getAllTOSCAComponentIds(idClass);
226 ArrayList<AbstractComponentInstanceResource> res = new ArrayList<AbstractComponentInstanceResource>(allTOSCAcomponentIds.size());
227 for (TOSCAComponentId id : allTOSCAcomponentIds) {
228 AbstractComponentInstanceResource r = AbstractComponentsResource.getComponentInstaceResource(id);
235 * Used by org.eclipse.winery.repository.repository.client and by the
236 * artifactcreationdialog.tag. Especially the "name" field is used there at
239 * @return A list of all ids of all instances of this component type. If the
240 * "name" attribute is required, that name is used as id <br />
242 * <code>[({"namespace": "<namespace>", "id": "<id>"},)* ]</code>. A
243 * <code>name<code> field is added if the model allows an additional name attribute
246 @Produces(MediaType.APPLICATION_JSON)
247 public String getListOfAllIds() {
248 Class<? extends TOSCAComponentId> idClass = Utils.getComponentIdClassForComponentContainer(this.getClass());
249 boolean supportsNameAttribute = Util.instanceSupportsNameAttribute(idClass);
250 SortedSet<? extends TOSCAComponentId> allTOSCAcomponentIds = Repository.INSTANCE.getAllTOSCAComponentIds(idClass);
251 JsonFactory jsonFactory = new JsonFactory();
252 StringWriter sw = new StringWriter();
255 JsonGenerator jg = jsonFactory.createGenerator(sw);
256 // We produce org.eclipse.winery.repository.client.WineryRepositoryClient.NamespaceAndId by hand here
257 // Refactoring could move this class to common and fill it here
258 jg.writeStartArray();
259 for (TOSCAComponentId id : allTOSCAcomponentIds) {
260 jg.writeStartObject();
261 jg.writeStringField("namespace", id.getNamespace().getDecoded());
262 jg.writeStringField("id", id.getXmlId().getDecoded());
263 if (supportsNameAttribute) {
264 AbstractComponentInstanceResource componentInstaceResource = AbstractComponentsResource.getComponentInstaceResource(id);
265 String name = ((IHasName) componentInstaceResource).getName();
266 jg.writeStringField("name", name);
272 } catch (Exception e) {
273 AbstractComponentsResource.logger.error(e.getMessage(), e);
276 return sw.toString();