Some fixes for resource cleaning spreadsheets in simupedia 68/468/2
authorjsimomaa <jani.simomaa@gmail.com>
Fri, 28 Apr 2017 10:07:23 +0000 (13:07 +0300)
committerJani Simomaa <jani.simomaa@vtt.fi>
Fri, 28 Apr 2017 10:08:01 +0000 (13:08 +0300)
refs #7173

Change-Id: I2b8cf12a0abae25f34e691c5251a34fa0e2ab1f9

13 files changed:
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardNodeManager.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardRealm.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/StandardSessionManager.java
bundles/org.simantics.document.server/scl/Document/All.scl
bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLNodeManager.java
bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLRealm.java
bundles/org.simantics.modeling/src/org/simantics/modeling/scl/SCLSessionManager.java
bundles/org.simantics.spreadsheet.graph/scl/Spreadsheet/All.scl
bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/GraphUI.java
bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetRealm.java
bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/SpreadsheetSessionManager.java
bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/adapter/SpreadsheetBookRemover.java
bundles/org.simantics.spreadsheet.graph/src/org/simantics/spreadsheet/graph/formula/SpreadsheetEvaluationEnvironment.java

index 099ab025f98bf8ad8f22fc86edf74187648a3a5a..c48c3f9d11983f333c78acdfb5e29d12b21011f5 100644 (file)
@@ -223,7 +223,7 @@ public abstract class StandardNodeManager<Node,Engine extends StandardEngine<Nod
     }
     
     protected String getRealmId() {
-       return realm.id;
+       return realm.getId();
     }
     
     public Node getRoot() {
@@ -319,7 +319,7 @@ public abstract class StandardNodeManager<Node,Engine extends StandardEngine<Nod
        checkThreadAccess();
        valueCache.put(node, value);
        realm.getEngine().setValue(node, value);
-       realm.nodeManager.valueCache.put(node, value);
+       realm.getNodeManager().valueCache.put(node, value);
        refreshVariables();
     }
     
@@ -382,5 +382,9 @@ public abstract class StandardNodeManager<Node,Engine extends StandardEngine<Nod
         }
         return null;
     }
-    
+
+    public void clear() {
+        valueCache.clear();
+        listeners.clear();
+    }
 }
index 2232497a22bb5fbc037dd4fd935b0dfb89bfbb63..68d002458b3af80e448f19f2f4314dc6b4794af2 100644 (file)
@@ -1,5 +1,6 @@
 package org.simantics.db.layer0;
 
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.Semaphore;
@@ -14,26 +15,19 @@ import org.simantics.scl.runtime.tuple.Tuple0;
 import org.simantics.simulator.variable.Realm;
 
 abstract public class StandardRealm<Node, Engine extends StandardEngine<Node>> implements Realm {
-       
-    String id;
-    Thread executorThread;
-    ExecutorService executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
-            new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
-        @Override
-        public Thread newThread(Runnable r) {
-            executorThread = new Thread(r);
-            return executorThread;
-        }
-    });
-    
-    Semaphore beginSyncExec = new Semaphore(0);
-    Semaphore endSyncExec = new Semaphore(0);
+
+    private String id;
+    private Thread executorThread;
+    private StandardRealmThreadFactory factory = new StandardRealmThreadFactory(this);
+    private ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<Runnable>(), factory);
+    private Semaphore beginSyncExec = new Semaphore(0);
+    private Semaphore endSyncExec = new Semaphore(0);
     
-    Engine engine;
-    StandardSessionManager<Node, Engine> sessionManager;
-    StandardNodeManager<Node, Engine> nodeManager;
+    private Engine engine;
+    private StandardNodeManager<Node, Engine> nodeManager;
     
