]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java
Merge branch 'feature/funcwrite'
[simantics/platform.git] / bundles / org.simantics.simulation / src / org / simantics / simulation / project / ExperimentManager.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.simulation.project;
13
14 import java.lang.reflect.InvocationTargetException;
15 import java.util.concurrent.CopyOnWriteArrayList;
16 import java.util.concurrent.atomic.AtomicBoolean;
17
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.jface.operation.IRunnableWithProgress;
20 import org.eclipse.ui.PlatformUI;
21 import org.eclipse.ui.progress.IProgressService;
22 import org.osgi.framework.BundleContext;
23 import org.osgi.framework.ServiceReference;
24 import org.simantics.Simantics;
25 import org.simantics.db.ReadGraph;
26 import org.simantics.db.Resource;
27 import org.simantics.db.common.procedure.adapter.ProcedureAdapter;
28 import org.simantics.db.common.request.ReadRequest;
29 import org.simantics.db.exception.DatabaseException;
30 import org.simantics.db.service.LifecycleSupport;
31 import org.simantics.layer0.Layer0;
32 import org.simantics.project.IProject;
33 import org.simantics.simulation.Activator;
34 import org.simantics.simulation.experiment.ExperimentState;
35 import org.simantics.simulation.experiment.IExperiment;
36 import org.simantics.simulation.experiment.IExperimentListener;
37 import org.simantics.simulation.model.IModel;
38 import org.simantics.ui.workbench.WorkbenchShutdownService;
39 import org.simantics.utils.datastructures.ListenerList;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * Simple local ExperimentManager implementation
45  */
46 public class ExperimentManager implements IExperimentManager {
47
48     private static final Logger LOGGER = LoggerFactory.getLogger(ExperimentManager.class);
49
50     CopyOnWriteArrayList<IExperimentManagerListener> listeners = new CopyOnWriteArrayList<IExperimentManagerListener>();
51     ListenerList<IExperiment> experiments = new ListenerList<IExperiment>(IExperiment.class);
52     IExperiment activeExperiment;
53     AtomicBoolean isDisposed = new AtomicBoolean(false);
54
55     public ExperimentManager() {
56         BundleContext context = Activator.getDefault().getBundle().getBundleContext();
57         ServiceReference<?> ref = context.getServiceReference(WorkbenchShutdownService.class.getName());
58         if (ref != null) {
59             WorkbenchShutdownService shutdown = (WorkbenchShutdownService) context.getService(ref);
60             shutdown.registerShutdownHook(new Runnable() {
61                 @Override
62                 public void run() {
63                     IRunnableWithProgress runnable = new IRunnableWithProgress() {
64                         @Override
65                         public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
66                             try {
67                                 ExperimentManager.this.dispose(monitor);
68                             } finally {
69                                 monitor.done();
70                             }
71                         }
72                     };
73                     try {
74                         if (PlatformUI.isWorkbenchRunning()) {
75                             IProgressService progress = (IProgressService) PlatformUI.getWorkbench().getService(IProgressService.class);
76                             progress.run(true, false, runnable);
77                         } else {
78                             runnable.run(null);
79                         }
80                     } catch (InvocationTargetException e) {
81                         Activator.logError("Experiment manager shutdown failed, see exception for details.", e.getCause());
82                     } catch (InterruptedException e) {
83                         Activator.logError("Experiment manager shutdown was interrupted, see exception for details.", e);
84                     }
85                 }
86             });
87             context.ungetService(ref);
88         }
89     }
90
91     class ManagingExperimentListener implements IExperimentListener {
92
93         IExperiment experiment;
94         boolean setActive;
95
96         public ManagingExperimentListener(IExperiment experiment, boolean setActive) {
97             this.experiment = experiment;
98             this.setActive = setActive;
99         }
100
101         @Override
102         public void stateChanged(ExperimentState state) {
103             if(state==ExperimentState.RUNNING || state==ExperimentState.STOPPED) {
104                 if(setActive && activeExperiment != experiment)
105                     setActiveExperiment(experiment);
106             }
107             else if(state==ExperimentState.DISPOSED) {
108                 removeActiveExperiment(experiment);
109                 experiments.remove(experiment);
110                 experiment.removeListener(this);
111                 experiment = null;
112             }
113         }
114     }
115
116     protected void manageExperiment(IExperiment experiment, boolean setActive) {
117         experiments.add(experiment);
118         experiment.addListener(new ManagingExperimentListener(experiment, setActive));
119     }
120
121     @Override
122     public void startExperiment(final Resource experimentResource,
123             final IExperimentActivationListener listener, final boolean setActive) {
124         // ENFORCE SINGLE_EXPERIMENT MODE POLICY:
125         // Shutdown active experiment before loading new experiment.
126         // IMPORTANT: Perform shutdown outside of a graph transaction to allow
127         // shutdown to perform graph requests at will.
128         synchronized (ExperimentManager.this) {
129             ExperimentManagerMode mode = getMode();
130             // Close previous active experiment before loading new
131             if (mode == ExperimentManagerMode.SINGLE_EXPERIMENT) {
132                 if (activeExperiment != null && setActive) {
133                     // TODO: provide a proper progress monitor for shutdown! 
134                     activeExperiment.shutdown(null);
135                 }
136             }
137         }
138
139         Simantics.getSession().asyncRequest(new ReadRequest() {
140             @Override
141             public void run(ReadGraph g) throws DatabaseException {
142                 
143                 LifecycleSupport ls = g.getService(LifecycleSupport.class);
144                 if(ls.isClosing() || ls.isClosed()) {
145                     return;
146                 }
147                 
148                 Layer0 L0 = Layer0.getInstance(g);
149                 final IModel model =
150                     g.adaptUnique(
151                             g.getSingleObject(experimentResource, L0.PartOf),
152                             IModel.class);
153
154                 final IExperimentActivationListener proxy = new ProxyExperimentActivationListener(listener) {
155                     @Override
156                     public void onExperimentActivated(IExperiment experiment) {
157                         if (experiment != null)
158                             manageExperiment(experiment, setActive);
159                         super.onExperimentActivated(experiment);
160                     }
161                     @Override
162                     public void onFailure(Throwable e) {
163                         super.onFailure(e);
164                     }
165                 };
166
167                 // Ignore return value, the experiment is
168                 // provided to proxy.onExperimentActivated.
169                 model.loadExperiment(g, experimentResource, proxy);
170             }
171         }, new ProcedureAdapter<Object>() {
172             @Override
173             public void exception(Throwable t) {
174                 listener.onFailure(t);
175             }
176         });
177     }
178
179     synchronized void setActiveExperiment(IExperiment experiment) {
180         // Multiple experiments may need to run concurrently - so don't shutdown
181         // if not explicitly requested to do so.
182         if (getMode() == ExperimentManagerMode.SINGLE_EXPERIMENT) {
183             if (activeExperiment != null) {
184                 activeExperiment.shutdown(null);
185                 for (IExperimentManagerListener listener : listeners)
186                     listener.activeExperimentUnloaded();
187             }
188         }
189         activeExperiment = experiment;
190         for(IExperimentManagerListener listener : listeners)
191             listener.activeExperimentLoaded(experiment);
192     }
193
194     synchronized private void removeActiveExperiment(IExperiment experiment) {
195         if(activeExperiment == experiment) {
196             activeExperiment = null;
197             for(IExperimentManagerListener listener : listeners)
198                 listener.activeExperimentUnloaded();
199         }
200     }
201
202     @Override
203     synchronized public void addListener(IExperimentManagerListener listener) {
204         listeners.add(listener);
205         if(activeExperiment != null)
206             listener.activeExperimentLoaded(activeExperiment);
207     }
208
209     @Override
210     synchronized public void removeListener(IExperimentManagerListener listener) {
211         listeners.remove(listener);
212     }
213
214     public void dispose(IProgressMonitor monitor) {
215         if(isDisposed.compareAndSet(false, true)) {
216             if(activeExperiment != null)
217                 activeExperiment.shutdown(monitor);
218             activeExperiment = null;
219
220             for(IExperimentManagerListener listener : listeners)
221                 listener.managerDisposed();
222
223             if (!listeners.isEmpty()) {
224                 // Some clients are leaking listeners. Shame on them.
225                 LOGGER.warn("ExperimentManager still contains the following listeners after disposal:");
226                 for (IExperimentManagerListener listener : listeners)
227                     LOGGER.warn("\t" + listener);
228             }
229         }
230     }
231
232     @Override
233     public IExperiment getActiveExperiment() {
234         return activeExperiment;
235     }
236
237     @Override
238     public IExperiment getExperiment(final String identifier) {
239         if(identifier == null) return null;
240         for(IExperiment experiment : experiments.getListeners()) {
241             if(experiment != null && identifier.equals(experiment.getIdentifier())) {
242                 return experiment;
243             }
244         }
245         return null;
246     }
247
248     @Override
249     public IExperiment[] getExperiments() {
250         return experiments.getListeners();
251     }
252
253     private ExperimentManagerMode getMode() {
254         ExperimentManagerMode mode = ExperimentManagerMode.MULTI_EXPERIMENT;
255         IProject project = org.simantics.Simantics.peekProject();
256         if (project == null)
257             return mode;
258         mode = project.getHint(ExperimentManagerKeys.EXPERIMENT_MANAGER_MODE);
259         return mode != null ? mode : ExperimentManagerMode.MULTI_EXPERIMENT;
260     }
261
262 }