blob: 66486acc42b2f480587355663d9153b113b20a7d [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.services;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
/**
* This is abstract DSF service with some APIs specific to EDC.
*/
public abstract class AbstractEDCService extends AbstractDsfService implements IEDCService {
private final String[] classNames;
private ITargetEnvironment targetEnvironmentService = null;
private final boolean snapshot;
private EDCServicesTracker fEDCTracker;
public AbstractEDCService(DsfSession session, String[] classNames) {
super(session);
this.classNames = classNames;
this.snapshot = Album.isSnapshotSession(session.getId());
}
public boolean isSnapshot() {
return snapshot;
}
/**
* @since 2.0
*/
public EDCServicesTracker getEDCServicesTracker() {
return fEDCTracker;
}
/**
* @since 2.0
*/
public <V> V getService(Class<V> serviceClass) {
if (IEDCService.class.isAssignableFrom(serviceClass))
return fEDCTracker.getService(serviceClass);
return getServicesTracker().getService(serviceClass);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
@Override
public void initialize(final RequestMonitor requestMonitor) {
fEDCTracker = new EDCServicesTracker(getBundleContext(), getSession().getId());
super.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
@Override
public void handleSuccess() {
doInitialize(requestMonitor);
}
});
}
@Override
public void shutdown(RequestMonitor rm) {
fEDCTracker.dispose();
fEDCTracker = null;
super.shutdown(rm);
}
protected void doInitialize(RequestMonitor requestMonitor) {
register(classNames, new Hashtable<String, String>());
if (targetEnvironmentService == null)
targetEnvironmentService = getServicesTracker().getService(ITargetEnvironment.class);
requestMonitor.done();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext()
*/
@Override
protected BundleContext getBundleContext() {
return EDCDebugger.getBundleContext();
}
public ITargetEnvironment getTargetEnvironmentService() {
return targetEnvironmentService;
}
/**
* Add implicit service class names
*
* @param classNames
* the class names our derivative gave us. Must not be null, but
* can be empty.
* @param implicitClassNames
* the implicit class names that should be specified. Must not be
* null, but can be empty.
* @return a new collection of class names that is [classNames] plus any
* missing implicit ones. The implicit ones will appear first in the
* list
*/
/*package*/ static String[] massageClassNames(String[] classNames, String[] implicitClassNames) {
List<String> newClassNames = new ArrayList<String>(Arrays.asList(implicitClassNames));
for (String className : classNames) {
if (!newClassNames.contains(className)) {
newClassNames.add(className);
}
}
return newClassNames.toArray(new String[newClassNames.size()]);
}
/**
* EDC services can use this method to execute blocking <i>thread-safe</i>
* code without blocking the DSF thread. The runnable is exercised on a
* separate thread obtained from a thread pool. This mechanism was
* introduced to allow an EDC service's logic to avoid bogging down the DSF
* thread in cases where it cannot reasonably avoid making a blocking call.
* One example is when a TCF service has to be invoked synchronously (the
* calling thread waits for the request monitor to complete). Such things
* should not be done on the DSF thread since that thread is meant to be
* readily available to handle a queue of requests, much like a UI thread
* is.
*
* In the event of an uncaught exception in the given code, the request
* monitor is automatically completed and given an error status.
*
* @throws RejectedExecutionException
* if the thread pool has been overwhelmed and given code cannot
* be scheduled to run
*
* @since 2.0
*/
protected void asyncExec(Runnable runnable, RequestMonitor rm) {
try {
ExecutorService executor = EDCLaunch.getThreadPool(getSession().getId());
if (executor.isShutdown())
{
rm.setStatus(new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, "Session has been shutdown.", null));
rm.done();
}
else
executor.execute(new SafeRunner(runnable, rm));
}
catch (RejectedExecutionException exc) {
// See EDCLaunch.newThreadPool()
String msg = Messages.AbstractEDCService_0;
EDCDebugger.getMessageLogger().log(IStatus.WARNING, msg, exc);
rm.setStatus(new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, msg, exc));
rm.done();
throw exc;
}
}
/**
* A safe runner used by {@link AbstractEDCService#asyncExec(Runnable)} to
* ensure an uncaught exception does not leave the request monitor hanging.
*/
private class SafeRunner implements Runnable {
private Runnable fCode;
private RequestMonitor fRm;
SafeRunner(Runnable code, RequestMonitor rm) {
fCode = code;
fRm = rm;
Assert.isNotNull(code);
}
public void run() {
try {
fCode.run();
} catch (Exception e) {
handleException(fCode, e);
} catch (LinkageError e) {
handleException(fCode, e);
} catch (AssertionError e) {
handleException(fCode, e);
}
}
private void handleException(Runnable code, Throwable e) {
IStatus status;
if (!(e instanceof OperationCanceledException)) {
// try to obtain the correct plug-in id for the bundle providing the safe runnable
if (e instanceof CoreException) {
status = new MultiStatus(EDCDebugger.PLUGIN_ID, -1, Messages.AbstractEDCService_1, e);
((MultiStatus)status).merge(((CoreException) e).getStatus());
} else {
status = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, -1, Messages.AbstractEDCService_2, e);
}
EDCDebugger.getMessageLogger().log(status);
}
else {
status = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, -1, Messages.AbstractEDCService_3, e);
}
if (fRm != null) {
fRm.setStatus(status);
fRm.done();
}
}
}
}