]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.simulation / src / org / simantics / simulation / project / ExperimentManager.java
diff --git a/bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java b/bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java
new file mode 100644 (file)
index 0000000..b9e2e08
--- /dev/null
@@ -0,0 +1,258 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.simulation.project;\r
+\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.util.concurrent.CopyOnWriteArrayList;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.jface.operation.IRunnableWithProgress;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.eclipse.ui.progress.IProgressService;\r
+import org.osgi.framework.BundleContext;\r
+import org.osgi.framework.ServiceReference;\r
+import org.simantics.Simantics;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.procedure.adapter.ProcedureAdapter;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.service.LifecycleSupport;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.project.IProject;\r
+import org.simantics.simulation.Activator;\r
+import org.simantics.simulation.experiment.ExperimentState;\r
+import org.simantics.simulation.experiment.IExperiment;\r
+import org.simantics.simulation.experiment.IExperimentListener;\r
+import org.simantics.simulation.model.IModel;\r
+import org.simantics.ui.workbench.WorkbenchShutdownService;\r
+import org.simantics.utils.datastructures.ListenerList;\r
+\r
+/**\r
+ * Simple local ExperimentManager implementation\r
+ */\r
+public class ExperimentManager implements IExperimentManager {\r
+\r
+    CopyOnWriteArrayList<IExperimentManagerListener> listeners = new CopyOnWriteArrayList<IExperimentManagerListener>();\r
+    ListenerList<IExperiment> experiments = new ListenerList<IExperiment>(IExperiment.class);\r
+    IExperiment activeExperiment;\r
+    AtomicBoolean isDisposed = new AtomicBoolean(false);\r
+\r
+    public ExperimentManager() {\r
+        BundleContext context = Activator.getDefault().getBundle().getBundleContext();\r
+        ServiceReference<?> ref = context.getServiceReference(WorkbenchShutdownService.class.getName());\r
+        if (ref != null) {\r
+            WorkbenchShutdownService shutdown = (WorkbenchShutdownService) context.getService(ref);\r
+            shutdown.registerShutdownHook(new Runnable() {\r
+                @Override\r
+                public void run() {\r
+                    IRunnableWithProgress runnable = new IRunnableWithProgress() {\r
+                        @Override\r
+                        public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {\r
+                            try {\r
+                                ExperimentManager.this.dispose(monitor);\r
+                            } finally {\r
+                                monitor.done();\r
+                            }\r
+                        }\r
+                    };\r
+                    try {\r
+                        if (PlatformUI.isWorkbenchRunning()) {\r
+                            IProgressService progress = (IProgressService) PlatformUI.getWorkbench().getService(IProgressService.class);\r
+                            progress.run(true, false, runnable);\r
+                        } else {\r
+                            runnable.run(null);\r
+                        }\r
+                    } catch (InvocationTargetException e) {\r
+                        Activator.logError("Experiment manager shutdown failed, see exception for details.", e);\r
+                    } catch (InterruptedException e) {\r
+                        Activator.logError("Experiment manager shutdown was interrupted, see exception for details.", e);\r
+                    }\r
+                }\r
+            });\r
+            context.ungetService(ref);\r
+        }\r
+    }\r
+\r
+    class ManagingExperimentListener implements IExperimentListener {\r
+\r
+        IExperiment experiment;\r
+        boolean setActive;\r
+\r
+        public ManagingExperimentListener(IExperiment experiment, boolean setActive) {\r
+            this.experiment = experiment;\r
+            this.setActive = setActive;\r
+        }\r
+\r
+        @Override\r
+        public void stateChanged(ExperimentState state) {\r
+            if(state==ExperimentState.RUNNING || state==ExperimentState.STOPPED) {\r
+                if(setActive && activeExperiment != experiment)\r
+                    setActiveExperiment(experiment);\r
+            }\r
+            else if(state==ExperimentState.DISPOSED) {\r
+                removeActiveExperiment(experiment);\r
+                experiments.remove(experiment);\r
+                experiment.removeListener(this);\r
+                experiment = null;\r
+            }\r
+        }\r
+    }\r
+\r
+    protected void manageExperiment(IExperiment experiment, boolean setActive) {\r
+        experiments.add(experiment);\r
+        experiment.addListener(new ManagingExperimentListener(experiment, setActive));\r
+    }\r
+\r
+    @Override\r
+    public void startExperiment(final Resource experimentResource,\r
+            final IExperimentActivationListener listener, final boolean setActive) {\r
+        // ENFORCE SINGLE_EXPERIMENT MODE POLICY:\r
+        // Shutdown active experiment before loading new experiment.\r
+        // IMPORTANT: Perform shutdown outside of a graph transaction to allow\r
+        // shutdown to perform graph requests at will.\r
+        synchronized (ExperimentManager.this) {\r
+            ExperimentManagerMode mode = getMode();\r
+            // Close previous active experiment before loading new\r
+            if (mode == ExperimentManagerMode.SINGLE_EXPERIMENT) {\r
+                if (activeExperiment != null && setActive) {\r
+                    // TODO: provide a proper progress monitor for shutdown! \r
+                    activeExperiment.shutdown(null);\r
+                }\r
+            }\r
+        }\r
+\r
+        Simantics.getSession().asyncRequest(new ReadRequest() {\r
+            @Override\r
+            public void run(ReadGraph g) throws DatabaseException {\r
+                \r
+                LifecycleSupport ls = g.getService(LifecycleSupport.class);\r
+                if(ls.isClosing() || ls.isClosed()) {\r
+                    return;\r
+                }\r
+                \r
+                Layer0 L0 = Layer0.getInstance(g);\r
+                final IModel model =\r
+                    g.adaptUnique(\r
+                            g.getSingleObject(experimentResource, L0.PartOf),\r
+                            IModel.class);\r
+\r
+                final IExperimentActivationListener proxy = new ProxyExperimentActivationListener(listener) {\r
+                    @Override\r
+                    public void onExperimentActivated(IExperiment experiment) {\r
+                        if (experiment != null)\r
+                            manageExperiment(experiment, setActive);\r
+                        super.onExperimentActivated(experiment);\r
+                    }\r
+                    @Override\r
+                    public void onFailure(Throwable e) {\r
+                        super.onFailure(e);\r
+                    }\r
+                };\r
+\r
+                // Ignore return value, the experiment is\r
+                // provided to proxy.onExperimentActivated.\r
+                model.loadExperiment(g, experimentResource, proxy);\r
+            }\r
+        }, new ProcedureAdapter<Object>() {\r
+            @Override\r
+            public void exception(Throwable t) {\r
+                listener.onFailure(t);\r
+            }\r
+        });\r
+    }\r
+\r
+    synchronized void setActiveExperiment(IExperiment experiment) {\r
+        // Multiple experiments may need to run concurrently - so don't shutdown\r
+        // if not explicitly requested to do so.\r
+        if (getMode() == ExperimentManagerMode.SINGLE_EXPERIMENT) {\r
+            if (activeExperiment != null) {\r
+                activeExperiment.shutdown(null);\r
+                for (IExperimentManagerListener listener : listeners)\r
+                    listener.activeExperimentUnloaded();\r
+            }\r
+        }\r
+        activeExperiment = experiment;\r
+        for(IExperimentManagerListener listener : listeners)\r
+            listener.activeExperimentLoaded(experiment);\r
+    }\r
+\r
+    synchronized private void removeActiveExperiment(IExperiment experiment) {\r
+        if(activeExperiment == experiment) {\r
+            activeExperiment = null;\r
+            for(IExperimentManagerListener listener : listeners)\r
+                listener.activeExperimentUnloaded();\r
+        }\r
+    }\r
+\r
+    @Override\r
+    synchronized public void addListener(IExperimentManagerListener listener) {\r
+        listeners.add(listener);\r
+        if(activeExperiment != null)\r
+            listener.activeExperimentLoaded(activeExperiment);\r
+    }\r
+\r
+    @Override\r
+    synchronized public void removeListener(IExperimentManagerListener listener) {\r
+        listeners.remove(listener);\r
+    }\r
+\r
+    public void dispose(IProgressMonitor monitor) {\r
+        if(isDisposed.compareAndSet(false, true)) {\r
+            if(activeExperiment != null)\r
+                activeExperiment.shutdown(monitor);\r
+            activeExperiment = null;\r
+\r
+            for(IExperimentManagerListener listener : listeners)\r
+                listener.managerDisposed();\r
+\r
+            if (!listeners.isEmpty()) {\r
+                // Some clients are leaking listeners. Shame on them.\r
+                System.err.println("ExperimentManager still contains the following listeners after disposal:");\r
+                for (IExperimentManagerListener listener : listeners)\r
+                    System.err.println("\t" + listener);\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public IExperiment getActiveExperiment() {\r
+        return activeExperiment;\r
+    }\r
+\r
+    @Override\r
+    public IExperiment getExperiment(final String identifier) {\r
+        if(identifier == null) return null;\r
+        for(IExperiment experiment : experiments.getListeners()) {\r
+            if(experiment != null && identifier.equals(experiment.getIdentifier())) {\r
+                return experiment;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    @Override\r
+    public IExperiment[] getExperiments() {\r
+       return experiments.getListeners();\r
+    }\r
+\r
+    private ExperimentManagerMode getMode() {\r
+        ExperimentManagerMode mode = ExperimentManagerMode.MULTI_EXPERIMENT;\r
+        IProject project = org.simantics.Simantics.peekProject();\r
+        if (project == null)\r
+            return mode;\r
+        mode = project.getHint(ExperimentManagerKeys.EXPERIMENT_MANAGER_MODE);\r
+        return mode != null ? mode : ExperimentManagerMode.MULTI_EXPERIMENT;\r
+    }\r
+\r
+}\r