# HG changeset patch # User casties # Date 1332258828 -3600 # Node ID 2f8c72ae4c435a3a9a95d7a6feb6db34e7de6342 # Parent 0bdfe01e30b5d2731f2223d2f499d8dde395a19a working on create and read api for annotator. diff -r 0bdfe01e30b5 -r 2f8c72ae4c43 src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorAnnotations.java --- /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 + * + */ +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 + * + * @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 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("%s", retVal.replace(">", "").replace("<", ""), + retVal.replace(">", ">").replace("<", "<")); + 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; + } + +} diff -r 0bdfe01e30b5 -r 2f8c72ae4c43 src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorResourceImpl.java --- 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); + } + } diff -r 0bdfe01e30b5 -r 2f8c72ae4c43 src/de/mpiwg/itgroup/annotationManager/restlet/AnnotatorSearch.java --- 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);