1 /*******************************************************************************
\r
2 * Copyright (c) 2012 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * Semantum Oy - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.event.writer;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Arrays;
\r
16 import java.util.Collection;
\r
17 import java.util.List;
\r
18 import java.util.Map;
\r
19 import java.util.concurrent.atomic.AtomicBoolean;
\r
21 import org.eclipse.core.runtime.IProgressMonitor;
\r
22 import org.eclipse.core.runtime.IStatus;
\r
23 import org.eclipse.core.runtime.Status;
\r
24 import org.simantics.DatabaseJob;
\r
25 import org.simantics.Simantics;
\r
26 import org.simantics.db.ReadGraph;
\r
27 import org.simantics.db.Resource;
\r
28 import org.simantics.db.Session;
\r
29 import org.simantics.db.VirtualGraph;
\r
30 import org.simantics.db.WriteGraph;
\r
31 import org.simantics.db.common.primitiverequest.PossibleObject;
\r
32 import org.simantics.db.common.request.ObjectsWithType;
\r
33 import org.simantics.db.common.request.WriteRequest;
\r
34 import org.simantics.db.common.utils.NameUtils;
\r
35 import org.simantics.db.exception.CancelTransactionException;
\r
36 import org.simantics.db.exception.DatabaseException;
\r
37 import org.simantics.db.layer0.genericrelation.Dependencies;
\r
38 import org.simantics.db.layer0.request.PossibleModel;
\r
39 import org.simantics.event.Activator;
\r
40 import org.simantics.event.ontology.EventResource;
\r
41 import org.simantics.layer0.Layer0;
\r
42 import org.simantics.operation.Layer0X;
\r
43 import org.simantics.scl.runtime.function.Function;
\r
44 import org.simantics.simulation.ontology.SimulationResource;
\r
45 import org.simantics.utils.datastructures.MapList;
\r
48 * A self-scheduling job that checks queued event resources and tries to resolve
\r
49 * modules from the model based on the event source module name.
\r
52 * The job will automatically stop self-scheduling when the experiment run
\r
53 * referenced by the event producer becomes inactive.
\r
56 * Must be disposed when no longer used.
\r
58 * @author Tuukka Lehtonen
\r
60 * TODO: optimize return event resolution somehow. Currently forced to perform a linear search of all
\r
62 public class EventSourceResolver extends DatabaseJob {
\r
65 * Externalizes the logic of checking whether a resource found from the
\r
66 * Dependencies index can be an event source. The usual implementation will
\r
67 * check whether the resource is an instance of a domain specific type.
\r
70 public static interface Filter {
\r
71 boolean accept(ReadGraph graph, Resource possibleSource)
\r
72 throws DatabaseException;
\r
75 private final boolean DEBUG = false;
\r
76 private final boolean DEBUG_DETAIL = false;
\r
78 private static final int RESOLUTION_SCHEDULE_PERIOD = 2000;
\r
80 public List<Resource> events = new ArrayList<Resource>();
\r
82 private boolean initialEventsResolved = false;
\r
83 private Resource eventLog;
\r
84 private Filter eventSourceFilter;
\r
85 private VirtualGraph virtualGraph;
\r
86 private AtomicBoolean polling = new AtomicBoolean(true);
\r
88 public EventSourceResolver(VirtualGraph virtualGraph, Resource eventLog, Filter eventSourceFilter) {
\r
89 super("Event Source Resolver");
\r
90 setPriority(DECORATE);
\r
93 this.virtualGraph = virtualGraph;
\r
94 this.eventLog = eventLog;
\r
95 this.eventSourceFilter = eventSourceFilter;
\r
98 public void dispose() {
\r
100 System.out.println(this + ": DISPOSE");
\r
101 polling.set(false);
\r
105 public boolean shouldRun() {
\r
106 return polling.get() && Simantics.peekSession() != null;
\r
110 protected IStatus run(final IProgressMonitor monitor) {
\r
112 Session session = Simantics.peekSession();
\r
113 if (session == null)
\r
114 return Status.CANCEL_STATUS;
\r
116 final Resource[] events = pruneEvents();
\r
117 if (events.length == 0 && initialEventsResolved)
\r
118 return Status.CANCEL_STATUS;
\r
121 System.out.println(this + ": resolve " + events.length + " events");
\r
122 if (!initialEventsResolved)
\r
123 System.out.println(this + ": resolve initial events");
\r
126 setThread(Thread.currentThread());
\r
127 monitor.beginTask("Resolve " + events.length + " events", events.length);
\r
129 session.sync(new WriteRequest(virtualGraph) {
\r
131 @SuppressWarnings("rawtypes")
\r
132 Function indexFunction;
\r
135 EventResource EVENT;
\r
138 public void perform(WriteGraph graph) throws DatabaseException {
\r
139 setThread(Thread.currentThread());
\r
141 L0 = Layer0.getInstance(graph);
\r
142 L0X = Layer0X.getInstance(graph);
\r
143 EVENT = EventResource.getInstance(graph);
\r
145 Resource run = graph.sync(new PossibleObject(eventLog, EVENT.HasEventProducer));
\r
147 throw new CancelTransactionException();
\r
148 if (!graph.hasStatement(run, SimulationResource.getInstance(graph).IsActive))
\r
149 throw new CancelTransactionException();
\r
151 model = graph.sync(new PossibleObject(eventLog, EVENT.IsEventLogOf));
\r
153 throw new CancelTransactionException();
\r
155 indexFunction = graph.adapt(L0X.Dependencies, Function.class);
\r
157 if (!initialEventsResolved) {
\r
158 MapList<String, Resource> initialEventsBySource = new MapList<String,Resource>();
\r
159 for(Resource slice : graph.getObjects(eventLog, L0.ConsistsOf)) {
\r
160 Collection<Resource> initialEvents = graph.syncRequest(new ObjectsWithType(slice, L0.ConsistsOf, EVENT.Event));
\r
162 System.out.println(EventSourceResolver.this + ": resolving " + initialEvents.size() + " initial events");
\r
163 MapList<String, Resource> bySource = sortEventsBySource(graph, initialEvents);
\r
164 for(String key : bySource.getKeys()) {
\r
165 initialEventsBySource.addAll(key, bySource.getValuesUnsafe(key));
\r
168 for (String sourceName : initialEventsBySource.getKeys())
\r
169 resolveEvents(graph, sourceName, initialEventsBySource.getValuesUnsafe(sourceName));
\r
170 initialEventsResolved = true;
\r
173 MapList<String, Resource> eventsBySource = sortEventsBySource(graph, Arrays.asList(events));
\r
174 for (String sourceName : eventsBySource.getKeys()) {
\r
175 resolveEvents(graph, sourceName, eventsBySource.getValuesUnsafe(sourceName));
\r
180 private MapList<String, Resource> sortEventsBySource(ReadGraph graph, Collection<Resource> events) throws DatabaseException {
\r
181 MapList<String, Resource> result = new MapList<String, Resource>();
\r
182 for (Resource event : events) {
\r
183 Collection<Resource> sources = graph.getObjects(event, EVENT.Event_source);
\r
184 if (!sources.isEmpty())
\r
187 String sourceName = graph.getPossibleRelatedValue(event, EVENT.Event_sourceName);
\r
188 if (sourceName != null && !sourceName.trim().isEmpty())
\r
189 result.add(sourceName, event);
\r
194 @SuppressWarnings({ "unchecked" })
\r
195 private void resolveEvents(WriteGraph graph, String sourceName, Collection<Resource> eventsForSource) throws DatabaseException {
\r
197 System.out.println(EventSourceResolver.this + ": resolving " + eventsForSource.size() + " events for source " + sourceName);
\r
198 if (sourceName == null)
\r
199 throw new NullPointerException("null source name");
\r
201 monitor.subTask("Resolve events with source " + sourceName);
\r
203 System.out.println(EventSourceResolver.this + ": resolving source name " + sourceName);
\r
204 List<Map<String, Object>> results = (List<Map<String, Object>>) indexFunction.apply(graph, model, "Name:" + sourceName);
\r
205 for (Map<String, Object> result : results) {
\r
206 Resource r = (Resource) result.get(Dependencies.FIELD_RESOURCE);
\r
207 if (eventSourceFilter != null && !eventSourceFilter.accept(graph, r))
\r
209 Resource rModel = graph.sync(new PossibleModel(r));
\r
210 if (model.equals(rModel)) {
\r
212 System.out.println(EventSourceResolver.this + ": found module " + NameUtils.getSafeName(graph, r, true));
\r
214 for (Resource event : eventsForSource) {
\r
215 Resource otherEvent = resolveAndMarkEventPair(graph, r, event);
\r
217 if (otherEvent == null) {
\r
218 // No match for this event, add also inverse of Event_source relation.
\r
219 graph.claim(event, EVENT.Event_source, EVENT.Event_source_inverse, r);
\r
221 // Found the event that matches this one, remove Event_source_inverse to
\r
222 // prevent bloating the event source resource with unnecessary statements.
\r
223 graph.deny(otherEvent, EVENT.Event_source, EVENT.Event_source_inverse, r);
\r
224 graph.claim(otherEvent, EVENT.Event_source, null, r);
\r
225 graph.claim(event, EVENT.Event_source, null, r);
\r
232 private Resource resolveAndMarkEventPair(WriteGraph graph, Resource source, Resource event) throws DatabaseException {
\r
233 Integer eventIndex = graph.getPossibleRelatedValue(event, EVENT.Event_index);
\r
234 boolean returnEvent = graph.hasStatement(event, EVENT.ReturnEvent);
\r
236 for (Resource otherEvent : graph.getObjects(source, EVENT.Event_source_inverse)) {
\r
237 Integer index = graph.getPossibleRelatedValue(otherEvent, EVENT.Event_index);
\r
238 boolean ret = graph.hasStatement(otherEvent, EVENT.ReturnEvent);
\r
239 if (index != null && index.equals(eventIndex) && returnEvent != ret) {
\r
242 System.out.println(EventSourceResolver.this + ": event " + event + " returns event " + otherEvent);
\r
243 graph.claim(event, EVENT.Returns, EVENT.ReturnedBy, otherEvent);
\r
246 System.out.println(EventSourceResolver.this + ": event " + event + " is returned by event " + otherEvent);
\r
247 graph.claim(otherEvent, EVENT.Returns, EVENT.ReturnedBy, event);
\r
256 } catch (CancelTransactionException e) {
\r
257 polling.set(false);
\r
258 return Status.CANCEL_STATUS;
\r
259 } catch (DatabaseException e) {
\r
260 return new Status(IStatus.ERROR, Activator.BUNDLE_ID, "Event source resolution failed, see exception for details.", e);
\r
263 return Status.OK_STATUS;
\r
266 schedule(RESOLUTION_SCHEDULE_PERIOD);
\r
270 private Resource[] pruneEvents() {
\r
271 synchronized ( this.events ) {
\r
272 Resource[] events = this.events.toArray(Resource.NONE);
\r
273 this.events.clear();
\r
278 public void queueEvents(Collection<Resource> events) {
\r
279 synchronized (this.events) {
\r
280 this.events.addAll(events);
\r
284 public void queueEvent(Resource event) {
\r
285 synchronized (this.events) {
\r
286 this.events.add(event);
\r