source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 14:629e15b345aa

Last change on this file since 14:629e15b345aa was 14:629e15b345aa, checked in by casties, 12 years ago

permissions mostly work. need more server-side checking.

File size: 17.1 KB
Line 
1/**
2 *
3 */
4package de.mpiwg.itgroup.annotations.neo4j;
5
6import java.util.ArrayList;
7import java.util.Calendar;
8import java.util.List;
9
10import org.apache.log4j.Logger;
11import org.neo4j.graphdb.Direction;
12import org.neo4j.graphdb.GraphDatabaseService;
13import org.neo4j.graphdb.Node;
14import org.neo4j.graphdb.Relationship;
15import org.neo4j.graphdb.RelationshipType;
16import org.neo4j.graphdb.Transaction;
17import org.neo4j.graphdb.index.Index;
18import org.neo4j.graphdb.index.IndexHits;
19
20import de.mpiwg.itgroup.annotations.Actor;
21import de.mpiwg.itgroup.annotations.Annotation;
22import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes;
23import de.mpiwg.itgroup.annotations.Group;
24import de.mpiwg.itgroup.annotations.Person;
25
26/**
27 * @author casties
28 *
29 */
30public class AnnotationStore {
31
32    protected static Logger logger = Logger.getLogger(AnnotationStore.class);
33
34    protected GraphDatabaseService graphDb;
35
36    public static enum NodeTypes {
37        ANNOTATION, PERSON, TARGET
38    }
39
40    protected List<Index<Node>> nodeIndexes;
41
42    public static enum RelationTypes implements RelationshipType {
43        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ
44    }
45
46    public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/";
47
48    public AnnotationStore(GraphDatabaseService graphDb) {
49        super();
50        this.graphDb = graphDb;
51        nodeIndexes = new ArrayList<Index<Node>>(3);
52        // List.set(enum.ordinal(), val) seems not to work.
53        nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
54        nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons"));
55        nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
56    }
57
58    protected Index<Node> getNodeIndex(NodeTypes type) {
59        return nodeIndexes.get(type.ordinal());
60    }
61
62    /**
63     * Returns the Annotation with the given id.
64     *
65     * @param id
66     * @return
67     */
68    public Annotation getAnnotationById(String id) {
69        Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
70        Annotation annot = createAnnotationFromNode(annotNode);
71        return annot;
72    }
73
74    /**
75     * Returns an Annotation object from an annotation-Node.
76     *
77     * @param annotNode
78     * @return
79     */
80    public Annotation createAnnotationFromNode(Node annotNode) {
81        Annotation annot = new Annotation();
82        annot.setUri((String) annotNode.getProperty("id", null));
83        annot.setBodyText((String) annotNode.getProperty("bodyText", null));
84        annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
85        // get annotation target from relation
86        Relationship targetRel = getRelation(annotNode, RelationTypes.ANNOTATES, null);
87        if (targetRel != null) {
88            Node target = targetRel.getEndNode();
89            annot.setTargetBaseUri((String) target.getProperty("uri", null));
90        } else {
91            logger.error("annotation " + annotNode + " has no target node!");
92        }
93        annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null));
94        String ft = (String) annotNode.getProperty("fragmentType", null);
95        if (ft != null) {
96            annot.setFragmentType(FragmentTypes.valueOf(ft));
97        }
98        // get creator from relation
99        Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
100        if (creatorRel != null) {
101            Node creatorNode = creatorRel.getStartNode();
102            Actor creator = createActorFromNode(creatorNode);
103            annot.setCreator(creator);
104        } else {
105            logger.error("annotation " + annotNode + " has no creator node!");
106        }
107        // get creation date
108        annot.setCreated((String) annotNode.getProperty("created", null));
109        // get permissions
110        Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
111        if (adminRel != null) {
112            Node adminNode = adminRel.getEndNode();
113            Actor admin = createActorFromNode(adminNode);
114            annot.setAdminPermission(admin);
115        }
116        Relationship deleteRel = getRelation(annotNode, RelationTypes.PERMITS_DELETE, null);
117        if (deleteRel != null) {
118            Node deleteNode = deleteRel.getEndNode();
119            Actor delete = createActorFromNode(deleteNode);
120            annot.setDeletePermission(delete);
121        }
122        Relationship updateRel = getRelation(annotNode, RelationTypes.PERMITS_UPDATE, null);
123        if (updateRel != null) {
124            Node updateNode = updateRel.getEndNode();
125            Actor update = createActorFromNode(updateNode);
126            annot.setUpdatePermission(update);
127        }
128        Relationship readRel = getRelation(annotNode, RelationTypes.PERMITS_READ, null);
129        if (readRel != null) {
130            Node readNode = readRel.getEndNode();
131            Actor read = createActorFromNode(readNode);
132            annot.setReadPermission(read);
133        }
134
135        return annot;
136    }
137
138    /**
139     * Returns an Actor object from a node.
140     *
141     * @param actorNode
142     * @return
143     */
144    protected Actor createActorFromNode(Node actorNode) {
145        String uri = (String) actorNode.getProperty("uri", null);
146        String name = (String) actorNode.getProperty("name", null);
147        String type = (String) actorNode.getProperty("TYPE", null);
148        if (type != null && type.equals("PERSON")) {
149            return new Person(uri, name);
150        } else if (type != null && type.equals("GROUP")) {
151            return new Group(uri, name);
152        }
153        return null;
154    }
155
156    /**
157     * Store a new annotation in the store or update an existing one. Returns
158     * the stored annotation.
159     *
160     * @param annot
161     * @return
162     */
163    public Annotation storeAnnotation(Annotation annot) {
164        Node annotNode = null;
165        Transaction tx = graphDb.beginTx();
166        try {
167            /*
168             * create or get the annotation
169             */
170            String id = annot.getUri();
171            if (id == null) {
172                id = createRessourceURI("annot:");
173            }
174            annotNode = getOrCreateAnnotationNode(id);
175
176            /*
177             * the annotation body
178             */
179            String bodyText = annot.getBodyText();
180            if (bodyText != null) {
181                annotNode.setProperty("bodyText", bodyText);
182            }
183            String bodyUri = annot.getBodyUri();
184            if (bodyUri != null) {
185                annotNode.setProperty("bodyUri", bodyUri);
186            }
187
188            /*
189             * the annotation target
190             */
191            String targetBaseUri = annot.getTargetBaseUri();
192            if (targetBaseUri != null) {
193                Node target = getOrCreateTargetNode(targetBaseUri);
194                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, target);
195            }
196
197            /*
198             * The fragment part of the annotation target.
199             */
200            String targetFragment = annot.getTargetFragment();
201            FragmentTypes fragmentType = annot.getFragmentType();
202            if (targetFragment != null) {
203                annotNode.setProperty("targetFragment", targetFragment);
204                annotNode.setProperty("fragmentType", fragmentType.name());
205            }
206
207            /*
208             * The creator of this annotation.
209             */
210            Actor creator = annot.getCreator();
211            if (creator != null) {
212                Node creatorNode = getOrCreatePersonNode(creator);
213                getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
214            }
215
216            /*
217             * The creation date of this annotation.
218             */
219            String created = annot.getCreated();
220            if (created != null) {
221                annotNode.setProperty("created", created);
222            }
223
224            /*
225             * Permissions for this annotation.
226             */
227            setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
228            setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
229            setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
230            setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
231
232            tx.success();
233        } finally {
234            tx.finish();
235        }
236
237        // re-read and return annotation
238        Annotation storedAnnot = createAnnotationFromNode(annotNode);
239        return storedAnnot;
240    }
241
242    /**
243     * Deletes the annotation with the given id.
244     *
245     * @param id
246     */
247    public void deleteById(String id) {
248        Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
249        if (annotNode != null) {
250            // delete related objects
251            Transaction tx = graphDb.beginTx();
252            try {
253                for (Relationship rel : annotNode.getRelationships()) {
254                    // delete relation and the related node if it has no other
255                    // relations
256                    Node other = rel.getOtherNode(annotNode);
257                    rel.delete();
258                    if (!other.hasRelationship()) {
259                        deleteNode(other);
260                    }
261                }
262                if (!annotNode.hasRelationship()) {
263                    deleteNode(annotNode);
264                } else {
265                    logger.error("deleteById: unable to delete: Node still has relations.");
266                }
267                tx.success();
268            } finally {
269                tx.finish();
270            }
271        }
272    }
273
274    /**
275     * Returns all annotations with the given uri and/or user.
276     *
277     * @param uri
278     * @param userUri
279     * @param limit
280     * @param offset
281     * @return
282     */
283    public List<Annotation> searchByUriUser(String targetUri, String userUri, String limit, String offset) {
284        List<Annotation> annotations = new ArrayList<Annotation>();
285        if (targetUri != null) {
286            // there should be only one
287            Node target = getNodeIndex(NodeTypes.TARGET).get("uri", targetUri).getSingle();
288            if (target != null) {
289                Iterable<Relationship> relations = target.getRelationships(RelationTypes.ANNOTATES);
290                for (Relationship relation : relations) {
291                    Node ann = relation.getStartNode();
292                    if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
293                        Annotation annot = createAnnotationFromNode(ann);
294                        annotations.add(annot);
295                    } else {
296                        logger.error("ANNOTATES relation does not start with ANNOTATION: " + ann);
297                    }
298                }
299            }
300        }
301        if (userUri != null) {
302            // there should be only one
303            Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri).getSingle();
304            if (person != null) {
305                Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
306                for (Relationship relation : relations) {
307                    Node ann = relation.getEndNode();
308                    if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
309                        Annotation annot = createAnnotationFromNode(ann);
310                        annotations.add(annot);
311                    } else {
312                        logger.error("CREATED relation does not end with ANNOTATION: " + ann);
313                    }
314                }
315            }
316        }
317        return annotations;
318    }
319
320    protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
321        if (start.hasRelationship()) {
322            // there are relations
323            Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
324            for (Relationship rel : rels) {
325                if (rel.getEndNode().equals(end)) {
326                    // relation exists
327                    return rel;
328                }
329            }
330        }
331        // create new one
332        Relationship rel;
333        Transaction tx = graphDb.beginTx();
334        try {
335            rel = start.createRelationshipTo(end, type);
336            tx.success();
337        } finally {
338            tx.finish();
339        }
340        return rel;
341    }
342
343    protected Node getOrCreateAnnotationNode(String id) {
344        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
345        IndexHits<Node> annotations = idx.get("id", id);
346        Node annotation = annotations.getSingle();
347        if (annotation == null) {
348            // does not exist yet
349            Transaction tx = graphDb.beginTx();
350            try {
351                annotation = graphDb.createNode();
352                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
353                annotation.setProperty("id", id);
354                idx.add(annotation, "id", id);
355                tx.success();
356            } finally {
357                tx.finish();
358            }
359        }
360        return annotation;
361    }
362
363    protected Node getOrCreateTargetNode(String uri) {
364        Index<Node> idx = getNodeIndex(NodeTypes.TARGET);
365        IndexHits<Node> targets = idx.get("uri", uri);
366        Node target = targets.getSingle();
367        if (target == null) {
368            // does not exist yet
369            Transaction tx = graphDb.beginTx();
370            try {
371                target = graphDb.createNode();
372                target.setProperty("TYPE", NodeTypes.TARGET.name());
373                target.setProperty("uri", uri);
374                idx.add(target, "uri", uri);
375                tx.success();
376            } finally {
377                tx.finish();
378            }
379        }
380        return target;
381    }
382
383    protected Node getOrCreatePersonNode(Actor actor) {
384        // Person is identified by URI
385        String uri = actor.getUriString();
386        String name = actor.getName();
387        Index<Node> idx = getNodeIndex(NodeTypes.PERSON);
388        IndexHits<Node> persons = idx.get("uri", uri);
389        Node person = persons.getSingle();
390        if (person == null) {
391            // does not exist yet
392            Transaction tx = graphDb.beginTx();
393            try {
394                person = graphDb.createNode();
395                person.setProperty("TYPE", NodeTypes.PERSON.name());
396                person.setProperty("uri", uri);
397                idx.add(person, "uri", uri);
398                if (name != null) {
399                    person.setProperty("name", name);
400                }
401                tx.success();
402            } finally {
403                tx.finish();
404            }
405        }
406        return person;
407    }
408
409    /**
410     * Create or update permissions relations for an annotation.
411     *
412     * @param annotNode
413     * @param type
414     * @param annot
415     */
416    protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
417        Node newActorNode = null;
418        if (actor != null) {
419            newActorNode = getOrCreatePersonNode(actor);
420        }
421        Relationship rel = getRelation(annotNode, type, null);
422        if (rel != null) {
423            // relation exists
424            Node oldActorNode = rel.getEndNode();
425            if (!oldActorNode.equals(newActorNode)) {
426                // new admin is different
427                rel.delete();
428                if (newActorNode != null) {
429                    rel = getOrCreateRelation(annotNode, type, newActorNode);
430                }
431            }
432        } else {
433            // no relation yet
434            if (newActorNode != null) {
435                rel = getOrCreateRelation(annotNode, type, newActorNode);
436            }
437        }
438    }
439
440    /**
441     * Unindexes and deletes given Node if it has no relations.
442     *
443     * @param node
444     */
445    protected void deleteNode(Node node) {
446        Transaction tx = graphDb.beginTx();
447        try {
448            if (node.hasRelationship()) {
449                logger.error("deleteNode: unable to delete: Node still has relations.");
450            } else {
451                String ts = (String) node.getProperty("TYPE", null);
452                try {
453                    NodeTypes type = NodeTypes.valueOf(ts);
454                    getNodeIndex(type).remove(node);
455                } catch (Exception e) {
456                    logger.error("deleteNode: unable to get TYPE of node: " + node);
457                }
458                node.delete();
459            }
460            tx.success();
461        } finally {
462            tx.finish();
463        }
464    }
465
466    protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
467        Iterable<Relationship> rels;
468        if (direction == null) {
469            // ignore direction
470            rels = start.getRelationships(type);
471        } else {
472            rels = start.getRelationships(type, direction);
473        }
474        for (Relationship rel : rels) {
475            return rel;
476        }
477        return null;
478    }
479
480    /**
481     * Erzeuge eine urn aus der aktuellen Zeit in millis
482     *
483     * @return
484     */
485    private String createRessourceURI(String prefix) {
486
487        Calendar cal = Calendar.getInstance();
488
489        long time = cal.getTimeInMillis();
490
491        return String.format("%s%s%s", ANNOTATION_URI_BASE, prefix, time);
492
493    }
494
495}
Note: See TracBrowser for help on using the repository browser.