source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 12:5928c5d9aae8

Last change on this file since 12:5928c5d9aae8 was 12:5928c5d9aae8, checked in by casties, 12 years ago

more work on permissions...

File size: 16.0 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            //FIXME
114        }
115       
116        return annot;
117    }
118
119    /**
120     * Returns an Actor object from a node.
121     *
122     * @param actorNode
123     * @return
124     */
125    protected Actor createActorFromNode(Node actorNode) {
126        String uri = (String) actorNode.getProperty("uri", null);
127        String name = (String) actorNode.getProperty("name", null);
128        String type = (String) actorNode.getProperty("TYPE", null);
129        Actor creator = null;
130        if (type != null && type.equals("PERSON")) {
131            creator = new Person(uri, name);
132        } else if (type != null && type.equals("GROUP")) {
133            creator = new Group(uri, name);
134        }
135        return creator;
136    }
137
138    /**
139     * Store a new annotation in the store or update an existing one. Returns
140     * the stored annotation.
141     *
142     * @param annot
143     * @return
144     */
145    public Annotation storeAnnotation(Annotation annot) {
146        Node annotNode = null;
147        Transaction tx = graphDb.beginTx();
148        try {
149            /*
150             * create or get the annotation
151             */
152            String id = annot.getUri();
153            if (id == null) {
154                id = createRessourceURI("annot:");
155            }
156            annotNode = getOrCreateAnnotationNode(id);
157
158            /*
159             * the annotation body
160             */
161            String bodyText = annot.getBodyText();
162            if (bodyText != null) {
163                annotNode.setProperty("bodyText", bodyText);
164            }
165            String bodyUri = annot.getBodyUri();
166            if (bodyUri != null) {
167                annotNode.setProperty("bodyUri", bodyUri);
168            }
169
170            /*
171             * the annotation target
172             */
173            String targetBaseUri = annot.getTargetBaseUri();
174            if (targetBaseUri != null) {
175                Node target = getOrCreateTargetNode(targetBaseUri);
176                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, target);
177            }
178
179            /*
180             * The fragment part of the annotation target.
181             */
182            String targetFragment = annot.getTargetFragment();
183            FragmentTypes fragmentType = annot.getFragmentType();
184            if (targetFragment != null) {
185                annotNode.setProperty("targetFragment", targetFragment);
186                annotNode.setProperty("fragmentType", fragmentType.name());
187            }
188
189            /*
190             * The creator of this annotation.
191             */
192            Actor creator = annot.getCreator();
193            if (creator != null) {
194                Node creatorNode = getOrCreatePersonNode(creator);
195                getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
196            }
197
198            /*
199             * The creation date of this annotation.
200             */
201            String created = annot.getCreated();
202            if (created != null) {
203                annotNode.setProperty("created", created);
204            }
205
206            /*
207             * Permissions for this annotation.
208             */
209            setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
210            setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
211            setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
212            setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
213
214            tx.success();
215        } finally {
216            tx.finish();
217        }
218
219        // re-read and return annotation
220        Annotation storedAnnot = createAnnotationFromNode(annotNode);
221        return storedAnnot;
222    }
223
224    /**
225     * Deletes the annotation with the given id.
226     *
227     * @param id
228     */
229    public void deleteById(String id) {
230        Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
231        if (annotNode != null) {
232            // delete related objects
233            Transaction tx = graphDb.beginTx();
234            try {
235                for (Relationship rel : annotNode.getRelationships()) {
236                    // delete relation and the related node if it has no other
237                    // relations
238                    Node other = rel.getOtherNode(annotNode);
239                    rel.delete();
240                    if (!other.hasRelationship()) {
241                        deleteNode(other);
242                    }
243                }
244                if (!annotNode.hasRelationship()) {
245                    deleteNode(annotNode);
246                } else {
247                    logger.error("deleteById: unable to delete: Node still has relations.");
248                }
249                tx.success();
250            } finally {
251                tx.finish();
252            }
253        }
254    }
255
256    /**
257     * Returns all annotations with the given uri and/or user.
258     *
259     * @param uri
260     * @param userUri
261     * @param limit
262     * @param offset
263     * @return
264     */
265    public List<Annotation> searchByUriUser(String targetUri, String userUri, String limit, String offset) {
266        List<Annotation> annotations = new ArrayList<Annotation>();
267        if (targetUri != null) {
268            // there should be only one
269            Node target = getNodeIndex(NodeTypes.TARGET).get("uri", targetUri).getSingle();
270            if (target != null) {
271                Iterable<Relationship> relations = target.getRelationships(RelationTypes.ANNOTATES);
272                for (Relationship relation : relations) {
273                    Node ann = relation.getStartNode();
274                    if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
275                        Annotation annot = createAnnotationFromNode(ann);
276                        annotations.add(annot);
277                    } else {
278                        logger.error("ANNOTATES relation does not start with ANNOTATION: " + ann);
279                    }
280                }
281            }
282        }
283        if (userUri != null) {
284            // there should be only one
285            Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri).getSingle();
286            if (person != null) {
287                Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
288                for (Relationship relation : relations) {
289                    Node ann = relation.getEndNode();
290                    if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
291                        Annotation annot = createAnnotationFromNode(ann);
292                        annotations.add(annot);
293                    } else {
294                        logger.error("CREATED relation does not end with ANNOTATION: " + ann);
295                    }
296                }
297            }
298        }
299        return annotations;
300    }
301
302    protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
303        if (start.hasRelationship()) {
304            // there are relations
305            Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
306            for (Relationship rel : rels) {
307                if (rel.getEndNode().equals(end)) {
308                    // relation exists
309                    return rel;
310                }
311            }
312        }
313        // create new one
314        Relationship rel;
315        Transaction tx = graphDb.beginTx();
316        try {
317            rel = start.createRelationshipTo(end, type);
318            tx.success();
319        } finally {
320            tx.finish();
321        }
322        return rel;
323    }
324
325    protected Node getOrCreateAnnotationNode(String id) {
326        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
327        IndexHits<Node> annotations = idx.get("id", id);
328        Node annotation = annotations.getSingle();
329        if (annotation == null) {
330            // does not exist yet
331            Transaction tx = graphDb.beginTx();
332            try {
333                annotation = graphDb.createNode();
334                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
335                annotation.setProperty("id", id);
336                idx.add(annotation, "id", id);
337                tx.success();
338            } finally {
339                tx.finish();
340            }
341        }
342        return annotation;
343    }
344
345    protected Node getOrCreateTargetNode(String uri) {
346        Index<Node> idx = getNodeIndex(NodeTypes.TARGET);
347        IndexHits<Node> targets = idx.get("uri", uri);
348        Node target = targets.getSingle();
349        if (target == null) {
350            // does not exist yet
351            Transaction tx = graphDb.beginTx();
352            try {
353                target = graphDb.createNode();
354                target.setProperty("TYPE", NodeTypes.TARGET.name());
355                target.setProperty("uri", uri);
356                idx.add(target, "uri", uri);
357                tx.success();
358            } finally {
359                tx.finish();
360            }
361        }
362        return target;
363    }
364
365    protected Node getOrCreatePersonNode(Actor actor) {
366        /*
367         * // Person is identified by URI Index<Node> idx =
368         * getNodeIndex(NodeTypes.PERSON); IndexHits<Node> persons =
369         * idx.get("uri", uri); Node person = persons.getSingle(); if (person ==
370         * null) { // does not exist yet Transaction tx = graphDb.beginTx(); try
371         * { person = graphDb.createNode(); person.setProperty("TYPE",
372         * NodeTypes.PERSON.name()); person.setProperty("uri", uri);
373         * idx.add(person, "uri", uri); if (name != null) {
374         * person.setProperty("name", name); } tx.success(); } finally {
375         * tx.finish(); } } return person;
376         */
377        return null;
378    }
379
380    /**
381     * Create or update permissions relations for an annotation.
382     *
383     * @param annotNode
384     * @param type
385     * @param annot
386     */
387    protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
388        Node newActorNode = null;
389        if (actor != null) {
390            newActorNode = getOrCreatePersonNode(actor);
391        }
392        Relationship rel = getRelation(annotNode, type, null);
393        if (rel != null) {
394            // relation exists
395            Node oldActorNode = rel.getEndNode();
396            if (!oldActorNode.equals(newActorNode)) {
397                // new admin is different
398                rel.delete();
399                if (newActorNode != null) {
400                    rel = getOrCreateRelation(annotNode, type, newActorNode);
401                }
402            }
403        } else {
404            // no relation yet
405            if (newActorNode != null) {
406                rel = getOrCreateRelation(annotNode, type, newActorNode);
407            }
408        }
409    }
410
411    /**
412     * Unindexes and deletes given Node if it has no relations.
413     *
414     * @param node
415     */
416    protected void deleteNode(Node node) {
417        Transaction tx = graphDb.beginTx();
418        try {
419            if (node.hasRelationship()) {
420                logger.error("deleteNode: unable to delete: Node still has relations.");
421            } else {
422                String ts = (String) node.getProperty("TYPE", null);
423                try {
424                    NodeTypes type = NodeTypes.valueOf(ts);
425                    getNodeIndex(type).remove(node);
426                } catch (Exception e) {
427                    logger.error("deleteNode: unable to get TYPE of node: " + node);
428                }
429                node.delete();
430            }
431            tx.success();
432        } finally {
433            tx.finish();
434        }
435    }
436
437    protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
438        Iterable<Relationship> rels;
439        if (direction == null) {
440            // ignore direction
441            rels = start.getRelationships(type);
442        } else {
443            rels = start.getRelationships(type, direction);
444        }
445        for (Relationship rel : rels) {
446            return rel;
447        }
448        return null;
449    }
450
451    /**
452     * Erzeuge eine urn aus der aktuellen Zeit in millis
453     *
454     * @return
455     */
456    private String createRessourceURI(String prefix) {
457
458        Calendar cal = Calendar.getInstance();
459
460        long time = cal.getTimeInMillis();
461
462        return String.format("%s%s%s", ANNOTATION_URI_BASE, prefix, time);
463
464    }
465
466}
Note: See TracBrowser for help on using the repository browser.