]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/genericrelation/DependenciesRelation.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / genericrelation / DependenciesRelation.java
index fc32222084c0f7e08509c152a882b893e7bd5b61..771bf8649df554121b0af9e86405d538b89422d0 100644 (file)
@@ -18,12 +18,12 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
 import org.simantics.databoard.Bindings;
 import org.simantics.databoard.util.ObjectUtils;
 import org.simantics.datatypes.literal.GUID;
-import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.ChangeSet;
 import org.simantics.db.ChangeSet.StatementChange;
 import org.simantics.db.MetadataI;
@@ -51,8 +51,8 @@ import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentAdditi
 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentModification;
 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentRemoval;
 import org.simantics.db.layer0.genericrelation.DependencyChanges.LinkChange;
-import org.simantics.db.procedure.AsyncContextMultiProcedure;
-import org.simantics.db.procedure.AsyncContextProcedure;
+import org.simantics.db.procedure.SyncContextMultiProcedure;
+import org.simantics.db.procedure.SyncContextProcedure;
 import org.simantics.db.service.CollectionSupport;
 import org.simantics.db.service.DirectQuerySupport;
 import org.simantics.db.service.GraphChangeListenerSupport;
@@ -78,7 +78,10 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                Pair.make(Dependencies.FIELD_RESOURCE, "Long"),
                Pair.make(Dependencies.FIELD_NAME, "String"),
                Pair.make(Dependencies.FIELD_TYPES, "Text"),
-               Pair.make(Dependencies.FIELD_GUID, "Text")
+               Pair.make(Dependencies.FIELD_GUID, "Text"),
+               Pair.make(Dependencies.FIELD_NAME_SEARCH, "Text"),
+               Pair.make(Dependencies.FIELD_TYPES_SEARCH, "Text"),
+               Pair.make(Dependencies.FIELD_TYPE_RESOURCE, "Text")
        };
 
        final Resource resource;
