Java tutorial
/******************************************************************************* * Copyright (c) 2012-2017 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, minor improvements * Lukas Harzenetter - added show all items query argument * Nicole Keppler - Bugfixes *******************************************************************************/ package org.eclipse.winery.repository.rest.resources; import java.io.IOException; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.SortedSet; import java.util.stream.Collectors; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; import javax.xml.namespace.QName; import org.eclipse.winery.common.Util; import org.eclipse.winery.common.ids.Namespace; import org.eclipse.winery.common.ids.definitions.ArtifactTemplateId; import org.eclipse.winery.common.ids.definitions.PolicyTemplateId; import org.eclipse.winery.common.ids.definitions.ServiceTemplateId; import org.eclipse.winery.common.ids.definitions.TOSCAComponentId; import org.eclipse.winery.repository.backend.BackendUtils; import org.eclipse.winery.repository.backend.RepositoryFactory; import org.eclipse.winery.repository.rest.RestUtils; import org.eclipse.winery.repository.rest.resources._support.ResourceCreationResult; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.sun.jersey.api.NotFoundException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Resource handling of a set of components. Each component has to provide a class to handle the set. This is required * to provide the correct instances of TOSCAcomponentIds. * * TODO: Add generics here! {@link RestUtils#getComponentIdClassForComponentContainer(java.lang.Class)} is then * obsolete */ public abstract class AbstractComponentsResource<R extends AbstractComponentInstanceResource> { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractComponentsResource.class); @Path("{namespace}/") public ComponentsOfOneNamespaceResource getAllResourcesInNamespaceResource( @PathParam("namespace") String namespace) { return new ComponentsOfOneNamespaceResource(this.getClass(), namespace); } /** * Creates a new component instance in the given namespace * * @param namespace plain namespace * @param name the name; used as id */ protected ResourceCreationResult onPost(String namespace, String name) { ResourceCreationResult res; if (StringUtils.isEmpty(namespace) || StringUtils.isEmpty(name)) { res = new ResourceCreationResult(Status.BAD_REQUEST); } else { String id = RestUtils.createXMLidAsString(name); TOSCAComponentId tcId; try { tcId = this.getTOSCAcomponentId(namespace, id, false); res = this.createComponentInstance(tcId); // in case the resource additionally supports a name attribute, we set the original name if (res.getStatus().equals(Status.CREATED)) { if ((tcId instanceof ServiceTemplateId) || (tcId instanceof ArtifactTemplateId) || (tcId instanceof PolicyTemplateId)) { // these three types have an additional name (instead of a pure id) // we store the name IHasName resource = (IHasName) AbstractComponentsResource.getComponentInstaceResource(tcId); resource.setName(name); } } } catch (Exception e) { AbstractComponentsResource.LOGGER.debug("Could not create id instance", e); res = new ResourceCreationResult(Status.INTERNAL_SERVER_ERROR); } } return res; } /** * Creates a TOSCAcomponentId for the given namespace / id combination * * Uses reflection to create a new instance */ protected TOSCAComponentId getTOSCAcomponentId(String namespace, String id, boolean URLencoded) { Class<? extends TOSCAComponentId> idClass = RestUtils .getComponentIdClassForComponentContainer(this.getClass()); return BackendUtils.getTOSCAcomponentId(idClass, namespace, id, URLencoded); } /** * Creates a new instance of the current component * * @return <ul> <li>Status.CREATED (201) if the resource has been created,</li> <li>Status.CONFLICT if the resource * already exists,</li> <li>Status.INTERNAL_SERVER_ERROR (500) if something went wrong</li> </ul> */ protected ResourceCreationResult createComponentInstance(TOSCAComponentId tcId) { return RestUtils.create(tcId); } @SuppressWarnings("unchecked") private static Class<? extends AbstractComponentInstanceResource> getComponentInstanceResourceClassForType( String type) { // Guess the package String pkg = "org.eclipse.winery.repository.rest.resources."; pkg += RestUtils.getIntermediateLocationStringForType(type, "."); // naming convention: Instance is named after container, but without the // plural s String className = pkg + "." + type + "Resource"; try { return (Class<? extends AbstractComponentInstanceResource>) Class.forName(className); } catch (ClassNotFoundException e) { throw new IllegalStateException("Could not find id class for component instance", e); } } /** * @param namespace encoded namespace * @param id encoded id * @return an instance of the requested resource */ @Path("{namespace}/{id}/") public R getComponentInstaceResource(@PathParam("namespace") String namespace, @PathParam("id") String id) { return this.getComponentInstaceResource(namespace, id, true); } /** * @param encoded specifies whether namespace and id are encoded * @return an instance of the requested resource */ @SuppressWarnings("unchecked") protected R getComponentInstaceResource(String namespace, String id, boolean encoded) { TOSCAComponentId tcId; try { tcId = this.getTOSCAcomponentId(namespace, id, encoded); } catch (Exception e) { throw new IllegalStateException("Could not create id instance", e); } return (R) AbstractComponentsResource.getComponentInstaceResource(tcId); } /** * @return an instance of the requested resource */ public R getComponentInstaceResource(QName qname) { return this.getComponentInstaceResource(qname.getNamespaceURI(), qname.getLocalPart(), false); } /** * @return an instance of the requested resource * @throws NotFoundException if resource doesn't exist. */ public static AbstractComponentInstanceResource getComponentInstaceResource(TOSCAComponentId tcId) { String type = Util.getTypeForComponentId(tcId.getClass()); if (!RepositoryFactory.getRepository().exists(tcId)) { AbstractComponentsResource.LOGGER.debug("TOSCA component id " + tcId.toString() + " not found"); throw new NotFoundException("TOSCA component id " + tcId.toString() + " not found"); } Class<? extends AbstractComponentInstanceResource> newResource = AbstractComponentsResource .getComponentInstanceResourceClassForType(type); Constructor<?>[] constructors = newResource.getConstructors(); assert (constructors.length == 1); AbstractComponentInstanceResource newInstance; try { newInstance = (AbstractComponentInstanceResource) constructors[0].newInstance(tcId); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { AbstractComponentsResource.LOGGER.error("Could not instantiate sub resource " + tcId); throw new IllegalStateException("Could not instantiate sub resource", e); } return newInstance; } /** * Returns resources for all known component instances * * Required by topologytemplateedit.jsp */ public Collection<AbstractComponentInstanceResource> getAll() { Class<? extends TOSCAComponentId> idClass = RestUtils .getComponentIdClassForComponentContainer(this.getClass()); SortedSet<? extends TOSCAComponentId> allTOSCAcomponentIds = RepositoryFactory.getRepository() .getAllTOSCAComponentIds(idClass); ArrayList<AbstractComponentInstanceResource> res = new ArrayList<>(allTOSCAcomponentIds.size()); for (TOSCAComponentId id : allTOSCAcomponentIds) { AbstractComponentInstanceResource r = AbstractComponentsResource.getComponentInstaceResource(id); res.add(r); } return res; } /** * Used by org.eclipse.winery.repository.repository.client and by the artifactcreationdialog.tag. Especially the * "name" field is used there at the UI * * @param grouped if given, the JSON output is grouped by namespace * @return A list of all ids of all instances of this component type. <br /> Format: <code>[({"namespace": * "[namespace]", "id": "[id]"},)* ]</code>. <br /><br /> If grouped is set, the list will be grouped by namespace. * <br /> <code>[{"id": "[namsepace encoded]", "test": "[namespace decoded]", "children":[{"id": "[qName]", "text": * "[id]"}]}]</code> */ @GET @Produces(MediaType.APPLICATION_JSON) public String getListOfAllIds(@QueryParam("grouped") String grouped) { Class<? extends TOSCAComponentId> idClass = RestUtils .getComponentIdClassForComponentContainer(this.getClass()); boolean supportsNameAttribute = Util.instanceSupportsNameAttribute(idClass); SortedSet<? extends TOSCAComponentId> allTOSCAcomponentIds = RepositoryFactory.getRepository() .getAllTOSCAComponentIds(idClass); JsonFactory jsonFactory = new JsonFactory(); StringWriter sw = new StringWriter(); try { JsonGenerator jg = jsonFactory.createGenerator(sw); // We produce org.eclipse.winery.repository.client.WineryRepositoryClient.NamespaceAndId by hand here // Refactoring could move this class to common and fill it here if (grouped == null) { jg.writeStartArray(); for (TOSCAComponentId id : allTOSCAcomponentIds) { jg.writeStartObject(); jg.writeStringField("namespace", id.getNamespace().getDecoded()); jg.writeStringField("id", id.getXmlId().getDecoded()); if (supportsNameAttribute) { AbstractComponentInstanceResource componentInstaceResource = AbstractComponentsResource .getComponentInstaceResource(id); String name = ((IHasName) componentInstaceResource).getName(); jg.writeStringField("name", name); } else { // used for winery-qNameSelector to avoid an if there jg.writeStringField("name", id.getXmlId().getDecoded()); } jg.writeStringField("qName", id.getQName().toString()); jg.writeEndObject(); } jg.writeEndArray(); } else { jg.writeStartArray(); Map<Namespace, ? extends List<? extends TOSCAComponentId>> groupedIds = allTOSCAcomponentIds .stream().collect(Collectors.groupingBy(id -> id.getNamespace())); groupedIds.keySet().stream().sorted().forEach(namespace -> { try { jg.writeStartObject(); jg.writeStringField("id", namespace.getEncoded()); jg.writeStringField("text", namespace.getDecoded()); jg.writeFieldName("children"); jg.writeStartArray(); groupedIds.get(namespace).forEach(id -> { try { jg.writeStartObject(); String text; if (supportsNameAttribute) { AbstractComponentInstanceResource componentInstaceResource = AbstractComponentsResource .getComponentInstaceResource(id); text = ((IHasName) componentInstaceResource).getName(); } else { text = id.getXmlId().getDecoded(); } jg.writeStringField("id", id.getQName().toString()); jg.writeStringField("text", text); jg.writeEndObject(); } catch (IOException e) { AbstractComponentsResource.LOGGER.error("Could not create JSON", e); } }); jg.writeEndArray(); jg.writeEndObject(); } catch (IOException e) { AbstractComponentsResource.LOGGER.error("Could not create JSON", e); } }); jg.writeEndArray(); } jg.close(); } catch (Exception e) { AbstractComponentsResource.LOGGER.error(e.getMessage(), e); return "[]"; } return sw.toString(); } }