--- /dev/null
+/*******************************************************************************\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