@@ -95,9 +98,9 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
        class Process {
 
                final ArrayList<Entry> result = new ArrayList<Entry>();
-               final AsyncContextMultiProcedure<Resource, Resource> structure;
-               final AsyncContextProcedure<Entry, String> names;
-               final AsyncContextProcedure<Entry, Resource> type;
+               final SyncContextMultiProcedure<Resource, Resource> structure;
+               final SyncContextProcedure<Entry, String> names;
+               final SyncContextProcedure<Entry, Resource> type;
 
                Process(ReadGraph graph, final Resource resource) throws DatabaseException {
 
@@ -105,38 +108,38 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                        final DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);
                        final CollectionSupport cs = graph.getService(CollectionSupport.class);
 
-                       names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new AsyncContextProcedure<Entry, String>() {
+                       names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new SyncContextProcedure<Entry, String>() {
 
                                @Override
-                               public void execute(AsyncReadGraph graph, Entry entry, String name) {
+                               public void execute(ReadGraph graph, Entry entry, String name) {
                                        entry.name = name;
                                }
 
                                @Override
-                               public void exception(AsyncReadGraph graph, Throwable throwable) {
+                               public void exception(ReadGraph graph, Throwable throwable) {
                                        LOGGER.error("Could not compile possible related value for resource {}", resource, throwable);
                                }
 
                        });
 
-                       type = new AsyncContextProcedure<Entry, Resource>() {
+                       type = new SyncContextProcedure<Entry, Resource>() {
 
                                @Override
-                               public void execute(AsyncReadGraph graph, Entry entry, Resource type) {
+                               public void execute(ReadGraph graph, Entry entry, Resource type) {
                                        entry.principalType = type;
                                }
 
                                @Override
-                               public void exception(AsyncReadGraph graph, Throwable throwable) {
+                               public void exception(ReadGraph graph, Throwable throwable) {
                                        LOGGER.error("Could not find type for resource {}", resource, throwable);
                                }
 
                        };
 
-                       structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new AsyncContextMultiProcedure<Resource, Resource>() {
+                       structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new SyncContextMultiProcedure<Resource, Resource>() {
 
                                @Override
-                               public void execute(AsyncReadGraph graph, Resource parent, Resource child) {
+                               public void execute(ReadGraph graph, Resource parent, Resource child) {
                                        // WORKAROUND: don't browse virtual child resources
                                        if(!child.isPersistent()) return;
                                        Entry entry = new Entry(parent, child, "", "", "");
@@ -147,11 +150,11 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                                }
 
                                @Override
-                               public void finished(AsyncReadGraph graph, Resource parent) {
+                               public void finished(ReadGraph graph, Resource parent) {
                                }
 
                                @Override
-                               public void exception(AsyncReadGraph graph, Throwable throwable) {
+                               public void exception(ReadGraph graph, Throwable throwable) {
                                    if (throwable instanceof NoSingleResultException) {
                                        // Ignore
                                        if (LOGGER.isDebugEnabled())
@@ -163,6 +166,8 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
 
                        });
 
+                       result.add(new Entry(graph, resource));
+
                        graph.syncRequest(new ReadRequest() {
 
                                @Override
@@ -172,30 +177,31 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
 
                        });
 
-            Map<Resource, String> typeStrings = cs.createMap(String.class);
+                       Map<Resource, Pair<String, String>> typeStrings = cs.createMap(String.class);
                        for(Entry e : result) {
                                if(e.principalType != null) {
-                                   String typeString = typeStrings.get(e.principalType);
-                                   if(typeString == null) {
-                                       typeString = graph.syncRequest(new SuperTypeString(e.principalType));
-                                       if (typeString.isEmpty()) {
-                                           LOGGER.error("No name for type", new DatabaseException("No name for type " + NameUtils.getURIOrSafeNameInternal(graph, e.resource) + " (" + e.resource + ")"));
-                                       }
-                                       typeStrings.put(e.principalType, typeString);
-                                   }
-                                   e.types = typeString;
+                                       Pair<String, String> typeString = typeStrings.get(e.principalType);
+                                       if(typeString == null) {
+                                               String superTypeString = graph.syncRequest(new SuperTypeString(e.principalType));
+                                               if (superTypeString.isEmpty()) {
+                                                       LOGGER.error("No name for type", new DatabaseException("No name for type " + NameUtils.getURIOrSafeNameInternal(graph, e.resource) + " (" + e.resource + ")"));
+                                               }
+                                               String superTypeIds = IndexQueries.toResourceIdString(e.principalType, graph.getSupertypes(e.principalType));
+                                               typeString = Pair.make(superTypeString, superTypeIds);
+                                               typeStrings.put(e.principalType, typeString);
+                                       }
+                                       e.types = typeString.first;
+                                       e.typeId = typeString.second;
                                } else {
-                                   e.types = graph.syncRequest(new TypeString(L0, graph.getTypes(e.resource)));
+                                       Set<Resource> typeSet = graph.getTypes(e.resource);
+                                       e.types = graph.syncRequest(new TypeString(L0, typeSet));
+                                       e.typeId = IndexQueries.toResourceIdString(typeSet);
                                }
-                               GUID id = graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING);
-                               if(id != null)
-                                       e.id = id.indexString();
-                               else 
-                                       e.id = "";
+                               e.id = IndexQueries.idFromGUID( graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING) );
                        }
 
                        //SessionGarbageCollection.gc(null, graph.getSession(), false, null);
-                       
+
                }
 
        }
@@ -220,20 +226,21 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
 
                                long time = System.nanoTime();
 
-                SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+                               SerialisationSupport ss = graph.getService(SerialisationSupport.class);
 
                                Resource subject = ss.getResource(subjectId); 
-                               
+
                                Collection<Entry> entries = find(graph, subject);
 
                                long time2 = System.nanoTime();
 
                                if (PROFILE)
-                                       System.out.println("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");
+                                       LOGGER.info("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");
 
-                               ArrayList<Object[]> result = new ArrayList<Object[]>();
+                               ArrayList<Object[]> result = new ArrayList<>();
                                for (Entry entry : entries) {
-                                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });
+                                       if(entry.name == null) continue;
+                                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id, entry.name, entry.types, entry.typeId });
                                }
                                return result;
 
@@ -249,21 +256,21 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
 
        @Override
        public List<Map<String, Object>> query(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
-               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
                IndexedRelations indexer = session.getService(IndexedRelations.class);
                return indexer.query(null, search, session, resource, (Resource)constants[0], maxResultCount);
        }
        
        @Override
        public List<Resource> queryResources(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
-               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
                IndexedRelations indexer = session.getService(IndexedRelations.class);
                return indexer.queryResources(null, search, session, resource, (Resource)constants[0], maxResultCount);
        }
 
        @Override
        public List<Map<String, Object>> list(RequestProcessor session, String bindingPattern, Object[] constants, int maxResultCount) {
-               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+               if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
                IndexedRelations indexer = session.getService(IndexedRelations.class);
                return indexer.query(null, null, session, resource, (Resource)constants[0], maxResultCount);
        }
@@ -290,7 +297,10 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                                if (modifiedComponent == null
                                                || modifiedComponent.getPredicate().equals(changeInformation))
                                        continue;
-                               //System.err.println("+comp modi " + NameUtils.getSafeName(graph, renamedComponent, true));
+                               if (DEBUG) {
+                                   LOGGER.info("+comp modi " + NameUtils.getSafeName(graph, modifiedComponent.getObject(), true));
+                                   LOGGER.info("    +value " + NameUtils.getSafeName(graph, value, true));
+                               }
                                w.addComponentModification(modifiedComponent.getObject());
                        }
                        for (Resource value : parameter.changedResources()) {
@@ -299,7 +309,8 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                                        w.addComponentModification(value);
                        }
                        for (StatementChange change : parameter.changedStatements()) {
-                               //System.err.println("-stm " + NameUtils.getSafeName(graph, change.getSubject(), true) + " " + NameUtils.getSafeName(graph, change.getPredicate(), true) + " " + NameUtils.getSafeName(graph, change.getObject(), true));
+                           if (DEBUG)
+                               LOGGER.info("-stm " + NameUtils.getSafeName(graph, change.getSubject(), true) + " " + NameUtils.getSafeName(graph, change.getPredicate(), true) + " " + NameUtils.getSafeName(graph, change.getObject(), true));
                                Resource subject = change.getSubject();
                                Resource predicate = change.getPredicate();
                                Resource object = change.getObject();
@@ -312,7 +323,8 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                                } else if (predicate.equals(l0.IsLinkedTo)) {
                                        w.addLinkChange(subject);
                                } else /*if (graph.isSubrelationOf(predicate, l0.DependsOn))*/ {
-                                       //System.err.println("-modi " + NameUtils.getSafeName(graph, subject, true));
+                                   if (DEBUG)
+                                       LOGGER.info("-modi " + NameUtils.getSafeName(graph, subject, true));
                                        w.addComponentModification(subject);
                                } 
                        }
@@ -351,201 +363,204 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
        @Override
        public synchronized void trackAndIndex(RequestProcessor processor, Resource model__) {
 
-           if(trackers == 0) {
+               if(trackers == 0) {
 
-               if(listener != null) throw new IllegalStateException("Dependency tracking was active");
-
-               listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {
-
-                   @Override
-                   public boolean preEventRequest() {
-                       return !Indexing.isDependenciesIndexingDisabled();
-                   }
-
-                   @Override
-                   public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {
-
-                       TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");
-
-                       if(DEBUG)
-                           System.err.println("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());
-
-                       WriteGraph w = (WriteGraph)graph;
-                       if(!event.isEmpty())
-                               w.addMetadata(event);
-
-                       final Session session = graph.getSession();
-                       final IndexedRelations indexer = session.getService(IndexedRelations.class);
-                       Layer0 L0 = Layer0.getInstance(graph);
-                       SerialisationSupport ss = graph.getService(SerialisationSupport.class);
-
-                       for(Map.Entry<Resource, Change[]>  modelEntry : event.get().entrySet()) {
-
-                           final Resource model = modelEntry.getKey();
-                           final Change[] changes = modelEntry.getValue();
-
-                           boolean linkChange = false;
-
-                           Collection<Object[]> _additions = Collections.emptyList();
-                           Collection<Object> _removals = Collections.emptyList();
-                           Collection<Object> _replacementKeys = Collections.emptyList();
-                           Collection<Object[]> _replacementObjects = Collections.emptyList();
-                           Collection<Pair<String, String>> _typeChanges = Collections.emptyList();
-
-                           if(DEBUG) System.out.println("MODEL: " + NameUtils.getSafeLabel(graph, model));
-                           //                final Change[] changes = event.get(model);
-                           if(DEBUG) System.out.println("  CHANGES: " + Arrays.toString(changes));
-                           if (changes != null) {
-                               _additions = new ArrayList<Object[]>();
-                               _removals = new ArrayList<Object>();
-                               _replacementKeys = new ArrayList<Object>();
-                               _replacementObjects = new ArrayList<Object[]>();
-                               _typeChanges = new HashSet<Pair<String, String>>();
-
-                               for (Change _entry : changes) {
-                                   if (_entry instanceof ComponentAddition) {
-                                       ComponentAddition entry = (ComponentAddition)_entry;
-                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
-                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
-                                       final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));
-                                       if (name != null && types != null) {
-                                               if(!entry.isValid(graph)) continue;
-                                           Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);
-                                           if (parent != null) {
-                                               _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });
-                                           } else {
-                                                   //System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);
-                                           }
-                                       } else {
-                                           //System.err.println("resource " + entry.component + ": " + name + " " + types);
-                                       }
-                                   } else if(_entry instanceof ComponentModification) {
-                                       ComponentModification entry = (ComponentModification)_entry;
-                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
-                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
-                                       if(graph.isInstanceOf(entry.component, L0.Type)) {
-                                           SerialisationSupport support = session.getService(SerialisationSupport.class);
-                                           _typeChanges.add(new Pair<String, String>(name, String.valueOf(support.getRandomAccessId((Resource) entry.component))));
-                                       } else {
-                                           final String types = graph.syncRequest(new TypeString(L0, graph.getTypes(entry.component)));
-                                           if (name != null && types != null) {
-                                               Resource part = graph.getPossibleObject(entry.component, L0.PartOf);
-                                               if(part != null) {
-                                                   _replacementKeys.add(ss.getRandomAccessId(entry.component));
-                                                   _replacementObjects.add(new Object[] { ss.getRandomAccessId(part), 
-                                                           ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });
-                                               }
-                                           }
-                                       }
-                                   } else if (_entry instanceof ComponentRemoval) {
-                                       ComponentRemoval entry = (ComponentRemoval)_entry;
-                                       if(!entry.isValid(graph)) continue;
-                                       _removals.add(ss.getRandomAccessId(((ComponentRemoval)_entry).component));
-                                   } else if (_entry instanceof LinkChange) {
-                                       linkChange = true;
-                                   }
-                               }
-                           }
-
-                           final boolean reset = linkChange || event.hasUnresolved;
-                           //System.err.println("dependencies(" + NameUtils.getSafeLabel(graph, model) + "): reset=" + reset + " linkChange=" + linkChange + " unresolved=" + event.hasUnresolved );
-
-                           if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {
-
-                               TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");
-
-                               final Collection<Object[]> additions = _additions;
-                               final Collection<Object> removals = _removals;
-                               final Collection<Object> replacementKeys = _replacementKeys;
-                               final Collection<Object[]> replacementObjects = _replacementObjects; 
-                               final boolean typeNameChanges = typeNameChanges(graph, indexer, model, _typeChanges);
-
-                            final UUID pending = Indexing.makeIndexPending();
-
-                            {
-                                {
-                                       try {
-                                           boolean didChange = false;
-                                           // Unresolved and linkChanges are not relevant any more
-                                           boolean doReset = typeNameChanges;
-
-                                           if (doReset) {
-
-                                            if(DEBUG) {
-                                                System.err.println("resetIndex " + reset + " " + typeNameChanges);
-                                            }
-
-                                               indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);
-                                               didChange = true;
-
-                                           } else {
-
-                                               if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {
-                                                   if(DEBUG) {
-                                                       System.out.println(replacementKeys.size() + " index replacements: " + replacementKeys);
-                                                   }
-                                                   didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);
-                                               }
-                                               if (!removals.isEmpty()) {
-                                                   if(DEBUG) {
-                                                       System.out.println(removals.size() + " index removals: " + removals);
-                                                   }
-                                                   indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);
-                                                   didChange = true;
-                                               }
-                                               if (!additions.isEmpty()) {
-                                                   if(DEBUG) {
-                                                       for(Object[] os : additions) System.err.println("Adding to index " + model + ": " + Arrays.toString(os));
-                                                   }
-                                                   //System.out.println(additions.size() + " index insertions");
-                                                   indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);
-                                                   didChange = true;
-                                               }
-
-                                           }
-
-                                           if (didChange)
-                                               // TODO: because this data is ran with
-                                               // ThreadUtils.getBlockingWorkExecutor()
-                                               // fireListeners needs to use peekService,
-                                               // not getService since there is no
-                                               // guarantee that the session isn't being
-                                               // disposed while this method is executing.
-                                               fireListeners(graph, model);
-
-                                       } catch (Throwable t) {
-                                           // Just to know if something unexpected happens here.
-                                           LOGGER.error("Dependencies index update failed for model "
-                                                + model + " and relation " + resource + ".", t);
-
-                                           // NOTE: Last resort: failure to update index
-                                           // properly results in removal of the whole index.
-                                           // This is the only thing that can be done
-                                           // at this point to ensure that the index will
-                                           // return correct results in the future, through
-                                           // complete reinitialization. 
-                                           //indexer.removeAll(null, session, DependenciesRelation.this, resource, model);
-                                       } finally {
-                                           Indexing.releaseIndexPending(pending);
-                                           Indexing.clearCaches(model);
-                                       }
-                                }
-                            }
-
-                               TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");
-                           }
-                       }
-
-                   }
-
-               };
+                       if(listener != null) throw new IllegalStateException("Dependency tracking was active");
 
