X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.structural.synchronization.client%2Fsrc%2Forg%2Fsimantics%2Fstructural%2Fsynchronization%2Fbase%2FSynchronizationEventHandlerBase.java;h=82dd224b6ec2f413f933a60ef5910bcbd72f1382;hb=72d52b5910e1ed0141b6768ef700e7321ac05553;hp=85023422e6d5a6fbc6d1cfe889e723bec8b53e54;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/base/SynchronizationEventHandlerBase.java b/bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/base/SynchronizationEventHandlerBase.java index 85023422e..82dd224b6 100644 --- a/bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/base/SynchronizationEventHandlerBase.java +++ b/bundles/org.simantics.structural.synchronization.client/src/org/simantics/structural/synchronization/base/SynchronizationEventHandlerBase.java @@ -1,424 +1,426 @@ -package org.simantics.structural.synchronization.base; - -import gnu.trove.map.hash.THashMap; -import gnu.trove.set.hash.THashSet; - -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Queue; - -import org.simantics.databoard.Bindings; -import org.simantics.databoard.adapter.AdaptException; -import org.simantics.db.exception.DatabaseException; -import org.simantics.structural.synchronization.internal.Policy; -import org.simantics.structural.synchronization.protocol.ChildInfo; -import org.simantics.structural.synchronization.protocol.Connection; -import org.simantics.structural.synchronization.protocol.SerializedVariable; -import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler; -import org.simantics.structural.synchronization.protocol.SynchronizationException; - -/** - * Handles synchronization events by updating the simulator designated by the - * provided {@link Solver} instance. - * - * @author Hannu Niemistö - */ -public abstract class SynchronizationEventHandlerBase> implements SynchronizationEventHandler { - - public static final boolean TRACE_EVENTS = false; - - public final Solver solver; - protected final SolverNameUtil nameUtil; - protected final MappingBase mapping; - final ModuleUpdaterFactoryBase moduleUpdaterFactory; - final ComponentFactory componentFactory; - public final ReferenceResolverBase resolver; - private boolean didChanges = false; - - protected T component; // Current active component - THashMap> moduleUpdaters = new THashMap<>(); - Queue postSynchronizationActions = new ArrayDeque<>(); - protected THashMap> solverComponentNameToComponent = new THashMap<>(); - - /** - * This is a set of components satisfying the following conditions - *
    - *
  • beginComponent is called for their parents - *
  • endComponent is not yet called for their parents - *
  • beginComponent is not yet called for them - *
- */ - THashSet potentiallyUpdatedComponents = new THashSet<>(); - - public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase resolver, SolverNameUtil nameUtil, - ComponentFactory componentFactory, ModuleUpdaterFactoryBase moduleUpdaterFactory, MappingBase mapping) { - this.solver = solver; - this.nameUtil = nameUtil; - this.mapping = mapping; - this.componentFactory = componentFactory; - this.moduleUpdaterFactory = moduleUpdaterFactory; - this.resolver = resolver; - } - - @Override - public void beginSynchronization() { - if(TRACE_EVENTS) { - System.out.println("beginSynchronization()"); - //mapping.printUidMap(); - } - component = null; - } - - @Override - public void endSynchronization() { - try { - if(TRACE_EVENTS) - System.out.println("endSynchronization()"); - if(component != null) - throw new SynchronizationException("beginComponent/endComponent calls do not match."); - - resolver.resolvePendingSelfReferences(); - resolver.printPending(); - - // Do removals - mapping.removePending(solver); - - // Post synchronization actions - Runnable action; - while((action = postSynchronizationActions.poll()) != null) - action.run(); - - // Rename modules to suggested names where possible. - nameUtil.applySuggestedNames((creationName, newName) -> { - ComponentBase component = solverComponentNameToComponent.get(creationName); - if (component != null) { - component.solverComponentName = newName; - } - }); - solverComponentNameToComponent.clear(); - } catch(Throwable e) { - Policy.logError(e); - throw new SynchronizationException(e); - } - } - - private boolean isAttached(Collection properties) { - for(SerializedVariable property : properties) - if(property.name.equals("IsAttached")) - try { - return (Boolean)property.value.getValue(Bindings.BOOLEAN); - } catch (AdaptException e) { - throw new SynchronizationException(e); - } - return false; - } - - private boolean isDesynchronized(Collection properties) { - for(SerializedVariable property : properties) - if(property.name.equals("IsDesynchronized")) - try { - return (Boolean)property.value.getValue(Bindings.BOOLEAN); - } catch (AdaptException e) { - throw new SynchronizationException(e); - } - return false; - } - - @Override - public void beginComponent(String name, String typeId, - Collection properties, - Collection connections, - Collection children) - throws SynchronizationException { - try { - if(TRACE_EVENTS) { - System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")"); - if(!children.isEmpty()) { - System.out.println(" Children:"); - for(ChildInfo child : children) - System.out.println(" " + child.name + " " + child.uid); - } - if(!connections.isEmpty()) { - System.out.println(" Connections:"); - for(Connection connection : connections) - System.out.println(" " + connection.relation + " " + connection.connectionPoints); - } - } - - String parentSolverComponentName; - - // Finds the composite - if(component == null) { - name = "COMP_ROOT"; - parentSolverComponentName = ""; - component = mapping.getConfiguration(); - component.setModuleId(solver.getId(name)); - component.solverComponentName = name; - } - else { - parentSolverComponentName = component.solverComponentName; - component = component.getChild(name); - if(component == null) - throw new SynchronizationException("Didn't find '"+name+"'. " - + "It should have been mentioned as a child in the parent beginComponent method."); - } - - potentiallyUpdatedComponents.remove(component); - - ModuleUpdaterBase updater = null; - if(typeId != null) { - updater = moduleUpdaters.get(typeId); - if(updater == null) - throw new SynchronizationException("Undefined typeId " + typeId + "."); - } - - // Handle composite - if(typeId == null || updater.isUserComponent || updater.isComposite) { - // Create or update a subprocess - int moduleId = component.getModuleId(); - boolean justCreated = false; - if(isAttached(properties)) - ; // Subprocesses are not created for attached composites - else if(moduleId <= 0) { - String subprocessName = nameUtil.getFreshName( - parentSolverComponentName, - getSubprocessName(name, properties)); - try { - solver.addSubprocess(subprocessName); - } catch(Exception e) { - reportProblem("Exception while adding subprocess.", e); - } - moduleId = solver.getId(subprocessName); - if(moduleId <= 0) - throw new SynchronizationException("Failed to create a subprocess " + subprocessName); - component.setModuleId(moduleId); - - // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites - if(component.getParent() != null) { - String parentName = solver.getName(component.getParent().getModuleId()); - solver.includeSubprocess(parentName, subprocessName); - component.solverComponentName = subprocessName; - solverComponentNameToComponent.put(subprocessName, component); - } - - if(updater.isComposite) { - final ModuleUpdateContext context = new ModuleUpdateContext(this, updater, component); - updater.create(context, properties, connections); - } - - justCreated = true; - - } else { - - component.solverComponentName = nameUtil.ensureNameIsVariationOf( - parentSolverComponentName, moduleId, - getSubprocessName(name, properties)); - - if(updater.isComposite) { - final ModuleUpdateContext context = new ModuleUpdateContext(this, updater, component); - updater.update(context, properties, connections); - } - - } - if(mapping.getTrustUids()) { - // Create a new child map - THashMap newChildMap = - new THashMap(); - for(ChildInfo info : children) { - // Detach from the existing configuration the children with - // the right uids or create new components if uid is unknown. - T conf = mapping.detachOrCreateComponent(info.uid); - newChildMap.put(info.name, conf); - resolver.markPending(conf); - potentiallyUpdatedComponents.add(conf); - } - - // Put old children not detached in the previous phase - // to the pending removal set. They might have been - // moved somewhere else. - THashMap oldChildMap = - component.setChildMapAndReturnOld(newChildMap); - resolver.unmarkPending(component); - if(oldChildMap != null) - for(T component : oldChildMap.values()) { - component.clearParent(); - mapping.addPendingRemoval(component); - } - } - // Alternative implementation when uids are not available. - else { - // Create a new child map - THashMap newChildMap = - new THashMap(); - Map oldChildMap = - component.getChildMap(); - if(oldChildMap == null) - oldChildMap = Collections.emptyMap(); - for(ChildInfo info : children) { - T conf = oldChildMap.remove(info.name); - if(conf == null) - conf = componentFactory.create(info.uid); - else - conf.uid = info.uid; - newChildMap.put(info.name, conf); - resolver.markPending(conf); - } - component.setChildMap(newChildMap); - - resolver.unmarkPending(component); - if(oldChildMap != null) - for(T component : oldChildMap.values()) { - component.clearParent(); - mapping.addPendingRemoval(component); - } - } - - postCompositeAction(justCreated, properties, connections, updater); - - } - // Handle component - else { - if(!children.isEmpty()) - throw new SynchronizationException("Component with type " + typeId + " cannot have children."); - - boolean attached = isAttached(properties); - component.attached = attached; - - // Create or update the component - final ModuleUpdateContext context = new ModuleUpdateContext(this, updater, component); - int moduleId = component.getModuleId(); - if(moduleId <= 0) { - if(attached) { - component.attached = true; - context.setModuleName(name); - context.setModuleId(solver.getId(name)); - if(context.getModuleId() <= 0) - reportProblem("Didn't find attached module " + name + "."); - else if(!isDesynchronized(properties)) - updater.update(context, properties, connections); - setDidChanges(); - } - else { - component.attached = false; - context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name)); - context.addPostUpdateAction(new Runnable() { - @Override - public void run() { - context.stateLoadedFromUndo = mapping.undoContext.loadState(solver, - context.component.componentId, - context.component.uid); - } - }); - updater.create(context, properties, connections); - solverComponentNameToComponent.put(context.getModuleName(), component); - } - } - else if(!isDesynchronized(properties)) { - context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName, - moduleId, name)); - updater.update(context, properties, connections); - } - else { - resolver.unmarkPending(component); - } - } - } catch(Throwable e) { - Policy.logError(e); - throw new SynchronizationException(e); - } - } - - private String getSubprocessName(String name, - Collection properties) { - for(SerializedVariable property : properties) - if(property.name.equals("HasSubprocessName")) - try { - String value = (String)property.value.getValue(Bindings.STRING); - if (!value.isEmpty()) - return value; - } catch (AdaptException e) { - // This is very improbable exception. - // Just ignore it and return the name. - e.printStackTrace(); - break; - } - return name; - } - - @Override - public void endComponent() { - try { - if(TRACE_EVENTS) - System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")"); - if(component == null) return; - for(T child : component.getChildren()) - if(potentiallyUpdatedComponents.remove(child)) - resolver.unmarkPending(child); - T parent = component.getParent(); - if (parent == null && mapping.getConfiguration() != component) - throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match."); - component = parent; - } catch(Throwable e) { - Policy.logError(e); - throw new SynchronizationException(e); - } - } - - @Override - public void beginType(String id, Collection properties) { - try { - /*if(TRACE_EVENTS) - System.out.println("beginType("+id+")");*/ - ModuleUpdaterBase updater; - try { - updater = moduleUpdaterFactory.createUpdater(id); - } catch (DatabaseException e) { - throw new RuntimeException(e); - } - if(updater == null) - throw new SynchronizationException("Failed to create module updater for id " + id + "."); - moduleUpdaters.put(id, updater); - } catch(Throwable e) { - Policy.logError(e); - throw new SynchronizationException(e); - } - } - - @Override - public void endType() { - /*if(TRACE_EVENTS) - System.out.println("endType()");*/ - } - - public boolean getDidChanges() { - return didChanges; - } - - public void setDidChanges() { - didChanges = true; - } - - public void reportProblem(String description) { - System.err.println(description); - } - - public void reportProblem(String description, Exception e) { - System.err.println(description); - e.printStackTrace(); - } - - public void addPostSynchronizationAction(Runnable action) { - postSynchronizationActions.add(action); - } - - protected void postCompositeAction(boolean justCreated, Collection properties, - Collection connections, ModuleUpdaterBase updater) throws Exception { - } - - - public long getFromRevision() { - return mapping.currentRevision; - } -} +package org.simantics.structural.synchronization.base; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Queue; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.adapter.AdaptException; +import org.simantics.db.exception.DatabaseException; +import org.simantics.structural.synchronization.internal.Policy; +import org.simantics.structural.synchronization.protocol.ChildInfo; +import org.simantics.structural.synchronization.protocol.Connection; +import org.simantics.structural.synchronization.protocol.SerializedVariable; +import org.simantics.structural.synchronization.protocol.SynchronizationEventHandler; +import org.simantics.structural.synchronization.protocol.SynchronizationException; +import org.slf4j.Logger; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; + +/** + * Handles synchronization events by updating the simulator designated by the + * provided {@link Solver} instance. + * + * @author Hannu Niemistö + */ +public abstract class SynchronizationEventHandlerBase> implements SynchronizationEventHandler { + + public static final boolean TRACE_EVENTS = false; + + public final Solver solver; + protected final SolverNameUtil nameUtil; + protected final MappingBase mapping; + final ModuleUpdaterFactoryBase moduleUpdaterFactory; + final ComponentFactory componentFactory; + public final ReferenceResolverBase resolver; + private boolean didChanges = false; + + protected T component; // Current active component + THashMap> moduleUpdaters = new THashMap<>(); + Queue postSynchronizationActions = new ArrayDeque<>(); + protected THashMap> solverComponentNameToComponent = new THashMap<>(); + + /** + * This is a set of components satisfying the following conditions + *
    + *
  • beginComponent is called for their parents + *
  • endComponent is not yet called for their parents + *
  • beginComponent is not yet called for them + *
+ */ + THashSet potentiallyUpdatedComponents = new THashSet<>(); + + public SynchronizationEventHandlerBase(Solver solver, ReferenceResolverBase resolver, SolverNameUtil nameUtil, + ComponentFactory componentFactory, ModuleUpdaterFactoryBase moduleUpdaterFactory, MappingBase mapping) { + this.solver = solver; + this.nameUtil = nameUtil; + this.mapping = mapping; + this.componentFactory = componentFactory; + this.moduleUpdaterFactory = moduleUpdaterFactory; + this.resolver = resolver; + } + + @Override + public void beginSynchronization() { + if(TRACE_EVENTS) { + System.out.println("beginSynchronization()"); + //mapping.printUidMap(); + } + component = null; + } + + @Override + public void endSynchronization() { + try { + if(TRACE_EVENTS) + System.out.println("endSynchronization()"); + if(component != null) + throw new SynchronizationException("beginComponent/endComponent calls do not match."); + + resolver.resolvePendingSelfReferences(); + resolver.printPending(); + + // Do removals + mapping.removePending(solver); + + // Post synchronization actions + Runnable action; + while((action = postSynchronizationActions.poll()) != null) + action.run(); + + // Rename modules to suggested names where possible. + nameUtil.applySuggestedNames((creationName, newName) -> { + ComponentBase component = solverComponentNameToComponent.get(creationName); + if (component != null) { + component.solverComponentName = newName; + } + }); + solverComponentNameToComponent.clear(); + } catch(Throwable e) { + Policy.logError(e); + throw new SynchronizationException(e); + } + } + + private boolean isAttached(Collection properties) { + for(SerializedVariable property : properties) + if(property.name.equals("IsAttached")) + try { + return (Boolean)property.value.getValue(Bindings.BOOLEAN); + } catch (AdaptException e) { + throw new SynchronizationException(e); + } + return false; + } + + private boolean isDesynchronized(Collection properties) { + for(SerializedVariable property : properties) + if(property.name.equals("IsDesynchronized")) + try { + return (Boolean)property.value.getValue(Bindings.BOOLEAN); + } catch (AdaptException e) { + throw new SynchronizationException(e); + } + return false; + } + + @Override + public void beginComponent(String name, String typeId, + Collection properties, + Collection connections, + Collection children) + throws SynchronizationException { + try { + if(TRACE_EVENTS) { + System.out.println("beginComponent("+name+", " + (component != null ? component.uid : "null") + "," + typeId + ")"); + if(!children.isEmpty()) { + System.out.println(" Children:"); + for(ChildInfo child : children) + System.out.println(" " + child.name + " " + child.uid); + } + if(!connections.isEmpty()) { + System.out.println(" Connections:"); + for(Connection connection : connections) + System.out.println(" " + connection.relation + " " + connection.connectionPoints); + } + } + + String parentSolverComponentName; + + // Finds the composite + if(component == null) { + name = "COMP_ROOT"; + parentSolverComponentName = ""; + component = mapping.getConfiguration(); + component.setModuleId(solver.getId(name)); + component.solverComponentName = name; + } + else { + parentSolverComponentName = component.solverComponentName; + component = component.getChild(name); + if(component == null) + throw new SynchronizationException("Didn't find '"+name+"'. " + + "It should have been mentioned as a child in the parent beginComponent method."); + } + + potentiallyUpdatedComponents.remove(component); + + ModuleUpdaterBase updater = null; + if(typeId != null) { + updater = moduleUpdaters.get(typeId); + if(updater == null) + throw new SynchronizationException("Undefined typeId " + typeId + "."); + } + + // Handle composite + if(typeId == null || updater.isUserComponent || updater.isComposite) { + // Create or update a subprocess + int moduleId = component.getModuleId(); + boolean justCreated = false; + if(isAttached(properties)) + ; // Subprocesses are not created for attached composites + else if(moduleId <= 0) { + String subprocessName = nameUtil.getFreshName( + parentSolverComponentName, + getSubprocessName(name, properties)); + try { + solver.addSubprocess(subprocessName, updater.subprocessType); + } catch(Exception e) { + reportProblem("Exception while adding subprocess.", e); + } + moduleId = solver.getId(subprocessName); + if(moduleId <= 0) + throw new SynchronizationException("Failed to create a subprocess " + subprocessName); + component.setModuleId(moduleId); + + // TODO these two lines can be removed when IncludedInSimulation -property is given to all composites + if(component.getParent() != null) { + String parentName = solver.getName(component.getParent().getModuleId()); + solver.includeSubprocess(parentName, subprocessName); + component.solverComponentName = subprocessName; + solverComponentNameToComponent.put(subprocessName, component); + } + + if(updater.isComposite) { + final ModuleUpdateContext context = new ModuleUpdateContext(this, updater, component); + updater.create(context, properties, connections); + } + + justCreated = true; + + } else { + + component.solverComponentName = nameUtil.ensureNameIsVariationOf( + parentSolverComponentName, moduleId, + getSubprocessName(name, properties)); + + if(updater.isComposite) { + final ModuleUpdateContext context = new ModuleUpdateContext(this, updater, component); + updater.update(context, properties, connections); + } + + } + if(mapping.getTrustUids()) { + // Create a new child map + THashMap newChildMap = + new THashMap(); + for(ChildInfo info : children) { + // Detach from the existing configuration the children with + // the right uids or create new components if uid is unknown. + T conf = mapping.detachOrCreateComponent(info.uid); + newChildMap.put(info.name, conf); + resolver.markPending(conf); + potentiallyUpdatedComponents.add(conf); + } + + // Put old children not detached in the previous phase + // to the pending removal set. They might have been + // moved somewhere else. + THashMap oldChildMap = + component.setChildMapAndReturnOld(newChildMap); + resolver.unmarkPending(component); + if(oldChildMap != null) + for(T component : oldChildMap.values()) { + component.clearParent(); + mapping.addPendingRemoval(component); + } + } + // Alternative implementation when uids are not available. + else { + // Create a new child map + THashMap newChildMap = + new THashMap(); + Map oldChildMap = + component.getChildMap(); + if(oldChildMap == null) + oldChildMap = Collections.emptyMap(); + for(ChildInfo info : children) { + T conf = oldChildMap.remove(info.name); + if(conf == null) + conf = componentFactory.create(info.uid); + else + conf.uid = info.uid; + newChildMap.put(info.name, conf); + resolver.markPending(conf); + } + component.setChildMap(newChildMap); + + resolver.unmarkPending(component); + if(oldChildMap != null) + for(T component : oldChildMap.values()) { + component.clearParent(); + mapping.addPendingRemoval(component); + } + } + + postCompositeAction(justCreated, properties, connections, updater); + + } + // Handle component + else { + if(!children.isEmpty()) + throw new SynchronizationException("Component with type " + typeId + " cannot have children."); + + boolean attached = isAttached(properties); + component.attached = attached; + + // Create or update the component + final ModuleUpdateContext context = new ModuleUpdateContext(this, updater, component); + int moduleId = component.getModuleId(); + if(moduleId <= 0) { + if(attached) { + component.attached = true; + context.setModuleName(name); + context.setModuleId(solver.getId(name)); + if(context.getModuleId() <= 0) + reportProblem("Didn't find attached module " + name + "."); + else if(!isDesynchronized(properties)) + updater.update(context, properties, connections); + setDidChanges(); + } + else { + component.attached = false; + context.setModuleName(nameUtil.getFreshName(parentSolverComponentName, name)); + context.addPostUpdateAction(new Runnable() { + @Override + public void run() { + context.stateLoadedFromUndo = mapping.undoContext.loadState(solver, + context.component.componentId, + context.component.uid); + } + }); + updater.create(context, properties, connections); + solverComponentNameToComponent.put(context.getModuleName(), component); + } + } + else if(!isDesynchronized(properties)) { + context.setModuleName(nameUtil.ensureNameIsVariationOf(parentSolverComponentName, + moduleId, name)); + updater.update(context, properties, connections); + } + else { + resolver.unmarkPending(component); + } + } + } catch(Throwable e) { + Policy.logError(e); + throw new SynchronizationException(e); + } + } + + private String getSubprocessName(String name, + Collection properties) { + for(SerializedVariable property : properties) + if(property.name.equals("HasSubprocessName")) + try { + String value = (String)property.value.getValue(Bindings.STRING); + if (!value.isEmpty()) + return value; + } catch (AdaptException e) { + // This is very improbable exception. + // Just ignore it and return the name. + e.printStackTrace(); + break; + } + return name; + } + + @Override + public void endComponent() { + try { + if(TRACE_EVENTS) + System.out.println("endComponent(" + (component != null ? component.solverComponentName : "null") + ")"); + if(component == null) return; + for(T child : component.getChildren()) + if(potentiallyUpdatedComponents.remove(child)) + resolver.unmarkPending(child); + T parent = component.getParent(); + if (parent == null && mapping.getConfiguration() != component) + throw new SynchronizationException("BUG: beginComponent/endComponent calls do not match."); + component = parent; + } catch(Throwable e) { + Policy.logError(e); + throw new SynchronizationException(e); + } + } + + @Override + public void beginType(String id, Collection properties) { + try { + /*if(TRACE_EVENTS) + System.out.println("beginType("+id+")");*/ + ModuleUpdaterBase updater; + try { + updater = moduleUpdaterFactory.createUpdater(id); + } catch (DatabaseException e) { + throw new RuntimeException(e); + } + if(updater == null) + throw new SynchronizationException("Failed to create module updater for id " + id + "."); + moduleUpdaters.put(id, updater); + } catch(Throwable e) { + Policy.logError(e); + throw new SynchronizationException(e); + } + } + + @Override + public void endType() { + /*if(TRACE_EVENTS) + System.out.println("endType()");*/ + } + + public boolean getDidChanges() { + return didChanges; + } + + public void setDidChanges() { + didChanges = true; + } + + public void reportProblem(String description) { + getLogger().error(description); + } + + public void reportProblem(String description, Exception e) { + getLogger().error(description, e); + } + + public void addPostSynchronizationAction(Runnable action) { + postSynchronizationActions.add(action); + } + + protected void postCompositeAction(boolean justCreated, Collection properties, + Collection connections, ModuleUpdaterBase updater) throws Exception { + } + + + public long getFromRevision() { + return mapping.currentRevision; + } + + public abstract Logger getLogger(); +}