view src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java @ 102:9140017e8962

fix bug with empty username. add logging for JSON exceptions.
author casties
date Thu, 09 Feb 2017 20:46:15 +0100
parents 7268c3ca025b
children 7417f5915181
line wrap: on
line source

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
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #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.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
 * <https://github.com/okfn/annotator/wiki/Storage>
 * 
 * @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("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<JSONObject> results = new ArrayList<JSONObject>();

        // read all annotations
        List<Annotation> annotations = store.getAnnotations(null, null, 0, 0);
        for (Annotation annotation : annotations) {
            // check permission
            if (!annotation.isActionAllowed("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("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("delete", authUser, store)) {
                setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
                return null;
            }
        }
        // delete annotation
        store.deleteAnnotationById(id);
        setStatus(Status.SUCCESS_NO_CONTENT);
        return null;
    }

}