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