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

Last change on this file since 102:9140017e8962 was 102:9140017e8962, checked in by casties, 7 years ago

fix bug with empty username. add logging for JSON exceptions.

File size: 10.5 KB
Line 
1package de.mpiwg.itgroup.annotations.restlet;
2
3/*
4 * #%L
5 * AnnotationManager
6 * %%
7 * Copyright (C) 2012 - 2014 MPIWG Berlin
8 * %%
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program.  If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * #L%
23 */
24
25import java.io.IOException;
26import java.io.UnsupportedEncodingException;
27import java.net.URLDecoder;
28import java.util.ArrayList;
29import java.util.List;
30
31import org.json.JSONArray;
32import org.json.JSONException;
33import org.json.JSONObject;
34import org.restlet.data.Form;
35import org.restlet.data.Status;
36import org.restlet.ext.json.JsonRepresentation;
37import org.restlet.representation.Representation;
38import org.restlet.resource.Delete;
39import org.restlet.resource.Get;
40import org.restlet.resource.Post;
41import org.restlet.resource.Put;
42
43import de.mpiwg.itgroup.annotations.Annotation;
44import de.mpiwg.itgroup.annotations.Person;
45import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore;
46import de.mpiwg.itgroup.annotations.restlet.utils.JSONObjectComparator;
47
48/**
49 * Implements the "annotations" uri of the Annotator API. see
50 * <https://github.com/okfn/annotator/wiki/Storage>
51 *
52 * @author dwinter, casties
53 *
54 */
55public class AnnotatorAnnotations extends AnnotatorResourceImpl {
56
57    /**
58     * GET with JSON content-type.
59     *
60     * @param entity
61     * @return
62     */
63    @Get("json")
64    public Representation doGetJSON(Representation entity) {
65        logger.fine("AnnotatorAnnotations doGetJSON!");
66        // id from URI /annotations/{id}
67        String id = null;
68        String jsonId = (String) getRequest().getAttributes().get("id");
69        if (jsonId != null) {
70            // URL decode
71            try {
72                jsonId = URLDecoder.decode(jsonId, "UTF-8");
73            } catch (UnsupportedEncodingException e) {
74                // this shouldn't happen
75            }
76            id = decodeJsonId(jsonId);
77            logger.fine("annotation-id=" + id);
78        }
79
80        // do authentication
81        Person authUser = getUserFromAuthToken(entity);
82        logger.fine("request authenticated=" + authUser);
83
84        if (id == null) {
85            // no id -- send all annotations
86            Form form = getRequest().getResourceRef().getQueryAsForm();
87            int limit = getInt(form.getFirstValue("limit", "1000"));
88            int offset = getInt(form.getFirstValue("offset", "0"));
89            String sortBy = form.getFirstValue("sortBy");
90            return getAllAnnotations(authUser, limit, offset, sortBy);
91        }
92
93        // send annotation with id
94        AnnotationStore store = getAnnotationStore();
95        Annotation annot = store.getAnnotationById(id);
96        if (annot != null) {
97            if (!annot.isActionAllowed("read", authUser, store)) {
98                setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
99                return null;
100            }
101            JSONObject result = createAnnotatorJson(annot, (authUser == null));
102            return new JsonRepresentation(result);
103        } else {
104            // not found
105            setStatus(Status.CLIENT_ERROR_NOT_FOUND);
106            return null;
107        }
108    }
109
110    private Representation getAllAnnotations(Person authUser, int limit, int offset, String sortBy) {
111        AnnotationStore store = getAnnotationStore();
112        ArrayList<JSONObject> results = new ArrayList<JSONObject>();
113
114        // read all annotations
115        List<Annotation> annotations = store.getAnnotations(null, null, 0, 0);
116        for (Annotation annotation : annotations) {
117            // check permission
118            if (!annotation.isActionAllowed("read", authUser, store))
119                continue;
120            // add annotation to list
121            JSONObject jo = createAnnotatorJson(annotation, false);
122            results.add(jo);
123        }
124
125        // sort if necessary
126        if (sortBy != null) {
127            JSONObjectComparator.sortAnnotations(results, sortBy);
128        }
129
130        // put in JSON list
131        JSONArray rows = new JSONArray();
132        int cnt = 0;
133        int max = limit + offset;
134        for (JSONObject result : results) {
135            cnt += 1;
136            if (cnt < offset) continue;
137            rows.put(result);
138            if (limit > 0 && cnt >= max) break;
139        }
140
141        // assemble result object
142        JSONObject result = new JSONObject();
143        try {
144            result.put("rows", rows);
145            result.put("total", rows.length());
146        } catch (JSONException e) {
147            setStatus(Status.SERVER_ERROR_INTERNAL, "JSON Error");
148            return null;
149        }
150        return new JsonRepresentation(result);
151    }
152
153    /**
154     * POST with JSON content-type. Creates a new Annotation.
155     *
156     * @return
157     */
158    @Post("json")
159    public Representation doPostJson(Representation entity) {
160        logger.fine("AnnotatorAnnotations doPostJSON!");
161
162        // do authentication TODO: who's allowed to create?
163        Person authUser = getUserFromAuthToken(entity);
164        logger.fine("request authenticated=" + authUser);
165        if (authUser == null) {
166            setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
167            return null;
168        }
169
170        Annotation annot = null;
171        try {
172            JsonRepresentation jrep = new JsonRepresentation(entity);
173            JSONObject jo = jrep.getJsonObject();
174            if (jo == null) {
175                setStatus(Status.SERVER_ERROR_INTERNAL);
176                return null;
177            }
178            // make sure id is not set for POST
179            jo.remove("id");
180            // create Annotation object from posted JSON
181            annot = createAnnotation(jo, entity);
182        } catch (IOException e) {
183                logger.warning(e.toString());
184            setStatus(Status.SERVER_ERROR_INTERNAL);
185            return null;
186        } catch (JSONException e) {
187                logger.warning(e.toString());
188            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
189            return null;
190        }
191        if (annot == null) {
192            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
193            return null;
194        }
195        Annotation storedAnnot;
196        // store Annotation
197        storedAnnot = getAnnotationStore().storeAnnotation(annot);
198        /*
199         * according to https://github.com/okfn/annotator/wiki/Storage we should
200         * return 303: see other. For now we return the annotation.
201         */
202        JSONObject jo = createAnnotatorJson(storedAnnot, (authUser == null));
203        JsonRepresentation retRep = new JsonRepresentation(jo);
204        return retRep;
205    }
206
207    /**
208     * PUT with JSON content-type. Modifies an Annotation.
209     *
210     * @param entity
211     * @return
212     */
213    @Put("json")
214    public Representation doPutJSON(Representation entity) {
215        logger.fine("AnnotatorAnnotations doPutJSON!");
216        // id from URI /annotations/{id}
217        String jsonId = (String) getRequest().getAttributes().get("id");
218        String id = decodeJsonId(jsonId);
219        logger.fine("annotation-id=" + id);
220
221        // do authentication
222        Person authUser = getUserFromAuthToken(entity);
223        logger.fine("request authenticated=" + authUser);
224
225        Annotation annot = null;
226        AnnotationStore store = getAnnotationStore();
227        try {
228            JsonRepresentation jrep = new JsonRepresentation(entity);
229            JSONObject jo = jrep.getJsonObject();
230            if (jo == null) {
231                setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
232                return null;
233            }
234            // get stored Annotation
235            Annotation storedAnnot = store.getAnnotationById(id);
236            if (storedAnnot == null) {
237                setStatus(Status.CLIENT_ERROR_NOT_FOUND);
238                return null;
239            }
240            if (!storedAnnot.isActionAllowed("update", authUser, store)) {
241                setStatus(Status.CLIENT_ERROR_FORBIDDEN);
242                return null;
243            }
244            // update from posted JSON
245            annot = updateAnnotation(storedAnnot, jo, entity);
246            // store Annotation
247            storedAnnot = store.storeAnnotation(annot);
248            /*
249             * according to https://github.com/okfn/annotator/wiki/Storage we
250             * should return 303: see other. but the client doesn't like it
251             * setStatus(Status.REDIRECTION_SEE_OTHER); // go to same URL as
252             * this one Reference thisUrl = this.getReference();
253             * this.getResponse().setLocationRef(thisUrl);
254             */
255            // return new annotation
256            jo = createAnnotatorJson(storedAnnot, (authUser == null));
257            JsonRepresentation retRep = new JsonRepresentation(jo);
258            return retRep;
259        } catch (JSONException e) {
260            logger.severe("Error in doPutJSON: "+e);
261            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
262        } catch (IOException e) {
263            logger.severe("Error in doPutJSON: "+e);
264            setStatus(Status.SERVER_ERROR_INTERNAL, "Other Error");
265        }
266        return null;
267    }
268
269    /**
270     * DELETE with JSON content-type. Deletes an Annotation.
271     *
272     * @param entity
273     * @return
274     */
275    @Delete("json")
276    public Representation doDeleteJSON(Representation entity) {
277        logger.fine("AnnotatorAnnotations doDeleteJSON!");
278        // id from URI /annotations/{id}
279        String jsonId = (String) getRequest().getAttributes().get("id");
280        String id = decodeJsonId(jsonId);
281        logger.fine("annotation-id=" + id);
282
283        // do authentication
284        Person authUser = getUserFromAuthToken(entity);
285        logger.fine("request authenticated=" + authUser);
286        AnnotationStore store = getAnnotationStore();
287        Annotation annot = store.getAnnotationById(id);
288        if (annot != null) {
289            if (!annot.isActionAllowed("delete", authUser, store)) {
290                setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!");
291                return null;
292            }
293        }
294        // delete annotation
295        store.deleteAnnotationById(id);
296        setStatus(Status.SUCCESS_NO_CONTENT);
297        return null;
298    }
299
300}
Note: See TracBrowser for help on using the repository browser.