changeset 11:2f8c72ae4c43

working on create and read api for annotator.
author casties
date Tue, 20 Mar 2012 16:53:48 +0100
parents 0bdfe01e30b5
children 2e5d526079de
files src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorAnnotations.java src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java
diffstat 3 files changed, 353 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorAnnotations.java	Tue Mar 20 16:53:48 2012 +0100
@@ -0,0 +1,265 @@
+/**
+ * Implements the "annotations" uri of the Annotator API. see
+ * <https://github.com/okfn/annotator/wiki/Storage>
+ */
+package de.mpiwg.itgroup.annotationManager.restlet;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Status;
+import org.restlet.ext.json.JsonRepresentation;
+import org.restlet.representation.Representation;
+import org.restlet.representation.StringRepresentation;
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+import org.restlet.security.User;
+
+import de.mpiwg.itgroup.annotationManager.Constants.NS;
+import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreSearchError;
+import de.mpiwg.itgroup.annotationManager.Errors.TripleStoreStoreError;
+import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert;
+import de.mpiwg.itgroup.annotationManager.RDFHandling.RDFSearcher;
+import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert.Annotation;
+import de.mpiwg.itgroup.annotationManager.drupal.AnnotationHandler;
+import de.mpiwg.itgroup.annotationManager.drupal.UnknowUserException;
+import de.mpiwg.itgroup.triplestoremanager.exceptions.TripleStoreHandlerException;
+
+/**
+ * Implements the "annotations" uri of the Annotator API. see <https://github.com/okfn/annotator/wiki/Storage>
+ * 
+ * @author dwinter, casties
+ * 
+ */
+public class AnnotatorAnnotations extends AnnotatorResourceImpl {
+
+    protected String getAllowedMethodsForHeader() {
+        return "OPTIONS,GET,POST,PUT,DELETE";
+    }
+
+    /**
+     * GET with JSON content-type.
+     * 
+     * @param entity
+     * @return
+     */
+    @Get("json")
+    public Representation doGetJSON(Representation entity) {
+
+        doOptions(entity);
+        // TODO: what to do with authentication?
+        boolean authenticated = isAuthenticated(entity);
+        logger.debug("request authenticated=" + authenticated);
+
+        // TODO: Annotator read request does not use parameters
+        Form form = getRequest().getResourceRef().getQueryAsForm();
+        String uri = form.getFirstValue("uri");
+        String user = form.getFirstValue("user");
+
+        String limit = form.getFirstValue("limit");
+        String offset = form.getFirstValue("offset");
+
+        RDFSearcher searcher = new RDFSearcher("file:///annotations"); // TODO should ge into config file
+
+        JSONArray results;
+        try {
+            List<Convert.Annotation> annots = searcher.search(uri, user, limit, offset);
+
+            results = new JSONArray();
+            for (Convert.Annotation annot : annots) {
+                JSONObject jo = annot2AnnotatorJSON(annot);
+                if (jo != null) {
+                    results.put(annot2AnnotatorJSON(annot));
+                } else {
+                    setStatus(Status.SERVER_ERROR_INTERNAL, "JSon Error");
+                    return null;
+                }
+            }
+        } catch (TripleStoreHandlerException e) {
+            e.printStackTrace();
+            setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStoreHandler Error");
+            return null;
+        } catch (TripleStoreSearchError e) {
+            e.printStackTrace();
+            setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStoreSearch Error");
+            return null;
+        }
+
+        // annotator read request returns a list of annotation objects
+        logger.debug("sending:");
+        logger.debug(results);
+        return new JsonRepresentation(results);
+    }
+
+    /**
+     * POST with JSON content-type. json hash: username: name des users xpointer: xpointer auf den Ausschnitt (incl. der URL des
+     * Dokumentes) text: text der annotation annoturl: url auf eine Annotation falls extern
+     * 
+     * @return
+     */
+    @Post("json")
+    public Representation doPostJson(Representation entity) {
+        JsonRepresentation jrep;
+        Annotation annot;
+        try {
+            jrep = new JsonRepresentation(entity);
+            JSONObject jo = jrep.getJsonObject();
+            if (jo == null) {
+                setStatus(Status.SERVER_ERROR_INTERNAL);
+                return null;
+            }
+            annot = createAnnotation(jo, entity);
+        } catch (IOException e1) {
+            setStatus(Status.SERVER_ERROR_INTERNAL);
+            return null;
+        } catch (JSONException e) {
+            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+            return null;
+        }
+        if (annot.xpointer == null || annot.creator == null) {
+            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+            return null;
+        }
+        Annotation retVal;
+        try {
+            retVal = new Convert("file:///annotations").storeAnnotation(annot);
+        } catch (TripleStoreStoreError e) {
+            e.printStackTrace();
+            setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStore Error");
+            return null;
+        }
+        JSONObject jo = annot2AnnotatorJSON(retVal);
+        JsonRepresentation retRep = new JsonRepresentation(jo);
+        return retRep;
+    }
+
+    /**
+     * POST with HTML content-type.
+     * 
+     * @param entity
+     * @return
+     */
+    @Post("html")
+    public Representation doPostHtml(Representation entity) {
+        Convert.Annotation annot;
+        annot = handleForm(entity);
+        if (annot.xpointer == null || annot.creator == null) {
+            setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
+
+            return null;
+        }
+
+        Annotation retValAnnot;
+        try {
+            retValAnnot = new Convert("file:///annotations").storeAnnotation(annot);
+        } catch (TripleStoreStoreError e) {
+            e.printStackTrace();
+            setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStore Error");
+            return null;
+        }
+        if (retValAnnot == null) {
+            return null;
+        }
+        String retVal = retValAnnot.annotationUri;
+        if (retVal == null) {
+            return null;
+        }
+
+        String text = String.format("<html><body><a href=\"%s\">%s</a></body></html>", retVal.replace(">", "").replace("<", ""),
+                retVal.replace(">", "&gt;").replace("<", "&lt;"));
+        Representation retRep = new StringRepresentation(text, MediaType.TEXT_HTML);
+        return retRep;
+    }
+
+
+    /**
+     * 
+     * @param entity should contain a form with the parameters "username", "password", "xpointer","text","uri","type"
+     * 
+     * username,password is optional, if not given BasicAuthentification is used.
+     * 
+     * 
+     * 
+     * If username given as a URI, the username will be transformed to an URI, username will be added to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
+     * 
+     * @return
+     */
+    protected Convert.Annotation handleForm(Representation entity) {
+        Convert.Annotation annot;
+        Form form = new Form(entity);
+        String username = form.getValues("username");
+        String mode = form.getValues("mode");
+        String password = form.getValues("password");
+        String xpointer = form.getValues("xpointer");
+        String text = form.getValues("text");
+        String title = form.getValues("title");
+        String url = form.getValues("url");
+        String type = form.getValues("type");
+        RestServer restServer = (RestServer) getApplication();
+
+        // falls user and password nicht null sind:
+        User userFromForm = null;
+        if (username != null && password != null) {
+            if (restServer.authenticate(username, password, getRequest())) {
+                userFromForm = new User(username);
+            }
+        }
+        User authUser = null;
+
+        if (userFromForm == null) {
+            authUser = handleBasicAuthentification(entity);
+        }
+
+        // weder BasicAuth noch FormAuth
+        if (authUser == null && userFromForm == null) {
+            setStatus(Status.CLIENT_ERROR_FORBIDDEN);
+            return null;
+        }
+
+        if (userFromForm != null) {
+            username = userFromForm.getIdentifier();
+        } else {
+            username = authUser.getIdentifier();
+        }
+
+        //username should be a URI, if not it will set to the MPIWG namespace defined in de.mpiwg.itgroup.annotationManager.Constants.NS
+        String usernameOrig=username;
+        if (!username.startsWith("http"))
+            username=NS.MPIWG_PERSONS+username;
+        
+        if (mode.equals("complexAnnotation")){// Annotation mit text in externer ressource
+            
+            Context context = getContext();
+            String drupalPath = context.getParameters().getFirstValue("de.mpiwg.itgroup.annotationManager.drupalServer");
+            
+            
+            AnnotationHandler ah = new AnnotationHandler(drupalPath);
+            JSONObject newAnnot;
+            try {
+                newAnnot = ah.createAnnotation(title, text, usernameOrig, password);
+            } catch (UnknowUserException e1) {
+                setStatus(Status.CLIENT_ERROR_FORBIDDEN);
+                e1.printStackTrace();
+                return null;
+            }
+            try {
+                annot= new Convert.Annotation(xpointer, username, null, text, type, newAnnot.getString("node_uri"));
+            } catch (JSONException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+                setStatus(Status.SERVER_ERROR_INTERNAL);
+                return null;
+            }
+        } else
+            annot = new Convert.Annotation(xpointer, username, null, text,
+                type, url);
+        return annot;
+    }
+
+}
--- a/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java	Tue Mar 20 15:55:51 2012 +0100
+++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java	Tue Mar 20 16:53:48 2012 +0100
@@ -5,6 +5,7 @@
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
+import java.net.URLEncoder;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -20,9 +21,11 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.restlet.data.Form;
+import org.restlet.data.Status;
 import org.restlet.representation.Representation;
 import org.restlet.resource.Options;
 import org.restlet.resource.ServerResource;
