+ @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++;