-               GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
-               changeSupport.addMetadataListener(listener);
+                       listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {
 
-           }
+                               @Override
+                               public boolean preEventRequest() {
+                                       return !Indexing.isDependenciesIndexingDisabled();
+                               }
+
+                               @Override
+                               public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {
+
+                                       TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");
+
+                                       if(DEBUG)
+                                               LOGGER.info("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());
+
+                                       WriteGraph w = (WriteGraph)graph;
+                                       if(!event.isEmpty())
+                                               w.addMetadata(event);
+
+                                       final Session session = graph.getSession();
+                                       final IndexedRelations indexer = session.getService(IndexedRelations.class);
+                                       Layer0 L0 = Layer0.getInstance(graph);
+                                       SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+
+                                       for(Map.Entry<Resource, Change[]>  modelEntry : event.get().entrySet()) {
+
+                                               final Resource model = modelEntry.getKey();
+                                               final Change[] changes = modelEntry.getValue();
+
+                                               boolean linkChange = false;
+
+                                               Collection<Object[]> _additions = Collections.emptyList();
+                                               Collection<Object> _removals = Collections.emptyList();
+                                               Collection<Object> _replacementKeys = Collections.emptyList();
+                                               Collection<Object[]> _replacementObjects = Collections.emptyList();
+                                               Collection<Pair<String, String>> _typeChanges = Collections.emptyList();
+
+                                               if(DEBUG) LOGGER.info("MODEL: " + NameUtils.getSafeLabel(graph, model));
+                                               if (changes != null) {
+                                                       if (DEBUG) {
+                                                               LOGGER.info("  CHANGE COUNT: " + changes.length);
+                                                               for (Change c : changes)
+                                                                       LOGGER.info("    CHANGE: " + c.toString(graph));
+                                                       }
+                                                       _additions = new ArrayList<>();
+                                                       _removals = new ArrayList<>();
+                                                       _replacementKeys = new ArrayList<>();
+                                                       _replacementObjects = new ArrayList<>();
+                                                       _typeChanges = new HashSet<>();
+
+                                                       for (Change _entry : changes) {
+                                                               if (_entry instanceof ComponentAddition) {
+                                                                       ComponentAddition entry = (ComponentAddition)_entry;
+                                                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
+                                                                       Set<Resource> typeSet = graph.getTypes(entry.component);
+                                                                       if (name != null && typeSet != null) {
+                                                                               if (!entry.isValid(graph))
+                                                                                       continue;
+                                                                               Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);
+                                                                               if (parent != null) {
+                                                                                       final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
+                                                                                       final String types = graph.syncRequest(new TypeString(L0, typeSet));
+                                                                                       final String typeIds = IndexQueries.toResourceIdString(typeSet);
+                                                                                       _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, IndexQueries.idFromGUID(id), name, types, typeIds});
+                                                                               } else {
+                                                                                       //LOGGER.info("resource " + entry.component + ": no parent for entry " + name + " " + types);
+                                                                               }
+                                                                       } else {
+                                                                               //LOGGER.info("resource " + entry.component + ": " + name + " " + types);
+                                                                       }
+                                                               } else if(_entry instanceof ComponentModification) {
+                                                                       ComponentModification entry = (ComponentModification)_entry;
+                                                                       final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
+                                                                       if(graph.isInstanceOf(entry.component, L0.Type)) {
+                                                                               SerialisationSupport support = session.getService(SerialisationSupport.class);
+                                                                               _typeChanges.add(new Pair<String, String>(name, String.valueOf(support.getRandomAccessId((Resource) entry.component))));
+                                                                       } else {
+                                                                               Set<Resource> typeSet = graph.getTypes(entry.component);
+                                                                               if (name != null && !typeSet.isEmpty()) {
+                                                                                       Resource part = graph.getPossibleObject(entry.component, L0.PartOf);
+                                                                                       if(part != null) {
+                                                                                               final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
+                                                                                               final String types = graph.syncRequest(new TypeString(L0, typeSet));
+                                                                                               final String typeIds = IndexQueries.toResourceIdString(typeSet);
+                                                                                               _replacementKeys.add(ss.getRandomAccessId(entry.component));
+                                                                                               _replacementObjects.add(new Object[] { ss.getRandomAccessId(part), 
+                                                                                                               ss.getRandomAccessId(entry.component), name, types, IndexQueries.idFromGUID(id), name, types, typeIds});
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               } else if (_entry instanceof ComponentRemoval) {
+                                                                       ComponentRemoval entry = (ComponentRemoval)_entry;
+                                                                       if(!entry.isValid(graph)) continue;
+                                                                       _removals.add(ss.getRandomAccessId(((ComponentRemoval)_entry).component));
+                                                               } else if (_entry instanceof LinkChange) {
+                                                                       linkChange = true;
+                                                               }
+                                                       }
+                                               }
+
+                                               final boolean reset = linkChange || event.hasUnresolved;
+                                               //LOGGER.info("dependencies(" + NameUtils.getSafeLabel(graph, model) + "): reset=" + reset + " linkChange=" + linkChange + " unresolved=" + event.hasUnresolved );
+
+                                               if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {
+
+                                                       TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");
+
+                                                       final Collection<Object[]> additions = _additions;
+                                                       final Collection<Object> removals = _removals;
+                                                       final Collection<Object> replacementKeys = _replacementKeys;
+                                                       final Collection<Object[]> replacementObjects = _replacementObjects; 
+                                                       final boolean typeNameChanges = typeNameChanges(graph, indexer, model, _typeChanges);
+
+                                                       final UUID pending = Indexing.makeIndexPending();
+                                                       try {
+                                                               boolean didChange = false;
+                                                               // Unresolved and linkChanges are not relevant any more
+                                                               boolean doReset = typeNameChanges;
+
+                                                               if (doReset) {
+
+                                                                       if(DEBUG) {
+                                                                               LOGGER.info("resetIndex " + reset + " " + typeNameChanges);
+                                                                       }
+
+                                                                       indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);
+                                                                       didChange = true;
+
+                                                               } else {
+
+                                                                       if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {
+                                                                               if(DEBUG) {
+                                                                                       LOGGER.info(replacementKeys.size() + " index replacements: " + replacementKeys);
+                                                                               }
+                                                                               didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);
+                                                                       }
+                                                                       if (!removals.isEmpty()) {
+                                                                               if(DEBUG) {
+                                                                                       LOGGER.info(removals.size() + " index removals: " + removals);
+                                                                               }
+                                                                               indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);
+                                                                               didChange = true;
+                                                                       }
+                                                                       if (!additions.isEmpty()) {
+                                                                               if(DEBUG) {
+                                                                                       for(Object[] os : additions) LOGGER.info("Adding to index " + model + ": " + Arrays.toString(os));
+                                                                               }
+                                                                               //LOGGER.info(additions.size() + " index insertions");
+                                                                               indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);
+                                                                               didChange = true;
+                                                                       }
+
+                                                               }
+
+                                                               if (didChange)
+                                                                       // TODO: because this data is ran with
+                                                                       // ThreadUtils.getBlockingWorkExecutor()
+                                                                       // fireListeners needs to use peekService,
+                                                                       // not getService since there is no
+                                                                       // guarantee that the session isn't being
+                                                                       // disposed while this method is executing.
+                                                                       fireListeners(graph, model);
+
+                                                       } catch (Throwable t) {
+                                                               // Just to know if something unexpected happens here.
+                                                               LOGGER.error("Dependencies index update failed for model "
+                                                                               + model + " and relation " + resource + ".", t);
+
+                                                               // NOTE: Last resort: failure to update index
+                                                               // properly results in removal of the whole index.
+                                                               // This is the only thing that can be done
+                                                               // at this point to ensure that the index will
+                                                               // return correct results in the future, through
+                                                               // complete reinitialization. 
+                                                               //indexer.removeAll(null, session, DependenciesRelation.this, resource, model);
+                                                       } finally {
+                                                               Indexing.releaseIndexPending(pending);
+                                                               Indexing.clearCaches(model);
+                                                       }
+
+                                                       TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");
+                                               }
+                                       }
+
+                               }
+
+                       };
+
+                       GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
+                       changeSupport.addMetadataListener(listener);
 
