# HG changeset patch # User casties # Date 1341261586 -7200 # Node ID 3599b29c393f1915e53f674f6ff4907d7b39c629 # Parent 47b53ae385d1f9e3491a07bbab97d9f35ca494ec store seems to work now :-) diff -r 47b53ae385d1 -r 3599b29c393f pom.xml --- a/pom.xml Fri Jun 29 20:38:27 2012 +0200 +++ b/pom.xml Mon Jul 02 22:39:46 2012 +0200 @@ -96,4 +96,5 @@ war + AnnotationManager \ No newline at end of file diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/Annotation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/mpiwg/itgroup/annotations/Annotation.java Mon Jul 02 22:39:46 2012 +0200 @@ -0,0 +1,172 @@ +/** + * + */ +package de.mpiwg.itgroup.annotations; + +/** + * @author casties + * + */ +public class Annotation { + /** + * The URI of this annotation. + */ + protected String uri; + + /** + * The annotation (body) text. + */ + protected String bodyText; + + /** + * The URI of the annotation text + */ + protected String bodyUri; + + /** + * The base URI of the annotation target. + */ + protected String targetBaseUri; + + /** + * The fragment part of the annotation target. + */ + protected String targetFragment; + + /** + * The types of annotation targets. + * + */ + public static enum FragmentTypes { + XPOINTER, AREA + }; + + /** + * The type of the annotation target fragment. + */ + protected FragmentTypes fragmentType; + + /** + * The URI of the creator of this annotation. + */ + protected String creatorUri; + + /** + * The creation date of this annotation. + */ + protected String created; + + /** + * @return the uri + */ + public String getUri() { + return uri; + } + + /** + * @param uri the uri to set + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * @return the bodyText + */ + public String getBodyText() { + return bodyText; + } + + /** + * @param bodyText the bodyText to set + */ + public void setBodyText(String bodyText) { + this.bodyText = bodyText; + } + + /** + * @return the bodyUri + */ + public String getBodyUri() { + return bodyUri; + } + + /** + * @param bodyUri the bodyUri to set + */ + public void setBodyUri(String bodyUri) { + this.bodyUri = bodyUri; + } + + /** + * @return the targetBaseUri + */ + public String getTargetBaseUri() { + return targetBaseUri; + } + + /** + * @param targetBaseUri the targetBaseUri to set + */ + public void setTargetBaseUri(String targetBaseUri) { + this.targetBaseUri = targetBaseUri; + } + + /** + * @return the targetFragment + */ + public String getTargetFragment() { + return targetFragment; + } + + /** + * @param targetFragment the targetFragment to set + */ + public void setTargetFragment(String targetFragment) { + this.targetFragment = targetFragment; + } + + /** + * @return the targetType + */ + public FragmentTypes getFragmentType() { + return fragmentType; + } + + /** + * @param fragmentType the fragmentType to set + */ + public void setFragmentType(FragmentTypes fragmentType) { + this.fragmentType = fragmentType; + } + + /** + * @return the creatorUri + */ + public String getCreatorUri() { + return creatorUri; + } + + /** + * @param creatorUri the creatorUri to set + */ + public void setCreatorUri(String creatorUri) { + this.creatorUri = creatorUri; + } + + /** + * @return the created + */ + public String getCreated() { + return created; + } + + /** + * @param created the created to set + */ + public void setCreated(String created) { + this.created = created; + } + + +} diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java Mon Jul 02 22:39:46 2012 +0200 @@ -0,0 +1,294 @@ +/** + * + */ +package de.mpiwg.itgroup.annotations.neo4j; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import org.apache.log4j.Logger; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.index.Index; +import org.neo4j.graphdb.index.IndexHits; + +import de.mpiwg.itgroup.annotations.Annotation; +import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes; + +/** + * @author casties + * + */ +public class AnnotationStore { + + protected static Logger logger = Logger.getLogger(AnnotationStore.class); + + protected GraphDatabaseService graphDb; + + public static enum RelationTypes implements RelationshipType { + ANNOTATES, CREATED + } + + public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/"; + + public AnnotationStore(GraphDatabaseService graphDb) { + super(); + this.graphDb = graphDb; + } + + public Annotation getAnnotationById(String id) { + Index idx = graphDb.index().forNodes("annotations"); + Node annotNode = idx.get("id", id).getSingle(); + Annotation annot = createAnnotation(annotNode); + return annot; + } + + /** + * Returns an Annotation object from an annotation-Node. + * + * @param annotNode + * @return + */ + public Annotation createAnnotation(Node annotNode) { + Annotation annot = new Annotation(); + annot.setUri((String) annotNode.getProperty("id", null)); + annot.setBodyText((String) annotNode.getProperty("bodyText", null)); + annot.setBodyUri((String) annotNode.getProperty("bodyUri", null)); + Iterable targets = annotNode.getRelationships(RelationTypes.ANNOTATES); + for (Relationship target : targets) { + annot.setTargetBaseUri((String) target.getProperty("uri", null)); + break; + } + annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null)); + String ft = (String) annotNode.getProperty("fragmentType", null); + if (ft != null) { + annot.setFragmentType(FragmentTypes.valueOf(ft)); + } + Iterable creators = annotNode.getRelationships(RelationTypes.CREATED); + for (Relationship creator : creators) { + annot.setCreatorUri((String) creator.getProperty("uri", null)); + break; + } + annot.setCreated((String) annotNode.getProperty("created", null)); + return annot; + } + + /** + * Store a new annotation in the store or update an existing one. Returns + * the stored annotation. + * + * @param annot + * @return + */ + public Annotation storeAnnotation(Annotation annot) { + Transaction tx = graphDb.beginTx(); + Node annotNode = null; + String id = annot.getUri(); + if (id == null) { + id = createRessourceURI("annot:"); + } + annotNode = createAnnotationNode(id); + + try { + // Mutating operations go here + /* + * the annotation body + */ + String bodyText = annot.getBodyText(); + if (bodyText != null) { + annotNode.setProperty("bodyText", bodyText); + } + String bodyUri = annot.getBodyUri(); + if (bodyUri != null) { + annotNode.setProperty("bodyUri", bodyUri); + } + + /* + * the annotation target + */ + String targetBaseUri = annot.getTargetBaseUri(); + if (targetBaseUri != null) { + Node target = createTargetNode(targetBaseUri); + annotNode.createRelationshipTo(target, RelationTypes.ANNOTATES); + } + + /* + * The fragment part of the annotation target. + */ + String targetFragment = annot.getTargetFragment(); + FragmentTypes fragmentType = annot.getFragmentType(); + if (targetFragment != null) { + annotNode.setProperty("targetFragment", targetFragment); + annotNode.setProperty("fragmentType", fragmentType.name()); + } + + /* + * The URI of the creator of this annotation. + */ + String creatorUri = annot.getCreatorUri(); + if (creatorUri != null) { + Node creator = createPersonNode(creatorUri, null); + creator.createRelationshipTo(annotNode, RelationTypes.CREATED); + } + + /* + * The creation date of this annotation. + */ + String created = annot.getCreated(); + if (created != null) { + annotNode.setProperty("created", created); + } + + tx.success(); + } finally { + tx.finish(); + } + + // re-read and return annotation + Annotation storedAnnot = createAnnotation(annotNode); + return storedAnnot; + } + + /** + * Deletes the annotation with the id. + * + * @param id + */ + public void deleteById(String id) { + // TODO Auto-generated method stub + + } + + /** + * Returns all annotations with the given uri and/or user. + * + * @param uri + * @param userUri + * @param limit + * @param offset + * @return + */ + public List searchByUriUser(String targetUri, String userUri, String limit, String offset) { + List annotations = new ArrayList(); + if (targetUri != null) { + Index targetIdx = graphDb.index().forNodes("targets"); + // there should be only one + Node target = targetIdx.get("uri", targetUri).getSingle(); + if (target != null) { + Iterable relations = target.getRelationships(RelationTypes.ANNOTATES); + for (Relationship relation: relations) { + Node ann = relation.getStartNode(); + if (ann.getProperty("TYPE", "").equals("ANNOTATION")) { + Annotation annot = createAnnotation(ann); + annotations.add(annot); + } else { + logger.error("ANNOTATES relation does not start with ANNOTATION: "+ann); + } + } + } + } + if (userUri != null) { + Index persIdx = graphDb.index().forNodes("persons"); + // there should be only one + Node person = persIdx.get("uri", userUri).getSingle(); + if (person != null) { + Iterable relations = person.getRelationships(RelationTypes.CREATED); + for (Relationship relation: relations) { + Node ann = relation.getEndNode(); + if (ann.getProperty("TYPE", "").equals("ANNOTATION")) { + Annotation annot = createAnnotation(ann); + annotations.add(annot); + } else { + logger.error("CREATED relation does not end with ANNOTATION: "+ann); + } + } + } + } + return annotations; + } + + protected Node createAnnotationNode(String id) { + Index idx = graphDb.index().forNodes("annotations"); + IndexHits annotations = idx.get("id", id); + Node annotation = annotations.getSingle(); + if (annotation == null) { + // does not exist yet + Transaction tx = graphDb.beginTx(); + try { + annotation = graphDb.createNode(); + annotation.setProperty("TYPE", "ANNOTATION"); + annotation.setProperty("id", id); + idx.add(annotation, "id", id); + tx.success(); + } finally { + tx.finish(); + } + } + return annotation; + } + + protected Node createTargetNode(String uri) { + Index idx = graphDb.index().forNodes("targets"); + IndexHits targets = idx.get("uri", uri); + Node target = targets.getSingle(); + if (target == null) { + // does not exist yet + Transaction tx = graphDb.beginTx(); + try { + target = graphDb.createNode(); + target.setProperty("TYPE", "TARGET"); + target.setProperty("uri", uri); + idx.add(target, "uri", uri); + tx.success(); + } finally { + tx.finish(); + } + } + return target; + } + + protected Node createPersonNode(String uri, String name) { + Index idx = graphDb.index().forNodes("persons"); + // Person is identified by URI + IndexHits persons = idx.get("uri", uri); + Node person = persons.getSingle(); + if (person == null) { + // does not exist yet + Transaction tx = graphDb.beginTx(); + try { + person = graphDb.createNode(); + person.setProperty("TYPE", "PERSON"); + person.setProperty("uri", uri); + idx.add(person, "uri", uri); + if (name != null) { + person.setProperty("name", name); + } + tx.success(); + } finally { + tx.finish(); + } + } + return person; + } + + /** + * Erzeuge eine urn aus der aktuellen Zeit in millis + * + * @return + */ + private String createRessourceURI(String prefix) { + + Calendar cal = Calendar.getInstance(); + + long time = cal.getTimeInMillis(); + + return String.format("%s%s%s", ANNOTATION_URI_BASE, prefix, time); + + } + + +} diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/old/NS.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/mpiwg/itgroup/annotations/old/NS.java Mon Jul 02 22:39:46 2012 +0200 @@ -0,0 +1,15 @@ +package de.mpiwg.itgroup.annotations.old; + +public class NS { + public static final String MPIWG_PERSONS_URL = "http://entities.mpiwg-berlin.mpg.de/persons/"; + // public static String + // ANNOTATION_TYPE="http://www.w3.org/2000/10/annotationType#"; + public static final String OAC_NS = "http://www.openannotation.org/ns/"; + public static final String CNT_NS = "http://www.w3.org/2011/content#"; + public static final String DCTERMS_NS = "http://www.purl.org/dc/terms/"; + public static final String RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + public static final String MPIWG_ANNOT_NS = "http://ontologies.mpiwg-berlin.mpg.de/annotations/"; + public static final String MPIWG_ANNOT_URL = "http://entities.mpiwg-berlin.mpg.de/annotations/"; + public static final String MPIWG_ANNOT_CTX = "file:///annotations2"; + +} diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java --- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java Fri Jun 29 20:38:27 2012 +0200 +++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java Mon Jul 02 22:39:46 2012 +0200 @@ -5,38 +5,24 @@ package de.mpiwg.itgroup.annotations.restlet; import java.io.IOException; -import java.util.ArrayList; -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.Reference; 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.Delete; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.Put; -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.Annotation; -import de.mpiwg.itgroup.annotationManager.RDFHandling.Convert; -import de.mpiwg.itgroup.annotationManager.RDFHandling.RDFSearcher; -import de.mpiwg.itgroup.annotationManager.drupal.AnnotationHandler; -import de.mpiwg.itgroup.annotationManager.drupal.UnknowUserException; -import de.mpiwg.itgroup.triplestoremanager.exceptions.TripleStoreHandlerException; +import de.mpiwg.itgroup.annotations.Annotation; +import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; /** - * Implements the "annotations" uri of the Annotator API. see + * Implements the "annotations" uri of the Annotator API. see + * * * @author dwinter, casties * @@ -68,49 +54,28 @@ boolean authenticated = isAuthenticated(entity); logger.debug("request authenticated=" + authenticated); - RDFSearcher searcher = new RDFSearcher(NS.MPIWG_ANNOT_CTX); // TODO should ge into config file - - try { - List annots = searcher.searchById(id); - if (annots.size() == 1) { - // there should be only one - JSONObject result = createAnnotatorJson(annots.get(0)); - logger.debug("sending:"); - logger.debug(result); - return new JsonRepresentation(result); - } else { - JSONArray results; - results = new JSONArray(); - for (Annotation annot : annots) { - JSONObject jo = createAnnotatorJson(annot); - if (jo != null) { - results.put(createAnnotatorJson(annot)); - } else { - setStatus(Status.SERVER_ERROR_INTERNAL, "JSon Error"); - return null; - } - } - // annotator read request returns a list of annotation objects - logger.debug("sending:"); - logger.debug(results); - return new JsonRepresentation(results); - } - } 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; + Annotation annots = getAnnotationStore().getAnnotationById(id); + if (annots != null) { + // there should be only one + JSONObject result = createAnnotatorJson(annots); + logger.debug("sending:"); + logger.debug(result); + return new JsonRepresentation(result); + } else { + JSONArray results = new JSONArray(); + // 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 + * 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 */ @@ -138,22 +103,16 @@ setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } - if (annot == null || annot.xpointer == null || annot.creator == null) { + if (annot == null) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } Annotation storedAnnot; - try { - // store Annotation - storedAnnot = new Convert(NS.MPIWG_ANNOT_CTX).storeAnnotation(annot); - } catch (TripleStoreStoreError e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStore Error"); - return null; - } - /* according to https://github.com/okfn/annotator/wiki/Storage - * we should return 303: see other. - * For now we return the annotation. + // 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); JsonRepresentation retRep = new JsonRepresentation(jo); @@ -161,129 +120,6 @@ } /** - * POST with HTML content-type. - * - * @param entity - * @return - */ - @Post("html") - public Representation doPostHtml(Representation entity) { - logger.debug("AnnotatorAnnotations doPostHtml!"); - 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(NS.MPIWG_ANNOT_CTX).storeAnnotation(annot); - } catch (TripleStoreStoreError e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStore Error"); - return null; - } - if (retValAnnot == null) { - return null; - } - String retVal = retValAnnot.getAnnotationUri(); - 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 Annotation handleForm(Representation entity) { - 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 = getHttpAuthUser(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_URL + 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 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 Annotation(xpointer, username, null, text, type, url); - return annot; - } - - - /** * PUT with JSON content-type. * * @param entity @@ -303,10 +139,11 @@ logger.debug("request authenticated=" + authenticated); if (!authenticated) { setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!"); - return null; + return null; } Annotation annot = null; + AnnotationStore store = getAnnotationStore(); try { JsonRepresentation jrep = new JsonRepresentation(entity); JSONObject jo = jrep.getJsonObject(); @@ -314,37 +151,27 @@ setStatus(Status.CLIENT_ERROR_BAD_REQUEST); return null; } - RDFSearcher searcher = new RDFSearcher(NS.MPIWG_ANNOT_CTX); // TODO should ge into config file // get stored Annotation - List annots = searcher.searchById(id); - if (annots.size() < 1) { + Annotation storedAnnot = store.getAnnotationById(id); + if (storedAnnot == null) { setStatus(Status.CLIENT_ERROR_NOT_FOUND); return null; } - Annotation storedAnnot = annots.get(0); - // delete - searcher.deleteById(id); // update from posted JSON annot = updateAnnotation(storedAnnot, jo, entity); // store Annotation - storedAnnot = new Convert(NS.MPIWG_ANNOT_CTX).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); */ + 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); JsonRepresentation retRep = new JsonRepresentation(jo); return retRep; - } catch (TripleStoreHandlerException e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStoreHandler Error"); - } catch (TripleStoreSearchError e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStoreSearch Error"); } catch (JSONException e) { e.printStackTrace(); setStatus(Status.CLIENT_ERROR_BAD_REQUEST); @@ -375,24 +202,13 @@ logger.debug("request authenticated=" + authenticated); if (!authenticated) { setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Not Authorized!"); - return null; + return null; } - - RDFSearcher searcher = new RDFSearcher(NS.MPIWG_ANNOT_CTX); // TODO should ge into config file - try { - // delete annotation - searcher.deleteById(id); - setStatus(Status.SUCCESS_NO_CONTENT); - } catch (TripleStoreHandlerException e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStoreHandler Error"); - } catch (TripleStoreSearchError e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL, "TripleStoreSearch Error"); - } + // delete annotation + getAnnotationStore().deleteById(id); + setStatus(Status.SUCCESS_NO_CONTENT); return null; } - } diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java --- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java Fri Jun 29 20:38:27 2012 +0200 +++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java Mon Jul 02 22:39:46 2012 +0200 @@ -13,6 +13,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.servlet.ServletContext; + import net.oauth.jsontoken.Checker; import net.oauth.jsontoken.JsonToken; import net.oauth.jsontoken.JsonTokenParser; @@ -25,16 +27,16 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.restlet.data.ClientInfo; 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.Annotation; +import de.mpiwg.itgroup.annotations.Annotation; +import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes; +import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; +import de.mpiwg.itgroup.annotations.old.NS; /** * Base class for Annotator resource classes. @@ -44,12 +46,24 @@ */ public abstract class AnnotatorResourceImpl extends ServerResource { - protected Logger logger = Logger.getRootLogger(); + protected static Logger logger = Logger.getLogger(AnnotatorResourceImpl.class); + + private AnnotationStore store; protected String getAllowedMethodsForHeader() { return "OPTIONS,GET,POST"; } + protected AnnotationStore getAnnotationStore() { + if (store == null) { + ServletContext sc = (ServletContext) getContext().getServerDispatcher().getContext().getAttributes() + .get("org.restlet.ext.servlet.ServletContext"); + logger.debug("Getting AnnotationStore from Context"); + store = (AnnotationStore) sc.getAttribute(RestServer.ANNSTORE_KEY); + } + return store; + } + public String encodeJsonId(String id) { try { return Base64.encodeBase64URLSafeString(id.getBytes("UTF-8")); @@ -114,7 +128,8 @@ } /** - * checks Annotator Auth plugin authentication information from headers. returns userId if successful. + * checks Annotator Auth plugin authentication information from headers. + * returns userId if successful. * * @param entity * @return @@ -165,52 +180,46 @@ boolean makeUserObject = true; JSONObject jo = new JSONObject(); try { - jo.put("text", annot.text); - jo.put("uri", annot.url); + jo.put("text", annot.getBodyText()); + jo.put("uri", annot.getTargetBaseUri()); if (makeUserObject) { // create user object JSONObject userObject = new JSONObject(); // save creator as uri - userObject.put("uri", annot.creator); + userObject.put("uri", annot.getCreatorUri()); // make short user id - String userID = annot.creator; - if (userID.startsWith(NS.MPIWG_PERSONS_URL)) { - userID = userID.replace(NS.MPIWG_PERSONS_URL, ""); // entferne + String userId = annot.getCreatorUri(); + if (userId != null && userId.startsWith(NS.MPIWG_PERSONS_URL)) { + userId = userId.replace(NS.MPIWG_PERSONS_URL, ""); // entferne // NAMESPACE } // save as id - userObject.put("id", userID); + userObject.put("id", userId); // get full name RestServer restServer = (RestServer) getApplication(); - String userName = restServer.getUserNameFromLdap(userID); + String userName = restServer.getUserNameFromLdap(userId); userObject.put("name", userName); // save user object jo.put("user", userObject); } else { // save user as string - jo.put("user", annot.creator); + jo.put("user", annot.getCreatorUri()); } - List xpointers = new ArrayList(); - if (annot.xpointers == null || annot.xpointers.size() == 0) - xpointers.add(annot.xpointer); - else { - for (String xpointerString : annot.xpointers) { - xpointers.add(xpointerString); - } - } - if (!xpointers.isEmpty()) { + if (annot.getTargetFragment() != null) { // we only look at the first xpointer - String xt = getXpointerType(xpointers.get(0)); - if (xt == "range") { - jo.put("ranges", transformToRanges(xpointers)); - } else if (xt == "area") { - jo.put("areas", transformToAreas(xpointers)); + List fragments = new ArrayList(); + fragments.add(annot.getTargetFragment()); + FragmentTypes xt = annot.getFragmentType(); + if (xt == FragmentTypes.XPOINTER) { + jo.put("ranges", transformToRanges(fragments)); + } else if (xt == FragmentTypes.AREA) { + jo.put("areas", transformToAreas(fragments)); } } // encode Annotation URL (=id) in base64 - String annotUrl = annot.getAnnotationUri(); + String annotUrl = annot.getUri(); String annotId = encodeJsonId(annotUrl); jo.put("id", annotId); return jo; @@ -221,22 +230,13 @@ return null; } - private String getXpointerType(String xpointer) { - if (xpointer.contains("#xpointer")) { - return "range"; - } else if (xpointer.contains("#xywh")) { - return "area"; - } - return null; - } - private JSONArray transformToRanges(List xpointers) { JSONArray ja = new JSONArray(); Pattern rg = Pattern - .compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)/range-to\\(end-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)\\)"); - Pattern rg1 = Pattern.compile("#xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)"); + .compile("xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)/range-to\\(end-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)\\)"); + Pattern rg1 = Pattern.compile("xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)"); try { for (String xpointer : xpointers) { @@ -277,7 +277,7 @@ JSONArray ja = new JSONArray(); - Pattern rg = Pattern.compile("#xywh=(\\w*:)([\\d\\.]+),([\\d\\.]+),([\\d\\.]+),([\\d\\.]+)"); + Pattern rg = Pattern.compile("xywh=(\\w*:)([\\d\\.]+),([\\d\\.]+),([\\d\\.]+),([\\d\\.]+)"); try { for (String xpointer : xpointers) { @@ -307,47 +307,76 @@ return ja; } + protected String parseArea(JSONObject area) throws JSONException, UnsupportedEncodingException { + String x = area.getString("x"); + String y = area.getString("y"); + String width = "0"; + String height = "0"; + if (area.has("width")) { + width = area.getString("width"); + height = area.getString("height"); + } + String fragment = URLEncoder.encode(String.format("xywh=fraction:%s,%s,%s,%s", x, y, width, height), "utf-8"); + return fragment; + } + + protected String parseRange(JSONObject range) throws JSONException, UnsupportedEncodingException { + String start = range.getString("start"); + String end = range.getString("end"); + String startOffset = range.getString("startOffset"); + String endOffset = range.getString("endOffset"); + + String fragment = 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"); + return fragment; + } + /** * creates an Annotation object with data from JSON. * - * uses the specification from the annotator project: {@link https ://github.com/okfn/annotator/wiki/Annotation-format} + * uses the specification from the annotator project: {@link 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 + * 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 + * @throws UnsupportedEncodingException */ - public Annotation createAnnotation(JSONObject jo, Representation entity) throws JSONException { + public Annotation createAnnotation(JSONObject jo, Representation entity) throws JSONException, UnsupportedEncodingException { return updateAnnotation(new Annotation(), jo, entity); } - public Annotation updateAnnotation(Annotation annot, JSONObject jo, Representation entity) throws JSONException { + public Annotation updateAnnotation(Annotation annot, JSONObject jo, Representation entity) throws JSONException, + UnsupportedEncodingException { // annotated uri - String url = annot.url; if (jo.has("uri")) { - url = jo.getString("uri"); + annot.setTargetBaseUri(jo.getString("uri")); } // annotation text - String text = annot.text; if (jo.has("text")) { - text = jo.getString("text"); + annot.setBodyText(jo.getString("text")); } // check authentication String authUser = checkAuthToken(entity); if (authUser == null) { - // try http auth - User httpUser = getHttpAuthUser(entity); - if (httpUser == null) { - setStatus(Status.CLIENT_ERROR_FORBIDDEN); - return null; - } - authUser = httpUser.getIdentifier(); + /* + * // try http auth User httpUser = getHttpAuthUser(entity); if + * (httpUser == null) { + */ + setStatus(Status.CLIENT_ERROR_FORBIDDEN); + return null; + /* + * } authUser = httpUser.getIdentifier(); + */ } // username not required, if no username given authuser will be used String username = null; - String userUri = annot.creator; + String userUri = annot.getCreatorUri(); if (jo.has("user")) { if (jo.get("user") instanceof String) { // user is just a String @@ -380,64 +409,19 @@ // TODO: should we overwrite the creator? // create xpointer from the first range/area - String xpointer = annot.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; - } + annot.setFragmentType(FragmentTypes.XPOINTER); + String fragment = parseRange(ranges); + annot.setTargetFragment(fragment); } if (jo.has("areas")) { JSONObject area = jo.getJSONArray("areas").getJSONObject(0); - String x = area.getString("x"); - String y = area.getString("y"); - String width = "0"; - String height = "0"; - if (area.has("width")) { - width = area.getString("width"); - height = area.getString("height"); - } - try { - xpointer = url + "#" + URLEncoder.encode(String.format("xywh=fraction:%s,%s,%s,%s", x, y, width, height), "utf-8"); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } + annot.setFragmentType(FragmentTypes.AREA); + String fragment = parseArea(area); + annot.setTargetFragment(fragment); } - return new Annotation(xpointer, userUri, annot.time, text, annot.type); - } - - /** - * returns the logged in User. - * - * @param entity - * @return - */ - protected User getHttpAuthUser(Representation entity) { - RestServer restServer = (RestServer) getApplication(); - if (!restServer.authenticate(getRequest(), getResponse())) { - // Not authenticated - return null; - } - - ClientInfo ci = getRequest().getClientInfo(); - logger.debug(ci); - return getRequest().getClientInfo().getUser(); - + return annot; } } diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorSearch.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorSearch.java Mon Jul 02 22:39:46 2012 +0200 @@ -0,0 +1,87 @@ +/** + * Implements the "search" uri of the Annotator API. + */ +package de.mpiwg.itgroup.annotations.restlet; + +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.Get; + +import de.mpiwg.itgroup.annotations.Annotation; +import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; + +/** + * Implements the "search" uri of the Annotator API. see + * + * + * @author casties + * + */ +public class AnnotatorSearch extends AnnotatorResourceImpl { + + protected String getAllowedMethodsForHeader() { + return "OPTIONS,GET"; + } + + /** + * result for JSON content-type. optional search parameters: uri user limit + * offset + * + * @param entity + * @return + */ + @Get("json") + public Representation doGetJSON(Representation entity) { + logger.debug("AnnotatorSearch doGetJSON!"); + setCorsHeaders(); + // TODO: what to do with authentication? + boolean authenticated = isAuthenticated(entity); + logger.debug("request authenticated=" + authenticated); + + 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"); + + AnnotationStore searcher = getAnnotationStore(); + + JSONArray ja; + + List annots = searcher.searchByUriUser(uri, user, limit, offset); + + ja = new JSONArray(); + for (Annotation annot : annots) { + JSONObject jo = createAnnotatorJson(annot); + if (jo != null) { + ja.put(createAnnotatorJson(annot)); + } else { + setStatus(Status.SERVER_ERROR_INTERNAL, "JSON Error"); + return null; + } + } + + JSONObject result = new JSONObject(); + try { + result.put("rows", ja); + result.put("total", ja.length()); + } catch (JSONException e) { + e.printStackTrace(); + setStatus(Status.SERVER_ERROR_INTERNAL, "JSON Error"); + return null; + } + + logger.debug("sending:"); + logger.debug(result); + return new JsonRepresentation(result); + } + +} diff -r 47b53ae385d1 -r 3599b29c393f src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java --- a/src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java Fri Jun 29 20:38:27 2012 +0200 +++ b/src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java Mon Jul 02 22:39:46 2012 +0200 @@ -27,15 +27,12 @@ import org.restlet.Context; import org.restlet.Restlet; import org.restlet.routing.Router; -import org.restlet.security.ChallengeAuthenticator; -import scala.sys.process.ProcessBuilderImpl.Dummy; +import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; public class RestServer extends Application { - public static Logger logger = Logger.getRootLogger(); - - private ChallengeAuthenticator authenticator; + public static Logger logger = Logger.getLogger(RestServer.class); /** * Properties holding consumer keys and secrets @@ -47,6 +44,12 @@ public static final String GRAPHDB_KEY = "annotationmanager.graphdb"; public String DB_PATH = "WEB-INF/neo4j-annotation-db"; + private WrappingNeoServerBootstrapper srv; + public static final String GRAPHDBSRV_KEY = "annotationmanager.graphdb.srv"; + + private AnnotationStore store; + public static final String ANNSTORE_KEY = "annotationmanager.store"; + /** * constructor * @@ -70,10 +73,17 @@ if (dbFn != null) { logger.debug("opening DB " + dbFn); graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(dbFn); + registerShutdownHook(graphDb); // store in context sc.setAttribute(GRAPHDB_KEY, graphDb); - WrappingNeoServerBootstrapper srv = new WrappingNeoServerBootstrapper((AbstractGraphDatabase) graphDb); + // AnnotationStore + store = new AnnotationStore(graphDb); + sc.setAttribute(ANNSTORE_KEY, store); + // admin server + srv = new WrappingNeoServerBootstrapper((AbstractGraphDatabase) graphDb); logger.debug("Starting DB admin server..."); + // store in context + sc.setAttribute(GRAPHDBSRV_KEY, srv); srv.start(); } else { logger.error("Unable to get resource " + DB_PATH); @@ -126,7 +136,7 @@ router.attach("/annotator/annotations", AnnotatorAnnotations.class); router.attach("/annotator/annotations/{id}", AnnotatorAnnotations.class); - // router.attach("/annotator/search", AnnotatorSearch.class); + router.attach("/annotator/search", AnnotatorSearch.class); // router.attach("",redirector); router.attach("/annotator", // ExtendedAnnotationInput.class); @@ -240,4 +250,38 @@ return filename; } + /* (non-Javadoc) + * @see org.restlet.Application#stop() + */ + @Override + public synchronized void stop() throws Exception { + /* + * trying to clean up databases, nur sure if this is the right way... + */ + if (srv != null) { + logger.debug("Stopping DB admin server..."); + srv.stop(); + srv = null; + } + if (graphDb != null) { + logger.debug("Stopping DB..."); + graphDb.shutdown(); + graphDb = null; + } + super.stop(); + } + + private static void registerShutdownHook(final GraphDatabaseService graphDb) { + // Registers a shutdown hook for the Neo4j instance so that it + // shuts down nicely when the VM exits (even if you "Ctrl-C" the + // running example before it's completed) + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + graphDb.shutdown(); + } + }); + } + + }