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