/******************************************************************************* * Copyright (c) 2007, 2013 Wind River Systems, Inc. and others. * 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.debug.ui.model; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchesListener; import org.eclipse.debug.core.Launch; import org.eclipse.tcf.internal.debug.model.TCFLaunch; import org.eclipse.tcf.internal.debug.ui.Activator; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchListener; import org.eclipse.ui.PlatformUI; /** * TCFModelManager listens debug launch manager and creates TCF debug models when necessary. */ public class TCFModelManager { private static final Map<TCFLaunch,TCFModel> sync_model_map = Collections.synchronizedMap(new HashMap<TCFLaunch,TCFModel>()); public interface ModelManagerListener { public void onConnected(TCFLaunch launch, TCFModel model); public void onDisconnected(TCFLaunch launch, TCFModel model); } private final Map<TCFLaunch,TCFModel> models = new HashMap<TCFLaunch,TCFModel>(); private final List<ModelManagerListener> listeners = new ArrayList<ModelManagerListener>(); private final TCFLaunch.LaunchListener tcf_launch_listener = new TCFLaunch.LaunchListener() { public void onCreated(TCFLaunch launch) { assert Protocol.isDispatchThread(); assert models.get(launch) == null; TCFModel model = new TCFModel(launch); models.put(launch, model); sync_model_map.put(launch, model); } public void onConnected(TCFLaunch launch) { assert Protocol.isDispatchThread(); TCFModel model = models.get(launch); if (model != null) model.onConnected(); for (ModelManagerListener l : listeners) { try { l.onConnected(launch, model); } catch (Throwable x) { Activator.log(x); } } } public void onDisconnected(TCFLaunch launch) { assert Protocol.isDispatchThread(); TCFModel model = models.get(launch); if (model != null) model.onDisconnected(); for (ModelManagerListener l : listeners) { try { l.onDisconnected(launch, model); } catch (Throwable x) { Activator.log(x); } } } public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data) { assert Protocol.isDispatchThread(); TCFModel model = models.get(launch); if (model != null) model.onProcessOutput(process_id, stream_id, data); } public void onProcessStreamError(TCFLaunch launch, String process_id, int stream_id, Exception error, int lost_size) { assert Protocol.isDispatchThread(); TCFModel model = models.get(launch); if (model != null) model.onProcessStreamError(process_id, stream_id, error, lost_size); } }; private final ILaunchesListener debug_launch_listener = new ILaunchesListener() { public void launchesAdded(final ILaunch[] launches) { } public void launchesChanged(final ILaunch[] launches) { Protocol.invokeAndWait(new Runnable() { public void run() { for (ILaunch launch : launches) { TCFModel model = models.get(launch); if (model != null) model.launchChanged(); } } }); } public void launchesRemoved(final ILaunch[] launches) { Protocol.invokeAndWait(new Runnable() { public void run() { for (ILaunch launch : launches) { TCFModel model = models.remove(launch); if (model != null) { sync_model_map.remove(launch); model.dispose(); } } } }); } }; private final IWorkbenchListener workbench_listener = new IWorkbenchListener() { @Override public boolean preShutdown(IWorkbench workbench, boolean forced) { for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { if (launch instanceof TCFLaunch) { try { ((TCFLaunch)launch).disconnect(); DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch); } catch (Exception x) { Activator.log("Cannot disconnect TCF launch", x); } } } TCFMemoryBlock.onWorkbenchShutdown(); return true; } @Override public void postShutdown(IWorkbench workbench) { } }; public TCFModelManager() { assert Protocol.isDispatchThread(); try { PlatformUI.getWorkbench().addWorkbenchListener(workbench_listener); } catch (IllegalStateException e) { // In headless environments the plug-in load can be still triggered. // Should not trigger an "Unhandled exception in TCF event dispatch thread" } DebugPlugin.getDefault().getLaunchManager().addLaunchListener(debug_launch_listener); TCFLaunch.addListener(tcf_launch_listener); } public void dispose() { assert Protocol.isDispatchThread(); try { PlatformUI.getWorkbench().removeWorkbenchListener(workbench_listener); } catch (IllegalStateException e) { // In headless environments the plug-in load can be still triggered. // Should not trigger an "Unhandled exception in TCF event dispatch thread" } DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(debug_launch_listener); TCFLaunch.removeListener(tcf_launch_listener); for (Iterator<TCFModel> i = models.values().iterator(); i.hasNext();) { TCFModel model = i.next(); sync_model_map.remove(model.getLaunch()); model.dispose(); i.remove(); } assert models.isEmpty(); } public void addListener(ModelManagerListener l) { listeners.add(l); } public void removeListener(ModelManagerListener l) { listeners.remove(l); } public Collection<TCFModel> getModels() { assert Protocol.isDispatchThread(); return models.values(); } public TCFModel getModel(TCFLaunch launch) { assert Protocol.isDispatchThread(); return models.get(launch); } public TCFNodeLaunch getRootNode(TCFLaunch launch) { TCFModel model = getModel(launch); if (model == null) return null; return model.getRootNode(); } public static TCFModelManager getModelManager() { return Activator.getModelManager(); } /** * Synchronized and thread-safe method to map a launch to TCFModel. */ public static TCFModel getModelSync(Launch launch) { if (launch instanceof TCFLaunch) return sync_model_map.get((TCFLaunch)launch); return null; } /** * Synchronized and thread-safe method to map a launch to TCFNodeLaunch. */ public static TCFNodeLaunch getRootNodeSync(Launch launch) { if (launch instanceof TCFLaunch) { TCFModel model = sync_model_map.get((TCFLaunch)launch); if (model != null) return model.getRootNode(); } return null; } }