1 package org.simantics.structural.synchronization.client;
3 import gnu.trove.map.hash.TObjectIntHashMap;
4 import gnu.trove.set.hash.THashSet;
6 import java.util.ArrayList;
7 import java.util.Collection;
8 import java.util.Collections;
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;
32 public class Synchronizer {
34 public static boolean TRACE = false;
38 StructuralResource2 STR;
39 THashSet<String> visitedTypes = new THashSet<String>();
40 IProgressMonitor monitor;
42 double workDone = 0.0;
46 public Synchronizer(ReadGraph graph) {
48 L0 = Layer0.getInstance(graph);
49 STR = StructuralResource2.getInstance(graph);
52 public void setMonitor(IProgressMonitor monitor, int maxWork) {
53 this.monitor = monitor;
54 this.maxWork = maxWork;
55 this.workDoneInteger = 0;
58 private void didWork(double amount) {
60 //System.out.println(workDone);
62 int t = (int)(workDone * maxWork);
63 if(t > workDoneInteger) {
64 monitor.worked(t-workDoneInteger);
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());
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)
81 result.add(mapChild(child));
82 } catch(Exception e) {
83 handler.reportProblem("Failed to get ChildInfo for " + child + ".", e);
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);
103 result.add(new Connection(name, cps));
111 private SerializedVariable serialize(SynchronizationEventHandler handler, Variable var, String name) throws DatabaseException {
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);
120 } catch (MissingVariableValueException e) {
121 handler.reportProblem("Failed to read " + name + ": " + e.getMessage());
124 while((cur = cur.getCause()) != null) {
125 if(!(cur instanceof MissingVariableValueException)) {
126 handler.reportProblem(" " + getSafeDescription(cur));
129 } catch (Exception e) {
130 handler.reportProblem("Failed to serialize " + name + ": " + getSafeDescription(e), e);
137 private String getSafeDescription(Throwable cur) {
138 return cur.getClass().getName() + (cur == null || cur.getMessage() != null ? ": " + cur.getMessage() : "");
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);
151 * Assumes that the variable points to a composite.
153 public void fullSynchronization(Variable variable, SynchronizationEventHandler handler) throws DatabaseException {
156 System.out.println("fullSynchronization " + variable.getURI(graph));
157 duration -= System.nanoTime();
159 SCLContext context = SCLContext.getCurrent();
160 Object oldGraph = context.put("graph", graph);
162 handler.beginSynchronization();
163 synchronizationRec(variable, handler, null, 1.0);
164 handler.endSynchronization();
166 context.put("graph", oldGraph);
169 duration += System.nanoTime();
170 System.out.println("full sync in " + 1e-9*duration + "s.");
175 * Recursive implementation of partial and full synchronization. If {@code changeFlags}
176 * is null, this is full synchronization, otherwise partial synchronization.
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);
184 // This same filtering is done separately in mapChildren when beginComponent has been called for the parent.
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);
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;
196 Collection<SerializedVariable> propertyMap = mapProperties(handler, variable);
197 Collection<ChildInfo> childMap = mapChildren(handler, children);
198 endComponentNeeded = true;
199 handler.beginComponent(name, typeId,
201 Collections.<Connection>emptyList(),
203 } catch(Exception e) {
204 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
205 if (endComponentNeeded)
206 handler.endComponent();
210 String typeId = graph.getPossibleURI(type);
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;
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,
226 } catch(Exception e) {
227 handler.reportProblem("Failed to synchronize " + name + ": " + e.getMessage(), e);
228 if (endComponentNeeded)
229 handler.endComponent();
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);
242 didWork(proportionOfWork);
243 // Full synchronization is cancelable
244 if(monitor != null && monitor.isCanceled())
245 throw new CancelTransactionException();
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;
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)
262 synchronizationRec(child, handler,
263 // Apply full synchronization for subtree if changeStatus > 1
264 changeStatus==1 ? changeFlags : null,
265 proportionOfWorkForChildren
270 didWork(proportionOfWork);
273 handler.endComponent();
276 public void partialSynchronization(Variable variable, SynchronizationEventHandler handler, TObjectIntHashMap<Variable> changeFlags) throws DatabaseException {
279 System.out.println("partialSynchronization " + variable.getURI(graph));
280 duration -= System.nanoTime();
282 int changeStatus = changeFlags.get(variable);
283 if(changeStatus == 0) return;
284 SCLContext context = SCLContext.getCurrent();
285 Object oldGraph = context.put("graph", graph);
287 handler.beginSynchronization();
288 synchronizationRec(variable, handler, changeStatus == 1 ? changeFlags : null, 1.0);
289 handler.endSynchronization();
291 context.put("graph", oldGraph);
294 duration += System.nanoTime();
295 System.out.println("partial sync in " + 1e-9*duration + "s.");
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>() {
305 public boolean execute(Variable a, int b) {
307 System.out.println("Changed: " + a.getURI(graph) + " " + b);
308 } catch (DatabaseException e) {
314 partialSynchronization(variable, handler, modifiedComponents);
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));
323 public long getHeadRevisionId() throws DatabaseException {
324 return graph.getService(ManagementSupport.class).getHeadRevisionId()+1;