/* * Copyright (C) 2013 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.jaxrs2.server.resource; import com.intel.mtwilson.jaxrs2.server.PATCH; import com.intel.dcsg.cpg.io.UUID; import com.intel.dcsg.cpg.validation.ValidationUtil; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import com.intel.mtwilson.jaxrs2.Document; import com.intel.mtwilson.jaxrs2.DocumentCollection; import com.intel.mtwilson.repository.FilterCriteria; import com.intel.mtwilson.repository.Locator; import com.intel.mtwilson.jaxrs2.PatchLink; import com.intel.mtwilson.jaxrs2.mediatype.DataMediaType; import java.util.List; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.BeanParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; /** * * Example simple JSON output: * * * {"hosts":[{"id":"06285da4-e170-4322-a843-480f3a55feec","name":"hostabc","connection_url":"http://1.2.3.4","description":"test * host","bios_mle":"bios-4.3.2"}]} * * Example XML output: * * <host_collection><hosts><host><id>bd7094d2-2ed3-468e-9c16-40999f9e4b8c</id><name>hostabc</name><connectionUrl>http://1.2.3.4</connectionUrl><description>test * host</description><biosMLE>bios-4.3.2</biosMLE></host></hosts></host_collection> * * This abstract class adds the following HTTP interface: * * GET /collection -> application/vnd.api+json * * * @author jbuhacoff */ public abstract class AbstractJsonapiResource<T extends Document, C extends DocumentCollection<T>, F extends FilterCriteria<T>, P extends PatchLink<T>, L extends Locator<T>> extends AbstractSimpleResource<T,C,F,P,L> { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractJsonapiResource.class); protected abstract C createEmptyCollection(); /* public AbstractJsonapiResource() { super(); }*/ @GET @Produces(DataMediaType.APPLICATION_VND_API_JSON) public C searchJsonapiCollection(@BeanParam F criteria) { log.debug("searchJsonapiCollection"); ValidationUtil.validate(criteria); // throw new MWException(e, ErrorCode.AS_INPUT_VALIDATION_ERROR, input, method.getName()); return getRepository().search(criteria); } /** * Add an item to the collection. Input Content-Type is * application/vnd.api+json Output Content-Type is application/vnd.api+json * * The input must represent a collection of items to add, even if the * collection only contains a single item. * * * @param hosts * @return */ @POST @Consumes({DataMediaType.APPLICATION_VND_API_JSON}) @Produces({DataMediaType.APPLICATION_VND_API_JSON}) public C createJsonapiCollection(C collection) { log.debug("createCollection"); ValidationUtil.validate(collection); // this behavior of autmoatically generating uuids if client didn't provide could be implemented in one place and reused in all create() methods... the utility could accept a DocumentCollection and set the ids... for (T item : collection.getDocuments()) { if (item.getId() == null) { item.setId(new UUID()); } getRepository().create(item); } return collection; } /** * Retrieve an item from the collection. Input Content-Type is not * applicable. Output Content-Type is application/vnd.api+json * * The output item is always wrapped in a collection. * * @param id * @return */ @Path("/{id}") @GET @Produces({DataMediaType.APPLICATION_VND_API_JSON}) public C retrieveJsonapiCollection(@BeanParam L locator) { // misnomer, what we really mean is "retrieve one but wrapped ina collection for jsonapi" log.debug("retrieveCollection"); T item = getRepository().retrieve(locator); // subclass is responsible for validating id if (item == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } C collection = createEmptyCollection(); collection.getDocuments().add(item); return collection; } /** * Replace an item in the collection. Input Content-Type is * application/vnd.api+json Output Content-Type is application/vnd.api+json * * The input item must be wrapped in a collection. The output item is always * wrapped in a collection. * * @param id * @param hostCollection * @return */ @Path("/{id}") @PUT @Consumes(DataMediaType.APPLICATION_VND_API_JSON) @Produces(DataMediaType.APPLICATION_VND_API_JSON) public C storeJsonapiCollection(@BeanParam L locator, C collection) {// misnomer, what we really mean is "store one but wrapped ina collection for jsonapi" log.debug("storeCollection"); ValidationUtil.validate(collection); List<T> list = collection.getDocuments(); if (list == null || list.isEmpty()) { throw new WebApplicationException(Response.Status.BAD_REQUEST); } T item = list.get(0); locator.copyTo(item); if (item == null) { getRepository().create(item); } else { getRepository().store(item); } return collection; } /** * Update an item in the collection. Input Content-Type is * application/vnd.api+json Output Content-Type is application/vnd.api+json * * The input is a JSON PATCH document. There are restrictions on what * operations are allowed because the back-end storage is a database schema * not a JSON document. The output is the modified item wrapped in a * collection. * * @param id * @return */ @Path("/{id}") @PATCH @Consumes(DataMediaType.APPLICATION_RELATIONAL_PATCH_JSON) @Produces(DataMediaType.APPLICATION_VND_API_JSON) public C patchJsonapiCollection(@BeanParam L locator /*, PatchDocumentCollection patch */) { log.debug("patchCollection"); // look it up first, update whtever fields are specified for update by the patch format, then issue updates... // HostFilterCriteria criteria = new HostFilterCriteria(); // criteria.id = UUID.valueOf(id); // return searchCollection(criteria); return null; } }