Copyright (c) 2000, 2018 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Pawel Piech - Bug 82003: The IDisconnect implementation by Launch module is too restrictive. Andrey Loskutov - Bug 506182 - Launch is not multi-thread safe
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Pawel Piech - Bug 82003: The IDisconnect implementation by Launch module is too restrictive. * Andrey Loskutov <loskutov@gmx.de> - Bug 506182 - Launch is not multi-thread safe *******************************************************************************/
package org.eclipse.debug.core; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.internal.core.DebugCoreMessages; import org.eclipse.debug.internal.core.LaunchManager;
A launch is the result of launching a debug session and/or one or more system processes. This class provides a public implementation of ILaunch for client use.

Clients may instantiate this class. Clients may subclass this class.

See Also:
/** * A launch is the result of launching a debug session * and/or one or more system processes. This class provides * a public implementation of <code>ILaunch</code> for client * use. * <p> * Clients may instantiate this class. Clients may subclass this class. * </p> * @see ILaunch * @see ILaunchManager */
public class Launch extends PlatformObject implements ILaunch, IDisconnect, ILaunchListener, ILaunchConfigurationListener, IDebugEventSetListener {
Lock object for controlling access to processes and targets
/** * Lock object for controlling access to processes and targets */
private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock();
The debug targets associated with this launch (the primary target is the first one in this collection), or empty if there are no debug targets.
/** * The debug targets associated with this * launch (the primary target is the first one * in this collection), or empty if * there are no debug targets. */
private List<IDebugTarget> fTargets = new ArrayList<>();
The configuration that was launched, or null.
/** * The configuration that was launched, or null. */
private ILaunchConfiguration fConfiguration= null;
The system processes associated with this launch, or empty if none.
/** * The system processes associated with * this launch, or empty if none. */
private List<IProcess> fProcesses = new ArrayList<>();
The source locator to use in the debug session or null if not supported.
/** * The source locator to use in the debug session * or <code>null</code> if not supported. */
private ISourceLocator fLocator= null;
The mode this launch was launched in.
/** * The mode this launch was launched in. */
private String fMode;
Table of client defined attributes
/** * Table of client defined attributes */
private HashMap<String, String> fAttributes;
Flag indicating that change notification should be suppressed. true until this launch has been initialized.
/** * Flag indicating that change notification should * be suppressed. <code>true</code> until this * launch has been initialized. */
private boolean fSuppressChange = true;
Constructs a launch with the specified attributes.
Params:
  • launchConfiguration – the configuration that was launched
  • mode – the mode of this launch - run or debug (constants defined by ILaunchManager)
  • locator – the source locator to use for this debug session, or null if not supported
/** * Constructs a launch with the specified attributes. * * @param launchConfiguration the configuration that was launched * @param mode the mode of this launch - run or debug (constants * defined by <code>ILaunchManager</code>) * @param locator the source locator to use for this debug session, or * <code>null</code> if not supported */
public Launch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { fConfiguration = launchConfiguration; setSourceLocator(locator); fMode = mode; fSuppressChange = false; getLaunchManager().addLaunchListener(this); getLaunchManager().addLaunchConfigurationListener(this); }
Registers debug event listener.
/** * Registers debug event listener. */
private void addEventListener() { DebugPlugin.getDefault().addDebugEventListener(this); }
Removes debug event listener.
/** * Removes debug event listener. */
private void removeEventListener() { DebugPlugin.getDefault().removeDebugEventListener(this); }
See Also:
  • canTerminate.canTerminate()
/** * @see org.eclipse.debug.core.model.ITerminate#canTerminate() */
@Override public boolean canTerminate() { readLock.lock(); try { for (IProcess process : getProcesses0()) { if (process.canTerminate()) { return true; } } for (IDebugTarget target : getDebugTargets0()) { if (target.canTerminate() || target.canDisconnect()) { return true; } } } finally { readLock.unlock(); } return false; }
See Also:
  • getChildren.getChildren()
/** * @see ILaunch#getChildren() */
@Override public Object[] getChildren() { readLock.lock(); ArrayList<Object> children; try { children = new ArrayList<>(getDebugTargets0()); children.addAll(getProcesses0()); } finally { readLock.unlock(); } return children.toArray(); }
See Also:
  • getDebugTarget.getDebugTarget()
/** * @see ILaunch#getDebugTarget() */
@Override public IDebugTarget getDebugTarget() { readLock.lock(); try { if (!getDebugTargets0().isEmpty()) { return getDebugTargets0().get(0); } } finally { readLock.unlock(); } return null; }
See Also:
  • getProcesses.getProcesses()
/** * @see ILaunch#getProcesses() */
@Override public IProcess[] getProcesses() { readLock.lock(); try { return getProcesses0().toArray(new IProcess[getProcesses0().size()]); } finally { readLock.unlock(); } }
Returns the processes associated with this launch, in its internal form - a list.
Returns:list of processes
/** * Returns the processes associated with this * launch, in its internal form - a list. * * @return list of processes */
protected List<IProcess> getProcesses0() { return fProcesses; }
See Also:
  • getSourceLocator.getSourceLocator()
/** * @see ILaunch#getSourceLocator() */
@Override public ISourceLocator getSourceLocator() { return fLocator; }
See Also:
  • setSourceLocator.setSourceLocator(ISourceLocator)
/** * @see ILaunch#setSourceLocator(ISourceLocator) */
@Override public void setSourceLocator(ISourceLocator sourceLocator) { fLocator = sourceLocator; }
See Also:
  • isTerminated.isTerminated()
/** * @see org.eclipse.debug.core.model.ITerminate#isTerminated() */
@Override public boolean isTerminated() { readLock.lock(); try { if (getProcesses0().isEmpty() && getDebugTargets0().isEmpty()) { return false; } for (IProcess process : getProcesses0()) { if (!process.isTerminated()) { return false; } } for (IDebugTarget target : getDebugTargets0()) { if (!(target.isTerminated() || target.isDisconnected())) { return false; } } } finally { readLock.unlock(); } return true; }
See Also:
  • terminate.terminate()
/** * @see org.eclipse.debug.core.model.ITerminate#terminate() */
@Override public void terminate() throws DebugException { MultiStatus status= new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.Launch_terminate_failed, null); //stop targets first to free up and sockets, etc held by the target // terminate or disconnect debug target if it is still alive IDebugTarget[] targets = getDebugTargets(); for (IDebugTarget target : targets) { if (target != null) { if (target.canTerminate()) { try { target.terminate(); } catch (DebugException e) { status.merge(e.getStatus()); } } else { if (target.canDisconnect()) { try { target.disconnect(); } catch (DebugException de) { status.merge(de.getStatus()); } } } } } //second kill the underlying process // terminate the system processes IProcess[] processes = getProcesses(); for (IProcess process : processes) { if (process.canTerminate()) { try { process.terminate(); } catch (DebugException e) { status.merge(e.getStatus()); } } } if (status.isOK()) { return; } IStatus[] children= status.getChildren(); if (children.length == 1) { throw new DebugException(children[0]); } throw new DebugException(status); }
See Also:
  • getLaunchMode.getLaunchMode()
/** * @see ILaunch#getLaunchMode() */
@Override public String getLaunchMode() { return fMode; }
See Also:
  • getLaunchConfiguration.getLaunchConfiguration()
/** * @see ILaunch#getLaunchConfiguration() */
@Override public ILaunchConfiguration getLaunchConfiguration() { return fConfiguration; }
See Also:
  • setAttribute.setAttribute(String, String)
/** * @see ILaunch#setAttribute(String, String) */
@Override public void setAttribute(String key, String value) { if (fAttributes == null) { fAttributes = new HashMap<>(5); } fAttributes.put(key, value); }
See Also:
  • getAttribute.getAttribute(String)
/** * @see ILaunch#getAttribute(String) */
@Override public String getAttribute(String key) { if (fAttributes == null) { return null; } return fAttributes.get(key); }
See Also:
  • getDebugTargets.getDebugTargets()
/** * @see ILaunch#getDebugTargets() */
@Override public IDebugTarget[] getDebugTargets() { readLock.lock(); try { return fTargets.toArray(new IDebugTarget[fTargets.size()]); } finally { readLock.unlock(); } }
Returns the debug targets associated with this launch, in its internal form - a list
Returns:list of debug targets
/** * Returns the debug targets associated with this * launch, in its internal form - a list * * @return list of debug targets */
protected List<IDebugTarget> getDebugTargets0() { return fTargets; }
See Also:
  • addDebugTarget.addDebugTarget(IDebugTarget)
/** * @see ILaunch#addDebugTarget(IDebugTarget) */
@Override public void addDebugTarget(IDebugTarget target) { if (target != null) { writeLock.lock(); boolean changed = false; try { if (!getDebugTargets0().contains(target)) { addEventListener(); changed = getDebugTargets0().add(target); } } finally { writeLock.unlock(); if (changed) { fireChanged(); } } } }
See Also:
  • removeDebugTarget.removeDebugTarget(IDebugTarget)
/** * @see ILaunch#removeDebugTarget(IDebugTarget) */
@Override public void removeDebugTarget(IDebugTarget target) { if (target != null) { writeLock.lock(); boolean changed = false; try { changed = getDebugTargets0().remove(target); } finally { writeLock.unlock(); if (changed) { fireChanged(); } } } }
See Also:
  • addProcess.addProcess(IProcess)
/** * @see ILaunch#addProcess(IProcess) */
@Override public void addProcess(IProcess process) { if (process != null) { writeLock.lock(); boolean changed = false; try { if (!getProcesses0().contains(process)) { addEventListener(); changed = getProcesses0().add(process); } } finally { writeLock.unlock(); if (changed) { fireChanged(); } } } }
See Also:
  • removeProcess.removeProcess(IProcess)
/** * @see ILaunch#removeProcess(IProcess) */
@Override public void removeProcess(IProcess process) { if (process != null) { writeLock.lock(); boolean changed = false; try { changed = getProcesses0().remove(process); } finally { writeLock.unlock(); if (changed) { fireChanged(); } } } }
Adds the given processes to this launch.
Params:
  • processes – processes to add
/** * Adds the given processes to this launch. * * @param processes processes to add */
protected void addProcesses(IProcess[] processes) { if (processes != null) { for (IProcess process : processes) { addProcess(process); fireChanged(); } } }
Notifies listeners that this launch has changed. Has no effect of this launch has not yet been properly created/initialized.
/** * Notifies listeners that this launch has changed. * Has no effect of this launch has not yet been * properly created/initialized. */
protected void fireChanged() { if (!fSuppressChange) { ((LaunchManager)getLaunchManager()).fireUpdate(this, LaunchManager.CHANGED); ((LaunchManager)getLaunchManager()).fireUpdate(new ILaunch[] {this}, LaunchManager.CHANGED); } }
Notifies listeners that this launch has terminated. Has no effect of this launch has not yet been properly created/initialized.
/** * Notifies listeners that this launch has terminated. * Has no effect of this launch has not yet been * properly created/initialized. */
protected void fireTerminate() { if (!fSuppressChange) { ((LaunchManager)getLaunchManager()).fireUpdate(this, LaunchManager.TERMINATE); ((LaunchManager)getLaunchManager()).fireUpdate(new ILaunch[] {this}, LaunchManager.TERMINATE); } removeEventListener(); }
See Also:
  • hasChildren.hasChildren()
/** * @see ILaunch#hasChildren() */
@Override public boolean hasChildren() { return getProcesses0().size() > 0 || (getDebugTargets0().size() > 0); }
Returns whether any processes or targets can be disconnected. Ones that are already terminated or disconnected are ignored.
See Also:
  • canDisconnect.canDisconnect()
/** * Returns whether any processes or targets can be disconnected. * Ones that are already terminated or disconnected are ignored. * * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect() */
@Override public boolean canDisconnect() { readLock.lock(); try { for (IProcess process : getProcesses0()) { if (process instanceof IDisconnect) { if (((IDisconnect) process).canDisconnect()) { return true; } } } for (IDebugTarget target : getDebugTargets0()) { if (target.canDisconnect()) { return true; } } } finally { readLock.unlock(); } return false; }
See Also:
  • disconnect.disconnect()
/** * @see org.eclipse.debug.core.model.IDisconnect#disconnect() */
@Override public void disconnect() throws DebugException { readLock.lock(); try { for (IProcess process : getProcesses0()) { if (process instanceof IDisconnect) { IDisconnect dis = (IDisconnect) process; if (dis.canDisconnect()) { dis.disconnect(); } } } for (IDebugTarget target : getDebugTargets0()) { if (target.canDisconnect()) { target.disconnect(); } } } finally { readLock.unlock(); } }
Returns whether all of the contained targets and processes are disconnected. Processes that don't support disconnecting are not counted.
See Also:
  • isDisconnected.isDisconnected()
/** * Returns whether all of the contained targets and processes are * disconnected. Processes that don't support disconnecting are not * counted. * * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected() */
@Override public boolean isDisconnected() { readLock.lock(); try { for (IProcess process : getProcesses0()) { if (process instanceof IDisconnect) { if (!((IDisconnect) process).isDisconnected()) { return false; } } } for (IDebugTarget target : getDebugTargets0()) { if (!target.isDisconnected()) { return false; } } } finally { readLock.unlock(); } // only return true if there are processes or targets that are disconnected return hasChildren(); } @Override public void launchRemoved(ILaunch launch) { if (this.equals(launch)) { removeEventListener(); getLaunchManager().removeLaunchListener(this); getLaunchManager().removeLaunchConfigurationListener(this); } }
Returns the launch manager.
Returns:the launch manager.
/** * Returns the launch manager. * * @return the launch manager. */
protected ILaunchManager getLaunchManager() { return DebugPlugin.getDefault().getLaunchManager(); } @Override public void launchAdded(ILaunch launch) { } @Override public void launchChanged(ILaunch launch) { } /* * If the launch configuration this launch is associated with is moved, * update the underlying handle to the new location. */ @Override public void launchConfigurationAdded(ILaunchConfiguration configuration) { ILaunchConfiguration from = getLaunchManager().getMovedFrom(configuration); if (from != null && from.equals(getLaunchConfiguration())) { fConfiguration = configuration; fireChanged(); } } @Override public void launchConfigurationChanged(ILaunchConfiguration configuration) {} /* * Update the launch configuration associated with this launch if the * underlying configuration is deleted. */ @Override public void launchConfigurationRemoved(ILaunchConfiguration configuration) { if (configuration.equals(getLaunchConfiguration())) { if (getLaunchManager().getMovedTo(configuration) == null) { fConfiguration = null; fireChanged(); } } } @Override public void handleDebugEvents(DebugEvent[] events) { for (DebugEvent event : events) { if (event.getKind() == DebugEvent.TERMINATE) { Object object = event.getSource(); ILaunch launch = null; if (object instanceof IProcess) { launch = ((IProcess)object).getLaunch(); } else if (object instanceof IDebugTarget) { launch = ((IDebugTarget)object).getLaunch(); } if (this.equals(launch)) { if (isTerminated()) { fireTerminate(); } } } } } @SuppressWarnings("unchecked") @Override public <T> T getAdapter(Class<T> adapter) { if (adapter.equals(ILaunch.class)) { return (T) this; } //CONTEXTLAUNCHING if(adapter.equals(ILaunchConfiguration.class)) { return (T) getLaunchConfiguration(); } return super.getAdapter(adapter); } }