changeset 4:3599b29c393f

store seems to work now :-)
author casties
date Mon, 02 Jul 2012 22:39:46 +0200
parents 47b53ae385d1
children bbf0cc5bee29
files pom.xml src/main/java/de/mpiwg/itgroup/annotations/Annotation.java src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java src/main/java/de/mpiwg/itgroup/annotations/old/NS.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorAnnotations.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorSearch.java src/main/java/de/mpiwg/itgroup/annotations/restlet/RestServer.java
diffstat 8 files changed, 756 insertions(+), 343 deletions(-) [+]
line wrap: on
line diff
--- 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 @@
 		</pluginManagement>
 	</build>
 	<packaging>war</packaging>
+	<name>AnnotationManager</name>
 </project>
\ No newline at end of file
--- /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;
+    }
+    
+    
+}
--- /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<Node> 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<Relationship> 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<Relationship> 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<Annotation> searchByUriUser(String targetUri, String userUri, String limit, String offset) {
+        List<Annotation> annotations = new ArrayList<Annotation>();
+        if (targetUri != null) {
+            Index<Node> targetIdx = graphDb.index().forNodes("targets");
+            // there should be only one
+            Node target = targetIdx.get("uri", targetUri).getSingle();
+            if (target != null) {
+                Iterable<Relationship> 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<Node> persIdx = graphDb.index().forNodes("persons");
+            // there should be only one
+            Node person = persIdx.get("uri", userUri).getSingle();
+            if (person != null) {
+                Iterable<Relationship> 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<Node> idx = graphDb.index().forNodes("annotations");
+        IndexHits<Node> 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<Node> idx = graphDb.index().forNodes("targets");
+        IndexHits<Node> 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<Node> idx = graphDb.index().forNodes("persons");
+        // Person is identified by URI
+        IndexHits<Node> 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);
+
+    }
+
+
+}
--- /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";    
+
+}
--- 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 <https://github.com/okfn/annotator/wiki/Storage>
+ * Implements the "annotations" uri of the Annotator API. see
+ * <https://github.com/okfn/annotator/wiki/Storage>
  * 
  * @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<Annotation> 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("<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 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<Annotation> 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;
     }
-    
 
 }
--- 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<String> xpointers = new ArrayList<String>();
-            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<String> fragments = new ArrayList<String>();
+                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<String> 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;
     }
 
 }
--- /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
+ * <https://github.com/okfn/annotator/wiki/Storage>
+ * 
+ * @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<Annotation> 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);
+    }
+
+}
--- 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();
+            }
+        });
+    }
+   
+    
 }