+import org.restlet.security.User;
 
 import de.mpiwg.itgroup.annotationManager.Constants.NS;
 import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert;
@@ -144,12 +147,12 @@
     }
 
     /**
-     * Erzeugt aus einer Annotation, das fuer den Annotator notwendige JSON-Format
+     * creates Annotator-JSON from an Annotation object.
      * 
      * @param annot
      * @return
      */
-    protected JSONObject annot2AnnotatorJSON(Convert.Annotation annot) {
+    public JSONObject annot2AnnotatorJSON(Convert.Annotation annot) {
         JSONObject jo = new JSONObject();
         try {
             jo.put("text", annot.text);
@@ -231,4 +234,86 @@
         return ja;
     }
 
+    /**
+     * creates an Annotation object with data from JSON.
+     * uses the specification from the annotator project.
+     * 
+     * @see{https://github.com/okfn/annotator/wiki/Annotation-format
+     * 
+     * The username will be transformed to an URI if not given already
+     * as URI, if not it will set to the MPIWG namespace defined in
+     * de.mpiwg.itgroup.annotationManager.Constants.NS
+     * @param jo
+     * @return
+     * @throws JSONException
+     */
+    public Convert.Annotation createAnnotation(JSONObject jo, Representation entity) throws JSONException {
+        Convert.Annotation annot;
+        String url = jo.getString("uri");
+        String text = jo.getString("text");
+    
+        String username = null;
+        if (jo.has("user")) { 
+            // not required, if no username given authuser
+            // will be used otherwise username and password
+            // has to be submitted
+            JSONObject user = jo.getJSONObject("user");
+            if (user.has("id")) {
+                username = user.getString("id");
+                if (!user.has("password")) {
+                    User authUser = handleBasicAuthentification(entity);
+                    if (authUser == null) {
+                        setStatus(Status.CLIENT_ERROR_FORBIDDEN);
+                        return null;
+                    }
+                    username = authUser.getIdentifier();
+                } else {
+                    String password = user.getString("password");
+                    if (!((RestServer) getApplication()).authenticate(username, password, getRequest())) {
+                        setStatus(Status.CLIENT_ERROR_FORBIDDEN);
+                        return null;
+                    }
+                }
+            }
+    
+        } else {
+            User authUser = handleBasicAuthentification(entity);
+            if (authUser == null) {
+                setStatus(Status.CLIENT_ERROR_FORBIDDEN);
+                return null;
+            }
+            username = authUser.getIdentifier();
+        }
+    
+        String xpointer;
+        if (jo.has("ranges")) {
+            JSONObject ranges = jo.getJSONArray("ranges").getJSONObject(0);
+            String start = ranges.getString("start");
+            String end = ranges.getString("end");
+            String startOffset = ranges.getString("startOffset");
+            String endOffset = ranges.getString("endOffset");
+    
+            try {
+                xpointer = url
+                        + "#"
+                        + URLEncoder.encode(String.format(
+                                "xpointer(start-point(string-range(\"%s\",%s,1))/range-to(end-point(string-range(\"%s\",%s,1))))",
+                                start, startOffset, end, endOffset), "utf-8");
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+                setStatus(Status.SERVER_ERROR_INTERNAL);
+                return null;
+            }
+        } else {
+            xpointer = url;
+        }
+    
+        // username should be a URI, if not it will set to the MPIWG namespace defined in
+        // de.mpiwg.itgroup.annotationManager.Constants.NS
+        if (!username.startsWith("http"))
+            username = NS.MPIWG_PERSONS + username;
+    
+        return new Convert.Annotation(xpointer, username, null, text, null);
+    }
+
 }
--- a/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java	Tue Mar 20 15:55:51 2012 +0100
+++ b/src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java	Tue Mar 20 16:53:48 2012 +0100
@@ -49,7 +49,7 @@
     public Representation doGetJSON(Representation entity) {
 
         doOptions(entity);
-
+        //TODO: what to do with authentication? 
         boolean authenticated = isAuthenticated(entity);
         logger.debug("request authenticated="+authenticated);