]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.event/src/org/simantics/event/writer/EventSourceResolver.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.event / src / org / simantics / event / writer / EventSourceResolver.java
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
8  *\r
9  * Contributors:\r
10  *     Semantum Oy - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.event.writer;\r
13 \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
20 \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
46 \r
47 /**\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
50  * \r
51  * <p>\r
52  * The job will automatically stop self-scheduling when the experiment run\r
53  * referenced by the event producer becomes inactive.\r
54  * \r
55  * <p>\r
56  * Must be disposed when no longer used.\r
57  * \r
58  * @author Tuukka Lehtonen\r
59  * \r
60  * TODO: optimize return event resolution somehow. Currently forced to perform a linear search of all \r
61  */\r
62 public class EventSourceResolver extends DatabaseJob {\r
63 \r
64     /**\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
68      * \r
69      */\r
70     public static interface Filter {\r
71         boolean accept(ReadGraph graph, Resource possibleSource)\r
72                 throws DatabaseException;\r
73     }\r
74 \r
75     private final boolean    DEBUG                      = false;\r
76     private final boolean    DEBUG_DETAIL               = false;\r
77 \r
78     private static final int RESOLUTION_SCHEDULE_PERIOD = 2000;\r
79 \r
80     public List<Resource>    events                     = new ArrayList<Resource>();\r
81 \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
87 \r
88     public EventSourceResolver(VirtualGraph virtualGraph, Resource eventLog, Filter eventSourceFilter) {\r
89         super("Event Source Resolver");\r
90         setPriority(DECORATE);\r
91         setUser(false);\r
92         setSystem(true);\r
93         this.virtualGraph = virtualGraph;\r
94         this.eventLog = eventLog;\r
95         this.eventSourceFilter = eventSourceFilter;\r
96     }\r
97 \r
98     public void dispose() {\r
99         if (DEBUG)\r
100             System.out.println(this + ": DISPOSE");\r
101         polling.set(false);\r
102     }\r
103 \r
104     @Override\r
105     public boolean shouldRun() {\r
106         return polling.get() && Simantics.peekSession() != null;\r
107     }\r
108 \r
109     @Override\r
110     protected IStatus run(final IProgressMonitor monitor) {\r
111         try {\r
112             Session session = Simantics.peekSession();\r
113             if (session == null)\r
114                 return Status.CANCEL_STATUS;\r
115 \r
116             final Resource[] events = pruneEvents();\r
117             if (events.length == 0 && initialEventsResolved)\r
118                 return Status.CANCEL_STATUS;\r
119 \r
120             if (DEBUG) {\r
121                 System.out.println(this + ": resolve " + events.length + " events");\r
122                 if (!initialEventsResolved)\r
123                     System.out.println(this + ": resolve initial events");\r
124             }\r
125 \r
126             setThread(Thread.currentThread());\r
127             monitor.beginTask("Resolve " + events.length + " events", events.length);\r
128             try {\r
129                 session.sync(new WriteRequest(virtualGraph) {\r
130                     Resource model;\r
131                     @SuppressWarnings("rawtypes")\r
132                     Function indexFunction;\r
133                     Layer0 L0;\r
134                     Layer0X L0X;\r
135                     EventResource EVENT;\r
136 \r
137                     @Override\r
138                     public void perform(WriteGraph graph) throws DatabaseException {\r
139                         setThread(Thread.currentThread());\r
140 \r
141                         L0 = Layer0.getInstance(graph);\r
142                         L0X = Layer0X.getInstance(graph);\r
143                         EVENT = EventResource.getInstance(graph);\r
144 \r
145                         Resource run = graph.sync(new PossibleObject(eventLog, EVENT.HasEventProducer));\r
146                         if (run == null)\r
147                             throw new CancelTransactionException();\r
148                         if (!graph.hasStatement(run, SimulationResource.getInstance(graph).IsActive))\r
149                             throw new CancelTransactionException();\r
150 \r
151                         model = graph.sync(new PossibleObject(eventLog, EVENT.IsEventLogOf));\r
152                         if (model == null)\r
153                             throw new CancelTransactionException();\r
154 \r
155                         indexFunction = graph.adapt(L0X.Dependencies, Function.class);\r
156 \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
161                                 if (DEBUG)\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
166                                 }\r
167                                 }\r
168                                 for (String sourceName : initialEventsBySource.getKeys())\r
169                                         resolveEvents(graph, sourceName, initialEventsBySource.getValuesUnsafe(sourceName));\r
170                             initialEventsResolved = true;\r
171                         }\r
172 \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
176                             monitor.worked(1);\r
177                         }\r
178                     }\r
179 \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
185                                 continue;\r
186 \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
190                         }\r
191                         return result;\r
192                     }\r
193 \r
194                     @SuppressWarnings({ "unchecked" })\r
195                     private void resolveEvents(WriteGraph graph, String sourceName, Collection<Resource> eventsForSource) throws DatabaseException {\r
196                         if (DEBUG)\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
200 \r
201                         monitor.subTask("Resolve events with source " + sourceName);\r
202                         if (DEBUG)\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
208                                 continue;\r
209                             Resource rModel = graph.sync(new PossibleModel(r));\r
210                             if (model.equals(rModel)) {\r
211                                 if (DEBUG)\r
212                                     System.out.println(EventSourceResolver.this + ": found module " + NameUtils.getSafeName(graph, r, true));\r
213 \r
214                                 for (Resource event : eventsForSource) {\r
215                                     Resource otherEvent = resolveAndMarkEventPair(graph, r, event);\r
216 \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
220                                     } else {\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
226                                     }\r
227                                 }\r
228                             }\r
229                         }\r
230                     }\r
231 \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
235 \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
240                                 if (returnEvent) {\r
241                                     if (DEBUG_DETAIL)\r
242                                         System.out.println(EventSourceResolver.this + ": event " + event + " returns event " + otherEvent);\r
243                                     graph.claim(event, EVENT.Returns, EVENT.ReturnedBy, otherEvent);\r
244                                 } else {\r
245                                     if (DEBUG_DETAIL)\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
248                                 }\r
249                                 return otherEvent;\r
250                             }\r
251                         }\r
252 \r
253                         return null;\r
254                     }\r
255                 });\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
261             }\r
262 \r
263             return Status.OK_STATUS;\r
264         } finally {\r
265             monitor.done();\r
266             schedule(RESOLUTION_SCHEDULE_PERIOD);\r
267         }\r
268     }\r
269 \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
274             return events;\r
275         }\r
276     }\r
277 \r
278     public void queueEvents(Collection<Resource> events) {\r
279         synchronized (this.events) {\r
280             this.events.addAll(events);\r
281         }\r
282     }\r
283 \r
284     public void queueEvent(Resource event) {\r
285         synchronized (this.events) {\r
286             this.events.add(event);\r
287         }\r
288     }\r
289 \r
290 }\r