package de.mpiwg.itgroup.annotations.restlet; /* * #%L * AnnotationManager * %% * Copyright (C) 2012 - 2014 MPIWG Berlin * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% */ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.restlet.data.Form; import org.restlet.data.Status; import org.restlet.ext.json.JsonRepresentation; import org.restlet.representation.Representation; import org.restlet.resource.Delete; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.Put; import de.mpiwg.itgroup.annotations.Annotation; import de.mpiwg.itgroup.annotations.Annotation.Action; import de.mpiwg.itgroup.annotations.Person; import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; import de.mpiwg.itgroup.annotations.restlet.utils.JSONObjectComparator; /** * Implements the "annotations" uri of the Annotator API. see * * * @author dwinter, casties * */ public class AnnotatorAnnotations extends AnnotatorResourceImpl { /** * GET with JSON content-type. * * @param entity * @return */ @Get("json") public Representation doGetJSON(Representation entity) { logger.fine("AnnotatorAnnotations doGetJSON!"); // id from URI /annotations/{id} String id = null; String jsonId = (String) getRequest().getAttributes().get("id"); if (jsonId != null) { // URL decode try { jsonId = URLDecoder.decode(jsonId, "UTF-8"); } catch (UnsupportedEncodingException e) { // this shouldn't happen } id = decodeJsonId(jsonId); logger.fine("annotation-id=" + id); } // do authentication Person authUser = getUserFromAuthToken(entity); logger.fine("request authenticated=" + authUser); if (id == null) { // no id -- send all annotations Form form = getRequest().getResourceRef().getQueryAsForm(); int limit = getInt(form.getFirstValue("limit", "1000")); int offset = getInt(form.getFirstValue("offset", "0")); String sortBy = form.getFirstValue("sortBy"); return getAllAnnotations(authUser, limit, offset, sortBy); } // send annotation with id AnnotationStore store = getAnnotationStore(); Annotation annot = store.getAnnotationById(id); if (annot != null) { if (!annot.isActionAllowed(Action.read, authUser, store)) { setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!"); return null; } JSONObject result = createAnnotatorJson(annot, (authUser == null)); return new JsonRepresentation(result); } else { // not found setStatus(Status.CLIENT_ERROR_NOT_FOUND); return null; } } private Representation getAllAnnotations(Person authUser, int limit, int offset, String sortBy) { AnnotationStore store = getAnnotationStore(); ArrayList results = new ArrayList(); // read all annotations List annotations = store.getAnnotations(null, null, 0, 0); for (Annotation annotation : annotations) { // check permission if (!annotation.isActionAllowed(Action.read, authUser, store)) continue; // add annotation to list JSONObject jo = createAnnotatorJson(annotation, false); results.add(jo); } // sort if necessary if (sortBy != null) { JSONObjectComparator.sortAnnotations(results, sortBy); } // put in JSON list JSONArray rows = new JSONArray(); int cnt = 0; int max = limit + offset; for (JSONObject result : results) { cnt += 1; if (cnt < offset) continue; rows.put(result); if (limit > 0 && cnt >= max) break; } // assemble result object JSONObject result = new JSONObject(); try { result.put("rows", rows); result.put("total", rows.length()); } catch (JSONException e) { setStatus(Status.SERVER_ERROR_INTERNAL, "JSON Error"); return null; } return new JsonRepresentation(result); } /** * POST with JSON content-type. Creates a new Annotation. * * @return */ @Post("json") public Representation doPostJson(Representation entity) { logger.fine("AnnotatorAnnotations doPostJSON!"); // do authentication TODO: who's allowed to create? Person authUser = getUserFromAuthToken(entity); logger.fine("request authenticated=" + authUser); if (authUser == null) { setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!"); return null; } Annotation annot = null; try { JsonRepresentation jrep = new JsonRepresentation(entity); JSONObject jo = jrep.getJsonObject(); if (jo == null) { setStatus(Status.SERVER_ERROR_INTERNAL); return null; } // make sure id is not set for POST jo.remove("id"); // create Annotation object from posted JSON annot = createAnnotation(jo, entity); } catch (IOException e) { logger.warning(e.toString()); setStatus(Status.SERVER_ERROR_INTERNAL); return null; } catch (JSONException e) { logger.warning(e.toString()); setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } if (annot == null) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } Annotation storedAnnot; // store Annotation storedAnnot = getAnnotationStore().storeAnnotation(annot); /* * according to https://github.com/okfn/annotator/wiki/Storage we should * return 303: see other. For now we return the annotation. */ JSONObject jo = createAnnotatorJson(storedAnnot, (authUser == null)); JsonRepresentation retRep = new JsonRepresentation(jo); return retRep; } /** * PUT with JSON content-type. Modifies an Annotation. * * @param entity * @return */ @Put("json") public Representation doPutJSON(Representation entity) { logger.fine("AnnotatorAnnotations doPutJSON!"); // id from URI /annotations/{id} String jsonId = (String) getRequest().getAttributes().get("id"); String id = decodeJsonId(jsonId); logger.fine("annotation-id=" + id); // do authentication Person authUser = getUserFromAuthToken(entity); logger.fine("request authenticated=" + authUser); Annotation annot = null; AnnotationStore store = getAnnotationStore(); try { JsonRepresentation jrep = new JsonRepresentation(entity); JSONObject jo = jrep.getJsonObject(); if (jo == null) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } // get stored Annotation Annotation storedAnnot = store.getAnnotationById(id); if (storedAnnot == null) { setStatus(Status.CLIENT_ERROR_NOT_FOUND); return null; } if (!storedAnnot.isActionAllowed(Action.update, authUser, store)) { setStatus(Status.CLIENT_ERROR_FORBIDDEN); return null; } // update from posted JSON annot = updateAnnotation(storedAnnot, jo, entity); // store Annotation storedAnnot = store.storeAnnotation(annot); /* * according to https://github.com/okfn/annotator/wiki/Storage we * should return 303: see other. but the client doesn't like it * setStatus(Status.REDIRECTION_SEE_OTHER); // go to same URL as * this one Reference thisUrl = this.getReference(); * this.getResponse().setLocationRef(thisUrl); */ // return new annotation jo = createAnnotatorJson(storedAnnot, (authUser == null)); JsonRepresentation retRep = new JsonRepresentation(jo); return retRep; } catch (JSONException e) { logger.severe("Error in doPutJSON: "+e); setStatus(Status.CLIENT_ERROR_BAD_REQUEST); } catch (IOException e) { logger.severe("Error in doPutJSON: "+e); setStatus(Status.SERVER_ERROR_INTERNAL, "Other Error"); } return null; } /** * DELETE with JSON content-type. Deletes an Annotation. * * @param entity * @return */ @Delete("json") public Representation doDeleteJSON(Representation entity) { logger.fine("AnnotatorAnnotations doDeleteJSON!"); // id from URI /annotations/{id} String jsonId = (String) getRequest().getAttributes().get("id"); String id = decodeJsonId(jsonId); logger.fine("annotation-id=" + id); // do authentication Person authUser = getUserFromAuthToken(entity); logger.fine("request authenticated=" + authUser); AnnotationStore store = getAnnotationStore(); Annotation annot = store.getAnnotationById(id); if (annot != null) { if (!annot.isActionAllowed(Action.delete, authUser, store)) { setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!"); return null; } } // delete annotation store.deleteAnnotationById(id); setStatus(Status.SUCCESS_NO_CONTENT); return null; } }