-    Runnable scheduleSyncExec = new Runnable() {
+    private Runnable scheduleSyncExec = new Runnable() {
         @Override
         public void run() {
             beginSyncExec.release();
@@ -44,13 +38,12 @@ abstract public class StandardRealm<Node, Engine extends StandardEngine<Node>> i
         }
     };
     
-    protected StandardRealm(StandardSessionManager<Node, Engine> sessionManager, Engine engine, String id) {
-       this.sessionManager = sessionManager;
+    protected StandardRealm(Engine engine, String id) {
         this.engine = engine;
         this.id = id;
         this.nodeManager = createManager();
     }
-    
+
     abstract protected StandardNodeManager<Node, Engine> createManager();
     
     protected String getSCLContextKey() {
@@ -149,16 +142,58 @@ abstract public class StandardRealm<Node, Engine extends StandardEngine<Node>> i
     }
     
     public void close() {
-        sessionManager.removeRealm(id);
         executor.shutdown();
         try {
-            executor.awaitTermination(500L, TimeUnit.MILLISECONDS);
+            if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) {
+                List<Runnable> runnablesLeft = executor.shutdownNow();
+                if (!runnablesLeft.isEmpty()) {
+                    getLogger().info("Runnables left for realm " + this + " after executor shutdown! " + runnablesLeft);
+                }
+            }
         } catch (InterruptedException e) {
+            getLogger().info("Could not shutdown executor " + executor + " for realm " + this, e);
         }
+        
+        factory.clear();
+        factory = null;
+        // Should never be true
+        if (!executorThread.isAlive())
+            executorThread.interrupt();
+        executorThread = null;
+        executor = null;
+        
+        // Clear nodeManager
+        nodeManager.clear();
+        nodeManager = null;
     }
 
     public StandardNodeManager<Node, Engine> getNodeManager() {
         return nodeManager;
     }
-    
+
+    public abstract org.slf4j.Logger getLogger();
+
+    private void setExecutorThread(Thread t) {
+        executorThread = t;
+    }
+
+    private static class StandardRealmThreadFactory implements ThreadFactory {
+        
+        private StandardRealm<?, ?> realm;
+
+        public StandardRealmThreadFactory(StandardRealm<?, ?> realm) {
+            this.realm = realm;
+        }
+        
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(r);
+            realm.setExecutorThread(t);
+            return t;
+        }
+        
+        void clear() {
+            realm = null;
+        }
+    }
 }
