]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.simulation/src/org/simantics/simulation/project/ExperimentManager.java
Make Write-interfaces as @FunctionalInterface for lambdas
[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
41 /**
42  * Simple local ExperimentManager implementation
43  */
44 public class ExperimentManager implements IExperimentManager {
45
46     CopyOnWriteArrayList<IExperimentManagerListener> listeners = new CopyOnWriteArrayList<IExperimentManagerListener>();
47     ListenerList<IExperiment> experiments = new ListenerList<IExperiment>(IExperiment.class);
48     IExperiment activeExperiment;
49     AtomicBoolean isDisposed = new AtomicBoolean(false);
50
51     public ExperimentManager() {
52         BundleContext context = Activator.getDefault().getBundle().getBundleContext();
53         ServiceReference<?> ref = context.getServiceReference(WorkbenchShutdownService.class.getName());
54         if (ref != null) {
55             WorkbenchShutdownService shutdown = (WorkbenchShutdownService) context.getService(ref);
56             shutdown.registerShutdownHook(new Runnable() {
57                 @Override
58                 public void run() {
59                     IRunnableWithProgress runnable = new IRunnableWithProgress() {
60                         @Override
61                         public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
62                             try {
63                                 ExperimentManager.this.dispose(monitor);
64                             } finally {
65                                 monitor.done();
66                             }
67                         }
68                     };
69                     try {
70                         if (PlatformUI.isWorkbenchRunning()) {
71                             IProgressService progress = (IProgressService) PlatformUI.getWorkbench().getService(IProgressService.class);
72                             progress.run(true, false, runnable);
73                         } else {
74                             runnable.run(null);
75                         }
76                     } catch (InvocationTargetException e) {
77                         Activator.logError("Experiment manager shutdown failed, see exception for details.", e.getCause());
78                     } catch (InterruptedException e) {
79                         Activator.logError("Experiment manager shutdown was interrupted, see exception for details.", e);
80                     }
81                 }
82             });
83             context.ungetService(ref);
84         }
85     }
86
87     class ManagingExperimentListener implements IExperimentListener {
88
89         IExperiment experiment;
90         boolean setActive;
91
92         public ManagingExperimentListener(IExperiment experiment, boolean setActive) {
93             this.experiment = experiment;
94             this.setActive = setActive;
95         }
96
97         @Override
98         public void stateChanged(ExperimentState state) {
99             if(state==ExperimentState.RUNNING || state==ExperimentState.STOPPED) {
100                 if(setActive && activeExperiment != experiment)
101                     setActiveExperiment(experiment);
102             }
103             else if(state==ExperimentState.DISPOSED) {
104                 removeActiveExperiment(experiment);
105                 experiments.remove(experiment);
106                 experiment.removeListener(this);
107                 experiment = null;
108             }
109         }
110     }
111
112     protected void manageExperiment(IExperiment experiment, boolean setActive) {
113         experiments.add(experiment);
114         experiment.addListener(new ManagingExperimentListener(experiment, setActive));
115     }
116
117     @Override
118     public void startExperiment(final Resource experimentResource,
119             final IExperimentActivationListener listener, final boolean setActive) {
120         // ENFORCE SINGLE_EXPERIMENT MODE POLICY:
121         // Shutdown active experiment before loading new experiment.
122         // IMPORTANT: Perform shutdown outside of a graph transaction to allow
123         // shutdown to perform graph requests at will.
124         synchronized (ExperimentManager.this) {
125             ExperimentManagerMode mode = getMode();
126             // Close previous active experiment before loading new
127             if (mode == ExperimentManagerMode.SINGLE_EXPERIMENT) {
128                 if (activeExperiment != null && setActive) {
129                     // TODO: provide a proper progress monitor for shutdown! 
130                     activeExperiment.shutdown(null);
131                 }
132             }
133         }
134
135         Simantics.getSession().asyncRequest(new ReadRequest() {
136             @Override
137             public void run(ReadGraph g) throws DatabaseException {
138                 
139                 LifecycleSupport ls = g.getService(LifecycleSupport.class);
140                 if(ls.isClosing() || ls.isClosed()) {
141                     return;
142                 }
143                 
144                 Layer0 L0 = Layer0.getInstance(g);
145                 final IModel model =
146                     g.adaptUnique(
147                             g.getSingleObject(experimentResource, L0.PartOf),
148                             IModel.class);
149
150                 final IExperimentActivationListener proxy = new ProxyExperimentActivationListener(listener) {
151                     @Override
152                     public void onExperimentActivated(IExperiment experiment) {
153                         if (experiment != null)
154                             manageExperiment(experiment, setActive);
155                         super.onExperimentActivated(experiment);
156                     }
157                     @Override
158                     public void onFailure(Throwable e) {
159                         super.onFailure(e);
160                     }
161                 };
162
163                 // Ignore return value, the experiment is
164                 // provided to proxy.onExperimentActivated.
165                 model.loadExperiment(g, experimentResource, proxy);
166             }
167         }, new ProcedureAdapter<Object>() {
168             @Override
169             public void exception(Throwable t) {
170                 listener.onFailure(t);
171             }
172         });
173     }
174
175     synchronized void setActiveExperiment(IExperiment experiment) {
176         // Multiple experiments may need to run concurrently - so don't shutdown
177         // if not explicitly requested to do so.
178         if (getMode() == ExperimentManagerMode.SINGLE_EXPERIMENT) {
179             if (activeExperiment != null) {
180                 activeExperiment.shutdown(null);
181                 for (IExperimentManagerListener listener : listeners)
182                     listener.activeExperimentUnloaded();
183             }
184         }
185         activeExperiment = experiment;
186         for(IExperimentManagerListener listener : listeners)
187             listener.activeExperimentLoaded(experiment);
188     }
189
190     synchronized private void removeActiveExperiment(IExperiment experiment) {
191         if(activeExperiment == experiment) {
192             activeExperiment = null;
193             for(IExperimentManagerListener listener : listeners)
194                 listener.activeExperimentUnloaded();
195         }
196     }
197
198     @Override
199     synchronized public void addListener(IExperimentManagerListener listener) {
200         listeners.add(listener);
201         if(activeExperiment != null)
202             listener.activeExperimentLoaded(activeExperiment);
203     }
204
205     @Override
206     synchronized public void removeListener(IExperimentManagerListener listener) {
207         listeners.remove(listener);
208     }
209
210     public void dispose(IProgressMonitor monitor) {
211         if(isDisposed.compareAndSet(false, true)) {
212             if(activeExperiment != null)
213                 activeExperiment.shutdown(monitor);
214             activeExperiment = null;
215
216             for(IExperimentManagerListener listener : listeners)
217                 listener.managerDisposed();
218
219             if (!listeners.isEmpty()) {
220                 // Some clients are leaking listeners. Shame on them.
221                 System.err.println("ExperimentManager still contains the following listeners after disposal:");
222                 for (IExperimentManagerListener listener : listeners)
223                     System.err.println("\t" + listener);
224             }
225         }
226     }
227
228     @Override
229     public IExperiment getActiveExperiment() {
230         return activeExperiment;
231     }
232
233     @Override
234     public IExperiment getExperiment(final String identifier) {
235         if(identifier == null) return null;
236         for(IExperiment experiment : experiments.getListeners()) {
237             if(experiment != null && identifier.equals(experiment.getIdentifier())) {
238                 return experiment;
239             }
240         }
241         return null;
242     }
243
244     @Override
245     public IExperiment[] getExperiments() {
246         return experiments.getListeners();
247     }
248
249     private ExperimentManagerMode getMode() {
250         ExperimentManagerMode mode = ExperimentManagerMode.MULTI_EXPERIMENT;
251         IProject project = org.simantics.Simantics.peekProject();
252         if (project == null)
253             return mode;
254         mode = project.getHint(ExperimentManagerKeys.EXPERIMENT_MANAGER_MODE);
255         return mode != null ? mode : ExperimentManagerMode.MULTI_EXPERIMENT;
256     }
257
258 }