-           trackers++;
+               }
+
+               trackers++;
 
        }
 
@@ -557,7 +572,7 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
 
                for (Pair<String, String> nr : typeChanges) {
                        String query = Dependencies.FIELD_RESOURCE + ":[" + nr.second + " TO " + nr.second + "]";
-                       //System.out.println("query: " + query);
+                       //LOGGER.info("query: " + query);
                        List<Map<String, Object>> results = indexer.query(null, query, graph, resource, model, Integer.MAX_VALUE);
                        if (results.size() != 1) {
                                return true;
@@ -567,7 +582,7 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
                                        return true;
                                }
                        }
-//                     System.err.println("Type " + nr.first + " was unchanged.");
+//                     LOGGER.info("Type " + nr.first + " was unchanged.");
                }
                return false;
        }
@@ -593,7 +608,7 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
        @Override
        public void reset(RequestProcessor processor, Resource input) {
                if (DEBUG) {
-                       System.out.println("DependenciesRelation.reset: " + input);
+                       LOGGER.info("DependenciesRelation.reset: " + input);
                        new Exception("DependenciesRelation.reset(" + listener + ")").printStackTrace(System.out);
                }
                DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
@@ -608,22 +623,22 @@ public class DependenciesRelation extends UnsupportedRelation implements Generic
        }
 
        public static void addSubtree(ReadGraph graph, Resource indexRoot, Resource subtreeRoot) throws DatabaseException {
-               
+
                DependenciesRelation dr = new DependenciesRelation(graph, indexRoot);
-        SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+               SerialisationSupport ss = graph.getService(SerialisationSupport.class);
 
-        ArrayList<Entry> entries = dr.find(graph, subtreeRoot);
-        entries.add(new Entry(graph, subtreeRoot));
+               ArrayList<Entry> entries = dr.find(graph, subtreeRoot);
+               entries.add(new Entry(graph, subtreeRoot));
 
                ArrayList<Object[]> result = new ArrayList<Object[]>(entries.size());
                for (Entry entry : entries) {
-                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });
+                       result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id, entry.name, entry.types, entry.typeId });
                }
 
                Layer0X L0X = Layer0X.getInstance(graph);
-        IndexedRelations indexer = graph.getService(IndexedRelations.class);
-        indexer.insert(null, graph, dr, L0X.DependenciesRelation, indexRoot, result);
-               
+               IndexedRelations indexer = graph.getService(IndexedRelations.class);
+               indexer.insert(null, graph, dr, L0X.DependenciesRelation, indexRoot, result);
+
        }
-       
+
 }