]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/genericrelation/DependenciesRelation.java
Added new field TypeId to dependency index for exact type searching
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / genericrelation / DependenciesRelation.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.layer0.genericrelation;
13
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.UUID;
23
24 import org.simantics.databoard.Bindings;
25 import org.simantics.databoard.util.ObjectUtils;
26 import org.simantics.datatypes.literal.GUID;
27 import org.simantics.db.AsyncReadGraph;
28 import org.simantics.db.ChangeSet;
29 import org.simantics.db.ChangeSet.StatementChange;
30 import org.simantics.db.MetadataI;
31 import org.simantics.db.ReadGraph;
32 import org.simantics.db.RequestProcessor;
33 import org.simantics.db.Resource;
34 import org.simantics.db.Session;
35 import org.simantics.db.Statement;
36 import org.simantics.db.WriteGraph;
37 import org.simantics.db.common.Indexing;
38 import org.simantics.db.common.changeset.GenericChangeListener;
39 import org.simantics.db.common.request.IndexRoot;
40 import org.simantics.db.common.request.ReadRequest;
41 import org.simantics.db.common.request.SuperTypeString;
42 import org.simantics.db.common.request.TypeString;
43 import org.simantics.db.common.request.UnaryRead;
44 import org.simantics.db.common.utils.NameUtils;
45 import org.simantics.db.event.ChangeListener;
46 import org.simantics.db.exception.DatabaseException;
47 import org.simantics.db.exception.NoSingleResultException;
48 import org.simantics.db.layer0.adapter.GenericRelation;
49 import org.simantics.db.layer0.adapter.GenericRelationIndex;
50 import org.simantics.db.layer0.genericrelation.DependencyChanges.Change;
51 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentAddition;
52 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentModification;
53 import org.simantics.db.layer0.genericrelation.DependencyChanges.ComponentRemoval;
54 import org.simantics.db.layer0.genericrelation.DependencyChanges.LinkChange;
55 import org.simantics.db.procedure.AsyncContextMultiProcedure;
56 import org.simantics.db.procedure.AsyncContextProcedure;
57 import org.simantics.db.service.CollectionSupport;
58 import org.simantics.db.service.DirectQuerySupport;
59 import org.simantics.db.service.GraphChangeListenerSupport;
60 import org.simantics.db.service.ManagementSupport;
61 import org.simantics.db.service.SerialisationSupport;
62 import org.simantics.layer0.Layer0;
63 import org.simantics.operation.Layer0X;
64 import org.simantics.utils.datastructures.Pair;
65 import org.simantics.utils.logging.TimeLogger;
66 import org.slf4j.LoggerFactory;
67
68 public class DependenciesRelation extends UnsupportedRelation implements GenericRelationIndex {
69
70     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DependenciesRelation.class);
71         private static final boolean DEBUG = false;
72         static final boolean DEBUG_LISTENERS = false;
73         private static final boolean PROFILE = false;
74
75         @SuppressWarnings("unchecked")
76         private final static Pair<String, String>[] fields = new Pair[] {
77                 Pair.make(Dependencies.FIELD_MODEL, "Long"),
78                 Pair.make(Dependencies.FIELD_PARENT, "Long"),
79                 Pair.make(Dependencies.FIELD_RESOURCE, "Long"),
80                 Pair.make(Dependencies.FIELD_NAME, "String"),
81                 Pair.make(Dependencies.FIELD_TYPES, "Text"),
82                 Pair.make(Dependencies.FIELD_GUID, "Text"),
83                 Pair.make(Dependencies.FIELD_NAME_SEARCH, "Text"),
84                 Pair.make(Dependencies.FIELD_TYPES_SEARCH, "Text"),
85                 Pair.make(Dependencies.FIELD_TYPE_RESOURCE, "Text")
86         };
87
88         final Resource resource;
89
90         public DependenciesRelation(ReadGraph graph, Resource resource) {
91                 this.resource = resource;
92                 synchronized(this) {
93                         Session session = graph.getSession();
94                         DependenciesListenerStore store = session.peekService(DependenciesListenerStore.class);
95                         if(store == null) session.registerService(DependenciesListenerStore.class, new DependenciesListenerStore());
96                 }
97         }
98
99         class Process {
100
101                 final ArrayList<Entry> result = new ArrayList<Entry>();
102                 final AsyncContextMultiProcedure<Resource, Resource> structure;
103                 final AsyncContextProcedure<Entry, String> names;
104                 final AsyncContextProcedure<Entry, Resource> type;
105
106                 Process(ReadGraph graph, final Resource resource) throws DatabaseException {
107
108                         final Layer0 L0 = Layer0.getInstance(graph);
109                         final DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);
110                         final CollectionSupport cs = graph.getService(CollectionSupport.class);
111
112                         names = dqs.compilePossibleRelatedValue(graph, L0.HasName, new AsyncContextProcedure<Entry, String>() {
113
114                                 @Override
115                                 public void execute(AsyncReadGraph graph, Entry entry, String name) {
116                                         entry.name = name;
117                                 }
118
119                                 @Override
120                                 public void exception(AsyncReadGraph graph, Throwable throwable) {
121                                         LOGGER.error("Could not compile possible related value for resource {}", resource, throwable);
122                                 }
123
124                         });
125
126                         type = new AsyncContextProcedure<Entry, Resource>() {
127
128                                 @Override
129                                 public void execute(AsyncReadGraph graph, Entry entry, Resource type) {
130                                         entry.principalType = type;
131                                 }
132
133                                 @Override
134                                 public void exception(AsyncReadGraph graph, Throwable throwable) {
135                                         LOGGER.error("Could not find type for resource {}", resource, throwable);
136                                 }
137
138                         };
139
140                         structure = dqs.compileForEachObject(graph, L0.ConsistsOf, new AsyncContextMultiProcedure<Resource, Resource>() {
141
142                                 @Override
143                                 public void execute(AsyncReadGraph graph, Resource parent, Resource child) {
144                                         // WORKAROUND: don't browse virtual child resources
145                                         if(!child.isPersistent()) return;
146                                         Entry entry = new Entry(parent, child, "", "", "");
147                                         result.add(entry);
148                                         dqs.forEachObjectCompiled(graph, child, child, structure);
149                                         dqs.forPossibleRelatedValueCompiled(graph, child, entry, names);
150                                         dqs.forPossibleDirectType(graph, child, entry, type);
151                                 }
152
153                                 @Override
154                                 public void finished(AsyncReadGraph graph, Resource parent) {
155                                 }
156
157                                 @Override
158                                 public void exception(AsyncReadGraph graph, Throwable throwable) {
159                                     if (throwable instanceof NoSingleResultException) {
160                                         // Ignore
161                                         if (LOGGER.isDebugEnabled())
162                                             LOGGER.debug("Could not compile for resource {}", resource, throwable);
163                                     } else {
164                                         LOGGER.error("Could not compile for resource {}", resource, throwable);
165                                     }
166                                 }
167
168                         });
169
170                         graph.syncRequest(new ReadRequest() {
171
172                                 @Override
173                                 public void run(ReadGraph graph) throws DatabaseException {
174                                         dqs.forEachObjectCompiled(graph, resource, resource, structure);
175                                 }
176
177                         });
178
179                         Map<Resource, Pair<String, String>> typeStrings = cs.createMap(String.class);
180                         for(Entry e : result) {
181                                 if(e.principalType != null) {
182                                         Pair<String, String> typeString = typeStrings.get(e.principalType);
183                                         if(typeString == null) {
184                                                 String superTypeString = graph.syncRequest(new SuperTypeString(e.principalType));
185                                                 if (superTypeString.isEmpty()) {
186                                                         LOGGER.error("No name for type", new DatabaseException("No name for type " + NameUtils.getURIOrSafeNameInternal(graph, e.resource) + " (" + e.resource + ")"));
187                                                 }
188                                                 String superTypeIds = IndexQueries.toResourceIdString(e.principalType, graph.getSupertypes(e.principalType));
189                                                 typeString = Pair.make(superTypeString, superTypeIds);
190                                                 typeStrings.put(e.principalType, typeString);
191                                         }
192                                         e.types = typeString.first;
193                                         e.typeId = typeString.second;
194                                 } else {
195                                         Set<Resource> typeSet = graph.getTypes(e.resource);
196                                         e.types = graph.syncRequest(new TypeString(L0, typeSet));
197                                         e.typeId = IndexQueries.toResourceIdString(typeSet);
198                                 }
199                                 e.id = IndexQueries.idFromGUID( graph.getPossibleRelatedValue(e.resource, L0.identifier, GUID.BINDING) );
200                         }
201
202                         //SessionGarbageCollection.gc(null, graph.getSession(), false, null);
203
204                 }
205
206         }
207
208         public ArrayList<Entry> find(ReadGraph graph, final Resource model) throws DatabaseException {
209                 return new Process(graph, model).result;
210         }
211
212         @Override
213         public GenericRelation select(String bindingPattern, Object[] constants) {
214                 checkSelectionArguments(bindingPattern, constants, new String[] { Dependencies.getBindingPattern() });
215                 final long subjectId = (Long)constants[0];
216                 return new UnsupportedRelation() {
217
218                         @Override
219                         public boolean isRealizable() {
220                                 return true;
221                         }
222
223                         @Override
224                         final public List<Object[]> realize(ReadGraph graph) throws DatabaseException {
225
226                                 long time = System.nanoTime();
227
228                                 SerialisationSupport ss = graph.getService(SerialisationSupport.class);
229
230                                 Resource subject = ss.getResource(subjectId); 
231
232                                 Collection<Entry> entries = find(graph, subject);
233
234                                 long time2 = System.nanoTime();
235
236                                 if (PROFILE)
237                                         System.out.println("Found " + entries.size() + " dependencies in " + 1e-6 * (time2 - time) + "ms for " + graph.getPossibleURI(subject) + ".");
238
239                                 ArrayList<Object[]> result = new ArrayList<>();
240                                 for (Entry entry : entries) {
241                                         result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id, entry.name, entry.types, entry.typeId });
242                                 }
243                                 return result;
244
245                         }
246
247                 };
248         }
249
250         @Override
251         public Pair<String, String>[] getFields() {
252                 return fields;
253         }
254
255         @Override
256         public List<Map<String, Object>> query(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
257                 if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
258                 IndexedRelations indexer = session.getService(IndexedRelations.class);
259                 return indexer.query(null, search, session, resource, (Resource)constants[0], maxResultCount);
260         }
261         
262         @Override
263         public List<Resource> queryResources(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
264                 if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
265                 IndexedRelations indexer = session.getService(IndexedRelations.class);
266                 return indexer.queryResources(null, search, session, resource, (Resource)constants[0], maxResultCount);
267         }
268
269         @Override
270         public List<Map<String, Object>> list(RequestProcessor session, String bindingPattern, Object[] constants, int maxResultCount) {
271                 if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
272                 IndexedRelations indexer = session.getService(IndexedRelations.class);
273                 return indexer.query(null, null, session, resource, (Resource)constants[0], maxResultCount);
274         }
275
276         public static class DependencyChangesRequest extends UnaryRead<ChangeSet, DependencyChanges> {
277
278                 @SuppressWarnings("unused")
279                 final private static boolean LOG = false;
280
281                 public DependencyChangesRequest(ChangeSet parameter) {
282                         super(parameter);
283                 }
284
285                 @Override
286                 public DependencyChanges perform(ReadGraph graph) throws DatabaseException {
287
288                         DependencyChangesWriter w = new DependencyChangesWriter(graph);
289                         Layer0 l0 = w.l0;
290                         Resource changeInformation = graph.getPossibleResource("http://www.simantics.org/Modeling-1.2/changeInformation/Inverse");
291
292                         for (Resource value : parameter.changedValues()) {
293                                 if(!value.isPersistent()) continue;
294                                 Statement modifiedComponent = graph.getPossibleStatement(value, l0.PropertyOf);
295                                 if (modifiedComponent == null
296                                                 || modifiedComponent.getPredicate().equals(changeInformation))
297                                         continue;
298                                 //System.err.println("+comp modi " + NameUtils.getSafeName(graph, modifiedComponent.getObject(), true));
299                                 w.addComponentModification(modifiedComponent.getObject());
300                         }
301                         for (Resource value : parameter.changedResources()) {
302                                 // No more info => need to check further
303                                 if(!graph.isImmutable(value))
304                                         w.addComponentModification(value);
305                         }
306                         for (StatementChange change : parameter.changedStatements()) {
307                                 //System.err.println("-stm " + NameUtils.getSafeName(graph, change.getSubject(), true) + " " + NameUtils.getSafeName(graph, change.getPredicate(), true) + " " + NameUtils.getSafeName(graph, change.getObject(), true));
308                                 Resource subject = change.getSubject();
309                                 Resource predicate = change.getPredicate();
310                                 Resource object = change.getObject();
311                                 if(!object.isPersistent()) continue;
312                                 if (predicate.equals(l0.ConsistsOf)) {
313                                         if (change.isClaim())
314                                                 w.addComponentAddition(subject, object);
315                                         else 
316                                                 w.addComponentRemoval(subject, object);
317                                 } else if (predicate.equals(l0.IsLinkedTo)) {
318                                         w.addLinkChange(subject);
319                                 } else /*if (graph.isSubrelationOf(predicate, l0.DependsOn))*/ {
320                                         //System.err.println("-modi " + NameUtils.getSafeName(graph, subject, true));
321                                         w.addComponentModification(subject);
322                                 } 
323                         }
324                         return w.getResult();
325                 }
326
327         };
328
329         private static int trackers = 0;
330         
331         private static ChangeListener listener;
332
333         public static void assertFinishedTracking() {
334             if(trackers != 0) throw new IllegalStateException("Trackers should be 0 (was " + trackers + ")");
335         }
336         
337         @Override
338         public synchronized void untrack(RequestProcessor processor, final Resource model) {
339
340             trackers--;
341             
342             if(trackers < 0) throw new IllegalStateException("Dependency tracking reference count is broken");
343             
344             if(trackers == 0) {
345                 
346                 if(listener == null) throw new IllegalStateException("Dependency tracking was not active");
347             
348                 GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
349                 changeSupport.removeMetadataListener(listener);
350                 listener = null;
351                         
352             }
353             
354         }
355
356         @Override
357         public synchronized void trackAndIndex(RequestProcessor processor, Resource model__) {
358
359                 if(trackers == 0) {
360
361                         if(listener != null) throw new IllegalStateException("Dependency tracking was active");
362
363                         listener = new GenericChangeListener<DependencyChangesRequest, DependencyChanges>() {
364
365                                 @Override
366                                 public boolean preEventRequest() {
367                                         return !Indexing.isDependenciesIndexingDisabled();
368                                 }
369
370                                 @Override
371                                 public void onEvent(ReadGraph graph, MetadataI metadata, DependencyChanges event) throws DatabaseException {
372
373                                         TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update processing");
374
375                                         if(DEBUG)
376                                                 System.err.println("Adding metadata " + event + " in revision " + graph.getService(ManagementSupport.class).getHeadRevisionId());
377
378                                         WriteGraph w = (WriteGraph)graph;
379                                         if(!event.isEmpty())
380                                                 w.addMetadata(event);
381
382                                         final Session session = graph.getSession();
383                                         final IndexedRelations indexer = session.getService(IndexedRelations.class);
384                                         Layer0 L0 = Layer0.getInstance(graph);
385                                         SerialisationSupport ss = graph.getService(SerialisationSupport.class);
386
387                                         for(Map.Entry<Resource, Change[]>  modelEntry : event.get().entrySet()) {
388
389                                                 final Resource model = modelEntry.getKey();
390                                                 final Change[] changes = modelEntry.getValue();
391
392                                                 boolean linkChange = false;
393
394                                                 Collection<Object[]> _additions = Collections.emptyList();
395                                                 Collection<Object> _removals = Collections.emptyList();
396                                                 Collection<Object> _replacementKeys = Collections.emptyList();
397                                                 Collection<Object[]> _replacementObjects = Collections.emptyList();
398                                                 Collection<Pair<String, String>> _typeChanges = Collections.emptyList();
399
400                                                 if(DEBUG) System.out.println("MODEL: " + NameUtils.getSafeLabel(graph, model));
401                                                 if (changes != null) {
402                                                         if (DEBUG) {
403                                                                 System.out.println("  CHANGE COUNT: " + changes.length);
404                                                                 for (Change c : changes)
405                                                                         System.out.println("    CHANGE: " + c.toString(graph));
406                                                         }
407                                                         _additions = new ArrayList<>();
408                                                         _removals = new ArrayList<>();
409                                                         _replacementKeys = new ArrayList<>();
410                                                         _replacementObjects = new ArrayList<>();
411                                                         _typeChanges = new HashSet<>();
412
413                                                         for (Change _entry : changes) {
414                                                                 if (_entry instanceof ComponentAddition) {
415                                                                         ComponentAddition entry = (ComponentAddition)_entry;
416                                                                         final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
417                                                                         Set<Resource> typeSet = graph.getTypes(entry.component);
418                                                                         if (name != null && typeSet != null) {
419                                                                                 if (!entry.isValid(graph))
420                                                                                         continue;
421                                                                                 Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);
422                                                                                 if (parent != null) {
423                                                                                         final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
424                                                                                         final String types = graph.syncRequest(new TypeString(L0, typeSet));
425                                                                                         final String typeIds = IndexQueries.toResourceIdString(typeSet);
426                                                                                         _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, IndexQueries.idFromGUID(id), name, types, typeIds});
427                                                                                 } else {
428                                                                                         //System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);
429                                                                                 }
430                                                                         } else {
431                                                                                 //System.err.println("resource " + entry.component + ": " + name + " " + types);
432                                                                         }
433                                                                 } else if(_entry instanceof ComponentModification) {
434                                                                         ComponentModification entry = (ComponentModification)_entry;
435                                                                         final String name = graph.getPossibleRelatedValue(entry.component, L0.HasName, Bindings.STRING);
436                                                                         if(graph.isInstanceOf(entry.component, L0.Type)) {
437                                                                                 SerialisationSupport support = session.getService(SerialisationSupport.class);
438                                                                                 _typeChanges.add(new Pair<String, String>(name, String.valueOf(support.getRandomAccessId((Resource) entry.component))));
439                                                                         } else {
440                                                                                 Set<Resource> typeSet = graph.getTypes(entry.component);
441                                                                                 if (name != null && !typeSet.isEmpty()) {
442                                                                                         Resource part = graph.getPossibleObject(entry.component, L0.PartOf);
443                                                                                         if(part != null) {
444                                                                                                 final GUID id = graph.getPossibleRelatedValue(entry.component, L0.identifier, GUID.BINDING);
445                                                                                                 final String types = graph.syncRequest(new TypeString(L0, typeSet));
446                                                                                                 final String typeIds = IndexQueries.toResourceIdString(typeSet);
447                                                                                                 _replacementKeys.add(ss.getRandomAccessId(entry.component));
448                                                                                                 _replacementObjects.add(new Object[] { ss.getRandomAccessId(part), 
449                                                                                                                 ss.getRandomAccessId(entry.component), name, types, IndexQueries.idFromGUID(id), name, types, typeIds});
450                                                                                         }
451                                                                                 }
452                                                                         }
453                                                                 } else if (_entry instanceof ComponentRemoval) {
454                                                                         ComponentRemoval entry = (ComponentRemoval)_entry;
455                                                                         if(!entry.isValid(graph)) continue;
456                                                                         _removals.add(ss.getRandomAccessId(((ComponentRemoval)_entry).component));
457                                                                 } else if (_entry instanceof LinkChange) {
458                                                                         linkChange = true;
459                                                                 }
460                                                         }
461                                                 }
462
463                                                 final boolean reset = linkChange || event.hasUnresolved;
464                                                 //System.err.println("dependencies(" + NameUtils.getSafeLabel(graph, model) + "): reset=" + reset + " linkChange=" + linkChange + " unresolved=" + event.hasUnresolved );
465
466                                                 if (reset || !_additions.isEmpty() || !_removals.isEmpty() || !_replacementKeys.isEmpty() || !_typeChanges.isEmpty()) {
467
468                                                         TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: starting index update");
469
470                                                         final Collection<Object[]> additions = _additions;
471                                                         final Collection<Object> removals = _removals;
472                                                         final Collection<Object> replacementKeys = _replacementKeys;
473                                                         final Collection<Object[]> replacementObjects = _replacementObjects; 
474                                                         final boolean typeNameChanges = typeNameChanges(graph, indexer, model, _typeChanges);
475
476                                                         final UUID pending = Indexing.makeIndexPending();
477                                                         try {
478                                                                 boolean didChange = false;
479                                                                 // Unresolved and linkChanges are not relevant any more
480                                                                 boolean doReset = typeNameChanges;
481
482                                                                 if (doReset) {
483
484                                                                         if(DEBUG) {
485                                                                                 System.err.println("resetIndex " + reset + " " + typeNameChanges);
486                                                                         }
487
488                                                                         indexer.removeAll(null, graph, DependenciesRelation.this, resource, model);
489                                                                         didChange = true;
490
491                                                                 } else {
492
493                                                                         if (!replacementKeys.isEmpty() && (replacementKeys.size() == replacementObjects.size())) {
494                                                                                 if(DEBUG) {
495                                                                                         System.out.println(replacementKeys.size() + " index replacements: " + replacementKeys);
496                                                                                 }
497                                                                                 didChange |= indexer.replace(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, replacementKeys, replacementObjects);
498                                                                         }
499                                                                         if (!removals.isEmpty()) {
500                                                                                 if(DEBUG) {
501                                                                                         System.out.println(removals.size() + " index removals: " + removals);
502                                                                                 }
503                                                                                 indexer.remove(null, graph, DependenciesRelation.this, resource, model, Dependencies.FIELD_RESOURCE, removals);
504                                                                                 didChange = true;
505                                                                         }
506                                                                         if (!additions.isEmpty()) {
507                                                                                 if(DEBUG) {
508                                                                                         for(Object[] os : additions) System.err.println("Adding to index " + model + ": " + Arrays.toString(os));
509                                                                                 }
510                                                                                 //System.out.println(additions.size() + " index insertions");
511                                                                                 indexer.insert(null, graph, DependenciesRelation.this, resource, model, additions);
512                                                                                 didChange = true;
513                                                                         }
514
515                                                                 }
516
517                                                                 if (didChange)
518                                                                         // TODO: because this data is ran with
519                                                                         // ThreadUtils.getBlockingWorkExecutor()
520                                                                         // fireListeners needs to use peekService,
521                                                                         // not getService since there is no
522                                                                         // guarantee that the session isn't being
523                                                                         // disposed while this method is executing.
524                                                                         fireListeners(graph, model);
525
526                                                         } catch (Throwable t) {
527                                                                 // Just to know if something unexpected happens here.
528                                                                 LOGGER.error("Dependencies index update failed for model "
529                                                                                 + model + " and relation " + resource + ".", t);
530
531                                                                 // NOTE: Last resort: failure to update index
532                                                                 // properly results in removal of the whole index.
533                                                                 // This is the only thing that can be done
534                                                                 // at this point to ensure that the index will
535                                                                 // return correct results in the future, through
536                                                                 // complete reinitialization. 
537                                                                 //indexer.removeAll(null, session, DependenciesRelation.this, resource, model);
538                                                         } finally {
539                                                                 Indexing.releaseIndexPending(pending);
540                                                                 Indexing.clearCaches(model);
541                                                         }
542
543                                                         TimeLogger.log(DependenciesRelation.class, "trackAndIndex.onEvent: index update done");
544                                                 }
545                                         }
546
547                                 }
548
549                         };
550
551                         GraphChangeListenerSupport changeSupport = processor.getService(GraphChangeListenerSupport.class);
552                         changeSupport.addMetadataListener(listener);
553
554                 }
555
556                 trackers++;
557
558         }
559
560         private boolean typeNameChanges(ReadGraph graph, IndexedRelations indexer,
561                         Resource model, final Collection<Pair<String, String>> typeChanges)
562                         throws DatabaseException {
563                 if (typeChanges.isEmpty())
564                         return false;
565
566                 for (Pair<String, String> nr : typeChanges) {
567                         String query = Dependencies.FIELD_RESOURCE + ":[" + nr.second + " TO " + nr.second + "]";
568                         //System.out.println("query: " + query);
569                         List<Map<String, Object>> results = indexer.query(null, query, graph, resource, model, Integer.MAX_VALUE);
570                         if (results.size() != 1) {
571                                 return true;
572                         } else {
573                                 Map<String, Object> result = results.get(0);
574                                 if (!ObjectUtils.objectEquals(result.get(Dependencies.FIELD_NAME), nr.first)) {
575                                         return true;
576                                 }
577                         }
578 //                      System.err.println("Type " + nr.first + " was unchanged.");
579                 }
580                 return false;
581         }
582
583         @Override
584         public void addListener(RequestProcessor processor, Resource model, Runnable observer) {
585                 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
586                 store.addListener(model, observer);
587         }
588
589         @Override
590         public void removeListener(RequestProcessor processor, Resource model, Runnable observer) {
591                 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
592                 store.removeListener(model, observer);
593         }
594
595         void fireListeners(RequestProcessor processor, Resource model) {
596                 DependenciesListenerStore store = processor.getSession().peekService(DependenciesListenerStore.class);
597                 if (store != null)
598                         store.fireListeners(model);
599         }
600
601         @Override
602         public void reset(RequestProcessor processor, Resource input) {
603                 if (DEBUG) {
604                         System.out.println("DependenciesRelation.reset: " + input);
605                         new Exception("DependenciesRelation.reset(" + listener + ")").printStackTrace(System.out);
606                 }
607                 DependenciesListenerStore store = processor.getSession().getService(DependenciesListenerStore.class);
608                 store.fireListeners(input);
609         }
610
611         public static void addSubtree(ReadGraph graph, Resource root) throws DatabaseException {
612
613                 Resource indexRoot = graph.syncRequest(new IndexRoot(root));
614                 addSubtree(graph, indexRoot, root);
615
616         }
617
618         public static void addSubtree(ReadGraph graph, Resource indexRoot, Resource subtreeRoot) throws DatabaseException {
619
620                 DependenciesRelation dr = new DependenciesRelation(graph, indexRoot);
621                 SerialisationSupport ss = graph.getService(SerialisationSupport.class);
622
623                 ArrayList<Entry> entries = dr.find(graph, subtreeRoot);
624                 entries.add(new Entry(graph, subtreeRoot));
625
626                 ArrayList<Object[]> result = new ArrayList<Object[]>(entries.size());
627                 for (Entry entry : entries) {
628                         result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id, entry.name, entry.types, entry.typeId });
629                 }
630
631                 Layer0X L0X = Layer0X.getInstance(graph);
632                 IndexedRelations indexer = graph.getService(IndexedRelations.class);
633                 indexer.insert(null, graph, dr, L0X.DependenciesRelation, indexRoot, result);
634
635         }
636
637 }