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: - ILaunch
- ILaunchManager
/**
* 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);
}
}