+package org.simantics.simulator.toolkit.db;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.ParametrizedPrimitiveRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.NodeSupport;
+import org.simantics.db.procedure.Listener;
+import org.simantics.simulator.toolkit.StandardNodeManagerSupport;
+import org.simantics.simulator.toolkit.StandardRealm;
+import org.simantics.simulator.variable.NodeManager;
+
+public abstract class StandardVariableSessionManager<Node, Engine extends StandardNodeManagerSupport<Node>> {
+
+ private ConcurrentHashMap<String, Listener<StandardRealm<Node,Engine>>> realmListeners = new ConcurrentHashMap<>();
+ private ConcurrentHashMap<String, StandardRealm<Node,Engine>> REALMS = new ConcurrentHashMap<>();
+ private ConcurrentHashMap<String, NodeSupport<Node>> SUPPORTS = new ConcurrentHashMap<>();
+
+ // Accessing Realms should be done over ParametrizedPrimitveRead for the
+ // case if a realm is destroyed and new one is created with the same id than
+ // the previously deleted one for the listeners to get discarded and new
+ // registered
+ private class RealmRequest extends ParametrizedPrimitiveRead<String, StandardRealm<Node, Engine>> {
+
+ public RealmRequest(String parameter) {
+ super(parameter);
+ }
+
+ @Override
+ public void register(ReadGraph graph, Listener<StandardRealm<Node, Engine>> procedure) {
+ StandardRealm<Node, Engine> realm = REALMS.get(parameter);
+ if (realm == null) {
+ try {
+ realm = createRealmInner(graph, parameter);
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if(procedure.isDisposed()) {
+ procedure.execute(realm);
+ return;
+ }
+
+ Listener<StandardRealm<Node,Engine>> existing = getOrDisposeListener(parameter);
+ assert(existing == null);
+ realmListeners.put(parameter, procedure);
+ procedure.execute(realm);
+ }
+
+ private StandardRealm<Node,Engine> createRealmInner(ReadGraph graph, String id) throws DatabaseException {
+ Engine engine = createEngine(graph, id);
+ StandardRealm<Node,Engine> realm = createRealm(engine, id);
+ modifyRealms(id, realm);
+ return realm;
+ }
+ }
+
+ protected StandardVariableSessionManager() {
+ }
+
+ private Listener<StandardRealm<Node,Engine>> getOrDisposeListener(String key) {
+ Listener<StandardRealm<Node,Engine>> listener = realmListeners.get(key);
+ if(listener != null) {
+ if(listener.isDisposed()) {
+ realmListeners.remove(key);
+ } else {
+ return listener;
+ }
+ }
+ return null;
+ }
+
+ private void modifyRealms(String key, StandardRealm<Node,Engine> realm) {
+ if(realm != null) {
+ REALMS.put(key, realm);
+ } else {
+ StandardRealm<Node, Engine> removedRealm = REALMS.remove(key);
+ if (removedRealm != null)
+ removedRealm.close();
+ }
+ Listener<StandardRealm<Node,Engine>> listener = getOrDisposeListener(key);
+ if(listener != null) {
+ listener.execute(realm);
+ }
+ }
+
+ public NodeSupport<Node> getOrCreateNodeSupport(ReadGraph graph, String id) throws DatabaseException {
+ synchronized(SUPPORTS) {
+ NodeSupport<Node> result = SUPPORTS.get(id);
+ if(result == null) {
+ StandardRealm<Node,Engine> realm = getOrCreateRealm(graph, id);
+ NodeManager nodeManager = realm.getNodeManager();
+ if(!(nodeManager instanceof StandardVariableNodeManager))
+ throw new DatabaseException("StandardVariableSessionManager requires an instance of StandardVariableNodeManager, got " + nodeManager.getClass());
+ StandardVariableNodeManager svnm = (StandardVariableNodeManager)nodeManager;
+ result = svnm.support;
+ SUPPORTS.put(id, result);
+ }
+ return result;
+ }
+ }
+
+ public StandardRealm<Node,Engine> getOrCreateRealm(ReadGraph graph, String id) throws DatabaseException {
+ synchronized(REALMS) {
+ return graph.syncRequest(new RealmRequest(id));
+ }
+ }
+
+ protected abstract Engine createEngine(ReadGraph graph, String id) throws DatabaseException;
+ protected abstract StandardRealm<Node,Engine> createRealm(Engine engine, String id);
+
+ public void removeRealm(WriteGraph graph, String id) throws DatabaseException {
+ modifyRealms(id, null);
+ // remove listeners from this realm
+ realmListeners.remove(id);
+ // if node support has been created remove it as well
+ NodeSupport<Node> support = SUPPORTS.remove(id);
+ if (support != null)
+ support.dispose();
+ }
+
+ public Collection<String> getRealms() {
+ return REALMS.keySet();
+ }
+}