]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/client/Synchronizer.java
Fix to handling of value access error reporting in synchronization.
[simantics/platform.git] / bundles / org.simantics.structural.synchronization.client / src / org / simantics / structural / synchronization / client / Synchronizer.java
1 package org.simantics.structural.synchronization.client;
2
3 import gnu.trove.map.hash.TObjectIntHashMap;
4 import gnu.trove.set.hash.THashSet;
5
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Collections;
9 import java.util.List;
10
11 import org.eclipse.core.runtime.IProgressMonitor;
12 import org.simantics.db.ReadGraph;
13 import org.simantics.db.Resource;
14 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
15 import org.simantics.db.exception.CancelTransactionException;
16 import org.simantics.db.exception.DatabaseException;
17 import org.simantics.db.layer0.exception.MissingVariableValueException;
18 import org.simantics.db.layer0.request.ResourceToPossibleVariable;
19 import org.simantics.db.layer0.variable.RVI;
20 import org.simantics.db.layer0.variable.Variable;
21 import org.simantics.db.service.ManagementSupport;
22 import org.simantics.layer0.Layer0;
23 import org.simantics.scl.runtime.SCLContext;
24 import org.simantics.structural.stubs.StructuralResource2;
25 import org.simantics.structural.synchronization.protocol.ChildInfo;
26 import org.simantics.structural.synchronization.protocol.Connection;
27 import org.simantics.structural.synchronization.protocol.SerializedVariable;
28 import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler;
29 import org.simantics.structural.synchronization.protocol.SynchronizationException;
30 import org.simantics.structural2.variables.VariableConnectionPointDescriptor;
31
32 public class Synchronizer {
33     
34     public static boolean TRACE = false;
35     
36     ReadGraph graph;
37     Layer0 L0;
38     StructuralResource2 STR;
39     THashSet<String> visitedTypes = new THashSet<String>();
40     IProgressMonitor monitor;
41     
42     double workDone = 0.0;
43     int workDoneInteger;
44     int maxWork;
45     
46     public Synchronizer(ReadGraph graph) {
47         this.graph = graph;
48         L0 = Layer0.getInstance(graph);
49         STR = StructuralResource2.getInstance(graph);
50     }
51     
52     public void setMonitor(IProgressMonitor monitor, int maxWork) {
53         this.monitor = monitor;
54         this.maxWork = maxWork;
55         this.workDoneInteger = 0;
56     }
57     
58     private void didWork(double amount) {
59         workDone += amount;
60         //System.out.println(workDone);
61         if(monitor != null) {
62             int t = (int)(workDone * maxWork);
63             if(t > workDoneInteger) {
64                 monitor.worked(t-workDoneInteger);
65                 workDoneInteger = t;
66             }
67         }
68     }
69
70     ChildInfo mapChild(Variable child) throws DatabaseException {
71         String name = child.getName(graph);
72         RVI rvi = child.getRVI(graph);
73         return new ChildInfo(name, rvi.toString());
74     }
75
76     Collection<ChildInfo> mapChildren(SynchronizationEventHandler handler, Collection<Variable> children) throws DatabaseException {
77         ArrayList<ChildInfo> result = new ArrayList<ChildInfo>(children.size());
78         for(Variable child : children) {
79             if(child.getPossibleType(graph, STR.Component) != null)
80                 try {
81                     result.add(mapChild(child));
82                 } catch(Exception e) {
83                     handler.reportProblem("Failed to get ChildInfo for " + child + ".", e);
84                 }
85         }
86         return result;
87     }
88
89     Collection<Connection> mapConnections(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
90         ArrayList<Connection> result = new ArrayList<Connection>();
91         for(Variable conn : child.getProperties(graph, StructuralResource2.URIs.SynchronizedConnectionRelation)) {
92             String name = conn.getName(graph);
93             org.simantics.structural2.variables.Connection vc = conn.getValue(graph);
94             Collection<VariableConnectionPointDescriptor> connectionPoints = vc.getConnectionPointDescriptors(graph, null);
95             List<String> cps = new ArrayList<String>(connectionPoints.size());
96             for(VariableConnectionPointDescriptor desc : connectionPoints) {
97                 if(desc.isFlattenedFrom(graph, conn)) continue;
98                 if(!desc.hasClassification(graph, StructuralResource2.URIs.ProvidingConnectionRelation)) continue;
99                 String cpRef = desc.getRelativeRVI(graph, child);
100                 cps.add(cpRef);
101             }
102             
103             result.add(new Connection(name, cps));
104             
105         }
106         
107         return result;
108         
109     }
110     
111     private SerializedVariable serialize(SynchronizationEventHandler handler, Variable var, String name) throws DatabaseException {
112         try {
113                 SerializedVariable result = new SerializedVariable(name, var.getVariantValue(graph));
114                 for(Variable prop : var.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
115                         String pName = prop.getName(graph);
116                         SerializedVariable v = serialize(handler, prop, pName);
117                         if(v != null) result.addProperty(pName, v);
118                 }
119                 return result;
120         } catch (MissingVariableValueException e) {
121             handler.reportProblem("Failed to read " + name + ": " + e.getMessage());
122             
123             Throwable cur = e;
124             while((cur = cur.getCause()) != null) {
125                 if(!(cur instanceof MissingVariableValueException)) {
126                     handler.reportProblem("  " + getSafeDescription(cur));
127                 }
128             }
129         } catch (Exception e) {
130                 handler.reportProblem("Failed to serialize " + name + ": " + getSafeDescription(e), e);
131         }
132         
133         return null;
134         
135     }
136
137     private String getSafeDescription(Throwable cur) {
138         return cur.getClass().getName() + (cur == null || cur.getMessage() != null ? ": " + cur.getMessage() : "");
139     }
140
141     Collection<SerializedVariable> mapProperties(SynchronizationEventHandler handler, Variable child) throws DatabaseException {
142         ArrayList<SerializedVariable> result = new ArrayList<SerializedVariable>();
143         for(Variable prop : child.getProperties(graph, StructuralResource2.URIs.SynchronizedRelation)) {
144                 SerializedVariable serialized = serialize(handler, prop, prop.getName(graph));
145                 if(serialized != null) result.add(serialized);
146         }
147         return result;
148     }
149
150     /**
151      * Assumes that the variable points to a composite.
152      */
153     public void fullSynchronization(Variable variable, SynchronizationEventHandler handler) throws DatabaseException {
154         long duration = 0;
155         if(TRACE) {
156             System.out.println("fullSynchronization " + variable.getURI(graph));
157             duration -= System.nanoTime();
158         }
159         SCLContext context = SCLContext.getCurrent();
160         Object oldGraph = context.put("graph", graph);
161         try {
162             handler.beginSynchronization();
163             synchronizationRec(variable, handler, null, 1.0);
164             handler.endSynchronization();
165         } finally {
166             context.put("graph", oldGraph);
167         }
168         if(TRACE) {
169             duration += System.nanoTime();
170             System.out.println("full sync in " + 1e-9*duration + "s.");
171         }
172     }
173     
174     /**
175      * Recursive implementation of partial and full synchronization. If {@code changeFlags}
176      * is null, this is full synchronization, otherwise partial synchronization.
177      */
178     private void synchronizationRec(Variable variable, SynchronizationEventHandler handler,
179             TObjectIntHashMap<Variable> changeFlags,
180             double proportionOfWork) throws DatabaseException {
181         String name = variable.getName(graph);
182         Resource type = variable.getPossibleType(graph, STR.Component);
183         if(type == null) {
184             // This same filtering is done separately in mapChildren when beginComponent has been called for the parent.
185             return;
186         }
187         Collection<Variable> children = variable.getChildren(graph);
188         if(graph.isInheritedFrom(type, STR.Composite) || graph.isInheritedFrom(type, STR.AbstractDefinedComponentType)) {
189             String typeId = graph.getPossibleURI(type);
190             if(typeId == null)
191                 throw new SynchronizationException("User component " + type + " does not have an URI.");
192             if(visitedTypes.add(typeId))
193                 visitType(typeId, type, handler);
194             boolean endComponentNeeded = false;
195             try {
196                 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
197                 Collection<ChildInfo> childMap = mapChildren(handler, children);
198                 endComponentNeeded = true;
199                 handler.beginComponent(name, typeId, 
200                         propertyMap,
201                         Collections.<Connection>emptyList(), 
202                         childMap);
203             } catch(Exception e) {
204                 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
205                 if (endComponentNeeded)
206                     handler.endComponent();
207                 return;
208             }
209         } else {
210             String typeId = graph.getPossibleURI(type);
211             if(typeId == null)
212                 throw new SynchronizationException("User component " + type + " does not have an URI.");
213             if(visitedTypes.add(typeId))
214                 visitType(typeId, type, handler);
215             boolean endComponentNeeded = false;
216             try {
217                 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
218                 Collection<Connection> connectionMap = mapConnections(handler, variable);
219                 Collection<ChildInfo> childMap = mapChildren(handler, children);
220                 endComponentNeeded = true;
221                 handler.beginComponent(name, 
222                         typeId,
223                         propertyMap,
224                         connectionMap,
225                         childMap);
226             } catch(Exception e) {
227                 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
228                 if (endComponentNeeded)
229                     handler.endComponent();
230                 return;
231             }
232         }
233         
234         if(changeFlags == null) {
235             // Full synchronization, synchronize all children
236             if(children.size() > 0) {
237                 double proportionOfWorkForChildren = proportionOfWork / children.size();
238                 for(Variable child : children)
239                     synchronizationRec(child, handler, null, proportionOfWorkForChildren);
240             }
241             else {
242                 didWork(proportionOfWork);
243                 // Full synchronization is cancelable
244                 if(monitor != null && monitor.isCanceled())
245                     throw new CancelTransactionException();
246             }
247         }
248         else {
249             // Partial synchronization, synchronize only children with positive changeFlag
250             int relevantChildCount = 0;
251             for(final Variable child : children) {
252                 int changeStatus = changeFlags.get(child);
253                 if(changeStatus != 0)
254                     ++relevantChildCount;
255             }
256             if(relevantChildCount > 0) {
257                 double proportionOfWorkForChildren = proportionOfWork / relevantChildCount;
258                 for(final Variable child : children) {
259                     int changeStatus = changeFlags.get(child);
260                     if(changeStatus == 0)
261                         continue;
262                     synchronizationRec(child, handler,
263                             // Apply full synchronization for subtree if changeStatus > 1
264                             changeStatus==1 ? changeFlags : null,
265                             proportionOfWorkForChildren
266                             );
267                 }
268             }
269             else {
270                 didWork(proportionOfWork);
271             }
272         }
273         handler.endComponent();
274     }
275     
276     public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
277         long duration = 0;
278         if(TRACE) {
279             System.out.println("partialSynchronization " + variable.getURI(graph));
280             duration -= System.nanoTime();
281         }
282         int changeStatus = changeFlags.get(variable);
283         if(changeStatus == 0) return;
284         SCLContext context = SCLContext.getCurrent();
285         Object oldGraph = context.put("graph", graph);
286         try {
287             handler.beginSynchronization();
288             synchronizationRec(variable, handler, changeStatus == 1 ? changeFlags : null, 1.0);
289             handler.endSynchronization();
290         } finally {
291             context.put("graph", oldGraph);
292         } 
293         if(TRACE) {
294             duration += System.nanoTime();
295             System.out.println("partial sync in " + 1e-9*duration + "s.");
296         }
297     }
298     
299     public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, long fromRevision) throws DatabaseException {
300         TObjectIntHashMap<Variable> modifiedComponents = StructuralChangeFlattener.getModifiedComponents(graph, variable, fromRevision);
301         /*System.out.println("----------------");
302         modifiedComponents.forEachEntry(
303                 new TObjectIntProcedure<Variable>() {
304                     @Override
305                     public boolean execute(Variable a, int b) {
306                         try {
307                             System.out.println("Changed: " + a.getURI(graph) + " " + b);
308                         } catch (DatabaseException e) {
309                             e.printStackTrace();
310                         }
311                         return true;
312                     }
313                 });*/
314         partialSynchronization(variable, handler, modifiedComponents);
315     }
316     
317     void visitType(String typeId, Resource typeResource, SynchronizationEventHandler handler) throws DatabaseException {
318         Variable typeVariable = graph.syncRequest(new ResourceToPossibleVariable(typeResource), TransientCacheAsyncListener.<Variable>instance());
319         handler.beginType(typeId, mapProperties(handler, typeVariable));
320         handler.endType();
321     }
322
323     public long getHeadRevisionId() throws DatabaseException {
324         return graph.getService(ManagementSupport.class).getHeadRevisionId()+1;
325     }
326
327     
328 }