index b82860de1010b79bcaa9f31e6737f4cc7e490348..cdd8e074c404ee4d698878b37eaf7d7478c36eca 100644 (file)
@@ -4,6 +4,7 @@ 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;
@@ -75,7 +76,9 @@ abstract public class StandardSessionManager<Node, Engine extends StandardEngine
         if(realm != null) {
             REALMS.put(key, realm);
         } else {
-            REALMS.remove(key);
+            StandardRealm<Node, Engine> removedRealm = REALMS.remove(key);
+            if (removedRealm != null)
+                removedRealm.close();
         }
         Listener<StandardRealm<Node,Engine>> listener = getOrDisposeListener(key);
         if(listener != null) {
@@ -104,10 +107,14 @@ abstract public class StandardSessionManager<Node, Engine extends StandardEngine
     protected abstract Engine createEngine(ReadGraph graph, String id) throws DatabaseException;
     protected abstract StandardRealm<Node,Engine> createRealm(Engine engine, String id);
     
-    public void removeRealm(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
-        SUPPORTS.remove(id);
+        NodeSupport<Node> support = SUPPORTS.remove(id);
+        if (support != null)
+            support.dispose();
     }
     
     public Collection<String> getRealms() {
index fa83a224acfc2d7076af4417e2f2e181680e93e7..98a63269978b01597d1d5ea7fb14628f39fec688 100644 (file)
@@ -150,3 +150,15 @@ consoleLog state message = do
 
 contextDocument :: CommandContext -> <Proc> IDocument
 contextDocument ctx = justValue ctx "__document__"
+
+importJava "org.simantics.document.server.io.IRequest" where
+    @private
+    data IRequest
+    
+    @private
+    getParameter :: IRequest -> String -> <Proc> Maybe String
+
+possibleQueryParameterFromContext :: CommandContext -> String -> <Proc> Maybe String
+possibleQueryParameterFromContext context parameter = do
+    request = fromJust $ possibleValue context "__request__"
+    getParameter request parameter
index e41dd35da4bf934f16c736568aae8fed7e096c3e..2e53ce85a8227591f968c592ed4e5fe789f8c662 100644 (file)
@@ -79,7 +79,7 @@ public class SCLNodeManager extends AbstractNodeManager<String> {
     @Override
     public String getName(String node) {
         if(ROOT.equals(node)) {
-               String id = realm.id;
+               String id = realm.getId();
                int lastSlash = id.lastIndexOf("/");
                if(lastSlash == -1) throw new IllegalStateException("Invalid realm id " + id);
                String name = id.substring(lastSlash+1); 
@@ -226,7 +226,7 @@ public class SCLNodeManager extends AbstractNodeManager<String> {
        checkThreadAccess();
        valueCache.put(node, value);
        realm.getConnection().setVariable(node, getType(node), value);
-       realm.nodeManager.valueCache.put(node, value);
+       realm.getNodeManager().valueCache.put(node, value);
        refreshVariables();
     }
 
@@ -241,8 +241,8 @@ public class SCLNodeManager extends AbstractNodeManager<String> {
        support.structureCache.put(ROOT, null);
        support.valueCache.put(node, null);
        
-       realm.nodeManager.valueCache.put(node, value);
-       realm.nodeManager.
+       realm.getNodeManager().valueCache.put(node, value);
+       realm.getNodeManager().
        refreshVariables();
     }
     
@@ -275,4 +275,9 @@ public class SCLNodeManager extends AbstractNodeManager<String> {
     public String getPropertyURI(String parent, String property) {
         return ModelingResources.URIs.SCLCommandSession_hasValue;
     }
+
+    public void clear() {
+        valueCache.clear();
+        listeners.clear();
+    }
 }
index ea82e7e4ccdbcb97b27d456dc7f5da5df8ea0045..9870ad785b4dabfa6cbc737eaf18bbe58c19aaaa 100644 (file)
@@ -1,8 +1,8 @@
 package org.simantics.modeling.scl;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.ThreadFactory;
@@ -19,7 +19,6 @@ import org.simantics.scl.compiler.types.Type;
 import org.simantics.scl.runtime.SCLContext;
 import org.simantics.scl.runtime.function.Function;
 import org.simantics.scl.runtime.tuple.Tuple0;
-import org.simantics.simulator.variable.NodeManager;
 import org.simantics.simulator.variable.Realm;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,26 +30,21 @@ public class SCLRealm implements Realm {
 
     public static final String SCL = "scl";
     
-    THashMap<String,Type> contextTypes = new THashMap<String,Type>();
+    private THashMap<String,Type> contextTypes = new THashMap<String,Type>();
     
-    CommandSession connection;
-    String id;
-    Thread executorThread;
-    ExecutorService executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
-            new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
-        @Override
-        public Thread newThread(Runnable r) {
-            executorThread = new Thread(r);
-            return executorThread;
-        }
-    });
+    private CommandSession connection;
+    private String id;
+    private Thread executorThread;
+    private SCLRealmThreadFactory factory = new SCLRealmThreadFactory(this);
+    private ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<Runnable>(), factory);
     
-    Semaphore beginSyncExec = new Semaphore(0);
-    Semaphore endSyncExec = new Semaphore(0);
+    private Semaphore beginSyncExec = new Semaphore(0);
+    private Semaphore endSyncExec = new Semaphore(0);
     
-    SCLNodeManager nodeManager;
+    private SCLNodeManager nodeManager;
     
-    Runnable scheduleSyncExec = new Runnable() {
+    private Runnable scheduleSyncExec = new Runnable() {
         @Override
         public void run() {
             beginSyncExec.release();
@@ -170,13 +164,30 @@ public class SCLRealm implements Realm {
         SCLSessionManager.CONNECTIONS.remove(id);
         executor.shutdown();
         try {
-            executor.awaitTermination(500L, TimeUnit.MILLISECONDS);
+            if (!executor.awaitTermination(500L, TimeUnit.MILLISECONDS)) {
+                List<Runnable> runnables = executor.shutdownNow();
+                if (!runnables.isEmpty()) {
+                    LOGGER.info("Some runnables left to execute in realm " + this + ": " + runnables);
+                }
+            }
         } catch (InterruptedException e) {
+            LOGGER.info("Could not shutdown executor " + executor + " in realm " + this, e);
         }
         //connection.close();
+        
+        factory.clear();
+        factory = null;
+        // Should not happen
+        if (executorThread.isAlive())
+            executorThread.interrupt();
+        executorThread = null;
+        executor = null;
+        
+        // clear nodeManager
+        nodeManager.clear();
     }
 
-    public NodeManager<String> getNodeManager() {
+    public SCLNodeManager getNodeManager() {
         return nodeManager;
     }
     
@@ -221,4 +232,27 @@ public class SCLRealm implements Realm {
                }
     }
     
+    private static class SCLRealmThreadFactory implements ThreadFactory {
+
+        private SCLRealm realm;
+
+        public SCLRealmThreadFactory(SCLRealm realm) {
+             this.realm = realm;
+         }
+        
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(r);
+            realm.setThread(t);
+            return t;
+        }
+        
+        void clear() {
+            realm = null;
+        }
+    }
+
+    private void setThread(Thread t) {
+        this.executorThread = t;
+    }
 }
index 8fe0c7095dfe61557e73e289a8bad66bc0a1e721..af036026b7b90a25829366d41f9a5929cd645d80 100644 (file)
@@ -51,8 +51,12 @@ public class SCLSessionManager {
     }
     
     public static synchronized void removeRealm(String id) {
-        CONNECTIONS.remove(id);
+        SCLRealm realm = CONNECTIONS.remove(id);
+        if (realm != null)
+            realm.close();
         // if node support has been created remove it as well
-        SUPPORTS.remove(id);
+        NodeSupport<String> support = SUPPORTS.remove(id);
+        if (support != null)
+            support.dispose();
     }
 }
index 532331e55eec36fab7f7c36a3fbb4cc643eb354d..88d8e47aad2448cc299e3721bded398f92dcccd7 100644 (file)
@@ -56,7 +56,10 @@ importJava "org.simantics.spreadsheet.util.SpreadsheetUtils" where
     selectColumn :: Integer -> TableCell -> Boolean
     setSCLLine :: Resource -> Integer -> String -> <WriteGraph> ()
     sheetRun :: Resource -> Variable -> <ReadGraph> Variable
-    
+
+importJava "org.simantics.spreadsheet.graph.SpreadsheetSessionManager" where
+    removeSpreadsheetSession :: Variable -> <Proc, WriteGraph> ()
+
 importJava "org.simantics.spreadsheet.graph.ExcelImport" where
     importBook :: Resource -> File -> <Proc> ()
 
index ddfe45688d42e1af7bf135c51ada40950110f8fc..5850eb84750d7303e64a33abac540fc850c7e4ab 100644 (file)
@@ -603,7 +603,7 @@ public class GraphUI implements Adaptable, ListenerSupport, AsyncListenerSupport
                                                 String contextURI = context.getURI(graph);
                                                 
                                                 String sessionName = context.getParent(graph).getURI(graph);
-                                                SpreadsheetSessionManager.getInstance().removeRealm(sessionName);
+                                                SpreadsheetSessionManager.getInstance().removeRealm(graph, sessionName);
                                                 SpreadsheetSessionManager.getInstance().getOrCreateRealm(graph, sessionName);
                                             }
                                             
index 0780410e2906eef38dddb1d8a11f3e6a4b9c3fac..90ef89fab61ad5ca4cd8b028b18af160e94f1e1a 100644 (file)
@@ -2,16 +2,25 @@ package org.simantics.spreadsheet.graph;
 
 import org.simantics.db.layer0.StandardNodeManager;
 import org.simantics.db.layer0.StandardRealm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class SpreadsheetRealm extends StandardRealm<SheetNode,SpreadsheetBook> {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(SpreadsheetRealm.class);
+    
        SpreadsheetRealm(SpreadsheetBook book, String id) {
-               super(SpreadsheetSessionManager.getInstance(), book, id);
+               super(book, id);
        }
 
        @Override
        protected StandardNodeManager<SheetNode, SpreadsheetBook> createManager() {
                return new SpreadsheetNodeManager(this);
        }
+
+    @Override
+    public Logger getLogger() {
+        return LOGGER;
+    }
     
 }
index 659eddf7458bbf9648ec2d8321cbd453104c4003..d90fe3106613fbad5907920a5711f00492c4bc51 100644 (file)
@@ -13,18 +13,24 @@ import java.util.Set;
 
 import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.StandardRealm;
 import org.simantics.db.layer0.StandardSessionManager;
 import org.simantics.db.layer0.variable.ProxyVariables;
 import org.simantics.db.layer0.variable.Variable;
 import org.simantics.db.layer0.variable.Variables;
+import org.simantics.spreadsheet.graph.formula.SpreadsheetEvaluationEnvironment;
 import org.simantics.spreadsheet.graph.synchronization.SpreadsheetSynchronizationEventHandler;
 import org.simantics.spreadsheet.resource.SpreadsheetResource;
 import org.simantics.structural.synchronization.Synchronizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class SpreadsheetSessionManager extends StandardSessionManager<SheetNode, SpreadsheetBook> {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(SpreadsheetSessionManager.class);
+    
        private static SpreadsheetSessionManager INSTANCE;
        
        public static SpreadsheetSessionManager getInstance() {
@@ -148,56 +154,18 @@ public class SpreadsheetSessionManager extends StandardSessionManager<SheetNode,
        protected StandardRealm<SheetNode, SpreadsheetBook> createRealm(SpreadsheetBook engine, String id) {
                return new SpreadsheetRealm(engine, id);
        }
-       
-//    static ConcurrentHashMap<String, SpreadsheetRealm> REALMS =
-//            new ConcurrentHashMap<String, SpreadsheetRealm>(); 
-//    
-//    static ConcurrentHashMap<String, NodeSupport<String>> SUPPORTS =
-//            new ConcurrentHashMap<String, NodeSupport<String>>(); 
-//    
-//    public static SpreadsheetRealm sclRealmById(String id) {
-//        // CONNECTIONS is ConcurrentHashMap so no synchronization is needed here
-//        return REALMS.get(id);
-//    }
-//    
-//    public static NodeSupport<String> getOrCreateNodeSupport(String id) {
-//        synchronized(SUPPORTS) {
-//             NodeSupport<String> result = SUPPORTS.get(id);
-//             if(result == null) {
-//                     SpreadsheetRealm realm = getOrCreateSCLRealm(id);
-//                     result = new NodeSupport<String>(realm.getNodeManager());
-//                     SUPPORTS.put(id, result);
-//             }
-//             return result;
-//        }
-//    }
-//    
-//    public static SpreadsheetRealm createRealm() {
-//        synchronized(REALMS) {
-//            String id = UUID.randomUUID().toString();
-//            return createRealm(id);
-//        }
-//    }
-//    
-//    public static SpreadsheetRealm getOrCreateSCLRealm(String id) {
-//        synchronized(REALMS) {
-//             SpreadsheetRealm session = sclRealmById(id);
-//            if(session == null)
-//                return createRealm(id);
-//            else
-//                return session;
-//        }
-//    }
-//    
-//    private static SpreadsheetRealm createRealm(String id) {
-//        SpreadsheetBook book = new SpreadsheetBook();
-//        SpreadsheetRealm realm = new SpreadsheetRealm(book, id);
-//        REALMS.put(id, realm);
-//        return realm;
-//    }
-//    
-//    public static void removeRealm(String id) {
-//             REALMS.remove(id);
-//    }
-       
+
+       @Override
+       public void removeRealm(WriteGraph graph, String id) throws DatabaseException {
+        StandardRealm<SheetNode, SpreadsheetBook> realm = getOrCreateRealm(graph, id);
+        SpreadsheetEvaluationEnvironment.removeInstance(realm.getEngine());
+        super.removeRealm(graph, id);
+    }
+    
+    // Utility function for SCL, this should maybe be replaced with something better in the future
+    public static void removeSpreadsheetSession(WriteGraph graph, Variable runVariable) throws DatabaseException {
+        String uri = runVariable.getParent(graph).getURI(graph);
+        getInstance().removeRealm(graph, uri);
+    }
 }
+
index 63b1bd4d2e2955b4fe598804ed7a2969485f2be6..1388299494c2240765d6609377d8108199a3a099 100644 (file)
@@ -27,7 +27,7 @@ public class SpreadsheetBookRemover extends EntityRemover {
         if (bookURI != null) {
             for (String realmId : SpreadsheetSessionManager.getInstance().getRealms()) {
                 if (realmId.startsWith(bookURI)) {
-                    SpreadsheetSessionManager.getInstance().removeRealm(realmId);
+                    SpreadsheetSessionManager.getInstance().removeRealm(graph, realmId);
                 }
             }
         }
index 2747a337b0e5a0eb108b68517ea8cff6c413870e..08483bccbd6299d502152d5dbed64a9194293165 100644 (file)
@@ -44,7 +44,7 @@ public class SpreadsheetEvaluationEnvironment {
         return book;
     }
 
-    public static Map<SpreadsheetBook, SpreadsheetEvaluationEnvironment> INSTANCES = new HashMap<>();
+    private static Map<SpreadsheetBook, SpreadsheetEvaluationEnvironment> INSTANCES = new HashMap<>();
 
     public static SpreadsheetEvaluationEnvironment getInstance(SpreadsheetBook book) {
         SpreadsheetEvaluationEnvironment env = INSTANCES.get(book);
@@ -55,4 +55,7 @@ public class SpreadsheetEvaluationEnvironment {
         return env;
     }
 
+    public static boolean removeInstance(SpreadsheetBook book) {
+        return INSTANCES.remove(book) != null;
+    }
 }