From: Jussi Koskela Date: Tue, 12 Jun 2018 11:41:51 +0000 (+0300) Subject: New implementation of server state based on StandardNodeManager X-Git-Tag: v1.43.0~136^2~467 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F30%2F1830%2F5;p=simantics%2Fplatform.git New implementation of server state based on StandardNodeManager gitlab #20 Change-Id: I51e56095fb1e76b4c67945eead07d1e9da54eed4 --- diff --git a/bundles/org.simantics.document.server/META-INF/MANIFEST.MF b/bundles/org.simantics.document.server/META-INF/MANIFEST.MF index 016ed9189..a86daf09a 100644 --- a/bundles/org.simantics.document.server/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.document.server/META-INF/MANIFEST.MF @@ -23,7 +23,9 @@ Require-Bundle: org.simantics.modeling;bundle-version="1.1.1", org.simantics.document.server.io;visibility:=reexport, org.simantics.scl.db;bundle-version="0.1.3", - org.slf4j.api + org.slf4j.api, + org.simantics.simulator.toolkit, + org.simantics.simulator.toolkit.db;visibility:=reexport Bundle-ActivationPolicy: lazy Bundle-Activator: org.simantics.document.server.Activator Export-Package: org.simantics.document.server, @@ -31,6 +33,7 @@ Export-Package: org.simantics.document.server, org.simantics.document.server.client, org.simantics.document.server.handler, org.simantics.document.server.request, - org.simantics.document.server.serverResponse + org.simantics.document.server.serverResponse, + org.simantics.document.server.state Import-Package: org.simantics.layer0.utils.direct Bundle-ClassPath: . diff --git a/bundles/org.simantics.document.server/adapters.xml b/bundles/org.simantics.document.server/adapters.xml new file mode 100644 index 000000000..7a1476025 --- /dev/null +++ b/bundles/org.simantics.document.server/adapters.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/bundles/org.simantics.document.server/build.properties b/bundles/org.simantics.document.server/build.properties index 1635b2b1f..af2f18a12 100644 --- a/bundles/org.simantics.document.server/build.properties +++ b/bundles/org.simantics.document.server/build.properties @@ -3,6 +3,7 @@ bin.includes = META-INF/,\ .,\ plugin.xml,\ webdefault.xml,\ - scl/ + scl/,\ + adapters.xml src.includes = scl/ source.. = src/ diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java index 979be3f26..66cc0047c 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/Functions.java @@ -68,6 +68,9 @@ import org.simantics.document.server.request.ServerSCLHandlerValueRequest; import org.simantics.document.server.request.ServerSCLValueRequest; import org.simantics.document.server.serverResponse.ServerResponse; import org.simantics.document.server.serverResponse.SuccessResponse; +import org.simantics.document.server.state.StateNodeManager; +import org.simantics.document.server.state.StateRealm; +import org.simantics.document.server.state.StateSessionManager; import org.simantics.modeling.ModelingResources; import org.simantics.modeling.scl.SCLRealm; import org.simantics.modeling.scl.SCLSessionManager; @@ -1147,11 +1150,6 @@ public class Functions { } else { - String id = sclStateKey(graph, base, self, ref); - - SCLRealm realm = SCLSessionManager.getOrCreateSCLRealm(base.getURI(graph) + "/__scl__"); - realm.getConnection().setVariable(id, getSCLType(defaultValue), defaultValue); - return defaultValue; } @@ -1161,9 +1159,9 @@ public class Functions { String id = sclStateKey(graph, base, self, ref); - SCLRealm realm = SCLSessionManager.getOrCreateSCLRealm(base.getURI(graph)+"/__scl__"); - realm.getConnection().setVariable(id, getSCLType(value), value); - realm.refreshVariablesSync(); + StateRealm realm = (StateRealm) StateSessionManager.getInstance().getOrCreateRealm(graph, base.getURI(graph)+"/__scl__"); + StateNodeManager nodeManager = (StateNodeManager) realm.getNodeManager(); + nodeManager.setState(id, value); } diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/State.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/State.java new file mode 100644 index 000000000..9be1a49e2 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/State.java @@ -0,0 +1,13 @@ +package org.simantics.document.server.state; + +import java.util.Map; +import java.util.TreeMap; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.binding.Binding; +import org.simantics.databoard.binding.mutable.Variant; + +public class State { + public static final Binding BINDING = Bindings.getBindingUnchecked(State.class); + public Map properties = new TreeMap<>(); +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNode.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNode.java new file mode 100644 index 000000000..df82475d7 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNode.java @@ -0,0 +1,7 @@ +package org.simantics.document.server.state; + +import org.simantics.simulator.toolkit.StandardNode; + +public class StateNode implements StandardNode { + +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNodeManager.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNodeManager.java new file mode 100644 index 000000000..0b48e2722 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNodeManager.java @@ -0,0 +1,149 @@ +package org.simantics.document.server.state; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.binding.Binding; +import org.simantics.databoard.binding.error.BindingConstructionException; +import org.simantics.databoard.binding.mutable.Variant; +import org.simantics.databoard.serialization.RuntimeSerializerConstructionException; +import org.simantics.databoard.serialization.SerializerConstructionException; +import org.simantics.db.layer0.variable.NodeSupport; +import org.simantics.simulator.toolkit.StandardNodeManager; +import org.simantics.simulator.toolkit.StandardRealm; +import org.simantics.simulator.variable.exceptions.NodeManagerException; +import org.slf4j.LoggerFactory; + +public class StateNodeManager extends StandardNodeManager { + + private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(StateNodeManager.class); + + private NodeSupport support; + + public StateNodeManager(StandardRealm realm, StateNode root) { + super(realm, root); + } + + public void registerSupport(NodeSupport support) { + this.support = support; + } + + @Override + public Set getClassifications(StateNode node) throws NodeManagerException { + return Collections.emptySet(); + } + + @Override + public void refreshVariable(StateNode node) { + super.refreshVariable(node); + support.valueCache.clearExpired(); + support.structureCache.clearExpired(); + } + + public void setState(String key, Object value) { + try { + getRealm().syncExec(() -> { + try { + StateRootNode rootNode = (StateRootNode) getRoot(); + StatePropertyNode propertyNode = rootNode.getProperty(key); + if (propertyNode == null) { + setValue(rootNode.createProperty(key), value, Bindings.OBJECT); + refreshVariable(rootNode); + } else { + setValue(propertyNode, value, Bindings.OBJECT); + } + } catch (NodeManagerException e) { + LOGGER.error("Failed to set state.", e); + } + }); + } catch (InterruptedException e) { + LOGGER.error("Setting state was interrupted.", e); + } + } + + public byte[] serialize() { + final byte [][] result = new byte[1][]; + try { + getRealm().syncExec(() -> { + State state = new State(); + StateRootNode root = (StateRootNode) getRoot(); + for (StateNode node : root.getProperties().values()) { + StatePropertyNode property = (StatePropertyNode)node; + try { + Binding binding = Bindings.getInstanceBinding(property.getValue()); + if (binding != null) { + state.properties.put(property.getName(), new Variant(binding, property.getValue())); + } + } catch (BindingConstructionException e) { + // skip + } + } + + try { + result[0] = Bindings.getSerializerUnchecked(State.BINDING).serialize(state); + } catch (RuntimeSerializerConstructionException | IOException e) { + } + }); + } catch (InterruptedException e) { + LOGGER.error("Serializing state was interrupted.", e); + } + return result[0]; + } + + public void deserialize(byte[] bytes) { + try { + getRealm().syncExec(() -> { + StateRootNode rootNode = (StateRootNode) getRoot(); + rootNode.clear(); + try { + State state = (State) Bindings.getSerializer(State.BINDING).deserialize(bytes); + for (Map.Entry entry : state.properties.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue().getValue(); + try { + setValue(rootNode.createProperty(key), value, Bindings.OBJECT); + } catch (NodeManagerException e) { + LOGGER.error("Failed to deserialize state.", e); + } + } + refreshVariable(rootNode); + } catch (IOException e1) { + LOGGER.error("Failed to deserialize state.", e1); + } catch (SerializerConstructionException e1) { + LOGGER.error("Failed to deserialize state.", e1); + } + }); + } catch (InterruptedException e) { + LOGGER.error("Deserializing state was interrupted.", e); + } + } + + public void clearState() { + try { + getRealm().syncExec(() -> { + StateRootNode rootNode = (StateRootNode) getRoot(); + rootNode.clear(); + refreshVariable(rootNode); + }); + } catch (InterruptedException e) { + LOGGER.error("Clearing state was interrupted.", e); + } + } + + public void removeState(String key) { + try { + getRealm().syncExec(() -> { + StateRootNode rootNode = (StateRootNode) getRoot(); + if (rootNode.removeProperty(key)) { + refreshVariable(rootNode); + } + }); + } catch (InterruptedException e) { + LOGGER.error("Removing state was interrupted.", e); + } + } + +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNodeManagerSupport.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNodeManagerSupport.java new file mode 100644 index 000000000..748d4f178 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateNodeManagerSupport.java @@ -0,0 +1,57 @@ +package org.simantics.document.server.state; + +import java.util.Collections; +import java.util.Map; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.binding.Binding; +import org.simantics.simulator.toolkit.StandardNodeManagerSupport; +import org.simantics.simulator.variable.exceptions.NoValueException; +import org.simantics.simulator.variable.exceptions.NodeManagerException; + +public class StateNodeManagerSupport implements StandardNodeManagerSupport { + + @Override + public Object getEngineValue(StateNode node) throws NodeManagerException { + if (node instanceof StatePropertyNode) { + return ((StatePropertyNode) node).getValue(); + } + else throw new NoValueException(); + } + + @Override + public Binding getEngineBinding(StateNode node) throws NodeManagerException { + if (node instanceof StatePropertyNode) { + return Bindings.OBJECT; + } + else throw new NoValueException(); + } + + @Override + public void setEngineValue(StateNode node, Object value) throws NodeManagerException { + if (node instanceof StatePropertyNode) { + ((StatePropertyNode) node).setValue(value); + } + else throw new NodeManagerException(); + } + + @Override + public String getName(StateNode node) { + if (node instanceof StatePropertyNode) { + return ((StatePropertyNode) node).getName(); + } else return "@"; + } + + @Override + public Map getChildren(StateNode node) { + return Collections.emptyMap(); + } + + @Override + public Map getProperties(StateNode node) { + if (node instanceof StateRootNode) { + return ((StateRootNode) node).getProperties(); + } else return Collections.emptyMap(); + } + +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StatePropertyNode.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StatePropertyNode.java new file mode 100644 index 000000000..4c0b41054 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StatePropertyNode.java @@ -0,0 +1,23 @@ +package org.simantics.document.server.state; + +public class StatePropertyNode extends StateNode { + + public String name; + public Object value; + + public StatePropertyNode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateRealm.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateRealm.java new file mode 100644 index 000000000..2436bad69 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateRealm.java @@ -0,0 +1,46 @@ +package org.simantics.document.server.state; + +import java.util.function.Function; + +import org.simantics.simulator.toolkit.StandardNodeManager; +import org.simantics.simulator.toolkit.StandardRealm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StateRealm extends StandardRealm { + + private static final Logger LOGGER = LoggerFactory.getLogger(StateRealm.class); + + protected StateRealm(StateNodeManagerSupport engine, String id) { + super(engine, id); + } + + @Override + public Logger getLogger() { + return LOGGER; + } + + @Override + protected StandardNodeManager createManager() { + return new StateNodeManager(this, new StateRootNode()); + } + + @Override + public void asyncExec(Function fun) { + try { + syncExec(fun); + } catch (InterruptedException e) { + LOGGER.error("Execution interrupted.", e); + } + } + + @Override + public void asyncExec(Runnable runnable) { + try { + syncExec(runnable); + } catch (InterruptedException e) { + LOGGER.error("Execution interrupted.", e); + } + } + +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateRootNode.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateRootNode.java new file mode 100644 index 000000000..669cbb2f2 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateRootNode.java @@ -0,0 +1,32 @@ +package org.simantics.document.server.state; + +import java.util.Map; +import java.util.TreeMap; + +public class StateRootNode extends StateNode { + + public Map properties = new TreeMap<>(); + + public Map getProperties() { + return properties; + } + + public StatePropertyNode getProperty(String key) { + return (StatePropertyNode) properties.get(key); + } + + public StatePropertyNode createProperty(String key) { + StatePropertyNode propertyNode = new StatePropertyNode(key); + properties.put(key, propertyNode); + return propertyNode; + } + + public boolean removeProperty(String key) { + return (properties.remove(key) != null); + } + + public void clear() { + properties.clear(); + } + +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateSessionManager.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateSessionManager.java new file mode 100644 index 000000000..267217c20 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateSessionManager.java @@ -0,0 +1,37 @@ +package org.simantics.document.server.state; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.NodeSupport; +import org.simantics.simulator.toolkit.StandardNodeManager; +import org.simantics.simulator.toolkit.StandardRealm; +import org.simantics.simulator.toolkit.db.StandardSessionManager; + +public class StateSessionManager extends StandardSessionManager { + + private static StateSessionManager INSTANCE; + + public static StateSessionManager getInstance() { + if(INSTANCE == null) { + INSTANCE = new StateSessionManager(); + } + return INSTANCE; + } + + @Override + protected StateNodeManagerSupport createEngine(ReadGraph graph, String id) throws DatabaseException { + return new StateNodeManagerSupport(); + } + + @Override + protected StandardRealm createRealm(StateNodeManagerSupport engine, String id) { + return new StateRealm(engine, id); + } + + @Override + public void registerNodeSupport(StandardNodeManager manager, + NodeSupport support) { + ((StateNodeManager)manager).registerSupport(support); + } + +} diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateVariableBuilder.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateVariableBuilder.java new file mode 100644 index 000000000..404a66e26 --- /dev/null +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/state/StateVariableBuilder.java @@ -0,0 +1,23 @@ +package org.simantics.document.server.state; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.NodeManagerVariableBuilder; +import org.simantics.db.layer0.variable.NodeSupport; +import org.simantics.document.server.state.StateNodeManager; +import org.simantics.document.server.state.StateSessionManager; + +public class StateVariableBuilder extends NodeManagerVariableBuilder { + + @Override + protected NodeSupport getNodeSupport(ReadGraph graph, String sessionName) throws DatabaseException { + return StateSessionManager.getInstance().getOrCreateNodeSupport(graph, sessionName); + } + + @Override + protected Object getRoot(ReadGraph graph, NodeSupport support, String sessionName) { + StateNodeManager manager = (StateNodeManager)support.manager; + return manager.getRoot(); + } + +} \ No newline at end of file diff --git a/bundles/org.simantics.modeling/adapters.xml b/bundles/org.simantics.modeling/adapters.xml index d96981fd4..ff53cb579 100644 --- a/bundles/org.simantics.modeling/adapters.xml +++ b/bundles/org.simantics.modeling/adapters.xml @@ -213,11 +213,6 @@ - - - - - realm, NodeSupport support) { + + } + public NodeSupport getOrCreateNodeSupport(ReadGraph graph, String id) throws DatabaseException { synchronized(SUPPORTS) { NodeSupport result = SUPPORTS.get(id); if(result == null) { StandardRealm realm = getOrCreateRealm(graph, id); result = new NodeSupport(realm.getNodeManager()); + registerNodeSupport(realm.getNodeManager(), result); SUPPORTS.put(id, result); } return result; diff --git a/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java b/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java index 87ae4f32f..8f1d1cf53 100644 --- a/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java +++ b/bundles/org.simantics.simulator.toolkit/src/org/simantics/simulator/toolkit/StandardNodeManager.java @@ -43,7 +43,7 @@ import gnu.trove.set.hash.THashSet; /** * StandardNodeManager gives default implementations to some methods * of NodeManager. - * + * * @author Antti Villberg */ public abstract class StandardNodeManager> implements NodeManager { @@ -108,15 +108,15 @@ public abstract class StandardNodeManager valueCache = new THashMap<>(); - protected THashMap> listeners = new THashMap<>(); + THashMap valueCache = new THashMap<>(); + protected THashMap> listeners = new THashMap<>(); AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false); Runnable fireNodeListeners = new Runnable() { @@ -136,7 +136,7 @@ public abstract class StandardNodeManager valueCache.clear(); + Runnable clearValueCache = () -> valueCache.clear(); public StandardNodeManager(StandardRealm realm, Node root) { assert(realm != null); @@ -263,6 +263,20 @@ public abstract class StandardNodeManager { + valueCache.remove(node); + synchronized(listeners) { + THashSet runnables = listeners.get(node); + if (runnables != null) { + for (Runnable r : runnables) { + r.run(); + } + } + } + }); + } + public void refreshVariables() { realm.asyncExec(clearValueCache); fireNodeListeners(); @@ -327,7 +341,7 @@ public abstract class StandardNodeManager