source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 81:3be57c18c693

Last change on this file since 81:3be57c18c693 was 81:3be57c18c693, checked in by casties, 10 years ago

more fixing bugs with transactions thanks to neo4j 2 :-(

File size: 39.9 KB
Line 
1/**
2 *
3 */
4package de.mpiwg.itgroup.annotations.neo4j;
5
6/*
7 * #%L
8 * AnnotationManager
9 * %%
10 * Copyright (C) 2012 - 2014 MPIWG Berlin
11 * %%
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as
14 * published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Lesser Public License for more details.
21 *
22 * You should have received a copy of the GNU General Lesser Public
23 * License along with this program.  If not, see
24 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
25 * #L%
26 * Author: Robert Casties (casties@mpiwg-berlin.mpg.de)
27 */
28
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Calendar;
32import java.util.HashSet;
33import java.util.List;
34import java.util.Set;
35import java.util.logging.Logger;
36
37import org.neo4j.graphdb.Direction;
38import org.neo4j.graphdb.GraphDatabaseService;
39import org.neo4j.graphdb.Node;
40import org.neo4j.graphdb.Relationship;
41import org.neo4j.graphdb.RelationshipType;
42import org.neo4j.graphdb.Transaction;
43import org.neo4j.graphdb.index.Index;
44import org.neo4j.graphdb.index.IndexHits;
45
46import de.mpiwg.itgroup.annotations.Actor;
47import de.mpiwg.itgroup.annotations.Annotation;
48import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes;
49import de.mpiwg.itgroup.annotations.Group;
50import de.mpiwg.itgroup.annotations.Person;
51import de.mpiwg.itgroup.annotations.Resource;
52import de.mpiwg.itgroup.annotations.Tag;
53import de.mpiwg.itgroup.annotations.Target;
54import de.mpiwg.itgroup.annotations.Uri;
55
56/**
57 * Neo4J based Annotation store.
58 *
59 * @author casties
60 *
61 */
62public class AnnotationStore {
63
64    protected static Logger logger = Logger.getLogger("de.mpiwg.itgroup.annotations.neo4j.AnnotationStore");
65
66    protected GraphDatabaseService graphDb;
67
68    public static enum NodeTypes {
69        ANNOTATION, PERSON, TARGET, GROUP, TAG, RESOURCE
70    }
71
72    // types of nodes that should not be automatically deleted.
73    public Set<String> permanentNodeTypes = new HashSet<String>(Arrays.asList("PERSON", "GROUP", "TAG"));
74
75    protected List<Index<Node>> nodeIndexes;
76
77    public static enum RelationTypes implements RelationshipType {
78        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF, HAS_TAG, PART_OF
79    }
80
81    public static String ANNOTATION_URI_PREFIX = "";
82
83    public AnnotationStore(GraphDatabaseService graphDb) {
84        super();
85        this.graphDb = graphDb;
86        nodeIndexes = new ArrayList<Index<Node>>(5);
87        // List.set(enum.ordinal(), val) seems not to work.
88        try (Transaction tx = graphDb.beginTx()) {
89            nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
90            nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons"));
91            nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
92            nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups"));
93            nodeIndexes.add(NodeTypes.TAG.ordinal(), graphDb.index().forNodes("tags"));
94            nodeIndexes.add(NodeTypes.RESOURCE.ordinal(), graphDb.index().forNodes("resources"));
95            tx.success();
96        }
97    }
98
99    protected Index<Node> getNodeIndex(NodeTypes type) {
100        return nodeIndexes.get(type.ordinal());
101    }
102
103    /**
104     * @param userUri
105     * @return
106     */
107    public Node getPersonNodeByUri(String userUri) {
108        return getNodeFromIndex("uri", userUri, NodeTypes.PERSON);
109    }
110
111    /**
112     * @param tagUri
113     * @return
114     */
115    public Node getTagNodeByUri(String tagUri) {
116        return getNodeFromIndex("uri", tagUri, NodeTypes.TAG);
117    }
118
119    /**
120     * @param resourceUri
121     * @return
122     */
123    public Node getResourceNodeByUri(String resourceUri) {
124        return getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
125    }
126
127    /**
128     * @param targetUri
129     * @return
130     */
131    public Node getTargetNodeByUri(String targetUri) {
132        return getNodeFromIndex("uri", targetUri, NodeTypes.RESOURCE);
133    }
134
135    /**
136     * Returns the Node with the given key and value. Key has to be indexed.
137     *
138     * @param key
139     * @param value
140     * @param type
141     * @return
142     */
143    public Node getNodeFromIndex(String key, String value, NodeTypes type) {
144        if (key == null || value == null)
145            return null;
146        Node node = null;
147        try (Transaction tx = graphDb.beginTx()) {
148            node = getNodeIndex(type).get(key, value).getSingle();
149            tx.success();
150        }
151        return node;
152    }
153
154    /**
155     * Returns list of Actors of given type (Group or Person). Key has to be
156     * indexed.
157     *
158     * @param key
159     * @param query
160     * @param type
161     * @return
162     */
163    @SuppressWarnings("unchecked")
164    protected <T extends Actor> List<T> getActors(String key, String query, NodeTypes type) {
165        ArrayList<T> actors = new ArrayList<T>();
166        Index<Node> idx = getNodeIndex(type);
167        if (key == null) {
168            key = "uri";
169            query = "*";
170        }
171        try (Transaction tx = graphDb.beginTx()) {
172            IndexHits<Node> actorNodes = idx.query(key, query);
173            for (Node actorNode : actorNodes) {
174                Actor actor = createActorFromNode(actorNode);
175                actors.add((T) actor);
176            }
177            tx.success();           
178        }
179        return actors;
180    }
181
182    /**
183     * Returns list of groups. Key has to be indexed.
184     *
185     * @param key
186     * @param query
187     * @return
188     */
189    public List<Group> getGroups(String key, String query) {
190        List<Group> groups = getActors(key, query, NodeTypes.GROUP);
191        return groups;
192    }
193
194    /**
195     * Returns list of Persons. Key has to be indexed.
196     *
197     * @param key
198     * @param query
199     * @return
200     */
201    public List<Person> getPersons(String key, String query) {
202        List<Person> persons = getActors(key, query, NodeTypes.PERSON);
203        return persons;
204    }
205
206    /**
207     * Returns list of uri-like objects of given type (Target or Resource). Key
208     * has to be indexed.
209     *
210     * @param key
211     * @param query
212     * @param type
213     * @return
214     */
215    @SuppressWarnings("unchecked")
216    protected <T extends Uri> List<T> getUris(String key, String query, NodeTypes type) {
217        ArrayList<T> uris = new ArrayList<T>();
218        Index<Node> idx = getNodeIndex(type);
219        if (key == null) {
220            key = "uri";
221            query = "*";
222        }
223        try (Transaction tx = graphDb.beginTx()) {
224            IndexHits<Node> actorNodes = idx.query(key, query);
225            for (Node actorNode : actorNodes) {
226                Uri uri = createUriFromNode(actorNode);
227                uris.add((T) uri);
228            }
229            tx.success();
230        }
231        return uris;
232    }
233
234    /**
235     * Returns list of Targets. Key has to be indexed.
236     *
237     * @param key
238     * @param query
239     * @return
240     */
241    public List<Target> getTargets(String key, String query) {
242        List<Target> targets = getUris(key, query, NodeTypes.TARGET);
243        return targets;
244    }
245
246    /**
247     * Returns list of Resources. Key has to be indexed.
248     *
249     * @param key
250     * @param query
251     * @return
252     */
253    public List<Resource> getResources(String key, String query) {
254        List<Resource> targets = getUris(key, query, NodeTypes.RESOURCE);
255        return targets;
256    }
257
258    /**
259     * Returns List of Annotations. Key has to be indexed.
260     *
261     * @param key
262     * @param query
263     * @return
264     */
265    public List<Annotation> getAnnotations(String key, String query) {
266        ArrayList<Annotation> annotations = new ArrayList<Annotation>();
267        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
268        if (key == null) {
269            key = "id";
270            query = "*";
271        }
272        try (Transaction tx = graphDb.beginTx()) {
273            IndexHits<Node> annotNodes = idx.query(key, query);
274            for (Node annotNode : annotNodes) {
275                Annotation annotation = createAnnotationFromNode(annotNode);
276                annotations.add(annotation);
277            }
278            tx.success();
279        }
280        return annotations;
281    }
282
283    /**
284     * Returns List of Tags. Key has to be indexed.
285     *
286     * @param key
287     * @param query
288     * @return
289     */
290    public List<Tag> getTags(String key, String query) {
291        ArrayList<Tag> tags = new ArrayList<Tag>();
292        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
293        if (key == null) {
294            key = "uri";
295            query = "*";
296        }
297        try (Transaction tx = graphDb.beginTx()) {
298            IndexHits<Node> groupNodes = idx.query(key, query);
299            for (Node groupNode : groupNodes) {
300                Tag tag = createTagFromNode(groupNode);
301                tags.add(tag);
302            }
303            tx.success();
304        }
305        return tags;
306    }
307
308    /**
309     * Returns List of Groups the person is member of.
310     *
311     * @param person
312     * @return
313     */
314    public List<Group> getGroupsForPersonNode(Node person) {
315        ArrayList<Group> groups = new ArrayList<Group>();
316        try (Transaction tx = graphDb.beginTx()) {
317                Iterable<Relationship> rels = person.getRelationships(RelationTypes.MEMBER_OF);
318                for (Relationship rel : rels) {
319                        Node groupNode = rel.getEndNode();
320                        Actor group = createActorFromNode(groupNode);
321                        // make sure we're getting a group
322                        if (!(group instanceof Group)) {
323                                logger.severe("target of MEMBER_OF is not GROUP! rel=" + rel);
324                                continue;
325                        }
326                        groups.add((Group) group);
327                }
328                tx.success();
329        }
330        return groups;
331    }
332
333    /**
334     * Returns if person with uri is in Group group.
335     *
336     * @param person
337     * @param group
338     * @return
339     */
340    public boolean isPersonInGroup(Person person, Group group) {
341        Node pn = getPersonNodeByUri(person.getUriString());
342        if (pn == null)
343            return false;
344        // optimized version of getGroupsForPersonNode
345        try (Transaction tx = graphDb.beginTx()) {
346            Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
347            for (Relationship rel : rels) {
348                Node gn = rel.getEndNode();
349                if (gn.getProperty("uri", "").equals(group.getUriString()) 
350                        || gn.getProperty("id", "").equals(group.getId())) {
351                    tx.success();
352                    return true;
353                }
354            }
355            tx.success();
356        }
357        return false;
358    }
359
360    /**
361     * Returns the members of the group.
362     *
363     * @param group
364     * @return
365     */
366    public List<Person> getMembersOfGroup(Group group) {
367        ArrayList<Person> members = new ArrayList<Person>();
368        Node gn = getActorNode(group);
369        try (Transaction tx = graphDb.beginTx()) {
370                Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
371                for (Relationship rel : rels) {
372                        Node memberNode = rel.getStartNode();
373                        Actor member = createActorFromNode(memberNode);
374                        // make sure we're getting a group
375                        if (!(member instanceof Person)) {
376                                logger.severe("source of MEMBER_OF is not PERSON! rel=" + rel);
377                                continue;
378                        }
379                        members.add((Person) member);
380                }
381                tx.success();
382        }
383        return members;
384    }
385
386    /**
387     * Add Person newMember to Group group.
388     *
389     * @param group
390     * @param member
391     */
392    public Person addGroupMember(Group group, Person member) {
393        Node gn = getActorNode(group);
394        Node pn = getActorNode(member);
395        Person addedMember = null;
396        if (gn != null && pn != null) {
397            getOrCreateRelation(pn, RelationTypes.MEMBER_OF, gn);
398            addedMember = member;
399        }
400        return addedMember;
401    }
402
403    /**
404     * Delete Person oldMember from Group group.
405     *
406     * @param group
407     * @param member
408     */
409    public void deleteGroupMember(Group group, Person member) {
410        Node gn = getActorNode(group);
411        Node pn = getActorNode(member);
412        try (Transaction tx = graphDb.beginTx()) {
413                Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
414                for (Relationship rel : rels) {
415                        Node mn = rel.getStartNode();
416                        if (mn.equals(pn)) {
417                    rel.delete();
418                }
419                // there should be only one
420                break;
421            }
422            tx.success();
423        }
424    }
425
426    /**
427     * Returns the stored Actor matching the given one.
428     *
429     * @param actor
430     * @return
431     */
432    public Actor getActor(Actor actor) {
433        Node actorNode = getActorNode(actor);
434        Actor storedActor = createActorFromNode(actorNode);
435        return storedActor;
436    }
437
438    /**
439     * Stores an Actor (Person or Group). Creates a new actor Node or update an
440     * existing one.
441     *
442     * @param actor
443     * @return
444     */
445    public Actor storeActor(Actor actor) {
446        Node actorNode = getOrCreateActorNode(actor);
447        try (Transaction tx = graphDb.beginTx()) {
448            // id
449            String id = actor.getId();
450            if (id != null) {
451                actorNode.setProperty("id", id);
452            }
453            // name
454            String name = actor.getName();
455            if (name != null) {
456                actorNode.setProperty("name", name);
457            }
458            // uri
459            String uri = actor.getUri();
460            if (uri != null) {
461                actorNode.setProperty("uri", uri);
462            }
463            tx.success();
464        }
465        Actor storedActor = createActorFromNode(actorNode);
466        return storedActor;
467    }
468
469    /**
470     * Deletes the given Actor.
471     *
472     * @param actor
473     */
474    public void deleteActor(Actor actor) {
475        String uri = actor.getUriString();
476        Index<Node> idx;
477        if (actor.isGroup()) {
478            idx = getNodeIndex(NodeTypes.GROUP);
479        } else {
480            idx = getNodeIndex(NodeTypes.PERSON);
481        }
482        try (Transaction tx = graphDb.beginTx()) {
483                Node actorNode = idx.get("uri", uri).getSingle();
484                if (actorNode != null) {
485                        // delete relations
486                for (Relationship rel : actorNode.getRelationships()) {
487                        rel.delete();
488                }
489                if (!actorNode.hasRelationship()) {
490                    // this shouldn't happen
491                    deleteNode(actorNode);
492                } else {
493                    logger.severe("deleteActor: unable to delete: Node still has relations.");
494                }
495            }
496            tx.success();
497        }
498    }
499
500    /**
501     * Returns the Annotation with the given id.
502     *
503     * @param id
504     * @return
505     */
506    public Annotation getAnnotationById(String id) {
507        Annotation annot = null;
508        try (Transaction tx = graphDb.beginTx()) {
509            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
510            annot = createAnnotationFromNode(annotNode);
511            tx.success();
512        }
513        return annot;
514    }
515
516    /**
517     * Returns an Annotation object from an annotation-Node.
518     *
519     * @param annotNode
520     * @return
521     */
522    public Annotation createAnnotationFromNode(Node annotNode) {
523        Annotation annot = new Annotation();
524        try (Transaction tx = graphDb.beginTx()) {
525            annot.setUri((String) annotNode.getProperty("id", null));
526            annot.setBodyText((String) annotNode.getProperty("bodyText", null));
527            annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
528            annot.setQuote((String) annotNode.getProperty("quote", null));
529            /*
530             * get annotation target and resource from relation
531             */
532            for (Relationship rel : annotNode.getRelationships(RelationTypes.ANNOTATES)) {
533                Node target = rel.getEndNode();
534                String type = (String) target.getProperty("TYPE");
535                if (type.equals("TARGET")) {
536                    annot.setTarget(new Target((String) target.getProperty("uri", null)));
537                } else if (type.equals("RESOURCE")) {
538                    annot.setResource(new Resource((String) target.getProperty("uri", null)));
539                }
540            }
541            if (annot.getTarget() == null) {
542                logger.warning("annotation " + annotNode + " has no target node!");
543            }
544            // get fragment from attribute
545            annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null));
546            String ft = (String) annotNode.getProperty("fragmentType", null);
547            if (ft != null) {
548                annot.setFragmentType(FragmentTypes.valueOf(ft));
549            }
550            /*
551             * get creator from relation
552             */
553            Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
554            if (creatorRel != null) {
555                Node creatorNode = creatorRel.getStartNode();
556                Actor creator = createActorFromNode(creatorNode);
557                annot.setCreator(creator);
558            } else {
559                logger.warning("annotation " + annotNode + " has no creator node!");
560            }
561            /*
562             * get creation date
563             */
564            annot.setCreated((String) annotNode.getProperty("created", null));
565            /*
566             * get permissions
567             */
568            Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
569            if (adminRel != null) {
570                Node adminNode = adminRel.getEndNode();
571                Actor admin = createActorFromNode(adminNode);
572                annot.setAdminPermission(admin);
573            }
574            Relationship deleteRel = getRelation(annotNode, RelationTypes.PERMITS_DELETE, null);
575            if (deleteRel != null) {
576                Node deleteNode = deleteRel.getEndNode();
577                Actor delete = createActorFromNode(deleteNode);
578                annot.setDeletePermission(delete);
579            }
580            Relationship updateRel = getRelation(annotNode, RelationTypes.PERMITS_UPDATE, null);
581            if (updateRel != null) {
582                Node updateNode = updateRel.getEndNode();
583                Actor update = createActorFromNode(updateNode);
584                annot.setUpdatePermission(update);
585            }
586            Relationship readRel = getRelation(annotNode, RelationTypes.PERMITS_READ, null);
587            if (readRel != null) {
588                Node readNode = readRel.getEndNode();
589                Actor read = createActorFromNode(readNode);
590                annot.setReadPermission(read);
591            }
592            /*
593             * get tags
594             */
595            Set<String> tags = new HashSet<String>();
596            for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
597                String tag = (String) rel.getEndNode().getProperty("name", null);
598                if (tag != null) {
599                    tags.add(tag);
600                }
601            }
602            annot.setTags(tags);
603
604            tx.success();
605        }
606        return annot;
607    }
608
609    /**
610     * Returns an Actor object from a node.
611     *
612     * @param actorNode
613     * @return
614     */
615    protected Actor createActorFromNode(Node actorNode) {
616        if (actorNode == null)
617            return null;
618        try (Transaction tx = graphDb.beginTx()) {
619            String id = (String) actorNode.getProperty("id", null);
620            String uri = (String) actorNode.getProperty("uri", null);
621            String name = (String) actorNode.getProperty("name", null);
622            String type = (String) actorNode.getProperty("TYPE", null);
623            tx.success();
624            if (type != null && type.equals("PERSON")) {
625                return new Person(id, uri, name);
626            } else if (type != null && type.equals("GROUP")) {
627                return new Group(id, uri, name);
628            }
629        }
630        return null;
631    }
632
633    public Tag createTagFromNode(Node tagNode) {
634        if (tagNode == null)
635            return null;
636        String id;
637        String uri;
638        String name;
639        try (Transaction tx = graphDb.beginTx()) {
640            name = (String) tagNode.getProperty("name", null);
641            uri = (String) tagNode.getProperty("uri", null);
642            id = (String) tagNode.getProperty("id", null);
643            tx.success();
644        }
645        return new Tag(id, uri, name);
646
647    }
648
649    /**
650     * @param resourceNode
651     * @return
652     */
653    public Resource createResourceFromNode(Node resourceNode) {
654        return (Resource) createUriFromNode(resourceNode);
655    }
656
657    /**
658     * @param targetNode
659     * @return
660     */
661    public Target createTargetFromNode(Node targetNode) {
662        return (Target) createUriFromNode(targetNode);
663    }
664
665    protected Uri createUriFromNode(Node uriNode) {
666        if (uriNode == null)
667            return null;
668        try (Transaction tx = graphDb.beginTx()) {
669            String uri = (String) uriNode.getProperty("uri", null);
670            String type = (String) uriNode.getProperty("TYPE", null);
671            tx.success();
672            if (type != null && type.equals("TARGET")) {
673                return new Target(uri);
674            } else if (type != null && type.equals("RESOURCE")) {
675                return new Resource(uri);
676            }
677        }
678        return null;
679    }
680
681    /**
682     * Store a new annotation in the store or update an existing one. Returns
683     * the stored annotation.
684     *
685     * @param annot
686     * @return
687     */
688    public Annotation storeAnnotation(Annotation annot) {
689        Node annotNode = null;
690        try (Transaction tx = graphDb.beginTx()) {
691            /*
692             * create or get the annotation
693             */
694            String id = annot.getUri();
695            if (id == null) {
696                id = createRessourceURI("annot:");
697            }
698            annotNode = getOrCreateAnnotationNode(id);
699
700            /*
701             * the annotation body
702             */
703            String bodyText = annot.getBodyText();
704            if (bodyText != null) {
705                annotNode.setProperty("bodyText", bodyText);
706            }
707            String bodyUri = annot.getBodyUri();
708            if (bodyUri != null) {
709                annotNode.setProperty("bodyUri", bodyUri);
710            }
711           
712            /*
713             * the annotation quote
714             */
715            String quote = annot.getQuote();
716            if (quote != null) {
717                annotNode.setProperty("quote", quote);
718            }
719               
720            /*
721             * the annotation target
722             */
723            Target target = annot.getTarget();
724            Node targetNode = null;
725            if (target != null) {
726                targetNode = getOrCreateUriNode(target.getUri(), NodeTypes.TARGET);
727                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, targetNode);
728            }
729
730            /*
731             * The fragment part of the annotation target.
732             */
733            String targetFragment = annot.getTargetFragment();
734            FragmentTypes fragmentType = annot.getFragmentType();
735            if (targetFragment != null) {
736                annotNode.setProperty("targetFragment", targetFragment);
737                annotNode.setProperty("fragmentType", fragmentType.name());
738            }
739
740            /*
741             * the annotation resource
742             */
743            Resource resource = annot.getResource();
744            if (resource != null) {
745                Node resourceNode = getOrCreateUriNode(resource.getUri(), NodeTypes.RESOURCE);
746                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, resourceNode);
747                getOrCreateRelation(targetNode, RelationTypes.PART_OF, resourceNode);
748            }
749
750            /*
751             * The creator of this annotation.
752             */
753            Actor creator = annot.getCreator();
754            if (creator != null) {
755                Node creatorNode = getOrCreateActorNode(creator);
756                getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
757            }
758
759            /*
760             * The creation date of this annotation.
761             */
762            String created = annot.getCreated();
763            if (created != null) {
764                annotNode.setProperty("created", created);
765            }
766
767            /*
768             * Permissions for this annotation.
769             */
770            setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
771            setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
772            setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
773            setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
774
775            /*
776             * Tags on this annotation.
777             */
778            Set<String> newTags = annot.getTags();
779            // we ignore existing tags if tags == null
780            if (newTags != null) {
781                List<Relationship> oldHasTags = new ArrayList<Relationship>();
782                for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
783                    oldHasTags.add(rel);
784                }
785                // adjust to new tags
786                if (newTags.isEmpty()) {
787                    // remove old tags
788                    if (!oldHasTags.isEmpty()) {
789                        for (Relationship rel : oldHasTags) {
790                            rel.delete();
791                            // TODO: should we delete orphan nodes too?
792                        }
793                    }
794                } else {
795                    if (!oldHasTags.isEmpty()) {
796                        // adjust old tags
797                        for (Relationship rel : oldHasTags) {
798                            String oldTag = (String) rel.getEndNode().getProperty("name", null);
799                            if (newTags.contains(oldTag)) {
800                                // tag exists
801                                newTags.remove(oldTag);
802                            } else {
803                                // tag exists no longer
804                                rel.delete();
805                                // TODO: should we delete orphan nodes too?
806                            }
807                        }
808                    }
809                    if (!newTags.isEmpty()) {
810                        // still tags to add
811                        for (String tag : newTags) {
812                            // create new tag
813                            Node tagNode = getOrCreateTagNode(new Tag(null, null, tag));
814                            getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode);
815                        }
816                    }
817
818                }
819            }
820            tx.success();
821        }
822
823        // re-read and return annotation
824        Annotation storedAnnot = createAnnotationFromNode(annotNode);
825        return storedAnnot;
826    }
827
828    /**
829     * Deletes the annotation with the given id.
830     *
831     * @param id
832     */
833    public void deleteAnnotationById(String id) {
834        try (Transaction tx = graphDb.beginTx()) {
835            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
836            if (annotNode != null) {
837                // delete related objects
838                for (Relationship rel : annotNode.getRelationships()) {
839                    // delete relation and the related node if it has no other
840                    // relations and is not permanent
841                    Node other = rel.getOtherNode(annotNode);
842                    rel.delete();
843                    if (!(other.hasRelationship() || permanentNodeTypes.contains(other.getProperty("TYPE", null)))) {
844                        deleteNode(other);
845                    }
846                }
847                if (!annotNode.hasRelationship()) {
848                    deleteNode(annotNode);
849                } else {
850                    logger.severe("deleteById: unable to delete: Node still has relations.");
851                }
852            }
853            tx.success();
854        }
855    }
856
857    /**
858     * Returns all annotations with the given uri and/or user.
859     *
860     * @param uri
861     * @param userUri
862     * @return
863     */
864    public List<Annotation> searchAnnotationByUriUser(String targetUri, String userUri) {
865        List<Annotation> annotations = new ArrayList<Annotation>();
866        if (targetUri != null) {
867            // there should be only one
868            Node target = getNodeFromIndex("uri", targetUri, NodeTypes.TARGET);
869            if (target != null) {
870                try (Transaction tx = graphDb.beginTx()) {
871                    Iterable<Relationship> relations = target.getRelationships(RelationTypes.ANNOTATES);
872                    for (Relationship relation : relations) {
873                        Node ann = relation.getStartNode();
874                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
875                            Annotation annot = createAnnotationFromNode(ann);
876                            annotations.add(annot);
877                        } else {
878                            logger.severe("ANNOTATES relation does not start with ANNOTATION: " + ann);
879                        }
880                    }
881                    tx.success();
882                }
883            }
884        }
885        if (userUri != null) {
886            // there should be only one
887            Node person = getPersonNodeByUri(userUri);
888            if (person != null) {
889                try (Transaction tx = graphDb.beginTx()) {
890                    Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
891                    for (Relationship relation : relations) {
892                        Node ann = relation.getEndNode();
893                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
894                            Annotation annot = createAnnotationFromNode(ann);
895                            annotations.add(annot);
896                        } else {
897                            logger.severe("CREATED relation does not end with ANNOTATION: " + ann);
898                        }
899                    }
900                    tx.success();
901                }
902            }
903        }
904        // TODO: if both uri and user are given we should intersect
905
906        return annotations;
907    }
908
909    /**
910     * Returns Relationship of type from Node start to Node end. Creates one if
911     * it doesn't exist.
912     *
913     * @param start
914     * @param type
915     * @param end
916     * @return
917     */
918    protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
919        if (start == null || end == null)
920            return null;
921        if (start.hasRelationship()) {
922            // there are relations
923            try (Transaction tx = graphDb.beginTx()) {
924                Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
925                for (Relationship rel : rels) {
926                    if (rel.getEndNode().equals(end)) {
927                        // relation exists
928                        tx.success();
929                        return rel;
930                    }
931                }
932                tx.success();
933            }
934        }
935        // create new one
936        Relationship rel;
937        try (Transaction tx = graphDb.beginTx()) {
938            rel = start.createRelationshipTo(end, type);
939            tx.success();
940        }
941        return rel;
942    }
943
944    protected Node getOrCreateAnnotationNode(String id) {
945        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
946        Node annotation;
947        try (Transaction tx = graphDb.beginTx()) {
948            IndexHits<Node> annotations = idx.get("id", id);
949            annotation = annotations.getSingle();
950            if (annotation == null) {
951                // does not exist yet
952                annotation = graphDb.createNode();
953                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
954                annotation.setProperty("id", id);
955                idx.add(annotation, "id", id);
956            }
957            tx.success();
958        }
959        return annotation;
960    }
961
962    protected Node getOrCreateUriNode(String uri, NodeTypes type) {
963        Index<Node> idx = getNodeIndex(type);
964        Node target;
965        try (Transaction tx = graphDb.beginTx()) {
966            IndexHits<Node> targets = idx.get("uri", uri);
967            target = targets.getSingle();
968            if (target == null) {
969                // does not exist yet
970                target = graphDb.createNode();
971                target.setProperty("TYPE", type.name());
972                target.setProperty("uri", uri);
973                idx.add(target, "uri", uri);
974            }
975            tx.success();
976        }
977        return target;
978    }
979
980    protected Node getActorNode(Actor actor) {
981        // Person/Group is identified by URI or id
982        String uri = actor.getUriString();
983        Index<Node> idx;
984        Node person;
985        if (actor.isGroup()) {
986            idx = getNodeIndex(NodeTypes.GROUP);
987        } else {
988            idx = getNodeIndex(NodeTypes.PERSON);
989        }
990        try (Transaction tx = graphDb.beginTx()) {
991            IndexHits<Node> persons = idx.get("uri", uri);
992            person = persons.getSingle();
993            tx.success();
994        }
995        return person;
996    }
997
998    protected Node getOrCreateActorNode(Actor actor) {
999        // Person/Group is identified by URI or id
1000        String uri = actor.getUriString();
1001        String name = actor.getName();
1002        String id = actor.getId();
1003        Node person;
1004        try (Transaction tx = graphDb.beginTx()) {
1005            Index<Node> idx;
1006            if (actor.isGroup()) {
1007                idx = getNodeIndex(NodeTypes.GROUP);
1008            } else {
1009                idx = getNodeIndex(NodeTypes.PERSON);
1010            }
1011            IndexHits<Node> persons = idx.get("uri", uri);
1012            person = persons.getSingle();
1013            if (person == null) {
1014                // does not exist yet
1015                person = graphDb.createNode();
1016                if (actor.isGroup()) {
1017                    person.setProperty("TYPE", NodeTypes.GROUP.name());
1018                } else {
1019                    person.setProperty("TYPE", NodeTypes.PERSON.name());
1020                }
1021                person.setProperty("uri", uri);
1022                idx.add(person, "uri", uri);
1023                if (name != null) {
1024                    person.setProperty("name", name);
1025                }
1026                if (id != null) {
1027                    person.setProperty("id", id);
1028                }
1029            }
1030            tx.success();
1031        }
1032        return person;
1033    }
1034
1035    protected Node getOrCreateTagNode(Tag inTag) {
1036        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
1037        String tagname = inTag.getName();
1038        Node tag;
1039        try (Transaction tx = graphDb.beginTx()) {
1040            IndexHits<Node> tags = idx.get("name", tagname);
1041            tag = tags.getSingle();
1042            if (tag == null) {
1043                // does not exist yet
1044                tag = graphDb.createNode();
1045                tag.setProperty("TYPE", NodeTypes.TAG.name());
1046                tag.setProperty("name", tagname);
1047                idx.add(tag, "name", tagname);
1048
1049                tag.setProperty("id", inTag.getId());
1050                tag.setProperty("uri", inTag.getUri());
1051                idx.add(tag, "uri", inTag.getUri());
1052            }
1053            tx.success();
1054        }
1055        return tag;
1056    }
1057
1058    /**
1059     * Create or update permissions relations for an annotation.
1060     *
1061     * @param annotNode
1062     * @param type
1063     * @param annot
1064     */
1065    protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
1066        Node newActorNode = null;
1067        if (actor != null) {
1068            newActorNode = getOrCreateActorNode(actor);
1069        }
1070        Relationship rel = getRelation(annotNode, type, null);
1071        if (rel != null) {
1072            // relation exists
1073            try (Transaction tx = graphDb.beginTx()) {
1074                Node oldActorNode = rel.getEndNode();
1075                if (!oldActorNode.equals(newActorNode)) {
1076                    // new admin is different
1077                    rel.delete();
1078                    tx.success();
1079                }
1080                if (newActorNode != null) {
1081                    rel = getOrCreateRelation(annotNode, type, newActorNode);
1082                }
1083                tx.success();
1084            }
1085        } else {
1086            // no relation yet
1087            if (newActorNode != null) {
1088                rel = getOrCreateRelation(annotNode, type, newActorNode);
1089            }
1090        }
1091    }
1092
1093    /**
1094     * Unindexes and deletes given Node if it has no relations.
1095     *
1096     * @param node
1097     */
1098    protected void deleteNode(Node node) {
1099        try (Transaction tx = graphDb.beginTx()) {
1100            if (node.hasRelationship()) {
1101                logger.severe("deleteNode: unable to delete: Node still has relations.");
1102            } else {
1103                String ts = (String) node.getProperty("TYPE", null);
1104                try {
1105                    NodeTypes type = NodeTypes.valueOf(ts);
1106                    getNodeIndex(type).remove(node);
1107                } catch (Exception e) {
1108                    logger.severe("deleteNode: unable to get TYPE of node: " + node);
1109                }
1110                node.delete();
1111            }
1112            tx.success();
1113        }
1114    }
1115
1116    /**
1117     * returns the (first) Relationship of RelationTypes type from Node start.
1118     *
1119     * @param start
1120     * @param type
1121     * @param direction
1122     * @return
1123     */
1124    protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
1125        Iterable<Relationship> rels;
1126        try (Transaction tx = graphDb.beginTx()) {
1127            if (direction == null) {
1128                // ignore direction
1129                rels = start.getRelationships(type);
1130            } else {
1131                rels = start.getRelationships(type, direction);
1132            }
1133            tx.success();
1134            for (Relationship rel : rels) {
1135                // just the first one
1136                return rel;
1137            }
1138        }
1139        return null;
1140    }
1141
1142    /**
1143     * Erzeuge eine urn aus der aktuellen Zeit in millis
1144     *
1145     * @return
1146     */
1147    private String createRessourceURI(String prefix) {
1148        Calendar cal = Calendar.getInstance();
1149        long time = cal.getTimeInMillis();
1150        return String.format("%s%s%s", ANNOTATION_URI_PREFIX, prefix, time);
1151    }
1152
1153    public List<Annotation> getAnnotationsByTag(String tagUri) {
1154        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1155        Node tag = getTagNodeByUri(tagUri);
1156        if (tag != null) {
1157            try (Transaction tx = graphDb.beginTx()) {
1158                Iterable<Relationship> rels = tag.getRelationships(Direction.INCOMING, RelationTypes.HAS_TAG);
1159                for (Relationship rel : rels) {
1160                    Node node = rel.getStartNode();
1161                    ret.add(createAnnotationFromNode(node));
1162                }
1163                tx.success();
1164            }
1165        }
1166        return ret;
1167    }
1168
1169    public List<Annotation> getAnnotationsByResource(String resourceUri) {
1170        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1171        Node res = getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
1172        if (res != null) {
1173            try (Transaction tx = graphDb.beginTx()) {
1174                Iterable<Relationship> rels = res.getRelationships(Direction.INCOMING, RelationTypes.ANNOTATES);
1175                for (Relationship rel : rels) {
1176                    Node an = rel.getStartNode();
1177                    Node rn = rel.getEndNode();
1178                    if (rn.getProperty("TYPE", "").equals("RESOURCE")) {
1179                        logger.severe("getAnnotationsByResource got ANNOTATES != RESOURCE");
1180                    }
1181                    ret.add(createAnnotationFromNode(an));
1182                }
1183                tx.success();
1184            }
1185        }
1186        return ret;
1187    }
1188
1189}
Note: See TracBrowser for help on using the repository browser.