blob: 3e26459cdc9dc01ead6d0767df1a6dc3cb86880e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 Nokia 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:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.launch;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.core.sourcelookup.AbsolutePathSourceContainer;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.ExecutablesSourceContainer;
import org.eclipse.cdt.debug.edc.internal.PathUtils;
import org.eclipse.cdt.debug.edc.internal.launch.ShutdownSequence;
import org.eclipse.cdt.debug.edc.internal.services.dsf.Processes;
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExitedEvent;
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.RootExecutionDMC;
import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
import org.eclipse.cdt.dsf.debug.model.DsfLaunch;
import org.eclipse.cdt.dsf.debug.model.DsfMemoryBlockRetrieval;
import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.Protocol;
/**
* The only object in the model that implements the traditional interfaces.
*/
@ThreadSafe
abstract public class EDCLaunch extends DsfLaunch {
private DefaultDsfExecutor executor;
private DsfSession session;
private DsfServicesTracker tracker;
private boolean initialized;
private boolean shutDown;
private boolean isLaunching;
private boolean isTerminating;
private DsfMemoryBlockRetrieval memRetrieval;
private IDsfDebugServicesFactory serviceFactory;
private final String debugModelID;
private String description;
private Album album;
private boolean snapshotSupportInitialized;
private boolean isFirstLaunch = true;
private ILaunchConfiguration activeLaunchConfiguration;
private List<ILaunchConfiguration> affiliatedLaunchConfigurations = Collections.synchronizedList(new ArrayList<ILaunchConfiguration>());
private boolean isTerminatedThanDisconnected = false;
private boolean shuttingDown;
private static final Map<EDCLaunch, List<IChannel>> launchChannels = Collections
.synchronizedMap(new HashMap<EDCLaunch, List<IChannel>>());
private static final Map<String, EDCLaunch> launchSessions = Collections
.synchronizedMap(new HashMap<String, EDCLaunch>());
/**
* Every EDC (DSF) session has a thread pool in which to delegate blocking
* code to. See AbstractEDCService.asyncExec()
*/
private static final Map<String, ExecutorService> threadPools = Collections
.synchronizedMap(new HashMap<String, ExecutorService>());
public EDCLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator, String ownerID) {
super(launchConfiguration, mode, locator);
debugModelID = ownerID;
}
private void startSession()
{
// Create the dispatch queue to be used by debugger control and services
// that belong to this launch
final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor("DSF executor - " + debugModelID); //$NON-NLS-1$
dsfExecutor.prestartCoreThread();
executor = dsfExecutor;
session = DsfSession.startSession(executor, debugModelID);
launchSessions.put(session.getId(), this);
threadPools.put(session.getId(), newThreadPool());
try {
setSourceLocator(createSourceLocator());
} catch (CoreException e) {
EDCDebugger.getMessageLogger().logException(e);
}
}
/**
* Obtains a new thread pool
*/
private ThreadPoolExecutor newThreadPool() {
// Thread pools can be a tricky thing. The behaviors available for
// ThreadPoolExecutor are particularly so. Grab a handful of
// aspirin and read the class javadoc if you're up for it. Basically,
// what we're going to do here is create a thread pool that hopes to
// meet demand with three threads. It will use a queue to allow itself
// to be backlogged by a maximum of 10,000 requests. If the requests
// keep pouring in and the backlog limit is blown, the pool will
// dispatch up to three additional threads (for a total of six) to try
// to handle the load. If it still can't keep up after that, then it
// throws up its hands and starts rejecting requests. We need to protect
// ourselves from exhausting cpu/system resources (a million threads) or
// memory (millions of backlogged requests). A coding or runtime mishap
// could lead to either if we don't set limits on the thread pool. But
// what are reasonable limits? The best we can do is set some values we
// think will work, use checks to make us or the user aware when they
// don't, and give the user a backdoor mechanism to tweak the values
// until we can provide better default values.
//
// Also, keep in mind that the thread pool will try to trim down the
// number of threads if and when it has had to resort to creating
// additional ones to handle a heavy load. If a thread is idle for more
// than ten seconds, it will be shut down.
int coreThreadCount = getBackdoorValue("org.eclipse.cdt.edc.poolthread.coreThreadCount", 3);
int maxThreadCount = getBackdoorValue("org.eclipse.cdt.edc.poolthread.maxThreadCount", coreThreadCount + 3);
long idleLimit = getBackdoorValue("org.eclipse.cdt.edc.poolthread.idleLimit", 10);
int queueLimit = getBackdoorValue("org.eclipse.cdt.edc.poolthread.queueLimit", 10000);
return new ThreadPoolExecutor(coreThreadCount, maxThreadCount,
idleLimit, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(queueLimit));
}
/**
* Provide a backdoor mechanism for tweaking the thread pool parameters on
* the field. Hopefully this will never be needed, but better safe than
* sorry.
*/
private static int getBackdoorValue(String prop, int defaultVal) {
String value = System.getProperty(prop);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException exc){}
}
return defaultVal;
}
public static EDCLaunch getLaunchForSession(String sessionID) {
return launchSessions.get(sessionID);
}
/**
* See {@link #threadPools}
* @since 2.0
*/
public static ExecutorService getThreadPool(String sessionID) {
return threadPools.get(sessionID);
}
public DsfExecutor getDsfExecutor() {
return executor;
}
public IDsfDebugServicesFactory getServiceFactory() {
return serviceFactory;
}
public void initialize() {
Runnable initRunnable = new DsfRunnable() {
public void run() {
tracker = new DsfServicesTracker(EDCDebugger.getDefault().getBundle().getBundleContext(), session
.getId());
session.addServiceEventListener(EDCLaunch.this, null);
memRetrieval = null;
initialized = true;
fireChanged();
}
};
// Invoke the execution code and block waiting for the result.
try {
executor.submit(initRunnable).get();
} catch (Exception e) {
EDCDebugger.getMessageLogger().log(
new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Error initializing launch", e)); //$NON-NLS-1$
}
}
public void initializeMemoryRetrieval() throws CoreException {
// Create a memory retrieval and register it with the session
try {
executor.submit(new Callable<Object>() {
public Object call() throws CoreException {
memRetrieval = new DsfMemoryBlockRetrieval(getDebugModelID(), getLaunchConfiguration(), session);
session.registerModelAdapter(IMemoryBlockRetrieval.class, memRetrieval);
return null;
}
}).get();
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, 0,
"Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$
} catch (ExecutionException e) {
throw (CoreException) e.getCause();
} catch (RejectedExecutionException e) {
throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, 0,
"Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
}
}
public DsfSession getSession() {
if (session == null)
startSession();
return session;
}
public DsfMemoryBlockRetrieval getMemoryBlockRetrieval() {
return memRetrieval;
}
public void setServiceFactory(IDsfDebugServicesFactory factory) {
serviceFactory = factory;
}
// Event handler when a thread or a threadGroup exits
@DsfServiceEventHandler
public void eventDispatched(IExitedDMEvent e) {
// Only shutdown the session if the RootDMC is exited, namely all processDMCs
// are terminated or disconnected.
if (! (e instanceof ExitedEvent))
return;
// Synchronize here in case a launch delegate is trying to do more
// launching at the same time.
synchronized (this)
{
if (e.getDMContext() instanceof RootExecutionDMC && !isLaunching()) {
// Don't terminate the launch if a delegate has already started
// using it for new activity.
// The ExitedEvent tells us whether the last context in the launch
// is terminated or disconnected.
setTerminating(true);
isTerminatedThanDisconnected = ((ExitedEvent)e).isTerminatedThanDisconnected();
shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
}
}
}
// /////////////////////////////////////////////////////////////////////////
// IServiceEventListener
@DsfServiceEventHandler
public void eventDispatched(ICommandControlShutdownDMEvent event) {
shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
}
// /////////////////////////////////////////////////////////////////////////
// ITerminate
@Override
public boolean canTerminate() {
return initialized && !shutDown;
}
@Override
public boolean isTerminated() {
// This return value is irrelevant to whether the session
// is terminated or disconnected.
return shutDown;
}
@Override
public void terminate() throws DebugException {
// Step one, tell the run control service to terminate everything
DsfExecutor dsfExecutor = getDsfExecutor();
if (dsfExecutor != null) {
setTerminating(true);
getDsfExecutor().execute(new Runnable() {
public void run() {
RunControl runControlService = tracker
.getService(RunControl.class);
if (runControlService != null)
runControlService.terminateAllContexts(null);
}
});
}
}
// ITerminate
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// IDisconnect
@Override
public boolean canDisconnect() {
return !(snapshotSupportInitialized && isSnapshotLaunch()) && canTerminate();
}
@Override
public boolean isDisconnected() {
// Indicates whether the launch (session) is terminated
// by "disconnect" command.
return isTerminated() && ! isTerminatedThanDisconnected;
}
@Override
public void disconnect() throws DebugException {
DsfExecutor dsfExecutor = getDsfExecutor();
if (dsfExecutor != null) {
getDsfExecutor().execute(new Runnable() {
public void run() {
Processes procService = tracker.getService(Processes.class);
if (procService != null)
procService.detachDebuggerFromSession(null);
}
});
}
}
// IDisconnect
// /////////////////////////////////////////////////////////////////////////
/**
* Shuts down the services, the session and the executor associated with
* this launch.
* <p>
* Note: The argument request monitor to this method should NOT use the
* executor that belongs to this launch. By the time the shutdown is
* complete, this executor will not be dispatching anymore and the request
* monitor will never be invoked. Instead callers should use the
* {@link ImmediateExecutor}.
* </p>
*
* @param rm
* The request monitor invoked when the shutdown is complete.
*/
@ConfinedToDsfExecutor("getSession().getExecutor()")
public void shutdownSession(final RequestMonitor rm) {
if (shutDown || shuttingDown || executor == null) {
rm.done();
return;
}
shuttingDown = true;
// Shut down the thread pool, otherwise its core threads might hang
// around indefinitely.
ExecutorService pool = threadPools.get(session.getId());
if (pool != null) {
pool.shutdown();
try {
// We need to wait for the threads actively handling a task
// to complete. If we proceed with the session shutdown before
// that, the active threads are likely to encounter problem as
// they try to operate within a defunct session. Don't wait
// indefinitely, though. Note that this does not block the
// UI from showing the session as terminated. For the most part,
// things on the surface should look like the debug session
// ended. Obviously, cleanup will be pending.
pool.awaitTermination(15, TimeUnit.SECONDS);
} catch (InterruptedException exc) {
EDCDebugger.getMessageLogger().logException(exc);
}
}
Sequence shutdownSeq = new ShutdownSequence(getDsfExecutor(), session.getId(), new RequestMonitor(session
.getExecutor(), rm) {
@Override
public void handleCompleted() {
session.removeServiceEventListener(EDCLaunch.this);
if (!isSuccess()) {
EDCDebugger.getMessageLogger().log(
new MultiStatus(EDCDebugger.PLUGIN_ID, -1, new IStatus[] { getStatus() },
"Session shutdown failed", null)); //$NON-NLS-1$
}
// Last order of business, shutdown the dispatch queue.
if (tracker != null)
tracker.dispose();
tracker = null;
DsfSession.endSession(session);
// DsfMemoryBlockRetrieval.saveMemoryBlocks();
if (memRetrieval != null) {
memRetrieval.saveMemoryBlocks();
}
// endSession takes a full dispatch to distribute the
// session-ended event, finish step only after the dispatch.
executor.shutdown();
executor = null;
shutDown = true; // note this controls whether the launch canTerminate().
fireTerminate();
try {
closeUnusedChannels();
} catch (Throwable e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
rm.setStatus(getStatus());
rm.done();
}
});
executor.execute(shutdownSeq);
try {
ILaunchConfiguration activeLaunchConfig = getLaunchConfiguration();
if (activeLaunchConfig.getAttribute(IEDCLaunchConfigurationConstants.ATTR_IS_ONE_USE, false))
activeLaunchConfig.delete();
if (isSnapshotLaunch()) {
// delete launch configuration
ILaunchConfiguration lc = SnapshotUtils.findExistingLaunchForAlbum(album);
if (lc != null){
lc.delete();
}
}
} catch (Throwable e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
protected void closeUnusedChannels() {
synchronized (launchChannels) {
List<IChannel> channelList = launchChannels.get(this);
launchChannels.remove(this);
if (channelList == null)
return;
Collection<List<IChannel>> remainingChannels = launchChannels.values();
for (List<IChannel> list : remainingChannels) {
channelList.removeAll(list);
}
for (final IChannel channel : channelList) {
Protocol.invokeAndWait(new Runnable() {
public void run() {
channel.close();
}
});
}
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
// Must force adapters to be loaded.
Platform.getAdapterManager().loadAdapter(this, adapter.getName());
return super.getAdapter(adapter);
}
public String getDebugModelID() {
return debugModelID;
}
public void usingTCFChannel(IChannel channel) {
synchronized (launchChannels) {
List<IChannel> channelList = launchChannels.get(this);
if (channelList == null) {
channelList = new ArrayList<IChannel>();
}
if (!channelList.contains(channel))
channelList.add(channel);
launchChannels.put(this, channelList);
}
}
public IAlbum getAlbum() {
return album;
}
public void setAlbum(IAlbum album) {
this.album = (Album) album;
}
public boolean isSnapshotLaunch() {
// this is not set in run mode
if (ILaunchManager.DEBUG_MODE.equals(getLaunchMode()))
assert snapshotSupportInitialized;
return album != null;
}
public ISourceLocator getExecutableLocator() {
CSourceLookupDirector director = new CSourceLookupDirector();
director.initializeParticipants();
try {
if (isSnapshotLaunch()) {
getAlbum().configureSourceLookupDirector(director);
} else {
String exePath = getLaunchConfiguration().getAttribute(
ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "");
director.setSourceContainers(createExecutableLocatorSourceContainers(exePath));
}
} catch (CoreException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
return director;
}
/**
* @since 2.0
*/
protected ISourceContainer[] createExecutableLocatorSourceContainers(String exePath) {
List<ISourceContainer> containers = new ArrayList<ISourceContainer>();
containers.add(new AbsolutePathSourceContainer());
if (exePath.length() > 0)
{
IPath exeDirectory = new Path(exePath).removeLastSegments(1);
containers.add(new DirectorySourceContainer(exeDirectory, false));
}
containers.add(new ExecutablesSourceContainer());
return containers.toArray(new ISourceContainer[containers.size()]);
}
public void initializeSnapshotSupport() {
try {
String albumFile = getLaunchConfiguration().getAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE,
"");
IPath albumPath = PathUtils.createPath(albumFile);
if (albumPath.toFile().exists()) {
album = Album.getAlbumByLocation(albumPath);
if (album == null) {
album = new Album();
album.setLocation(albumPath);
album.loadAlbum(false);
}
album.setSessionID(session.getId());
IAlbum album = Album.getAlbumBySession(session.getId());
DsfSourceLookupDirector director = (DsfSourceLookupDirector) getSourceLocator();
album.configureSourceLookupDirector(director);
} else {
}
} catch (Exception e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
snapshotSupportInitialized = true;
}
/**
* @since 2.0
*/
public ILaunchConfiguration[] getAffiliatedLaunchConfigurations() {
return affiliatedLaunchConfigurations.toArray(new ILaunchConfiguration[affiliatedLaunchConfigurations.size()]);
}
/**
* @since 2.0
*/
public void addAffiliatedLaunchConfiguration(
ILaunchConfiguration configuration) {
affiliatedLaunchConfigurations.add(configuration);
}
/**
* @since 2.0
*/
public void setActiveLaunchConfiguration(ILaunchConfiguration activeLaunchConfiguration) {
this.activeLaunchConfiguration = activeLaunchConfiguration;
}
/**
* @since 2.0
*/
public void setFirstLaunch(boolean isFirstLaunch) {
this.isFirstLaunch = isFirstLaunch;
}
/**
* @since 2.0
*/
public boolean isFirstLaunch() {
return isFirstLaunch;
}
/**
* @since 2.0
*/
public ISourceLocator createSourceLocator()
throws CoreException {
DsfSourceLookupDirector locator = new DsfSourceLookupDirector(session);
String memento = getLaunchConfiguration().getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null);
if (memento == null) {
locator.initializeDefaults(getLaunchConfiguration());
} else {
locator.initializeFromMemento(memento, getLaunchConfiguration());
}
return locator;
}
/**
* @since 2.0
*/
public static EDCLaunch[] findLaunchesUsingPeer(final IPeer ipeer) {
final List<EDCLaunch> results = new ArrayList<EDCLaunch>();
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
final List<ILaunch> launchList = Arrays.asList(manager.getLaunches());
Protocol.invokeAndWait(new Runnable() {
public void run() {
for (ILaunch iLaunch : launchList) {
if (iLaunch instanceof EDCLaunch) {
EDCLaunch edcLaunch = (EDCLaunch) iLaunch;
List<IChannel> channels = launchChannels.get(edcLaunch);
if (channels != null)
{
for (IChannel iChannel : channels) {
if (iChannel.getRemotePeer().equals(ipeer)) {
results.add(edcLaunch);
break;
}
}
}
}
}
}
});
return results.toArray(new EDCLaunch[results.size()]);
}
/**
* @since 2.0
*/
@Override
public ILaunchConfiguration getLaunchConfiguration() {
if (activeLaunchConfiguration == null)
activeLaunchConfiguration = super.getLaunchConfiguration();
return activeLaunchConfiguration;
}
/**
* @since 2.0
*/
public String getCompilationPath(String filename) {
// TODO Use the source lookup service
IPath path = Path.EMPTY;
if (Path.EMPTY.isValidPath(filename)) {
filename = PathUtils.convertPathToNative(filename);
ISourceLocator sl = getSourceLocator();
if (sl instanceof CSourceLookupDirector) {
path = ((CSourceLookupDirector) sl).getCompilationPath(filename);
}
if (path == null) {
path = PathUtils.findExistingPathIfCaseSensitive(new Path(filename));
}
}
return path.toOSString();
}
/**
* @since 2.0
*/
public String getStartupStopAtPoint() {
String ret = null;
try {
if (getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
false)) {
ret = getLaunchConfiguration().getAttribute(
ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
}
} catch (CoreException e) {
// ignore
}
return ret;
}
/**
* Set a short description for this launch.
* The default is the name of the initial launch
* configuration but usually somewhere in the
* initial launch sequence a description of the
* debug target can be gathered, either from the
* TCF peer attributes or from the connection
* settings.
* @since 2.0
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Returns a short description of the launch.
* Used as the label for the launch in the
* Debug view.
* @since 2.0
*/
public String getDescription() {
if (description == null)
return getLaunchConfiguration().getName();
return description;
}
/**
* Once a launch has been selected for use by a launch delegate
* the launching flag is set so clients can know the launch is
* in use. Once the launch process completes this launching flag
* will be reset.
* @since 2.0
*/
public void setLaunching(boolean isLaunching) {
this.isLaunching = isLaunching;
}
/**
* Returns true if this launch being used by a delegate
* for new launch activity.
* @since 2.0
*/
public boolean isLaunching() {
return isLaunching;
}
/**
* When the termination process begins this is called to flag
* the launch so clients can know it is shutting down.
* @since 2.0
*/
public void setTerminating(boolean isTerminating) {
this.isTerminating = isTerminating;
}
/**
* Returns true if this launch is in the process of terminating.
* Termination is asynchronous and clients can call this to see
* if termination is in progress.
* @since 2.0
*/
public boolean isTerminating() {
return isTerminating;
}
}