1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

[179102] Re-aligned plugin and package names to confirm with Eclipse policy.

This commit is contained in:
Pawel Piech 2008-02-13 20:27:01 +00:00
parent 6d5de9c319
commit e98b65ea7c
67 changed files with 7883 additions and 0 deletions

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.dd.examples.dsf</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,65 @@
#Thu Jun 07 11:07:55 PDT 2007
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
org.eclipse.jdt.core.compiler.problem.nullReference=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=error
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.5

View file

@ -0,0 +1,18 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Debug Services Framework Examples
Bundle-Vendor: Eclipse.org
Bundle-SymbolicName: org.eclipse.dd.examples.dsf;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: org.eclipse.dd.examples.dsf.DsfExamplesPlugin
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.debug.core,
org.eclipse.debug.ui,
org.eclipse.dd.dsf,
org.eclipse.ui,
org.eclipse.dd.dsf.ui
Eclipse-LazyStart: true
Bundle-RequiredExecutionEnvironment: J2SE-1.5

View file

@ -0,0 +1,24 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head><body lang="EN-US">
<h2>About This Content</h2>
<p>June 5, 2007</p>
<h3>License</h3>
<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
indicated below, the Content is provided to you under the terms and conditions of the
Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
For purposes of the EPL, "Program" will mean the Content.</p>
<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
being redistributed by another party ("Redistributor") and different terms and conditions may
apply to your use of any object code in the Content. Check the Redistributor's license that was
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
</body></html>

View file

@ -0,0 +1,7 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml,\
plugin.properties,\
about.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

View file

@ -0,0 +1,13 @@
###############################################################################
# Copyright (c) 2006 Wind River Systems 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
###############################################################################
pluginName=DSDP/DD Debugger Services Framework (DSF) Examples
providerName=Eclipse.org

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
point="org.eclipse.ui.views">
<category
name="DSF Examples"
id="org.eclipse.dd.dsf.examples.timers">
</category>
<view
name="Timers View"
icon="icons/timer.gif"
category="org.eclipse.dd.dsf.examples.timers"
class="org.eclipse.dd.examples.dsf.timers.TimersView"
id="org.eclipse.dd.dsf.examples.model.TimersAlarmsView">
</view>
</extension>
<extension
point="org.eclipse.ui.actionSets">
<actionSet
id="org.eclipse.dd.dsf.test.actionSet"
label="DSF Examples">
<menu
id="org.eclipse.dd.dsf.examples.timers"
label="DSF Examples"
path="additions">
<groupMarker name="concurrent"/>
</menu>
<action
class="org.eclipse.dd.examples.dsf.concurrent.CancellableInputCoalescingSlowDataProviderAction"
id="org.eclipse.dd.dsf.test.cancellableInputCoalescingSlowDataProvider"
label="Open Dialog with Cancellable Input-Coalescing Slow Data Provider"
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
style="push"/>
<action
class="org.eclipse.dd.examples.dsf.concurrent.InputCoalescingSlowDataProviderAction"
id="org.eclipse.dd.dsf.test.inputCoalescingSlowDataProvider"
label="Open Dialog with Input-Coalescing Slow Data Provider"
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
style="push"/>
<action
class="org.eclipse.dd.examples.dsf.concurrent.SlowDataProviderAction"
id="org.eclipse.dd.dsf.test.slowDataProvider"
label="Open Dialog with Slow Data Provider"
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
style="push"/>
<action
class="org.eclipse.dd.examples.dsf.filebrowser.FileBrowserAction"
id="org.eclipse.dd.dsf.test.fileBrowser"
label="Open File Browser Dialog"
menubarPath="org.eclipse.dd.dsf.examples.timers/concurrent"
style="push"/>
</actionSet>
</extension>
</plugin>

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class DsfExamplesPlugin extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "org.eclipse.dd.examples.dsf"; //$NON-NLS-1$
public static final String IMG_LAYOUT_TOGGLE = "icons/layout.gif"; //$NON-NLS-1$
public static final String IMG_ALARM = "icons/alarm.gif"; //$NON-NLS-1$
public static final String IMG_ALARM_TRIGGERED = "icons/alarm_triggered.gif"; //$NON-NLS-1$
public static final String IMG_TIMER = "icons/timer.gif"; //$NON-NLS-1$
public static final String IMG_REMOVE = "icons/remove.gif"; //$NON-NLS-1$
// The shared instance
private static DsfExamplesPlugin fgPlugin;
private static BundleContext fgBundleContext;
/**
* The constructor
*/
public DsfExamplesPlugin() {
fgPlugin = this;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(BundleContext context) throws Exception {
fgBundleContext = context;
super.start(context);
getImageRegistry().put(IMG_ALARM, imageDescriptorFromPlugin(PLUGIN_ID, IMG_ALARM));
getImageRegistry().put(IMG_ALARM_TRIGGERED, imageDescriptorFromPlugin(PLUGIN_ID, IMG_ALARM_TRIGGERED));
getImageRegistry().put(IMG_TIMER, imageDescriptorFromPlugin(PLUGIN_ID, IMG_TIMER));
getImageRegistry().put(IMG_REMOVE, imageDescriptorFromPlugin(PLUGIN_ID, IMG_REMOVE));
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception {
super.stop(context);
fgPlugin = null;
fgBundleContext = null;
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static DsfExamplesPlugin getDefault() {
return fgPlugin;
}
public static BundleContext getBundleContext() {
return fgBundleContext;
}
/**
* Returns an image descriptor for the image file at the given
* plug-in relative path
*
* @param path the path
* @return the image descriptor
*/
public static ImageDescriptor getImageDescriptor(String path) {
return imageDescriptorFromPlugin(PLUGIN_ID, path);
}
}

View file

@ -0,0 +1,440 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
/**
* Example data provider which has a built-in delay when fetching data. This
* data provider simulates a service which retrieves data from an external
* source such as a networked target, which incurs a considerable delay when
* retrieving data. The data items are simulated values which consist of the
* time when data is being retrieved followed by the item's index.
* <p>
* This version of the data provider builds on the input-coalescing feature,
* it adds a test to check if the requests were cancelled by the user, before
* they are coalesced and queued to be processed by the provider thread.
*/
public class CancellableInputCoalescingSlowDataProvider implements DataProvider {
/** Minimum count of data items */
private final static int MIN_COUNT = 1000;
/** Maximum count of data items */
private final static int MAX_COUNT = 2000;
/** Time interval how often random changes occur. */
private final static int RANDOM_CHANGE_MILIS = 10000;
/** Number of times random changes are made, before count is changed. */
private final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
/** Percentage of values that is changed upon random change (0-100). */
private final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
/**
* Amount of time (in miliseconds) how long the requests to provider, and
* events from provider are delayed by.
*/
private final static int TRANSMISSION_DELAY_TIME = 500;
/**
* Amount of time (in milliseconds) how long the provider takes to process
* a request.
*/
private final static int PROCESSING_TIME = 100;
/**
* Maximum number of item requests that can be coalesced into a single
* request.
*/
private final static int COALESCING_COUNT_LIMIT = 10;
/**
* Delay in processing the buffer of getItem() calls. This delay helps
* to ensure that a meaningful number of items is present in the buffer
* before the buffer data is coalesced into a request.
*/
private final static int COALESCING_DELAY_TIME = 100;
/**
* Maximum allowed number of requests in transmission to provider thread.
* This limit causes most of the client calls to be buffered at the input
* rather than in the request queue, which in truns allows the stale
* requests to be cancelled, before they are sent to the provider thread
* for processing.
*/
private final static int REQUEST_QUEUE_SIZE_LIMIT = 100;
/** Delay before processing calls buffer, if the request queue is full */
private final static int REQUEST_BUFFER_FULL_RETRY_DELAY = PROCESSING_TIME;
/** Dispatch-thread executor that this provider uses. */
private DsfExecutor fExecutor;
/** List of listeners registered for events from provider. */
private List<Listener> fListeners = new LinkedList<Listener>();
/** Thread that handles data requests. */
private ProviderThread fProviderThread;
/** Queue of currently pending data requests. */
private final BlockingQueue<Request> fQueue = new DelayQueue<Request>();
/**
* Runnable to be submitted when the data provider thread is shut down.
* This variable acts like a flag: when client want to shut down the
* provider, it sets this runnable, and when the backgroun thread sees
* that it's set, it shuts itself down, and posts this runnable with
* the executor.
*/
private RequestMonitor fShutdownRequestMonitor = null;
/**
* Buffers for coalescing getItem() calls into a single request.
*/
private List<Integer> fGetItemIndexesBuffer = new LinkedList<Integer>();
private List<DataRequestMonitor<String>> fGetItemRequestMonitorsBuffer = new LinkedList<DataRequestMonitor<String>>();
/**
* Base class for requests that are queued by the data provider. It
* implements java.util.concurrent.Delayed to allow for use of DelayedQueue.
* Every request into the queue is delayed by the simulated transmission
* time.
*/
private static abstract class Request implements Delayed {
/** Sequence counter and number are used to ensure FIFO order **/
private static int fSequenceCounter = 0;
private int fSequenceNumber = fSequenceCounter++;
/** Time delay tracks how items will be delayed. **/
private long fTime = System.currentTimeMillis() + TRANSMISSION_DELAY_TIME;
// @see java.util.concurrent.Delayed
public long getDelay(TimeUnit unit) {
return unit.convert(fTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
// @see java.lang.Comparable
public int compareTo(Delayed other) {
if (other == this) // compare zero ONLY if same object
return 0;
Request x = (Request)other;
long diff = fTime - x.fTime;
if (diff < 0) return -1;
else if (diff > 0) return 1;
else if (fSequenceNumber < x.fSequenceNumber) return -1;
else return 1;
}
/** All requests have an associated array of RequestMonitor tokens **/
abstract RequestMonitor[] getRequestMonitors();
}
/**
* Object used to encapsulate the "getItemCount" requests. Instances of it
* are queued till processed.
*/
private static class CountRequest extends Request
{
DataRequestMonitor<Integer> fRequestMonitor;
CountRequest(DataRequestMonitor<Integer> rm) { fRequestMonitor = rm; }
@Override
DataRequestMonitor<?>[] getRequestMonitors() { return new DataRequestMonitor[] { fRequestMonitor }; }
}
/**
* Object used to encapsulate the "getItem" requests. Instances of it
* are queued till processed.
*/
private static class ItemRequest extends Request
{
DataRequestMonitor<String>[] fRequestMonitors;
Integer[] fIndexes;
ItemRequest(Integer[] indexes, DataRequestMonitor<String>[] rms) { fIndexes = indexes; fRequestMonitors = rms; }
@Override
DataRequestMonitor<?>[] getRequestMonitors() { return fRequestMonitors; }
}
/**
* The background thread of data provider. This thread retrieves the
* requests from the provider's queue and processes them. It also
* initiates random changes in the data set and issues corresponding
* events.
*/
private class ProviderThread extends Thread
{
/**
* Current count of items in the data set. It is changed
* periodically for simulation purposes.
*/
private int fCount = MIN_COUNT;
/**
* Incremented with every data change, it causes the count to be reset
* every four random changes.
*/
private int fCountTrigger = 0;
/** Time when the last change was performed. */
private long fLastChangeTime = System.currentTimeMillis();
/** Random number generator */
private Random fRandom = new java.util.Random();
@Override
public void run() {
try {
// Initialize the count.
randomCount();
// Perform the loop until the shutdown runnable is set.
while(fShutdownRequestMonitor == null) {
// Get the next request from the queue. The time-out
// ensures that that we get to process the random changes.
final Request request = fQueue.poll(RANDOM_CHANGE_MILIS / 10, TimeUnit.MILLISECONDS);
// If a request was dequeued, process it.
if (request != null) {
// Simulate a processing delay.
Thread.sleep(PROCESSING_TIME);
if (request instanceof CountRequest) {
processCountRequest((CountRequest)request);
} else if (request instanceof ItemRequest) {
processItemRequest((ItemRequest)request);
}
// Whatever the results, post it to dispatch thread
// executor (with transmission delay).
fExecutor.schedule(
new DsfRunnable() {
public void run() {
for (RequestMonitor requestMonitor : request.getRequestMonitors()) {
requestMonitor.done();
}
}
},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
// Simulate data changes.
randomChanges();
}
}
catch (InterruptedException x) {
DsfExamplesPlugin.getDefault().getLog().log( new Status(
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 0, "Interrupted exception in slow data provider thread.", x )); //$NON-NLS-1$
}
// Notify the client that requested shutdown, that shutdown is complete.
fShutdownRequestMonitor.done();
fShutdownRequestMonitor = null;
}
private void processCountRequest(CountRequest request) {
// Calculate the simulated values.
request.fRequestMonitor.setData(fCount);
}
private void processItemRequest(ItemRequest request) {
// Check to make sure that the number of indexes matches the number
// of return tokens.
assert request.fRequestMonitors.length == request.fIndexes.length;
// Calculate the simulated values for each index in request.
for (int i = 0; i < request.fIndexes.length; i++) {
request.fRequestMonitors[i].setData(Long.toHexString(fLastChangeTime) + "." + request.fIndexes[i]); //$NON-NLS-1$
}
}
/**
* This method simulates changes in provider's data set.
*/
private void randomChanges()
{
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_MILIS) {
fLastChangeTime = System.currentTimeMillis();
// once in every 30 seconds broadcast item count change
if (++fCountTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0) randomCount();
else randomDataChange();
}
}
/**
* Calculates new size for provider's data set.
*/
private void randomCount()
{
fCount = MIN_COUNT + Math.abs(fRandom.nextInt()) % (MAX_COUNT - MIN_COUNT);
// Generate the event that the count has changed, and post it to
// dispatch thread with transmission delay.
fExecutor.schedule(
new Runnable() { public void run() {
for (Listener listener : fListeners) {
listener.countChanged();
}
}},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* Invalidates a random range of indexes.
*/
private void randomDataChange()
{
final Set<Integer> set = new HashSet<Integer>();
// Change one in ten values.
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
set.add( new Integer(Math.abs(fRandom.nextInt()) % fCount) );
}
// Generate the event that the data has changed.
// Post dispatch thread with transmission delay.
fExecutor.schedule(
new Runnable() { public void run() {
for (Listener listener : fListeners) {
listener.dataChanged(set);
}
}},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
}
public CancellableInputCoalescingSlowDataProvider(DsfExecutor executor) {
fExecutor = executor;
fProviderThread = new ProviderThread();
fProviderThread.start();
}
/**
* Requests shutdown of this data provider.
* @param requestMonitor Monitor to call when shutdown is complete.
*/
public void shutdown(RequestMonitor requestMonitor) {
fShutdownRequestMonitor = requestMonitor;
}
///////////////////////////////////////////////////////////////////////////
// DataProvider
public DsfExecutor getDsfExecutor() {
return fExecutor;
}
public void getItemCount(final DataRequestMonitor<Integer> rm) {
fQueue.add(new CountRequest(rm));
}
public void getItem(final int index, final DataRequestMonitor<String> rm) {
// Schedule a buffer-servicing call, if one is needed.
if (fGetItemIndexesBuffer.isEmpty()) {
fExecutor.schedule(
new Runnable() { public void run() {
fileBufferedRequests();
}},
COALESCING_DELAY_TIME,
TimeUnit.MILLISECONDS);
}
// Add the call data to the buffer.
// Note: it doesn't matter that the items were added to the buffer
// after the buffer-servicing request was scheduled. This is because
// the buffers are guaranteed not to be modified until this dispatch
// cycle is over.
fGetItemIndexesBuffer.add(index);
fGetItemRequestMonitorsBuffer.add(rm);
}
@SuppressWarnings("unchecked")
public void fileBufferedRequests() {
// Check if there is room in the request queue. If not, re-schedule the
// buffer-servicing for later.
if (fQueue.size() >= REQUEST_QUEUE_SIZE_LIMIT) {
if (fGetItemIndexesBuffer.isEmpty()) {
fExecutor.schedule(
new Runnable() { public void run() {
fileBufferedRequests();
}},
REQUEST_BUFFER_FULL_RETRY_DELAY,
TimeUnit.MILLISECONDS);
}
return;
}
// Remove a number of getItem() calls from the buffer, and combine them
// into a request.
List<Integer> indexes = new LinkedList<Integer>();
List<DataRequestMonitor> rms = new LinkedList<DataRequestMonitor>();
int numToCoalesce = 0;
while (!fGetItemIndexesBuffer.isEmpty() && numToCoalesce < COALESCING_COUNT_LIMIT) {
// Get the next call from buffer.
Integer index = fGetItemIndexesBuffer.remove(0);
DataRequestMonitor rm = fGetItemRequestMonitorsBuffer.remove(0);
// If call is already cancelled, ignore it.
if (rm.getStatus().getSeverity() == IStatus.CANCEL) continue;
// Otherwise add it to the request.
indexes.add(index);
rms.add(rm);
numToCoalesce++;
}
// Queue the coalesced request.
fQueue.add( new ItemRequest(
indexes.toArray(new Integer[indexes.size()]),
rms.toArray(new DataRequestMonitor[rms.size()]))
);
// If there are still calls left in the buffer, execute another
// buffer-servicing call, but without any delay.
if (!fGetItemIndexesBuffer.isEmpty()) {
fExecutor.execute(new Runnable() { public void run() {
fileBufferedRequests();
}});
}
}
public void addListener(Listener listener) {
assert fExecutor.isInExecutorThread();
fListeners.add(listener);
}
public void removeListener(Listener listener) {
assert fExecutor.isInExecutorThread();
fListeners.remove(listener);
}
//
///////////////////////////////////////////////////////////////////////////
}

View file

@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.actions.ActionDelegate;
public class CancellableInputCoalescingSlowDataProviderAction extends ActionDelegate
implements IWorkbenchWindowActionDelegate
{
private IWorkbenchWindow fWindow;
@Override
public void run(IAction action) {
if (fWindow != null) {
// Create the standard data provider.
final CancellableInputCoalescingSlowDataProvider dataProvider =
new CancellableInputCoalescingSlowDataProvider(new DefaultDsfExecutor());
// Create the dialog and open it.
Dialog dialog = new SlowDataProviderDialog(
fWindow.getShell(), new CancellingSlowDataProviderContentProvider(), dataProvider);
dialog.open();
// Shut down the data provider thread and the DSF executor thread.
// Note, since data provider is running in background thread, we have to
// wait until this background thread has completed shutdown before
// killing the executor thread itself.
dataProvider.shutdown(new RequestMonitor(dataProvider.getDsfExecutor(), null) {
@Override
public void handleCompleted() {
dataProvider.getDsfExecutor().shutdown();
}
});
}
}
public void init(IWorkbenchWindow window) {
fWindow = window;
}
}

View file

@ -0,0 +1,292 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.jface.viewers.ILazyContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
public class CancellingSlowDataProviderContentProvider
implements ILazyContentProvider, DataProvider.Listener
{
TableViewer fTableViewer;
DataProvider fDataProvider;
Display fDisplay;
List<ItemGetDataRequestMonitor> fItemDataRequestMonitors = new LinkedList<ItemGetDataRequestMonitor>();
Set<Integer> fCancelledIdxs = new HashSet<Integer>();
AtomicInteger fCancelCallsPending = new AtomicInteger();
///////////////////////////////////////////////////////////////////////////
// ILazyContentProvider
public void dispose() {
if (fDataProvider != null) {
final DataProvider dataProvider = fDataProvider;
dataProvider.getDsfExecutor().execute(
new Runnable() { public void run() {
dataProvider.removeListener(CancellingSlowDataProviderContentProvider.this);
fTableViewer = null;
fDisplay = null;
fDataProvider = null;
}});
} else {
fTableViewer = null;
fDisplay = null;
}
}
public void inputChanged(final Viewer viewer, Object oldInput, final Object newInput) {
// If old data provider is not-null, unregister from it as listener.
if (fDataProvider != null) {
final DataProvider dataProvider = fDataProvider;
dataProvider.getDsfExecutor().execute(
new Runnable() { public void run() {
dataProvider.removeListener(CancellingSlowDataProviderContentProvider.this);
}});
}
// Register as listener with new data provider.
// Note: if old data provider and new data provider use different executors,
// there is a chance of a race condition here.
if (newInput != null) {
((DataProvider)newInput).getDsfExecutor().execute(
new Runnable() { public void run() {
if ( ((TableViewer)viewer).getTable().isDisposed() ) return;
fTableViewer = (TableViewer)viewer;
fDisplay = fTableViewer.getTable().getDisplay();
fDataProvider = (DataProvider)newInput;
fDataProvider.addListener(CancellingSlowDataProviderContentProvider.this);
queryItemCount();
}});
}
}
public void updateElement(final int index) {
assert fTableViewer != null;
if (fDataProvider == null) return;
// Calculate the visible index range.
final int topIdx = fTableViewer.getTable().getTopIndex();
final int botIdx = topIdx + getVisibleItemCount(topIdx);
fCancelCallsPending.incrementAndGet();
fDataProvider.getDsfExecutor().execute(
new Runnable() { public void run() {
// Must check again, in case disposed while re-dispatching.
if (fDataProvider == null || fTableViewer.getTable().isDisposed()) return;
if (index >= topIdx && index <= botIdx) {
queryItemData(index);
}
cancelStaleRequests(topIdx, botIdx);
}});
}
protected int getVisibleItemCount(int top) {
Table table = fTableViewer.getTable();
int itemCount = table.getItemCount();
return Math.min((table.getBounds().height / table.getItemHeight()) + 2, itemCount - top);
}
///////////////////////////////////////////////////////////////////////////
// DataProvider.Listener
public void countChanged() {
// Check for dispose.
if (fDataProvider == null) return;
// Request new count.
queryItemCount();
}
public void dataChanged(final Set<Integer> indexes) {
// Check for dispose.
if (fDataProvider == null) return;
// Clear changed items in table viewer.
final TableViewer tableViewer = fTableViewer;
fDisplay.asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (fTableViewer == null || fTableViewer.getTable().isDisposed()) return;
for (Integer index : indexes) {
tableViewer.clear(index);
}
}});
}
//
///////////////////////////////////////////////////////////////////////////
/**
* Convenience extension to standard data return runnable. This extension
* automatically checks for errors and asynchronous dispose.
* @param <V>
*/
private abstract class CPGetDataRequestMonitor<V> extends DataRequestMonitor<V> {
public CPGetDataRequestMonitor(DsfExecutor executor) { super(executor, null); }
abstract protected void doRun();
@Override
public void handleCompleted() {
// If there is an error processing request, return.
if (!getStatus().isOK()) return;
// If content provider was disposed, return.
if (fTableViewer == null || fTableViewer.getTable().isDisposed()) return;
// Otherwise execute runnable.
doRun();
}
}
/**
* Executes the item count query with DataProvider. Must be called on
* data provider's dispatch thread.
*/
private void queryItemCount() {
assert fDataProvider.getDsfExecutor().isInExecutorThread();
// Request coumt from data provider. When the count is returned, we
// have to re-dispatch into the display thread to avoid calling
// the table widget on the DSF dispatch thread.
fCancelledIdxs.clear();
fDataProvider.getItemCount(
new CPGetDataRequestMonitor<Integer>(fDataProvider.getDsfExecutor()) {
@Override
protected void doRun() {
final TableViewer tableViewer = fTableViewer;
tableViewer.getTable().getDisplay().asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (tableViewer.getTable().isDisposed()) return; // disposed
tableViewer.setItemCount(getData());
tableViewer.getTable().clearAll();
}});
}});
}
/**
* Dedicated class for data item requests. This class holds the index
* argument so it can be examined when cancelling stale requests.
*/
// Request data from data provider. Likewise, when the data is
// returned, we have to re-dispatch into the display thread to
// call the table widget.
class ItemGetDataRequestMonitor extends CPGetDataRequestMonitor<String> {
/** Index is used when cancelling stale requests. */
int fIndex;
ItemGetDataRequestMonitor(DsfExecutor executor, int index) {
super(executor);
fIndex = index;
}
// Remove the request from list of outstanding requests. This has
// to be done in run() because doRun() is not always called.
@Override
public void handleCompleted() {
fItemDataRequestMonitors.remove(this);
super.handleCompleted();
}
// Process the result as usual.
@Override
protected void doRun() {
final TableViewer tableViewer = fTableViewer;
tableViewer.getTable().getDisplay().asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (tableViewer.getTable().isDisposed()) return; // disposed
tableViewer.replace(getData(), fIndex);
}});
}
}
/**
* Executes the data query with DataProvider. Must be called on dispatch
* thread.
* @param index Index of item to fetch.
*/
private void queryItemData(final int index) {
assert fDataProvider.getDsfExecutor().isInExecutorThread();
ItemGetDataRequestMonitor rm = new ItemGetDataRequestMonitor(fDataProvider.getDsfExecutor(), index);
fItemDataRequestMonitors.add(rm);
fDataProvider.getItem(index, rm);
}
/**
* Iterates through the outstanding requests to data provider and
* cancells any that are nto visible any more.
* @param topIdx Top index of the visible items
* @param botIdx Bottom index of the visible items
*/
private void cancelStaleRequests(int topIdx, int botIdx) {
// Go through the outstanding requests and cencel any that
// are not visible anymore.
for (Iterator<ItemGetDataRequestMonitor> itr = fItemDataRequestMonitors.iterator(); itr.hasNext();) {
ItemGetDataRequestMonitor item = itr.next();
if (item.fIndex < topIdx || item.fIndex > botIdx) {
// Set the item to cancelled status, so that the data provider
// will ignore it.
item.setStatus(new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, 0, "Cancelled", null)); //$NON-NLS-1$
// Add the item index to list of indexes that were cancelled,
// which will be sent to the table widget.
fCancelledIdxs.add(item.fIndex);
// Remove the item from the outstanding cancel requests.
itr.remove();
}
}
int cancelRequestsPending = fCancelCallsPending.decrementAndGet();
if (!fCancelledIdxs.isEmpty() && cancelRequestsPending == 0) {
final Set<Integer> cancelledIdxs = fCancelledIdxs;
fCancelledIdxs = new HashSet<Integer>();
final TableViewer tableViewer = fTableViewer;
tableViewer.getTable().getDisplay().asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (tableViewer.getTable().isDisposed()) return;
// Clear the indexes of the cancelled request, so that the
// viewer knows to request them again when needed.
// Note: clearing using TableViewer.clear(int) seems very
// inefficient, it's better to use Table.clear(int[]).
int[] cancelledIdxsArray = new int[cancelledIdxs.size()];
int i = 0;
for (Integer index : cancelledIdxs) {
cancelledIdxsArray[i++] = index;
}
tableViewer.getTable().clear(cancelledIdxsArray);
}});
}
}
}

View file

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import java.util.Set;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
public interface DataProvider {
/**
* Interface for listeners for changes in Provider's data.
*/
public interface Listener {
/**
* Indicates that the count of data items has changed.
*/
void countChanged();
/**
* Indicates that some of the data values have changed.
* @param indexes Indexes of the changed items.
*/
void dataChanged(Set<Integer> indexes);
}
/**
* Returns the DSF executor that has to be used to call this data
* provider.
*/
DsfExecutor getDsfExecutor();
/**
* Retrieves the current item count.
* @param rm Request monitor, to be filled in with the Integer value.
*/
void getItemCount(DataRequestMonitor<Integer> rm);
/**
* Retrieves data value for given index.
* @param index Index of the item to retrieve
* @param rm Return data token, to be filled in with a String value
*/
void getItem(int index, DataRequestMonitor<String> rm);
/**
* Registers given listener with data provider.
*/
void addListener(Listener listener);
/**
* Removes given listener from data provider.
*/
void removeListener(Listener listener);
}

View file

@ -0,0 +1,408 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
/**
* Example data provider which has a built-in delay when fetching data. This
* data provider simulates a service which retrieves data from an external
* source such as a networked target, which incurs a considerable delay when
* retrieving data. The data items are simulated values which consist of the
* time when data is being retrieved followed by the item's index.
* <p>
* This version of the data provider features an optimization which causes
* item requests to be grouped together even before they are filed into the
* processing queue. This example demonstrates how the service can implement
* coalescing impelemntation in a situation where the provider has an
* interface which only accepts aggregate requests, so the requests have to be
* coalesed before they are sent to the provider.
*/
public class InputCoalescingSlowDataProvider implements DataProvider {
/** Minimum count of data items */
private final static int MIN_COUNT = 1000;
/** Maximum count of data items */
private final static int MAX_COUNT = 2000;
/** Time interval how often random changes occur. */
private final static int RANDOM_CHANGE_MILIS = 10000;
/** Number of times random changes are made, before count is changed. */
private final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
/** Percentage of values that is changed upon random change (0-100). */
private final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
/**
* Amount of time (in miliseconds) how long the requests to provider, and
* events from provider are delayed by.
*/
private final static int TRANSMISSION_DELAY_TIME = 500;
/**
* Amount of time (in milliseconds) how long the provider takes to process
* a request.
*/
private final static int PROCESSING_TIME = 100;
/**
* Maximum number of item requests that can be coalesced into a single
* request.
*/
private final static int COALESCING_COUNT_LIMIT = 10;
/**
* Delay in processing the buffer of getItem() calls. This delay helps
* to ensure that a meaningful number of items is present in the buffer
* before the buffer data is coalesced into a request.
*/
private final static int COALESCING_DELAY_TIME = 10;
/** Dispatch-thread executor that this provider uses. */
private DsfExecutor fExecutor;
/** List of listeners registered for events from provider. */
private List<Listener> fListeners = new LinkedList<Listener>();
/** Thread that handles data requests. */
private ProviderThread fProviderThread;
/** Queue of currently pending data requests. */
private final BlockingQueue<Request> fQueue = new DelayQueue<Request>();
/**
* Runnable to be submitted when the data provider thread is shut down.
* This variable acts like a flag: when client want to shut down the
* provider, it sets this runnable, and when the backgroun thread sees
* that it's set, it shuts itself down, and posts this runnable with
* the executor.
*/
private RequestMonitor fShutdownRequestMonitor = null;
/**
* Buffers for coalescing getItem() calls into a single request.
*/
private List<Integer> fGetItemIndexesBuffer = new LinkedList<Integer>();
private List<DataRequestMonitor<String>> fGetItemRequestMonitorsBuffer = new LinkedList<DataRequestMonitor<String>>();
/**
* Base class for requests that are queued by the data provider. It
* implements java.util.concurrent.Delayed to allow for use of DelayedQueue.
* Every request into the queue is delayed by the simulated transmission
* time.
*/
private static abstract class Request implements Delayed {
/** Sequence counter and number are used to ensure FIFO order **/
private static int fSequenceCounter = 0;
private int fSequenceNumber = fSequenceCounter++;
/** Time delay tracks how items will be delayed. **/
private long fTime = System.currentTimeMillis() + TRANSMISSION_DELAY_TIME;
// @see java.util.concurrent.Delayed
public long getDelay(TimeUnit unit) {
return unit.convert(fTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
// @see java.lang.Comparable
public int compareTo(Delayed other) {
if (other == this) // compare zero ONLY if same object
return 0;
Request x = (Request)other;
long diff = fTime - x.fTime;
if (diff < 0) return -1;
else if (diff > 0) return 1;
else if (fSequenceNumber < x.fSequenceNumber) return -1;
else return 1;
}
/** All requests have an associated array of RequestMonitor tokens **/
abstract RequestMonitor[] getRequestMonitors();
}
/**
* Object used to encapsulate the "getItemCount" requests. Instances of it
* are queued till processed.
*/
private static class CountRequest extends Request
{
DataRequestMonitor<Integer> fRequestMonitors;
CountRequest(DataRequestMonitor<Integer> rms) { fRequestMonitors = rms; }
@Override
DataRequestMonitor<?>[] getRequestMonitors() { return new DataRequestMonitor[] { fRequestMonitors }; }
}
/**
* Object used to encapsulate the "getItem" requests. Instances of it
* are queued till processed.
*/
private static class ItemRequest extends Request
{
DataRequestMonitor<String>[] fRequestMonitors;
Integer[] fIndexes;
ItemRequest(Integer[] indexes, DataRequestMonitor<String>[] rms) { fIndexes = indexes; fRequestMonitors = rms; }
@Override
DataRequestMonitor<?>[] getRequestMonitors() { return fRequestMonitors; }
}
/**
* The background thread of data provider. This thread retrieves the
* requests from the provider's queue and processes them. It also
* initiates random changes in the data set and issues corresponding
* events.
*/
private class ProviderThread extends Thread
{
/**
* Current count of items in the data set. It is changed
* periodically for simulation purposes.
*/
private int fCount = MIN_COUNT;
/**
* Incremented with every data change, it causes the count to be reset
* every four random changes.
*/
private int fCountTrigger = 0;
/** Time when the last change was performed. */
private long fLastChangeTime = System.currentTimeMillis();
/** Random number generator */
private Random fRandom = new java.util.Random();
@Override
public void run() {
try {
// Initialize the count.
randomCount();
// Perform the loop until the shutdown runnable is set.
while(fShutdownRequestMonitor == null) {
// Get the next request from the queue. The time-out
// ensures that that we get to process the random changes.
final Request request = fQueue.poll(RANDOM_CHANGE_MILIS / 10, TimeUnit.MILLISECONDS);
// If a request was dequeued, process it.
if (request != null) {
// Simulate a processing delay.
Thread.sleep(PROCESSING_TIME);
if (request instanceof CountRequest) {
processCountRequest((CountRequest)request);
} else if (request instanceof ItemRequest) {
processItemRequest((ItemRequest)request);
}
// Whatever the results, post it to dispatch thread
// executor (with transmission delay).
fExecutor.schedule(
new DsfRunnable() {
public void run() {
for (RequestMonitor requestMonitor : request.getRequestMonitors()) {
requestMonitor.done();
}
}
},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
// Simulate data changes.
randomChanges();
}
}
catch (InterruptedException x) {
DsfExamplesPlugin.getDefault().getLog().log( new Status(
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 0, "Interrupted exception in slow data provider thread.", x )); //$NON-NLS-1$
}
// Notify the client that requested shutdown, that shutdown is complete.
fShutdownRequestMonitor.done();
fShutdownRequestMonitor = null;
}
private void processCountRequest(CountRequest request) {
// Calculate the simulated values.
request.fRequestMonitors.setData(fCount);
}
private void processItemRequest(ItemRequest request) {
// Check to make sure that the number of indexes matches the number
// of return tokens.
assert request.fRequestMonitors.length == request.fIndexes.length;
// Calculate the simulated values for each index in request.
for (int i = 0; i < request.fIndexes.length; i++) {
request.fRequestMonitors[i].setData(Long.toHexString(fLastChangeTime) + "." + request.fIndexes[i]); //$NON-NLS-1$
}
}
/**
* This method simulates changes in provider's data set.
*/
private void randomChanges()
{
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_MILIS) {
fLastChangeTime = System.currentTimeMillis();
// once in every 30 seconds broadcast item count change
if (++fCountTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0) randomCount();
else randomDataChange();
}
}
/**
* Calculates new size for provider's data set.
*/
private void randomCount()
{
fCount = MIN_COUNT + Math.abs(fRandom.nextInt()) % (MAX_COUNT - MIN_COUNT);
// Generate the event that the count has changed, and post it to
// dispatch thread with transmission delay.
fExecutor.schedule(
new Runnable() { public void run() {
for (Listener listener : fListeners) {
listener.countChanged();
}
}},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* Invalidates a random range of indexes.
*/
private void randomDataChange()
{
final Set<Integer> set = new HashSet<Integer>();
// Change one in ten values.
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
set.add( new Integer(Math.abs(fRandom.nextInt()) % fCount) );
}
// Generate the event that the data has changed.
// Post dispatch thread with transmission delay.
fExecutor.schedule(
new Runnable() { public void run() {
for (Listener listener : fListeners) {
listener.dataChanged(set);
}
}},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
}
public InputCoalescingSlowDataProvider(DsfExecutor executor) {
fExecutor = executor;
fProviderThread = new ProviderThread();
fProviderThread.start();
}
/**
* Requests shutdown of this data provider.
* @param requestMonitor Monitor to call when shutdown is complete.
*/
public void shutdown(RequestMonitor requestMonitor) {
fShutdownRequestMonitor = requestMonitor;
}
///////////////////////////////////////////////////////////////////////////
// DataProvider
public DsfExecutor getDsfExecutor() {
return fExecutor;
}
public void getItemCount(final DataRequestMonitor<Integer> rm) {
fExecutor.schedule(
new Runnable() { public void run() {
fQueue.add(new CountRequest(rm));
}},
TRANSMISSION_DELAY_TIME,
TimeUnit.MILLISECONDS);
}
public void getItem(final int index, final DataRequestMonitor<String> rm) {
// Schedule a buffer-servicing call, if one is needed.
if (fGetItemIndexesBuffer.isEmpty()) {
fExecutor.schedule(
new Runnable() { public void run() {
fileBufferedRequests();
}},
COALESCING_DELAY_TIME,
TimeUnit.MILLISECONDS);
}
// Add the call data to the buffer.
// Note: it doesn't matter that the items were added to the buffer
// after the buffer-servicing request was scheduled. This is because
// the buffers are guaranteed not to be modified until this dispatch
// cycle is over.
fGetItemIndexesBuffer.add(index);
fGetItemRequestMonitorsBuffer.add(rm);
}
@SuppressWarnings("unchecked")
public void fileBufferedRequests() {
// Remove a number of getItem() calls from the buffer, and combine them
// into a request.
int numToCoalesce = Math.min(fGetItemIndexesBuffer.size(), COALESCING_COUNT_LIMIT);
final ItemRequest request = new ItemRequest(new Integer[numToCoalesce], new DataRequestMonitor[numToCoalesce]);
for (int i = 0; i < numToCoalesce; i++) {
request.fIndexes[i] = fGetItemIndexesBuffer.remove(0);
request.fRequestMonitors[i] = fGetItemRequestMonitorsBuffer.remove(0);
}
// Queue the coalesced request, with the appropriate transmission delay.
fQueue.add(request);
// If there are still calls left in the buffer, execute another
// buffer-servicing call, but without any delay.
if (!fGetItemIndexesBuffer.isEmpty()) {
fExecutor.execute(new Runnable() { public void run() {
fileBufferedRequests();
}});
}
}
public void addListener(Listener listener) {
assert fExecutor.isInExecutorThread();
fListeners.add(listener);
}
public void removeListener(Listener listener) {
assert fExecutor.isInExecutorThread();
fListeners.remove(listener);
}
//
///////////////////////////////////////////////////////////////////////////
}

View file

@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.actions.ActionDelegate;
public class InputCoalescingSlowDataProviderAction extends ActionDelegate
implements IWorkbenchWindowActionDelegate
{
private IWorkbenchWindow fWindow;
@Override
public void run(IAction action) {
if (fWindow != null) {
// Create the standard data provider.
final InputCoalescingSlowDataProvider dataProvider =
new InputCoalescingSlowDataProvider(new DefaultDsfExecutor());
// Create the dialog and open it.
Dialog dialog = new SlowDataProviderDialog(
fWindow.getShell(), new SlowDataProviderContentProvider(), dataProvider);
dialog.open();
// Shut down the data provider thread and the DSF executor thread.
// Note, since data provider is running in background thread, we have to
// wait until this background thread has completed shutdown before
// killing the executor thread itself.
dataProvider.shutdown(new RequestMonitor(dataProvider.getDsfExecutor(), null) {
@Override
public void handleCompleted() {
dataProvider.getDsfExecutor().shutdown();
}
});
}
}
public void init(IWorkbenchWindow window) {
fWindow = window;
}
}

View file

@ -0,0 +1,330 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
/**
* Example data provider which has a built-in delay when fetching data. This
* data provider simulates a service which retrieves data from an external
* source such as a networked target, which incurs a considerable delay when
* retrieving data. The data items are simulated values which consist of the
* time when data is being retrieved followed by the item's index.
*/
public class SlowDataProvider implements DataProvider {
/** Minimum count of data items */
private final static int MIN_COUNT = 1000;
/** Maximum count of data items */
private final static int MAX_COUNT = 2000;
/** Time interval how often random changes occur. */
private final static int RANDOM_CHANGE_MILIS = 10000;
/** Number of times random changes are made, before count is changed. */
private final static int RANDOM_COUNT_CHANGE_INTERVALS = 3;
/** Percentage of values that is changed upon random change (0-100). */
private final static int RANDOM_CHANGE_SET_PERCENTAGE = 10;
/**
* Amount of time (in miliseconds) how long the requests to provider, and
* events from provider are delayed by.
*/
private final static int TRANSMISSION_DELAY_TIME = 500;
/**
* Amount of time (in milliseconds) how long the provider takes to process
* a request.
*/
private final static int PROCESSING_TIME = 100;
/** Dispatch-thread executor that this provider uses. */
private DsfExecutor fExecutor;
/** List of listeners registered for events from provider. */
private List<Listener> fListeners = new LinkedList<Listener>();
/** Thread that handles data requests. */
private ProviderThread fProviderThread;
/** Queue of currently pending data requests. */
private final BlockingQueue<Request> fQueue = new DelayQueue<Request>();
/**
* Runnable to be submitted when the data provider thread is shut down.
* This variable acts like a flag: when client want to shut down the
* provider, it sets this runnable, and when the backgroun thread sees
* that it's set, it shuts itself down, and posts this runnable with
* the executor.
*/
private RequestMonitor fShutdownRequestMonitor = null;
/**
* Base class for requests that are queued by the data provider. It
* implements java.util.concurrent.Delayed to allow for use of DelayedQueue.
* Every request into the queue is delayed by the simulated transmission
* time.
*/
private static abstract class Request implements Delayed {
/** Sequence counter and number are used to ensure FIFO order **/
private static int fSequenceCounter = 0;
private int fSequenceNumber = fSequenceCounter++;
/** Time delay tracks how items will be delayed. **/
private long fTime = System.currentTimeMillis() + TRANSMISSION_DELAY_TIME;
// @see java.util.concurrent.Delayed
public long getDelay(TimeUnit unit) {
return unit.convert(fTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
// @see java.lang.Comparable
public int compareTo(Delayed other) {
if (other == this) // compare zero ONLY if same object
return 0;
Request x = (Request)other;
long diff = fTime - x.fTime;
if (diff < 0) return -1;
else if (diff > 0) return 1;
else if (fSequenceNumber < x.fSequenceNumber) return -1;
else return 1;
}
/** All requests have an associated RequestMonitor token **/
abstract RequestMonitor getRequestMonitor();
}
/**
* Object used to encapsulate the "getItemCount" requests. Instances of it
* are queued till processed.
*/
private static class CountRequest extends Request
{
DataRequestMonitor<Integer> fRequestMonitor;
CountRequest(DataRequestMonitor<Integer> rm) { fRequestMonitor = rm; }
@Override
DataRequestMonitor<?> getRequestMonitor() { return fRequestMonitor; }
}
/**
* Object used to encapsulate the "getItem" requests. Instances of it
* are queued till processed.
*/
private static class ItemRequest extends Request
{
DataRequestMonitor<String> fRequestMonitor;
int fIndex;
ItemRequest(int index, DataRequestMonitor<String> rm) { fIndex = index; fRequestMonitor = rm; }
@Override
DataRequestMonitor<?> getRequestMonitor() { return fRequestMonitor; }
}
/**
* The background thread of data provider. This thread retrieves the
* requests from the provider's queue and processes them. It also
* initiates random changes in the data set and issues corresponding
* events.
*/
private class ProviderThread extends Thread
{
/**
* Current count of items in the data set. It is changed
* periodically for simulation purposes.
*/
private int fCount = MIN_COUNT;
/**
* Incremented with every data change, it causes the count to be reset
* every four random changes.
*/
private int fCountTrigger = 0;
/** Time when the last change was performed. */
private long fLastChangeTime = System.currentTimeMillis();
/** Random number generator */
private Random fRandom = new java.util.Random();
@Override
public void run() {
try {
// Initialize the count.
randomCount();
// Perform the loop until the shutdown runnable is set.
while(fShutdownRequestMonitor == null) {
// Get the next request from the queue. The time-out
// ensures that that we get to process the random changes.
final Request request = fQueue.poll(RANDOM_CHANGE_MILIS / 10, TimeUnit.MILLISECONDS);
// If a request was dequeued, process it.
if (request != null) {
// Simulate a processing delay.
Thread.sleep(PROCESSING_TIME);
if (request instanceof CountRequest) {
processCountRequest((CountRequest)request);
} else if (request instanceof ItemRequest) {
processItemRequest((ItemRequest)request);
}
// Whatever the result, post it to dispatch thread
// executor (with transmission delay).
fExecutor.schedule(
new DsfRunnable() {
public void run() {
request.getRequestMonitor().done();
}
},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
// Simulate data changes.
randomChanges();
}
}
catch (InterruptedException x) {
DsfExamplesPlugin.getDefault().getLog().log( new Status(
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 0, "Interrupted exception in slow data provider thread.", x )); //$NON-NLS-1$
}
// Notify the client that requested shutdown, that shutdown is complete.
fShutdownRequestMonitor.done();
fShutdownRequestMonitor = null;
}
private void processCountRequest(CountRequest request) {
// Calculate the simulated values.
request.fRequestMonitor.setData(fCount);
}
private void processItemRequest(ItemRequest request) {
// Calculate the simulated values.
request.fRequestMonitor.setData(Long.toHexString(fLastChangeTime) + "." + request.fIndex); //$NON-NLS-1$
}
/**
* This method simulates changes in provider's data set.
*/
private void randomChanges()
{
if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_MILIS) {
fLastChangeTime = System.currentTimeMillis();
// once in every 30 seconds broadcast item count change
if (++fCountTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0) randomCount();
else randomDataChange();
}
}
/**
* Calculates new size for provider's data set.
*/
private void randomCount()
{
fCount = MIN_COUNT + Math.abs(fRandom.nextInt()) % (MAX_COUNT - MIN_COUNT);
// Generate the event that the count has changed, and post it to
// dispatch thread with transmission delay.
fExecutor.schedule(
new Runnable() { public void run() {
for (Listener listener : fListeners) {
listener.countChanged();
}
}},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* Invalidates a random range of indexes.
*/
private void randomDataChange()
{
final Set<Integer> set = new HashSet<Integer>();
// Change one in ten values.
for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) {
set.add( new Integer(Math.abs(fRandom.nextInt()) % fCount) );
}
// Generate the event that the data has changed.
// Post dispatch thread with transmission delay.
fExecutor.schedule(
new Runnable() { public void run() {
for (Listener listener : fListeners) {
listener.dataChanged(set);
}
}},
TRANSMISSION_DELAY_TIME, TimeUnit.MILLISECONDS);
}
}
public SlowDataProvider(DsfExecutor executor) {
fExecutor = executor;
fProviderThread = new ProviderThread();
fProviderThread.start();
}
/**
* Requests shutdown of this data provider.
* @param requestMonitor Request completion monitor.
*/
public void shutdown(RequestMonitor requestMonitor) {
fShutdownRequestMonitor = requestMonitor;
}
///////////////////////////////////////////////////////////////////////////
// DataProvider
public DsfExecutor getDsfExecutor() {
return fExecutor;
}
public void getItemCount(final DataRequestMonitor<Integer> rm) {
fQueue.add(new CountRequest(rm));
}
public void getItem(final int index, final DataRequestMonitor<String> rm) {
fQueue.add(new ItemRequest(index, rm));
}
public void addListener(Listener listener) {
assert fExecutor.isInExecutorThread();
fListeners.add(listener);
}
public void removeListener(Listener listener) {
assert fExecutor.isInExecutorThread();
fListeners.remove(listener);
}
//
///////////////////////////////////////////////////////////////////////////
}

View file

@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.actions.ActionDelegate;
public class SlowDataProviderAction extends ActionDelegate
implements IWorkbenchWindowActionDelegate
{
private IWorkbenchWindow fWindow;
@Override
public void run(IAction action) {
if (fWindow != null) {
// Create the standard data provider.
final SlowDataProvider dataProvider = new SlowDataProvider(new DefaultDsfExecutor());
// Create the dialog and open it.
Dialog dialog = new SlowDataProviderDialog(
fWindow.getShell(), new SlowDataProviderContentProvider(), dataProvider);
dialog.open();
// Shut down the data provider thread and the DSF executor thread.
// Note, since data provider is running in background thread, we have to
// wait until this background thread has completed shutdown before
// killing the executor thread itself.
dataProvider.shutdown(new RequestMonitor(dataProvider.getDsfExecutor(), null) {
@Override
public void handleCompleted() {
dataProvider.getDsfExecutor().shutdown();
}
});
}
}
public void init(IWorkbenchWindow window) {
fWindow = window;
}
}

View file

@ -0,0 +1,190 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import java.util.Set;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.jface.viewers.ILazyContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
public class SlowDataProviderContentProvider
implements ILazyContentProvider, DataProvider.Listener
{
TableViewer fTableViewer;
DataProvider fDataProvider;
///////////////////////////////////////////////////////////////////////////
// ILazyContentProvider
public void dispose() {
if (fDataProvider != null) {
final DataProvider dataProvider = fDataProvider;
dataProvider.getDsfExecutor().execute(
new Runnable() { public void run() {
dataProvider.removeListener(SlowDataProviderContentProvider.this);
fTableViewer = null;
fDataProvider = null;
}});
} else {
fTableViewer = null;
}
}
public void inputChanged(final Viewer viewer, Object oldInput, final Object newInput) {
// If old data provider is not-null, unregister from it as listener.
if (fDataProvider != null) {
final DataProvider dataProvider = fDataProvider;
dataProvider.getDsfExecutor().execute(
new Runnable() { public void run() {
dataProvider.removeListener(SlowDataProviderContentProvider.this);
}});
}
// Register as listener with new data provider.
// Note: if old data provider and new data provider use different executors,
// there is a chance of a race condition here.
if (newInput != null) {
((DataProvider)newInput).getDsfExecutor().execute(
new Runnable() { public void run() {
fTableViewer = (TableViewer)viewer;
fDataProvider = (DataProvider)newInput;
fDataProvider.addListener(SlowDataProviderContentProvider.this);
queryItemCount();
}});
}
}
public void updateElement(final int index) {
assert fTableViewer != null;
if (fDataProvider == null) return;
fDataProvider.getDsfExecutor().execute(
new Runnable() { public void run() {
// Must check again, in case disposed while re-dispatching.
if (fDataProvider == null) return;
queryItemData(index);
}});
}
///////////////////////////////////////////////////////////////////////////
// DataProvider.Listener
public void countChanged() {
// Check for dispose.
if (fDataProvider == null) return;
// Request new count.
queryItemCount();
}
public void dataChanged(final Set<Integer> indexes) {
// Check for dispose.
if (fDataProvider == null) return;
// Clear changed items in table viewer.
if (fTableViewer != null) {
final TableViewer tableViewer = fTableViewer;
tableViewer.getTable().getDisplay().asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (tableViewer.getTable().isDisposed()) return; // disposed
for (Integer index : indexes) {
tableViewer.clear(index);
}
}});
}
}
//
///////////////////////////////////////////////////////////////////////////
/**
* Convenience extension to standard data return runnable. This extension
* automatically checks for errors and asynchronous dipose.
* @param <V>
*/
private abstract class CPGetDataRequestMonitor<V> extends DataRequestMonitor<V> {
CPGetDataRequestMonitor(DsfExecutor executor) { super(executor, null); }
abstract protected void doRun();
@Override
final public void handleCompleted() {
// If there is an error processing request, return.
if (!getStatus().isOK()) return;
// If content provider was disposed, return.
if (fTableViewer == null) return;
// Otherwise execute runnable.
doRun();
}
}
/**
* Executes the item count query with DataProvider. Must be called on
* data provider's dispatch thread.
*/
private void queryItemCount() {
assert fDataProvider.getDsfExecutor().isInExecutorThread();
// Request coumt from data provider. When the count is returned, we
// have to re-dispatch into the display thread to avoid calling
// the table widget on the DSF dispatch thread.
fDataProvider.getItemCount(
new CPGetDataRequestMonitor<Integer>(fDataProvider.getDsfExecutor()) {
@Override
protected void doRun() {
final TableViewer tableViewer = fTableViewer;
tableViewer.getTable().getDisplay().asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (tableViewer.getTable().isDisposed()) return; // disposed
tableViewer.setItemCount(getData());
tableViewer.getTable().clearAll();
}});
}});
}
/**
* Executes the data query with DataProvider. Must be called on dispatch
* thread.
* @param index Index of item to fetch.
*/
private void queryItemData(final int index) {
assert fDataProvider.getDsfExecutor().isInExecutorThread();
// Request data from data provider. Likewise, when the data is
// returned, we have to re-dispatch into the display thread to
// call the table widget.
fDataProvider.getItem(
index,
new CPGetDataRequestMonitor<String>(fDataProvider.getDsfExecutor()) {
@Override
protected void doRun() {
final TableViewer tableViewer = fTableViewer;
tableViewer.getTable().getDisplay().asyncExec(
new Runnable() { public void run() {
// Check again if table wasn't disposed when
// switching to the display thread.
if (tableViewer.getTable().isDisposed()) return; // disposed
tableViewer.replace(getData(), index);
}});
}});
}
}

View file

@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.concurrent;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
/**
* Dialog shared by all slow data provider examples. It accepts the data
* provider and the content provider as arguments to the constructor. So the
* only thing that the dialog does is to create the table viewer and
* initialize it with the providers.
*/
public class SlowDataProviderDialog extends Dialog {
private TableViewer fDataViewer;
private DataProvider fDataProvider;
private IContentProvider fContentProvider;
public SlowDataProviderDialog(Shell parent, IContentProvider contentProvider, DataProvider dataProvider) {
super(parent);
setShellStyle(getShellStyle() | SWT.RESIZE);
fContentProvider = contentProvider;
fDataProvider = dataProvider;
}
@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
fDataViewer = new TableViewer(area, SWT.VIRTUAL);
fDataViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
fDataViewer.setContentProvider(fContentProvider);
fDataViewer.setInput(fDataProvider);
return area;
}
}

View file

@ -0,0 +1,289 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>DSF Slow Data Provider Example</title>
</head>
<body>
<p class="MsoNormal" style="line-height: normal;"><b><span
style="font-size: 12pt; font-family: &quot;Times New Roman&quot;;">Version
1.0<br>
Pawel Piech<br>
&copy; 2006, Wind River Systems.<span style="">&nbsp; </span>Release
under EPL version 1.0.</span></b><b><span
style="font-size: 18pt; font-family: &quot;Times New Roman&quot;;"><o:p></o:p></span></b></p>
<h3>Slow Data Provider Example</h3>
The point of DSF concurrency can be most easily explained through
a practical example.&nbsp; Suppose there is a viewer which needs to
show data that originates from a remote "provider".&nbsp; There is a
considerable delay in transmitting the data to and from the provider,
and some delay in processing the data.&nbsp; The viewer is a
lazy-loading table, which means that it request information only about
items that are visible on the screen, and as the table is scrolled, new
requests for data are generated.&nbsp; The diagram below illustrates
the
logical relationship between components:<br>
<br>
.<img alt="" title="Slow Data Provider Diagram"
src="doc-files%5Cdsf_concurrency_model-1.png"
style="width: 636px; height: 128px;"><br>
<p>In detail, these components look like this:<span
style="text-decoration: underline;"></span></p>
<p><span style="text-decoration: underline;"></span></p>
<span style="text-decoration: underline;">Table Viewer</span><br>
<p>The table viewer is the standard
<span style="font-family: monospace;">org.eclipse.jface.viewers.TableViewer</span>,
created with <span style="font-family: monospace;">SWT.VIRTUAL</span>
flag.&nbsp; It has an associated content
provider, SlowDataProviderContentProvider) which handles all the
interactions with the data provider.&nbsp; The lazy content provider
operates in a very simple cycle:</p>
<ol>
<li>Table viewer tells content provider that the input has changed by
calling <span style="font-family: monospace;">IContentProvider.inputChanged()</span>.&nbsp;
This means that the content provider has to query initial state of the
data.</li>
<li>Next the content provider tells the viewer how many elements
there are, by calling <span style="font-family: monospace;">TableViewer.setItemCount()</span>.</li>
<li>At this point, the table resizes, and it requests data values for
items that are visible.&nbsp; So for each visible item it calls: <span
style="font-family: monospace;">ILazyContentProvider.updateElement()</span>.</li>
<li>After calculating the value, the content provider tells the table
what the value is, by calling <span style="font-family: monospace;">TableViewer.replace().</span></li>
<li>If the data ever changes, the content provider tells the table to
rerequest the data, by calling <span style="font-family: monospace;">TableViewer.clear()</span>.</li>
</ol>
Table viewer operates in the
SWT display thread, which means that the content provider must switch
from the display thread to the DSF dispatch thread, whenever it is
called by the table viewer, as in the example below:<br>
<pre> public void updateElement(final int index) {<br> assert fTableViewer != null;<br> if (fDataProvider == null) return;<br><br> fDataProvider.getExecutor().execute(<br> new Runnable() { public void run() {<br> // Must check again, in case disposed while redispatching.<br> if (fDataProvider == null) return;<br> <br> queryItemData(index);<br> }});<br> }<br></pre>
Likewise, when the content provider calls the table viewer, it also has
to switch back into the display thread as in following example, when
the content provider receives an event from the data provider, that an
item value has changed.<br>
<pre> public void dataChanged(final Set&lt;Integer&gt; indexes) {<br> // Check for dispose.<br> if (fDataProvider == null) return;<br><br> // Clear changed items in table viewer.<br> if (fTableViewer != null) {<br> final TableViewer tableViewer = fTableViewer;<br> tableViewer.getTable().getDisplay().asyncExec(<br> new Runnable() { public void run() {<br> // Check again if table wasn't disposed when <br> // switching to the display thread.<br> if (tableViewer.getTable().isDisposed()) return; // disposed<br> for (Integer index : indexes) {<br> tableViewer.clear(index);<br> }<br> }});<br> }<br> }<br></pre>
All of this switching back and forth between threads makes the code
look a lot more complicated than it really is, and it takes some
getting used to, but this is the price to be paid for multi-threading.
Whether the participants use semaphores or the dispatch thread, the
logic is equally complicated, and we believe that using a single
dispatch thread, makes the synchronization very explicit and thus less
error-prone.<br>
<p><span style="text-decoration: underline;">Data Provider Service</span></p>
<p>The data provider service interface, <span
style="font-family: monospace;">DataProvider</span>, is very similar
to that of the lazy content provider.&nbsp; It has methods to: </p>
<ul>
<li>get item count</li>
<li>get a value for given item</li>
<li>register as listener for changes in data count and data values</li>
</ul>
But this is a DSF interface, and all methods must be called on the
service's dispatch thread.&nbsp; For this reason, the <span
style="font-family: monospace;">DataProvider </span>interface returns
an instance of <span style="font-family: monospace;">DsfExecutor</span>,
which must be used with the interface.<br>
<p><span style="text-decoration: underline;">Slow Data Provider</span></p>
<p>The data provider is actually implemented as a thread which is an
inner class of <span style="font-family: monospace;">SlowDataProvider</span>
service.&nbsp; The provider thread
communicates with the service by reading Request objects from a shared
queue, and by posting Runnable objects directly to the <span
style="font-family: monospace;">DsfExecutor</span> but
with a simulated transmission delay.&nbsp; Separately, an additional
flag is also used to control the shutdown of the provider thread.</p>
To simulate a real back end, the data provider randomly invalidates a
set of items and notifies the listeners to update themselves.&nbsp; It
also periodically invalidates the whole table and forces the clients to
requery all items.<br>
<h4>Data and Control Flow<br>
</h4>
This can be described in following steps:<br>
<ol>
<li>The table viewer requests data for an item at a given index (<span
style="font-family: monospace;">SlowDataProviderContentProvider.updateElement</span>).<br>
</li>
<li>The table viewer's content provider executes a <span
style="font-family: monospace;">Runnable </span>in the DSF
dispatch thread and calls the data provider interface (<span
style="font-family: monospace;">SlowDataProviderContentProvider.queryItemData</span>).</li>
<li>Data provider service creates a Request object, and files it in a
queue (<span style="font-family: monospace;">SlowDataProvider.getItem</span>).</li>
<li>Data provider thread de-queues the Request object and acts on it,
calculating the value (<span style="font-family: monospace;">ProviderThread.processItemRequest</span>).</li>
<li>Data provider thread schedules the calculation result to be
posted with DSF executor (<span style="font-family: monospace;">SlowDataProvider.java:185</span>).</li>
<li>The RequestMonitor callback sets the result data in the table
viewer (<span style="font-family: monospace;">SlowDataProviderContentProvider.java:167</span>).<br>
</li>
</ol>
<h4>Running the example and full sources</h4>
This example is implemented in the <span
style="font-family: monospace;">org.eclipse.dd.examples.dsf</span>
plugin, in the <span style="font-family: monospace;">org.eclipse.dd.examples.dsf.concurrent</span>
package.&nbsp; <br>
<br>
To run the example:<br>
<ol>
<li>Build the test plugin (along with the <span
style="font-family: monospace;">org.eclipse.dsdp.DSF plugin</span>)
and launch the PDE.&nbsp; <br>
</li>
<li>Make sure to add the <span style="font-style: italic;">DSF
Tests</span> action set to your current perspective.</li>
<li>From the main menu, select <span style="font-style: italic;">DSF
Tests -&gt; Slow Data Provider</span>.</li>
<li>A dialog will open and after a delay it will populate with data.</li>
<li>Scroll and resize dialog and observe the update behavior.</li>
</ol>
<h4>Initial Notes<br>
</h4>
This example is supposed to be representative of a typical embedded
debugger design problem.&nbsp; Embedded debuggers are often slow in
retrieving and processing data, and can sometimes be accessed through a
relatively slow data channel, such as serial port or JTAG
connection.&nbsp; But as such, this basic example presents a couple
of major usability problems<br>
<ol>
<li>The data provider service interface mirrors the table's content
provider interface, in that it has a method to retrieve a single piece
of data at a time.&nbsp; The result of this is visible to the user as
lines of data are filled in one-by-one in the table.&nbsp; However,
most debugger back ends are in fact capable of retrieving data in
batches and are much more efficient at it than retrieving data items
one-by-one.</li>
<li>When scrolling quickly through the table, the requests are
generated by the table viewer for items which are quickly scrolled out
of view, but the service still queues them up and calculates them in
the order they were received.&nbsp; As a result, it takes a very long
time for the table to be populated with data at the location where the
user is looking.&nbsp; <br>
</li>
</ol>
These two problems are very common in creating UI for embedded
debugging, and there are common patterns which can be used to solve
these problems in DSF services. <br>
<h3>Coalescing</h3>
Coalescing many single-item requests into fewer multi-item requests is
the surest way to improve performance in communication with a remote
debugger, although it's not necessarily the simplest.&nbsp; There are
two basic patterns in which coalescing is achieved:<br>
<ol>
<li>The back end provides an interface for retrieving data in large
chunks.&nbsp; So when the service implementation receives a request for
a single item, it retrieves a whole chunk of data, returns the single
item, and stores the rest of the data in a local cache.</li>
<li>The back end providers an interface for retrieving data in
variable size chunks.&nbsp; When the service implementation receives a
request for a single item, it buffers the request, and waits for other
requests to come in.&nbsp; After a delay, the service clears the buffer
and submits a request for the combined items to the data provider.</li>
</ol>
In practice, a combination of the two patterns is needed, but for
purpose of an example, we implemented the second pattern in the
"Input-Coalescing Slow Data Provider" (<span
style="font-family: monospace;">InputCoalescingSlowDataProvider.java</span>).&nbsp;
<br>
<p><span style="text-decoration: underline;">Input Buffer</span></p>
<p>The main feature of this pattern is a buffer for holding the
requests before sending them to the data provider.&nbsp; In this
example the user requests are buffered in two arrays: <span
style="font-family: monospace;">fGetItemIndexesBuffer</span> and <span
style="font-family: monospace;">fGetItemRequestMonitorsBuffer.&nbsp; </span>The
<span style="font-family: monospace;">DataProvider.getItem()</span>
implementation is changed as follows:</p>
<pre> public void getItem(final int index, final DataRequestMonitor&lt;String&gt; rm) {<br> // Schedule a buffer-servicing call, if one is needed.<br> if (<span
style="font-family: monospace;">fGetItemRequestMonitorsBuffer</span>.isEmpty()) {<br> fExecutor.schedule(<br> new Runnable() { public void run() {<br> fileBufferedRequests();<br> }},<br> COALESCING_DELAY_TIME, <br> TimeUnit.MILLISECONDS);<br> }<br> <br> // Add the call data to the buffer. <br> // Note: it doesn't matter that the items were added to the buffer <br> // after the buffer-servicing request was scheduled. This is because<br> // the buffers are guaranteed not to be modified until this dispatch<br> // cycle is over.<br> fGetItemIndexesBuffer.add(index);<br> <span
style="font-family: monospace;">fGetItemRequestMonitorsBuffer</span>.add(rm);<br> } <br><br></pre>
And method that services the buffer looks like this:<br>
<pre> public void fileBufferedRequests() { <br> // Remove a number of getItem() calls from the buffer, and combine them<br> // into a request.<br> int numToCoalesce = Math.min(fGetItemIndexesBuffer.size(), COALESCING_COUNT_LIMIT);<br> final ItemRequest request = new ItemRequest(new Integer[numToCoalesce], new DataRequestMonitor[numToCoalesce]); <br> for (int i = 0; i &lt; numToCoalesce; i++) {<br> request.fIndexes[i] = fGetItemIndexesBuffer.remove(0);<br> request.fDones[i] = <span
style="font-family: monospace;">fGetItemRequestMonitorsBuffer</span>.remove(0);<br> }<br><br> // Queue the coalesced request, with the appropriate transmission delay.<br> fQueue.add(request);<br> <br> // If there are still calls left in the buffer, execute another <br> // buffer-servicing call, but without any delay.<br> if (!fGetItemIndexesBuffer.isEmpty()) {<br> fExecutor.execute(new Runnable() { public void run() {<br> fileBufferedRequests();<br> }});<br> }<br> }<br></pre>
The most interesting feature of this implementation is the fact that
there are no semaphores anywhere to control access to the input
buffers.&nbsp; Even though the buffers are serviced with a delay and
multiple clients can call the <span style="font-family: monospace;">getItem()</span>
method, the use of a single
dispatch thread prevents any race conditions that could corrupt the
buffer data.&nbsp; In real-world implementations, the buffers and
caches that need to be used are far more sophisticated with much more
complicated logic, and this is where managing access to them using the
dispatch thread is ever more important.<br>
<h3>Cancellability</h3>
<p><span style="text-decoration: underline;">Table Viewer</span></p>
<p><span style="text-decoration: underline;"></span></p>
Unlike coalescing, which can be implemented entirely within the
service, cancellability requires that the client be modified as well
to take advantage of this capability.&nbsp; For the table viewer
content provider, this means that additional features have to be
added.&nbsp; In <span style="font-family: monospace;">CancellingSlowDataProviderContentProvider.java</span>
<span style="font-family: monospace;">ILazyContentProvider.updateElement()</span>
was changes as follows:<br>
<pre> public void updateElement(final int index) {<br> assert fTableViewer != null;<br> if (fDataProvider == null) return;<br> <br> // Calculate the visible index range.<br> final int topIdx = fTableViewer.getTable().getTopIndex();<br> final int botIdx = topIdx + getVisibleItemCount(topIdx);<br> <br> fCancelCallsPending.incrementAndGet();<br> fDataProvider.getExecutor().execute(<br> new Runnable() { public void run() {<br> // Must check again, in case disposed while redispatching.<br> if (fDataProvider == null || fTableViewer.getTable().isDisposed()) return;<br> if (index &gt;= topIdx &amp;&amp; index &lt;= botIdx) {<br> queryItemData(index);<br> }<br> cancelStaleRequests(topIdx, botIdx);<br> }});<br> }<br></pre>
Now the client keeps track of the requests it made to the service in <span
style="font-family: monospace;">fItemDataDones</span>, and above, <span
style="font-family: monospace;">cancelStaleRequests()</span> iterates
through all the outstanding requests and cancels the ones that are no
longer in the visible range.<br>
<p><span style="text-decoration: underline;">Data Provider Service<span
style="text-decoration: underline;"></span></span></p>
<p><span style="text-decoration: underline;"><span
style="text-decoration: underline;"></span></span></p>
<p>The data provider implementation
(<span style="font-family: monospace;">CancellableInputCoalescingSlowDataProvider.java</span>),
builds on top of the
coalescing data provider.&nbsp; To make the canceling feature useful,
the data provider service has to limit the size of the request
queue.&nbsp; This is because in this example which simulates
communication with a target and once requests are filed into the
request
queue, they cannot be canceled, just like a client can't cancel
request once it sends them over a socket.&nbsp; So instead, if a flood
of <span style="font-family: monospace;">getItem()</span>
calls comes in, the service has to hold most of them in the coalescing
buffer in case the client decides to cancel them.&nbsp; Therefore the
<span style="font-family: monospace;">fileBufferedRequests()</span>
method includes a simple check before servicing
the buffer, and if the request queue is full, the buffer servicing call
is delayed.</p>
<pre> if (fQueue.size() &gt;= REQUEST_QUEUE_SIZE_LIMIT) {<br> if (fGetItemIndexesBuffer.isEmpty()) {<br> fExecutor.schedule(<br> new Runnable() { public void run() {<br> fileBufferedRequests();<br> }},<br> REQUEST_BUFFER_FULL_RETRY_DELAY, <br> TimeUnit.MILLISECONDS);<br> }<br> return;<br> } <br></pre>
Beyond this change, the only other significant change is that before
the requests are queued, they are checked for cancellation.<br>
<h3>Final Notes<br>
</h3>
The example given here is fairly simplistic, and chances are that the
same example could be implemented using semaphores and free threading
with perhaps fewer lines of code.&nbsp; But what we have found is that
as the problem gets bigger, the amount of
features in the data provider increases, the state of the
communication protocol gets more complicated, and the number of modules
needed in the service layer increases, using free threading and
semaphores does not safely scale.&nbsp; Using a dispatch thread for
synchronization certainly doesn't make the inherent problems of the
system less complicated, but it does help eliminate the race conditions
and deadlocks from the overall system.<br>
<p>Coalescing and Cancellability are both optimizations.&nbsp; Neither
of these optimizations affected the original interface of the service,
and one of them only needed a service-side modification.&nbsp; But as
with all optimizations, it is often better to first make sure that the
whole system is working correctly and then add optimizations where they
can make the biggest difference in user experience.&nbsp; </p>
<p>The above examples of optimizations can take many forms, and as
mentioned with coalescing, caching data that is retrieved from the data
provider is the most common form of data coalescing.&nbsp; For
cancellation, many services in DSF build on top of other services,
which means that even a low-level service can cause a higher
level service to retrieve data, while another event might cause it to
cancel those requests.&nbsp; The perfect example of this is a Variables
service, which is responsible for calculating the value of expressions
shown in the Variables view.&nbsp; The Variables service reacts to the
Run Control service, which issues a suspended event and then requests a
set of variables to be evaluated by the debugger back end.&nbsp; But as
soon as a resumed event is issued by Run Control, the Variables service
needs to cancel&nbsp; the pending evaluation requests.<br>
</p>
<br>
<br>
</body>
</html>

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.actions.ActionDelegate;
/**
* Action that opens the File Browser example dialog.
*/
public class FileBrowserAction extends ActionDelegate
implements IWorkbenchWindowActionDelegate
{
private IWorkbenchWindow fWindow;
@Override
public void run(IAction action) {
if (fWindow != null) {
// Create the dialog and open it.
Dialog dialog = new FileBrowserDialog(fWindow.getShell());
dialog.open();
}
}
public void init(IWorkbenchWindow window) {
fWindow = window;
}
}

View file

@ -0,0 +1,113 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* File Browser example dialog. It hold a tree viewer that displays
* file system contents and a text box for entering a file path to be
* shown in the tree.
*/
@SuppressWarnings("restriction")
public class FileBrowserDialog extends Dialog {
/**
* Tree viewer for showing the filesystem contents.
*/
private TreeModelViewer fViewer;
/**
* The model adapter for the tree viewer.
*/
private FileBrowserModelAdapter fModelAdapter;
/**
* Flag used to disable text-box changed events, when the text
* box is updated due to selection change in tree.
*/
private boolean fDisableTextChangeNotifications = false;
public FileBrowserDialog(Shell parent) {
super(parent);
setShellStyle(getShellStyle() | SWT.RESIZE);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
IPresentationContext presentationContext = new PresentationContext("org.eclipse.dd.examples.dsf.filebrowser"); //$NON-NLS-1$
fViewer = new TreeModelViewer(area, SWT.VIRTUAL, presentationContext);
fViewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH));
fModelAdapter = new FileBrowserModelAdapter(presentationContext);
fViewer.setInput(fModelAdapter.getVMProvider().getViewerInputObject());
final Text text = new Text(area, SWT.SINGLE | SWT.BORDER);
text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
/*
* Update the file name in the text control, to match the
* selection in the tree. Do this only if the user is not
* actively typing in the text field (test if text has focus).
*/
if (!text.isFocusControl() &&
event.getSelection() instanceof IStructuredSelection &&
((IStructuredSelection)event.getSelection()).getFirstElement() instanceof FileVMContext)
{
FileVMContext fileVmc = (FileVMContext)((IStructuredSelection)event.getSelection()).getFirstElement();
fDisableTextChangeNotifications = true;
text.setText(fileVmc.getFile().getAbsolutePath());
fDisableTextChangeNotifications = false;
}
}
});
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
if (!fDisableTextChangeNotifications) {
fModelAdapter.getVMProvider().selectionTextChanged(text.getText());
}
}
});
return area;
}
@Override
public boolean close() {
if (super.close()) {
fModelAdapter.dispose();
fModelAdapter = null;
return true;
}
return false;
}
}

View file

@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter;
import org.eclipse.dd.dsf.ui.viewmodel.IVMProvider;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
/**
* This is the adapter that implements the flexible hierarchy viewer interfaces
* for providing content, labels, and event proxy-ing for the viewer. This
* adapter is registered with the DSF Session object, and is returned by the
* IDMContext.getAdapter() and IVMContext.getAdapter() methods,
* which both call {@link DsfSession#getModelAdapter(Class)}.
* <p>
* The adapter implementation for this excercise is hard-coded to provide
* contents for only one view. In turn the view contens are determined using
* the configurable ViewModelProvider. For demonstration purposes, this model
* adapter has two different layout configurations that can be used. These
* layout configurations can be set by calling the {@link #setViewLayout} method.
* <p>
* This class is primarily accessed by the flexible hierarchy viewer from a
* non-executor thread. So the class is thread-safe, except for a view methods
* which must be called on the executor thread.
*
* @see AbstractDMVMProvider
*/
@SuppressWarnings("restriction")
@ThreadSafe
public class FileBrowserModelAdapter extends AbstractVMAdapter
{
FileBrowserVMProvider fViewModelProvider;
@Override
protected IVMProvider createViewModelProvider(IPresentationContext context) {
/*
* In this example there is only one viewer, so there is only one
* VMProvider.
*/
return fViewModelProvider;
}
public FileBrowserModelAdapter(IPresentationContext presentationContext) {
super();
fViewModelProvider = new FileBrowserVMProvider(this, presentationContext);
}
FileBrowserVMProvider getVMProvider() {
return fViewModelProvider;
}
}

View file

@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider;
import org.eclipse.dd.dsf.ui.viewmodel.IRootVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.IVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.RootVMNode;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
/**
*
*/
@SuppressWarnings("restriction")
public class FileBrowserVMProvider extends AbstractVMProvider
{
/**
* The object to be set to the viewer that shows contents supplied by this provider.
* @see org.eclipse.jface.viewers.TreeViewer#setInput(Object)
*/
private final IAdaptable fViewerInputObject =
new IAdaptable() {
/**
* The input object provides the viewer access to the viewer model adapter.
*/
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if ( adapter.isInstance(getVMAdapter()) ) {
return getVMAdapter();
}
return null;
}
@Override
public String toString() {
return "File Browser Viewer Input"; //$NON-NLS-1$
}
};
/**
* Constructor creates and configures the layout nodes to display file
* system contents.
* @param adapter The viewer model adapter that this provider is registered with.
* @param presentationContext The presentation context that this provider is
* generating contents for.
*/
public FileBrowserVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) {
super(adapter, presentationContext);
IRootVMNode root = new RootVMNode(this);
IVMNode fileSystemRoots = new FilesystemRootsVMNode(this);
addChildNodes(root, new IVMNode[] { fileSystemRoots });
IVMNode files = new FileVMNode(this);
addChildNodes(fileSystemRoots, new IVMNode[] { files });
setRootNode(root);
}
/**
* Returns the input object to be set to the viewer that shows contents
* supplied by this provider.
*/
public Object getViewerInputObject() {
return fViewerInputObject;
}
/**
* Event handler for file selection text changes in the dialog.
* @param text New text entered in file selection text box.
*/
void selectionTextChanged(final String text) {
if (isDisposed()) return;
// We're in the UI thread. Re-dispach to VM Adapter executor thread
// and then call root layout node.
try {
getExecutor().execute(new Runnable() {
public void run() {
if (isDisposed()) return;
handleEvent(text);
}});
} catch (RejectedExecutionException e) {
// Ignore. This exception could be thrown if the provider is being
// shut down.
}
}
}

View file

@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import java.io.File;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMContext;
import org.eclipse.dd.dsf.ui.viewmodel.IVMAdapter;
import org.eclipse.dd.dsf.ui.viewmodel.IVMNode;
class FileVMContext extends AbstractVMContext {
private File fFile;
FileVMContext(IVMAdapter adapter, IVMNode layoutNode, File file) {
super(adapter, layoutNode);
fFile = file;
}
File getFile() { return fFile; }
@Override
public boolean equals(Object obj) {
return obj instanceof FileVMContext && ((FileVMContext)obj).getFile().equals(fFile);
}
@Override
public int hashCode() {
return fFile.hashCode();
}
}

View file

@ -0,0 +1,308 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.dsf.ui.viewmodel.IVMContext;
import org.eclipse.dd.dsf.ui.viewmodel.IVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.IVMProvider;
import org.eclipse.dd.dsf.ui.viewmodel.VMDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
/**
* File view model node which returns file elements that are found in the directory
* specified by the parent element. The child nodes of this node are fixed to
* reference this element, and therefore this node will recursively populate
* the contents of the tree reflecting the underlying filesystem directories.
* <br>
* Note: this node does NOT sub-class the {@link org.eclipse.dd.dsf.ui.viewmodel.AbstractVMNode}
*/
@SuppressWarnings("restriction")
class FileVMNode
implements IElementLabelProvider, IVMNode
{
/**
* Reference to the viewer model provider. It's mainly used to access the
* viewer model adapter and its executor.
*/
private final FileBrowserVMProvider fProvider;
public FileVMNode(FileBrowserVMProvider provider) {
fProvider = provider;
}
public void dispose() {
// All resources garbage collected.
}
public void setChildNodes(IVMNode[] childNodes) {
throw new UnsupportedOperationException("This node does not support children."); //$NON-NLS-1$
}
/**
* List of child nodes containing only a reference to this.
*/
private final IVMNode[] fChildNodes = { this };
public IVMNode[] getChildNodes() {
return fChildNodes;
}
public void update(final IHasChildrenUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
for (IHasChildrenUpdate update : updates) {
/*
* Do not retrieve directory contents just to mark the plus
* sign in the tree. If it's a directory, just assume that
* it has children.
*/
FileVMContext vmc = (FileVMContext)update.getElement();
update.setHasChilren(vmc.getFile().isDirectory());
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
public void update(final IChildrenCountUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
for (IChildrenCountUpdate update : updates) {
update.setChildCount(getFiles(update).length);
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
public void update(final IChildrenUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
for (IChildrenUpdate update : updates) {
File[] files = getFiles(update);
int offset = update.getOffset() != -1 ? update.getOffset() : 0;
int length = update.getLength() != -1 ? update.getLength() : files.length;
for (int i = offset; (i < files.length) && (i < (offset + length)); i++) {
update.setChild(new FileVMContext(fProvider.getVMAdapter(), FileVMNode.this, files[i]), i);
}
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
public void update(final ILabelUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
for (ILabelUpdate update : updates) {
update.setLabel(getLabel((FileVMContext)update.getElement()), 0);
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
private static final File[] EMPTY_FILE_LIST = new File[0];
/**
* Retrieves the list of files for this node. The list of files is based
* on the parent element in the tree, which must be of type FileVMC.
*
* @param update Update object containing the path (and the parent element)
* in the tree viewer.
* @return List of files contained in the directory specified in the
* update object. An empty list if the parent element is not a directory.
* @throws ClassCastException If the parent element contained in the update
* is NOT of type FileVMC.
*/
private File[] getFiles(IViewerUpdate update) {
FileVMContext vmc = (FileVMContext)update.getElement();
File[] files = vmc.getFile().listFiles();
return files != null ? files : EMPTY_FILE_LIST;
}
/**
* Returs the text label to show in the tree for given element.
*/
private String getLabel(FileVMContext vmc) {
return vmc.getFile().getName();
}
public void getContextsForEvent(VMDelta parentDelta, Object event, DataRequestMonitor<IVMContext[]> rm) {
rm.setStatus(new Status(IStatus.OK, DsfUIPlugin.PLUGIN_ID, IDsfService.NOT_SUPPORTED, "", null)); //$NON-NLS-1$
rm.done();
}
public int getDeltaFlags(Object e) {
/*
* @see buildDelta()
*/
int retVal = IModelDelta.NO_CHANGE;
if (e instanceof String) {
retVal |= IModelDelta.SELECT | IModelDelta.EXPAND;
}
return retVal;
}
public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) {
/*
* The FileLayoutNode is recursive, with itself as the only child. In this
* method the delta is calculated for a full path VMContext elements, and the
* implementation of this method is not recursive.
*/
if (event instanceof String) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
/*
* Requirements for a selection event to be issued is that the file exist, and
* that the parentDelta contain a FileVMC of a parent directory as its element.
*
* The test for first the former requirement could be performed inside getDeltaFlags()
* but getDeltaFlags() is synchronous, so it is better to perform this test here using
* a background thread (job).
*
* The latter is requirement is needed because this node does not have the algorithm
* calculate the complete list of root nodes. That algorithm is implemented inside the
* {@link FileSystemRootsLayoutNode#updateElements} method.
*/
final File eventFile = new File((String)event);
File parentFile = null;
if (parentDelta.getElement() instanceof FileVMContext) {
parentFile = ((FileVMContext)parentDelta.getElement()).getFile();
}
// The file has to exist in order for us to be able to select
// it in the tree.
if (eventFile.exists() && parentFile != null) {
// Create a list containing all files in path
List<File> filePath = new LinkedList<File>();
for (File file = eventFile; file != null && !file.equals(parentFile); file = file.getParentFile()) {
filePath.add(0, file);
}
if (filePath.size() != 0) {
// Build the delta for all files in path.
ModelDelta delta = parentDelta;
File[] allFilesInDirectory = parentFile.listFiles();
for (File pathSegment : filePath) {
// All files in path should be directories, and should therefore
// have a valid list of elements.
assert allFilesInDirectory != null;
File[] pathSegmentDirectoryFiles = pathSegment.listFiles();
delta = delta.addNode(
new FileVMContext(fProvider.getVMAdapter(), FileVMNode.this, pathSegment),
nodeOffset + Arrays.asList(allFilesInDirectory).indexOf(pathSegment),
IModelDelta.NO_CHANGE,
pathSegmentDirectoryFiles != null ? pathSegmentDirectoryFiles.length : 0);
allFilesInDirectory = pathSegmentDirectoryFiles;
}
// The last file in path gets the EXPAND | SELECT flags.
delta.setFlags(delta.getFlags() | IModelDelta.SELECT | IModelDelta.EXPAND);
}
}
// Invoke the request monitor.
requestMonitor.done();
return Status.OK_STATUS;
}
}.schedule();
} else {
requestMonitor.done();
}
}
/**
* Override the behavior which checks for delta flags of all the child nodes,
* because we would get stuck in a recursive loop. Instead call only the child
* nodes which are not us.
*/
protected Map<IVMNode, Integer> getChildNodesWithDeltas(Object e) {
Map<IVMNode, Integer> nodes = new HashMap<IVMNode, Integer>();
for (final IVMNode childNode : getChildNodes()) {
int delta = childNode.getDeltaFlags(e);
if (delta != IModelDelta.NO_CHANGE) {
nodes.put(childNode, delta);
}
}
return nodes;
}
public IVMProvider getVMProvider() {
return fProvider;
}
}

View file

@ -0,0 +1,192 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.filebrowser;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMProvider;
import org.eclipse.dd.dsf.ui.viewmodel.VMDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
/**
* Viewer model node that populates the filesystem root elements.
*/
@SuppressWarnings("restriction")
class FilesystemRootsVMNode extends AbstractVMNode
implements IElementLabelProvider
{
public FilesystemRootsVMNode(AbstractVMProvider provider) {
super(provider);
}
public void update(final IChildrenUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
File[] files = File.listRoots();
for (IChildrenUpdate update : updates) {
int offset = update.getOffset() != -1 ? update.getOffset() : 0;
int length = update.getLength() != -1 ? update.getLength() : files.length;
for (int i = offset; (i < files.length) && (i < (offset + length)); i++) {
update.setChild(new FileVMContext(getVMProvider().getVMAdapter(), FilesystemRootsVMNode.this, files[i]), i);
}
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
public void update(final IHasChildrenUpdate[] updates) {
for (IHasChildrenUpdate update : updates) {
/*
* Assume that all filesystem roots have children. If user attempts
* to expand an empty directory, the plus sign will be removed
* from the element.
*/
update.setHasChilren(true);
update.done();
}
}
public void update(final IChildrenCountUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
for (IChildrenCountUpdate update : updates) {
if (!checkUpdate(update)) continue;
update.setChildCount(File.listRoots().length);
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
public void update(final ILabelUpdate[] updates) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
for (ILabelUpdate update : updates) {
update.setLabel(getLabel((FileVMContext)update.getElement()), 0);
update.done();
}
return Status.OK_STATUS;
}
}.schedule();
}
/**
* Returs the text label to show in the tree for given element. Filesystem
* roots return an empty string for call to File.getName(), use the abolute path
* string instead.
*/
private String getLabel(FileVMContext vmc) {
return vmc.getFile().getAbsolutePath();
}
public int getDeltaFlags(Object e) {
/*
* @see buildDelta()
*/
int retVal = IModelDelta.NO_CHANGE;
if (e instanceof String) {
retVal |= IModelDelta.SELECT | IModelDelta.EXPAND;
}
return retVal;
}
public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) {
if (event instanceof String) {
new Job("") { //$NON-NLS-1$
{
setSystem(true);
setPriority(INTERACTIVE);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
final File eventFile = new File((String)event);
if (eventFile.exists()) {
// Create a list containing all files in path of the file from the event
List<File> filePath = new LinkedList<File>();
for (File file = eventFile; file != null; file = file.getParentFile()) {
filePath.add(0, file);
}
File eventRoot = filePath.get(0);
// Get the index of the file in list of filesystem roots.
File[] roots = File.listRoots();
int index = 0;
for (; index < roots.length; index++) {
if (eventRoot.equals(roots[index])) break;
}
// Check if the specified file is not one of the roots.
if (index < roots.length) {
ModelDelta delta = parentDelta.addNode(
new FileVMContext(getVMProvider().getVMAdapter(), FilesystemRootsVMNode.this, eventRoot),
index, IModelDelta.NO_CHANGE);
if (eventFile.equals(eventRoot)) {
// The event is for the root node. Select it and extend parent node.
delta.setFlags(delta.getFlags() | IModelDelta.SELECT | IModelDelta.EXPAND);
}
}
}
// Invoke the request monitor.
requestMonitor.done();
return Status.OK_STATUS;
}
}.schedule();
} else {
requestMonitor.done();
}
}
}

View file

@ -0,0 +1,127 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>DSF Filesystem Browser Example</title>
</head>
<body>
<h2>DSF Filesystem Browser Example</h2>
<h3>Goals</h3>
This example demonstrates an implementation of a viewer model with a
layout node that has itself as a child.&nbsp; Such layout nodes are
needed to represents elements which themselves have a natural tree
structures.&nbsp; This example uses filesystem folders as the
tree-structured data, which is retrieved directly from the java.io.File
class.&nbsp; This example also demonstrates a viewer model
implementation which does not retrieve data using DSF services and
associated data model interfaces.&nbsp; <br>
<h3><span style="font-weight: bold;">Design</span></h3>
<span style="text-decoration: underline;">Model Adapter Hookup</span><br>
A flexible-hierarchy tree viewer {@link
org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer}
is created within a model dialog.&nbsp; Corresponding {@link
FileBrowserModelAdapter} and {@link FileBrowserVMProvider} classes are
instanciated, and the root element object created by
FileBrowserVMProvider is set as input to the tree viewer.&nbsp; From
there FileBrowserModelAdapter is returned as the {@link
IElementContentProvier} and {@link IModelProxyFactory} for all elements
in the tree.<br>
<br>
<p><span style="text-decoration: underline;">Layout Nodes</span><br>
There are three layout nodes:<br>
</p>
<ul>
<li>{@link FileBrowserVMProvider.VMRootLayoutNode} is just a root
node, which generates the input element for the viewer.</li>
<li>{@link FilesystemRootsLayoutNode} retrieves the roots of the
filesystem hierarchy ("C:\", "D:\", etc on Windows).&nbsp; <br>
</li>
<li>{@link FileLayoutNode} is a child of <span
style="font-family: monospace;">FilesystemRootsLayoutNode</span> and
it recursively retrieves all folders and files under the given parent
file element.&nbsp; This layout node does not allow any children nodes
to be added to it, and it returns only itself as a child node (through
a call to <span style="font-family: monospace;">IVMLayoutNode.getChildLayoutNodes</span>).<br>
</li>
</ul>
Both <span style="font-family: monospace;">FilesystemRootsLayoutNode</span>
and <span style="font-family: monospace;">FileLayoutNode</span> create
elements of the same type: {@link FileVMContext}.&nbsp; Additionally,
when populating elements in the tree, the <span
style="font-family: monospace;">FileLayoutNode</span> requires that a <span
style="font-family: monospace;">FileVMContext</span> element be the
parent element in order to be able to retrieve its children.&nbsp; <br>
<span style="font-family: monospace;"></span>
<p><span style="font-family: monospace;"></span></p>
<span style="text-decoration: underline;">Event Handling/Generating
Model Deltas</span><br>
The view model responds to events generated by a text box in the
dialog, where the user can type in a filesystem path.&nbsp; If the
entered path resolves to a file on the filesystem, the view model
generates a delta to select and reveal the given file in the
tree.&nbsp; The two file layout nodes handle generating the delta in
different ways:<br>
<ul>
<li><span style="font-family: monospace;">FilesystemRootsLayoutNode</span>
is a standard layout node.&nbsp; <br>
</li>
<ol>
<li>In the event handler implementation {@link
org.eclipse.dd.dsf.ui.viewermodel.IVMLayoutNode#buildDelta}, the user
specified file-path is compared to the list of file-system roots.&nbsp;
<br>
</li>
<li>If the user file-path contains one of the filesystem roots, a
new delta node is added for that root and the child layout node is
called to continue the delta processing.&nbsp; <br>
</li>
<li>If the user file-path points to one of the filesystem roots,
the <span style="font-family: monospace;">IModelDelta.SELECT</span>
and <span style="font-family: monospace;">IModelDelta.EXPAND</span>
flags are also added to the delta so that the root will be selected in
the viewer.<br>
</li>
</ol>
<li><span style="font-family: monospace;">FileLayoutNode</span> is
the special case, because it is a recusrive node.&nbsp; This node does
not call any child nodes to process the delta, instead it calculates
the delta for all file elements in user file-path, starting at the
parent element. <br>
</li>
<ol>
<li>First the parent <span style="font-family: monospace;">FileVMContext</span>
element is retrieved from the delta.&nbsp; <br>
</li>
<li>Then the user file-path is broken down into {@link
java.io.File} objects representing each segment in the path, starting
at the parent file element retrieved in step 1.</li>
<li>Then a delta node is added for each segment of the calculated
path.&nbsp; <br>
</li>
<li><span style="font-family: monospace;">IModelDelta.SELECT</span>
and <span style="font-family: monospace;">IModelDelta.EXPAND</span>
flags are added to the last delta.<br>
</li>
</ol>
</ul>
<h3>How to use</h3>
<ol>
<li>Make sure that the DSF examples menu is visible in the perspective</li>
<ul>
<li>Go to Windows -&gt; Customize Perspective...</li>
<li>Select Commands tab</li>
<li>Check the "DSF Examples" in the "Available command groups"
table.</li>
</ul>
<li>Open the dialog by selecting DSF Examples-&gt;Open File Browser
Dialog menu item.</li>
<li>Expand the items in the tree to see filesystem contents.</li>
<li>Select elements in the tree, to fill in text box with selected
file's path.</li>
<li>Type in a file path in text box and have the tree expand to the
specified element.<br>
</li>
</ol>
</body>
</html>

View file

@ -0,0 +1,263 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.Query;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor;
import org.eclipse.dd.dsf.service.DsfServices;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmDMC;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmData;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.swt.widgets.Shell;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.util.tracker.ServiceTracker;
/**
*
*/
@ThreadSafeAndProhibitedFromDsfExecutor("")
public class AlarmCellModifier implements ICellModifier {
private final DsfSession fSession;
/**
* Need to use the OSGi service tracker here (instead of DsfServiceTracker),
* because we're accessing it in non-dispatch thread. DsfServiceTracker is not
* thread-safe.
*/
@ThreadSafe
private ServiceTracker fServiceTracker;
/**
* Constructor for the modifier requires a valid DSF session in order to
* initialize the service tracker.
* @param session DSF session this modifier will use.
*/
public AlarmCellModifier(DsfSession session) {
fSession = session;
}
public boolean canModify(Object element, String property) {
return TimersViewColumnPresentation.COL_VALUE.equals(property) && getAlarmDMC(element) != null;
}
public Object getValue(Object element, String property) {
if (!TimersViewColumnPresentation.COL_VALUE.equals(property)) return ""; //$NON-NLS-1$
// Get the DMC and the session. If element is not an alarm DMC, or
// session is stale, then bail out.
AlarmDMC dmc = getAlarmDMC(element);
if (dmc == null) return ""; //$NON-NLS-1$
DsfSession session = DsfSession.getSession(dmc.getSessionId());
if (session == null) return ""; //$NON-NLS-1$
/*
* Create the query to request the value from service.
* Note: no need to guard against RejectedExecutionException, because
* DsfSession.getSession() above would only return an active session.
*/
GetValueQuery query = new GetValueQuery(dmc);
session.getExecutor().execute(query);
try {
return query.get().toString();
} catch (InterruptedException e) {
assert false;
return ""; //$NON-NLS-1$
} catch (ExecutionException e) {
return ""; //$NON-NLS-1$
}
}
public void modify(Object element, String property, Object value) {
if (!TimersViewColumnPresentation.COL_VALUE.equals(property)) return;
AlarmDMC dmc = getAlarmDMC(element);
if (dmc == null) return;
DsfSession session = DsfSession.getSession(dmc.getSessionId());
if (session == null) return;
// Shell is used in displaying error dialogs.
Shell shell = getShell();
if (shell == null) return;
Integer intValue = null;
if (value instanceof String) {
try {
intValue = new Integer(((String)value).trim());
} catch (NumberFormatException e) {
MessageDialog.openError(shell, "Invalid Value", "Please enter a positive integer"); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
if (intValue.intValue() <= 0) {
MessageDialog.openError(shell, "Invalid Value", "Please enter a positive integer"); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
}
/*
* Create the query to write the value to the service.
* Note: no need to guard against RejectedExecutionException, because
* DsfSession.getSession() above would only return an active session.
*/
SetValueQuery query = new SetValueQuery(dmc, intValue);
session.getExecutor().execute(query);
try {
// Return value is irrelevant, any error would come through with an exception.
query.get().toString();
} catch (InterruptedException e) {
assert false;
} catch (ExecutionException e) {
// View must be shutting down, no need to show erro dialog.
}
}
/**
* Need to dispose the cell modifier property because of the service
* tracker.
*/
@ThreadSafe
public synchronized void dispose() {
if (fServiceTracker != null) {
fServiceTracker.close();
}
}
private Shell getShell() {
if (DsfExamplesPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow() != null) {
return DsfExamplesPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell();
}
return null;
}
private AlarmDMC getAlarmDMC(Object element) {
if (element instanceof IAdaptable) {
return (AlarmDMC)((IAdaptable)element).getAdapter(AlarmDMC.class);
}
return null;
}
@ThreadSafe
private synchronized AlarmService getService(AlarmDMC dmc) {
String serviceId = DsfServices.createServiceFilter( AlarmService.class, fSession.getId() );
if (fServiceTracker == null) {
try {
fServiceTracker = new ServiceTracker(
DsfExamplesPlugin.getBundleContext(),
DsfExamplesPlugin.getBundleContext().createFilter(serviceId),
null);
fServiceTracker.open();
} catch (InvalidSyntaxException e) {
return null;
}
}
return (AlarmService)fServiceTracker.getService();
}
private class GetValueQuery extends Query<Integer> {
final AlarmDMC fDmc;
private GetValueQuery(AlarmDMC dmc) {
super();
fDmc = dmc;
}
@Override
protected void execute(final DataRequestMonitor<Integer> rm) {
/*
* Guard against the session being disposed. If session is disposed
* it could mean that the executor is shut-down, which in turn
* could mean that we can't execute the "done" argument.
* In that case, cancel to notify waiting thread.
*/
final DsfSession session = DsfSession.getSession(fDmc.getSessionId());
if (session == null) {
cancel(false);
return;
}
AlarmService service = getService(fDmc);
if (service == null) {
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfService.INVALID_STATE,
"Service not available", null)); //$NON-NLS-1$
return;
}
service.getAlarmData(fDmc, new DataRequestMonitor<AlarmData>(session.getExecutor(), rm) {
@Override
protected void handleCompleted() {
// We're in another dispatch, so we must guard against executor shutdown again.
if (DsfSession.isSessionActive(session.getId())) {
super.handleCompleted();
}
}
@Override
protected void handleOK() {
rm.setData(getData().getTriggeringValue());
rm.done();
}
});
}
}
private class SetValueQuery extends Query<Object> {
AlarmDMC fDmc;
int fValue;
SetValueQuery(AlarmDMC dmc, int value) {
super();
fDmc = dmc;
fValue = value;
}
@Override
protected void execute(final DataRequestMonitor<Object> rm) {
// Guard against terminated session
final DsfSession session = DsfSession.getSession(fDmc.getSessionId());
if (session == null) {
cancel(false);
return;
}
// Guard against a disposed service
AlarmService service = getService(fDmc);
if (service == null) {
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfService.INVALID_STATE,
"Service not available", null)); //$NON-NLS-1$
rm.done();
return;
}
// Finally set the value and return.
service.setAlarmValue(fDmc, fValue);
// Return value is irrelevant.
rm.setData(new Object());
rm.done();
}
}
}

View file

@ -0,0 +1,361 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import java.util.Hashtable;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.datamodel.AbstractDMContext;
import org.eclipse.dd.dsf.datamodel.AbstractDMEvent;
import org.eclipse.dd.dsf.datamodel.DMContexts;
import org.eclipse.dd.dsf.datamodel.IDMContext;
import org.eclipse.dd.dsf.datamodel.IDMData;
import org.eclipse.dd.dsf.datamodel.IDMService;
import org.eclipse.dd.dsf.service.AbstractDsfService;
import org.eclipse.dd.dsf.service.DsfServiceEventHandler;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerDMC;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerData;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerTickEvent;
import org.osgi.framework.BundleContext;
/**
* Alarm service tracks a set of alarm objects which are occacionally
* triggered by the timers from the TimerService.
* <p>
* This service depends on the TimerService, so the TimerService has to be
* running before this service is initialized. However, the alarm objects
* themeselves do not depend on the timers, they can be listed, created,
* removed without any timers present. So a separate context object exists
* to track alarm status, which requires both an alarm and a timer in order
* to exist.
*/
public class AlarmService extends AbstractDsfService
implements IDMService
{
/**
* Event indicating that the list of alarms is changed and the clients
* which display alarms should re-query this list.
*/
public class AlarmsChangedEvent extends AbstractDMEvent<IDMContext> {
AlarmsChangedEvent() { super(fAlarmsContext); }
}
/**
* Context representing an alarm tracked by this service.
*/
public static class AlarmDMC extends AbstractDMContext {
/** Alarm number, also index into alarm map */
final int fAlarm;
public AlarmDMC(AlarmService service, int alarm) {
super(service, new IDMContext[] { service.fAlarmsContext });
fAlarm = alarm;
}
@Override
public boolean equals(Object other) {
return baseEquals(other) && ((AlarmDMC)other).fAlarm == fAlarm;
}
@Override
public int hashCode() { return baseHashCode() + fAlarm; }
@Override
public String toString() { return baseToString() + ".alarm[" + fAlarm + "]"; } //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Data object containing information about the alarm. This object
* references internal service data, so it has to guard agains this data
* being obsolete.
*/
public class AlarmData implements IDMData {
private int fAlarmNumber;
AlarmData(int alarmNumber) { fAlarmNumber = alarmNumber; }
public boolean isValid() { return fAlarms.containsKey(fAlarmNumber); }
public int getAlarmNumber() { return fAlarmNumber; }
public int getTriggeringValue() {
if (!isValid()) return -1;
return fAlarms.get(fAlarmNumber);
}
}
/**
* Context representing the "triggered" status of an alarm with respect to
* a specific timer. Having this object separate from the alarm itself
* allows the alarm object to exist independently of the timers.
*/
public class AlarmStatusContext extends AbstractDMContext {
/**
* An alarm status requires both a timer and alarm context, both of which
* become parents of the status context.
*/
public AlarmStatusContext(AbstractDsfService service, TimerDMC timerCtx, AlarmDMC alarmCtx) {
super(service.getSession().getId(), new IDMContext[] { timerCtx, alarmCtx });
}
@Override
public boolean equals(Object other) { return baseEquals(other); }
@Override
public int hashCode() { return baseHashCode(); }
@Override
public String toString() {
return baseToString() + ":alarm_status"; //$NON-NLS-1$
}
}
/**
* Data about alarm status. No surprises here.
*
*/
public class AlarmStatusData implements IDMData {
private boolean fIsTriggered;
public boolean isValid() { return true; }
AlarmStatusData(boolean triggered) { fIsTriggered = triggered; }
public boolean isTriggered() { return fIsTriggered; }
}
/**
* Event indicating that an alarm has been triggered by a timer. The
* status context object's parents indicate which alarm and timer are
* involved.
*/
public class AlarmTriggeredEvent extends AbstractDMEvent<AlarmStatusContext> {
public AlarmTriggeredEvent(AlarmStatusContext context) {
super(context);
}
}
/** Parent context for all alarms */
private final IDMContext fAlarmsContext;
/** Counter for generating alarm numbers */
private int fAlarmCounter = 1;
/** Map holding the alarms */
private Map<Integer,Integer> fAlarms = new TreeMap<Integer,Integer>();
/** Constructor requires only the session for this service */
AlarmService(DsfSession session) {
super(session);
fAlarmsContext = new AbstractDMContext(this, new IDMContext[0]) {
private final Object fHashObject = new Object();
@Override
public boolean equals(Object obj) { return (this == obj); };
@Override
public int hashCode() { return fHashObject.hashCode(); }
@Override
public String toString() { return "#alarms"; } //$NON-NLS-1$
};
}
@Override
protected BundleContext getBundleContext() {
return DsfExamplesPlugin.getBundleContext();
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(
new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleOK() {
doInitialize(requestMonitor);
}});
}
/**
* Initialization routine registers the service, and adds it as a listener
* to service events.
*/
private void doInitialize(RequestMonitor requestMonitor) {
getSession().addServiceEventListener(this, null);
register(new String[]{AlarmService.class.getName()}, new Hashtable<String,String>());
requestMonitor.done();
}
@Override
public void shutdown(RequestMonitor requestMonitor) {
getSession().removeServiceEventListener(this);
unregister();
super.shutdown(requestMonitor);
}
public boolean isValid() { return true; }
@SuppressWarnings("unchecked")
public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
if (dmc instanceof AlarmDMC) {
getAlarmData((AlarmDMC)dmc, (DataRequestMonitor<AlarmData>)rm);
return;
} else if (dmc instanceof AlarmStatusContext) {
getAlarmStatusData((AlarmStatusContext)dmc, (DataRequestMonitor<AlarmStatusData>)rm);
return;
} else if (dmc == fAlarmsContext) {
((DataRequestMonitor<AlarmService>)rm).setData(this);
} else {
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$
}
rm.done();
}
/**
* Listener for timer ticks events. If a timer triggers an alarm, this
* service needs to issue an alarm triggered event.
* @param event
*/
@DsfServiceEventHandler
public void eventDispatched(TimerTickEvent event) {
final TimerDMC timerContext = event.getDMContext();
getServicesTracker().getService(TimerService.class).getTimerData(
event.getDMContext(),
new DataRequestMonitor<TimerData>(getExecutor(), null) {
@Override
protected void handleCompleted() {
if (!getStatus().isOK()) return;
checkAlarmsForTimer(timerContext, getData().getTimerValue());
}
@Override public String toString() { return "Got timer data: " + getData(); } //$NON-NLS-1$
});
}
/**
* Checks the existing alarms for whether they are triggered by given timer.
* @param timerContext Context of the timer that is changed.
* @param timerValue Current value of the timer.
*/
private void checkAlarmsForTimer(TimerDMC timerContext, int timerValue) {
for (Map.Entry<Integer,Integer> entry : fAlarms.entrySet()) {
if (timerValue == entry.getValue()) {
getSession().dispatchEvent(new AlarmTriggeredEvent(
new AlarmStatusContext(this, timerContext, new AlarmDMC(this, entry.getKey()))),
getProperties());
}
}
}
/**
* Retrieves the list of alarm contexts.
*
* <br>Note: this method doesn't need to be asynchronous, because all the
* data is stored locally. But using an asynchronous method makes this a
* more applicable example.
*
* @param rm Return data token.
*/
public void getAlarms(DataRequestMonitor<AlarmDMC[]> rm) {
AlarmDMC[] alarmContexts = new AlarmDMC[fAlarms.size()];
int i = 0;
for (int alarm : fAlarms.keySet()) {
alarmContexts[i++] = new AlarmDMC(this, alarm);
}
rm.setData(alarmContexts);
rm.done();
}
/**
* Retrieves the data object for given alarm context.
*
* <br>Note: likewise this method doesn't need to be asynchronous.
*/
public void getAlarmData(AlarmDMC alarmCtx, DataRequestMonitor<AlarmData> rm) {
if (!fAlarms.containsKey(alarmCtx.fAlarm)) {
rm.setStatus(new Status(
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, INVALID_HANDLE, "Alarm context invalid", null)); //$NON-NLS-1$
rm.done();
return;
}
rm.setData(new AlarmData(alarmCtx.fAlarm));
rm.done();
}
/**
* Returns the alarm status context object, for given timer and alarms.
*
* <br>Note: this method is synchronous... for variety.
*/
public AlarmStatusContext getAlarmStatus(AlarmDMC alarmCtx, TimerDMC timerCtx) {
return new AlarmStatusContext(this, timerCtx, alarmCtx);
}
/**
* Returns the data object for given alarm status object.
*/
public void getAlarmStatusData(AlarmStatusContext alarmStatusCtx, final DataRequestMonitor<AlarmStatusData> rm) {
final TimerService.TimerDMC timerCtx = DMContexts.getAncestorOfType(
alarmStatusCtx, TimerService.TimerDMC.class);
final AlarmDMC alarmCtx = DMContexts.getAncestorOfType(
alarmStatusCtx, AlarmDMC.class);
assert alarmCtx != null && timerCtx != null;
getServicesTracker().getService(TimerService.class).getTimerData(
timerCtx,
new DataRequestMonitor<TimerData>(getExecutor(), rm) {
@Override
protected void handleOK() {
if (!fAlarms.containsKey(alarmCtx.fAlarm)) {
rm.setStatus(new Status(
IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, INVALID_HANDLE, "Alarm context invalid", null)); //$NON-NLS-1$
rm.done();
return;
}
boolean isTriggered = getData().getTimerValue() >= fAlarms.get(alarmCtx.fAlarm);
rm.setData(new AlarmStatusData(isTriggered));
rm.done();
}
});
}
/**
* Creates a new alarm object with given value.
* @return context of the new alarm.
*/
public AlarmDMC createAlarm(int value) {
int newAlarm = fAlarmCounter++;
fAlarms.put(newAlarm, value);
getSession().dispatchEvent(new AlarmsChangedEvent(), getProperties());
return new AlarmDMC(this, newAlarm);
}
/** Removes given alarm from service. */
public void deleteAlarm(AlarmDMC alarmCtx) {
fAlarms.remove(alarmCtx.fAlarm);
getSession().dispatchEvent(new AlarmsChangedEvent(), getProperties());
}
/**
* Changes the value of the given alarm.
* @param dmc Alarm to change
* @param newValue New alarm value.
*/
public void setAlarmValue(AlarmDMC dmc, int newValue) {
if (fAlarms.containsKey(dmc.fAlarm)) {
fAlarms.put(dmc.fAlarm, newValue);
}
getSession().dispatchEvent(new AlarmsChangedEvent(), getProperties());
}
}

View file

@ -0,0 +1,112 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.ui.viewmodel.VMDelta;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmDMC;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmStatusContext;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmStatusData;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerDMC;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
/**
* View model node that determines whether an "alarm triggered" indicator is
* shown in the tree. This indicator is only shown if a given alarm is
* triggered for a given timer.
*
* @see AlarmStatusContext
*/
@SuppressWarnings("restriction")
class AlarmStatusVMNode extends AbstractDMVMNode
implements IElementLabelProvider
{
public AlarmStatusVMNode(AbstractDMVMProvider provider, DsfSession session) {
super(provider, session, AlarmStatusContext.class);
}
@Override
protected void updateElementsInSessionThread(final IChildrenUpdate update) {
if (!checkService(AlarmService.class, null, update)) return;
if (!checkService(TimerService.class, null, update)) return;
AlarmDMC alarmDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), AlarmDMC.class);
TimerDMC timerDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), TimerDMC.class);
if (alarmDmc == null || timerDmc == null) {
update.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Required elements not found in path")); //$NON-NLS-1$
update.done();
return;
}
// Get the alarm status DMC then check the triggered value to make sure it's triggered.
final AlarmStatusContext alarmStatusDmc = getServicesTracker().getService(AlarmService.class).
getAlarmStatus(alarmDmc, timerDmc);
getServicesTracker().getService(AlarmService.class).getAlarmStatusData(
alarmStatusDmc,
new DataRequestMonitor<AlarmStatusData>(getSession().getExecutor(), null) {
@Override
public void handleCompleted() {
if (isDisposed()) return;
if (!getStatus().isOK()) {
update.setStatus(getStatus());
} else {
if (getData().isTriggered()) {
update.setChild(createVMContext(alarmStatusDmc), 0);
}
}
update.done();
}});
}
public void update(ILabelUpdate[] updates) {
for (ILabelUpdate update : updates) {
update.setLabel("ALARM TRIGGERED", 0); //$NON-NLS-1$
update.setImageDescriptor(
DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(
DsfExamplesPlugin.IMG_ALARM_TRIGGERED),
0);
update.done();
}
}
public int getDeltaFlags(Object e) {
// This node generates delta if the timers have changed, or if the
// label has changed.
if (e instanceof AlarmService.AlarmTriggeredEvent) {
return IModelDelta.ADDED | IModelDelta.SELECT | IModelDelta.EXPAND;
}
return IModelDelta.NO_CHANGE;
}
public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) {
// An element is added when and selected upon a triggered event.
// Parent element is also expanded allow element to be selected.
if (e instanceof AlarmService.AlarmTriggeredEvent) {
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.EXPAND);
parentDelta.addNode(
createVMContext( ((AlarmService.AlarmTriggeredEvent)e).getDMContext() ),
0,
IModelDelta.ADDED | IModelDelta.SELECT);
}
requestMonitor.done();
}
}

View file

@ -0,0 +1,212 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import java.text.MessageFormat;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.dsf.ui.viewmodel.VMDelta;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.dd.dsf.ui.viewmodel.properties.IElementPropertiesProvider;
import org.eclipse.dd.dsf.ui.viewmodel.properties.IPropertiesUpdate;
import org.eclipse.dd.dsf.ui.viewmodel.properties.LabelAttribute;
import org.eclipse.dd.dsf.ui.viewmodel.properties.LabelColumnInfo;
import org.eclipse.dd.dsf.ui.viewmodel.properties.LabelImage;
import org.eclipse.dd.dsf.ui.viewmodel.properties.LabelText;
import org.eclipse.dd.dsf.ui.viewmodel.properties.PropertyBasedLabelProvider;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmDMC;
import org.eclipse.dd.examples.dsf.timers.AlarmService.AlarmData;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.widgets.Composite;
/**
* View model node that defines how alarm DMContexts are displayed in the view. Alarm
* nodes are fairly static, once they are created their label doesn't change.
* @see AlarmDMC
*/
@SuppressWarnings("restriction")
class AlarmsVMNode extends AbstractDMVMNode
implements IElementEditor, IElementPropertiesProvider, IElementLabelProvider
{
public static final String PROP_ALARM_NUMBER = "alarmNumber"; //$NON-NLS-1$
public static final String PROP_ALARM_TRIGGER_VALUE = "alarmTriggerValue"; //$NON-NLS-1$
private AlarmCellModifier fAlarmCellModifier;
private PropertyBasedLabelProvider fLabelProvider;
public AlarmsVMNode(AbstractDMVMProvider provider, DsfSession session) {
super(provider, session, AlarmDMC.class);
fLabelProvider = new PropertyBasedLabelProvider();
LabelColumnInfo idCol = new LabelColumnInfo(
new LabelAttribute[] {
new LabelText(new MessageFormat("Alarm #{0}"), new String[] { PROP_ALARM_NUMBER }), //$NON-NLS-1$
new LabelImage(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(DsfExamplesPlugin.IMG_ALARM))
});
fLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_ID, idCol);
LabelText valueText = new LabelText(new MessageFormat("{0}"), new String[] { PROP_ALARM_TRIGGER_VALUE }); //$NON-NLS-1$
LabelColumnInfo valueCol = new LabelColumnInfo(
new LabelAttribute[] {
new LabelText(new MessageFormat("{0}"), new String[] { PROP_ALARM_TRIGGER_VALUE }) //$NON-NLS-1$
});
fLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_VALUE, valueCol);
}
@Override
protected void updateElementsInSessionThread(final IChildrenUpdate update) {
if (!checkService(AlarmService.class, null, update)) return;
// Retrieve the alarm DMContexts, create the corresponding VMCs array, and
// set them as result.
getServicesTracker().getService(AlarmService.class).getAlarms(
new DataRequestMonitor<AlarmDMC[]>(getSession().getExecutor(), null) {
@Override
public void handleCompleted() {
if (!getStatus().isOK()) {
update.setStatus(getStatus());
} else {
fillUpdateWithVMCs(update, getData());
}
update.done();
}});
}
public void update(ILabelUpdate[] updates) {
fLabelProvider.update(updates);
}
public void update(final IPropertiesUpdate[] updates) {
try {
getSession().getExecutor().execute(new DsfRunnable() {
public void run() {
for (IPropertiesUpdate update : updates) {
updatePropertiesInSessionThread(update);
}
}});
} catch (RejectedExecutionException e) {
for (IViewerUpdate update : updates) {
handleFailedUpdate(update);
}
}
}
@ConfinedToDsfExecutor("getSession#getExecutor")
protected void updatePropertiesInSessionThread(final IPropertiesUpdate update) {
final AlarmDMC dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), AlarmDMC.class);
if (!checkDmc(dmc, update) || !checkService(AlarmService.class, null, update)) return;
getDMVMProvider().getModelData(
this, update,
getServicesTracker().getService(AlarmService.class, null),
dmc,
new DataRequestMonitor<AlarmData>(getSession().getExecutor(), null) {
@Override
protected void handleCompleted() {
/*
* Check that the request was evaluated and data is still
* valid. The request could fail if the state of the
* service changed during the request, but the view model
* has not been updated yet.
*/
if (!getStatus().isOK() || !getData().isValid()) {
assert getStatus().isOK() ||
getStatus().getCode() != IDsfService.INTERNAL_ERROR ||
getStatus().getCode() != IDsfService.NOT_SUPPORTED;
handleFailedUpdate(update);
return;
}
update.setProperty(PROP_ALARM_NUMBER, getData().getAlarmNumber());
update.setProperty(PROP_ALARM_TRIGGER_VALUE, getData().getTriggeringValue());
update.done();
}
},
getExecutor());
}
public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) {
if (TimersViewColumnPresentation.COL_VALUE.equals(columnId)) {
return new TextCellEditor(parent);
}
return null;
}
// Note: this method is synchronized because IElementEditor.getCellModifier can be called
// on any thread, even though in practice it should be only called on the UI thread.
public synchronized ICellModifier getCellModifier(IPresentationContext context, Object element) {
if (fAlarmCellModifier == null) {
fAlarmCellModifier = new AlarmCellModifier(getSession());
}
return fAlarmCellModifier;
}
public int getDeltaFlags(Object e) {
// Since the label for alarms doesn't change, this node will generate
// delta info only if the list of alarms is changed.
if (e instanceof AlarmService.AlarmsChangedEvent) {
return IModelDelta.CONTENT;
}
return IModelDelta.NO_CHANGE;
}
public void buildDelta(Object event, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) {
if (event instanceof AlarmService.AlarmsChangedEvent) {
// The list of alarms has changed, which means that the parent
// node needs to refresh its contents, which in turn will re-fetch the
// elements from this node.
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
}
requestMonitor.done();
}
@Override
public synchronized void dispose() {
synchronized(this) {
if (fAlarmCellModifier != null) {
fAlarmCellModifier.dispose();
}
}
super.dispose();
}
public String getPropertyDescription(String property) {
// TODO Auto-generated method stub
return null;
}
public String getPropertyName(String property) {
// TODO Auto-generated method stub
return null;
}
}

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
/**
* Shutdown sequence that stops the services in the timers session.
*
*/
class ServicesShutdownSequence extends Sequence {
DsfSession fSession;
DsfServicesTracker fTracker;
ServicesShutdownSequence(DsfSession session) {
super(session.getExecutor());
fSession = session;
}
Step[] fSteps = new Step[] {
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fTracker = new DsfServicesTracker(DsfExamplesPlugin.getBundleContext(), fSession.getId());
requestMonitor.done();
}
@Override
public void rollBack(RequestMonitor requestMonitor) {
fTracker.dispose();
fTracker = null;
requestMonitor.done();
}
},
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(AlarmService.class, requestMonitor);
}
},
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(TimerService.class, requestMonitor);
}
},
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fTracker.dispose();
fTracker = null;
requestMonitor.done();
}
}
};
@Override
public Step[] getSteps() { return fSteps; }
/**
* Convenience method that shuts down given service. Only service class
* is used to identify the service.
*/
private <V extends IDsfService> void shutdownService(Class<V> clazz, RequestMonitor requestMonitor) {
IDsfService service = fTracker.getService(clazz);
if (service != null) {
service.shutdown(requestMonitor);
}
else {
requestMonitor.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR,
"Service '" + clazz.getName() + "' not found.", null)); //$NON-NLS-1$ //$NON-NLS-2$
requestMonitor.done();
}
}
}

View file

@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.service.DsfSession;
/**
* Startup sequence for the timers session. With only two services, this is
* a very simple sequence. Last step creates the first timer and alarm.
*/
class ServicesStartupSequence extends Sequence {
DsfSession fSession;
private TimerService fTimerService = null;
private AlarmService fAlarmService = null;
ServicesStartupSequence(DsfSession session) {
super(session.getExecutor());
fSession = session;
}
Step[] fSteps = new Step[] {
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fTimerService = new TimerService(fSession);
fTimerService.initialize(requestMonitor);
}},
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fAlarmService = new AlarmService(fSession);
fAlarmService.initialize(requestMonitor);
}},
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fTimerService.startTimer();
fAlarmService.createAlarm(5);
requestMonitor.done();
}}
};
@Override
public Step[] getSteps() { return fSteps; }
}

View file

@ -0,0 +1,260 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import java.util.Hashtable;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.datamodel.AbstractDMContext;
import org.eclipse.dd.dsf.datamodel.AbstractDMEvent;
import org.eclipse.dd.dsf.datamodel.IDMContext;
import org.eclipse.dd.dsf.datamodel.IDMData;
import org.eclipse.dd.dsf.datamodel.IDMService;
import org.eclipse.dd.dsf.service.AbstractDsfService;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.osgi.framework.BundleContext;
/**
* Timer service tracks a set of timers, which are created per user request.
* The timers and their data are provided by the service using the DSF data
* model interfaces.
* <p>
* When each timer is created, an event is issued that the service contents are
* changed, and clients should re-query the list of timers. The timers
* increment their value at rate of one per second (but they are not synchronous),
* and an event is issued for every tick.
*/
public class TimerService extends AbstractDsfService
implements IDMService
{
/**
* Event indicating that the list of timers is changed and the clients
* which display timers should re-query this list.
*/
public class TimersChangedEvent extends AbstractDMEvent<IDMContext> {
TimersChangedEvent() { super(fTimersContext); }
}
/**
* Timer context represents a timer in this service. Clients can use this
* context to retrieve timer data. This class implements the <code>Comaparable</code>
* interfaces so that the objects can be stored in a TreeMap, which keeps them sorted.
*/
public static class TimerDMC extends AbstractDMContext
implements Comparable<TimerDMC>
{
/**
* Timer number, which is also index to timers map.
*/
final int fTimer;
public TimerDMC(TimerService service, int timer) {
super(service, new IDMContext[] { service.fTimersContext });
fTimer = timer;
}
/**
* Timer context objects are created as needed and not cached, so the
* equals method implementation is critical.
*/
@Override
public boolean equals(Object other) {
return baseEquals(other) && ((TimerDMC)other).fTimer == fTimer;
}
@Override
public int hashCode() { return baseHashCode() + fTimer; }
@Override
public String toString() {
return baseToString() + ".timer[" + fTimer + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
public int compareTo(TimerDMC other) {
TimerDMC otherTimer = other;
return (fTimer < otherTimer.fTimer ? -1 : (fTimer == otherTimer.fTimer ? 0 : 1));
}
}
/**
* Data about the timer in the service. This object references internal
* service data, so it has to guard agains this data being obsolete.
*/
public class TimerData implements IDMData {
TimerDMC fTimerDMC;
TimerData(TimerDMC timer) { fTimerDMC = timer; }
public boolean isValid() { return fTimers.containsKey(fTimerDMC); }
public int getTimerNumber() { return fTimerDMC.fTimer; }
public int getTimerValue() {
if (!isValid()) return -1;
return fTimers.get(fTimerDMC);
}
@Override public String toString() { return "Timer " + fTimerDMC.fTimer + " = " + getTimerValue(); } //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Event indicating that a timer's value has incremented. The context in
* the event points to the timer that has changed.
*/
public class TimerTickEvent extends AbstractDMEvent<TimerDMC> {
public TimerTickEvent(TimerDMC context) {
super(context);
}
}
/** Parnet context for all timers */
private final IDMContext fTimersContext;
/** Counter for generating timer numbers */
private int fTimerCounter = 1;
/** Map holding the timers */
private Map<TimerDMC, Integer> fTimers = new TreeMap<TimerDMC, Integer>();
private Map<TimerDMC, Future<?>> fTimerFutures = new TreeMap<TimerDMC, Future<?>>();
/** Constructor requires only the session for this service */
TimerService(DsfSession session) {
super(session);
fTimersContext = new AbstractDMContext(this, new IDMContext[0]) {
private final Object fHashObject = new Object();
@Override
public boolean equals(Object obj) { return (this == obj); };
@Override
public int hashCode() { return fHashObject.hashCode(); }
@Override
public String toString() { return "#timers"; } //$NON-NLS-1$
};
}
@Override
protected BundleContext getBundleContext() {
return DsfExamplesPlugin.getBundleContext();
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(
new RequestMonitor(getExecutor(), requestMonitor) {
@Override
public void handleOK() {
doInitialize(requestMonitor);
}});
}
@Override
public void shutdown(RequestMonitor requestMonitor) {
/*
* Go through all the timer futures and cancel them, so that they
* don't fire any more events.
*/
for (Future<?> future : fTimerFutures.values()) {
future.cancel(false);
}
unregister();
super.shutdown(requestMonitor);
}
/**
* Performs the relevant initialization for this service: registering and
* scheduling the timer.
* @param requestMonitor
*/
private void doInitialize(RequestMonitor requestMonitor) {
register(new String[]{TimerService.class.getName()}, new Hashtable<String,String>());
requestMonitor.done();
}
public boolean isValid() { return true; }
@SuppressWarnings("unchecked")
public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
if (dmc instanceof TimerDMC) {
getTimerData((TimerDMC)dmc, (DataRequestMonitor<TimerData>)rm);
return;
} else if (dmc == fTimersContext) {
((DataRequestMonitor<TimerService>)rm).setData(this);
} else {
rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$
}
rm.done();
}
/**
* Retrieves the list of timer contexts.
*
* <br>Note: this method doesn't need to be asynchronous, because all the
* data is stored locally. But using an asynchronous method makes this a
* more applicable example.
*
* @param rm Return data token.
*/
public void getTimers(DataRequestMonitor<TimerDMC[]> rm) {
rm.setData( fTimers.keySet().toArray(new TimerDMC[fTimers.size()]) );
rm.done();
}
/**
* Retrieves the data object for given timer context.
*
* <br>Note: likewise this method doesn't need to be asynchronous.
*/
public void getTimerData(TimerDMC context, DataRequestMonitor<TimerData> rm) {
rm.setData(new TimerData(context));
rm.done();
}
/**
* Creates a new timer and returns its context.
*/
public TimerDMC startTimer() {
final TimerDMC newTimer = new TimerDMC(this, fTimerCounter++);
fTimers.put(newTimer, 0);
Future<?> timerFuture = getExecutor().scheduleAtFixedRate(
new Runnable() {
public void run() {
fTimers.put(newTimer, fTimers.get(newTimer) + 1);
getSession().dispatchEvent(new TimerTickEvent(newTimer), getProperties());
}
@Override
public String toString() { return "Scheduled timer runnable for timer " + newTimer; } //$NON-NLS-1$
},
1, 1, TimeUnit.SECONDS);
fTimerFutures.put(newTimer, timerFuture);
getSession().dispatchEvent(new TimersChangedEvent(), getProperties());
return newTimer;
}
/**
* Removes given timer from list of timers.
*/
public void killTimer(TimerDMC timerContext) {
if (fTimers.containsKey(timerContext)) {
fTimers.remove(timerContext);
fTimerFutures.remove(timerContext).cancel(false);
}
getSession().dispatchEvent(new TimersChangedEvent(), getProperties());
}
}

View file

@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.ui.viewmodel.IVMProvider;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMAdapter;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
/**
* This is the adapter that implements the flexible hierarchy viewer interfaces
* for providing content, labels, and event proxy-ing for the viewer. This
* adapter is registered with the DSF Session object, and is returned by the
* IDMContext.getAdapter() and IVMContext.getAdapter() methods,
* which both call {@link DsfSession#getModelAdapter(Class)}.
* <p>
* The adapter implementation for this excercise is hard-coded to provide
* contents for only one view. In turn the view contens are determined using
* the configurable ViewModelProvider. For demonstration purposes, this model
* adapter has two different layout configurations that can be used. These
* layout configurations can be set by calling the {@link #setViewLayout} method.
* <p>
* This class is primarily accessed by the flexible hierarchy viewer from a
* non-executor thread. So the class is thread-safe, except for a view methods
* which must be called on the executor thread.
*
* @see AbstractDMVMProvider
*/
@SuppressWarnings("restriction")
@ThreadSafe
public class TimersModelAdapter extends AbstractDMVMAdapter
{
TimersVMProvider fViewModelProvider;
@Override
protected IVMProvider createViewModelProvider(IPresentationContext context) {
/*
* In this example there is only one viewer, so there is only one
* VMProvider.
*/
return fViewModelProvider;
}
public TimersModelAdapter(DsfSession session, IPresentationContext presentationContext) {
super(session);
fViewModelProvider = new TimersVMProvider(this, presentationContext, getSession());
}
TimersVMProvider getTimersVMProvider() {
return fViewModelProvider;
}
}

View file

@ -0,0 +1,160 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.dsf.ui.viewmodel.VMDelta;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerDMC;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerData;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
/**
* View model node that defines how timer DMContexts are displayed in the view. Timers
* change with every tick of the timer, so the label has to be repained
* upon timer tick events.
* @see TimerDMC
*/
@SuppressWarnings("restriction")
class TimersVMNode extends AbstractDMVMNode
implements IElementLabelProvider
{
public TimersVMNode(AbstractDMVMProvider provider, DsfSession session) {
super(provider, session, TimerDMC.class);
}
@Override
protected void updateElementsInSessionThread(final IChildrenUpdate update) {
if (!checkService(AlarmService.class, null, update)) return;
// Retrieve the timer DMContexts, create the corresponding VMCs array, and
// set them as result.
getServicesTracker().getService(TimerService.class).getTimers(
new DataRequestMonitor<TimerDMC[]>(getSession().getExecutor(), null) {
@Override
public void handleCompleted() {
if (!getStatus().isOK()) {
update.setStatus(getStatus());
} else {
fillUpdateWithVMCs(update, getData());
}
update.done();
}});
}
public void update(final ILabelUpdate[] updates) {
try {
getSession().getExecutor().execute(new DsfRunnable() {
public void run() {
updateLabelInSessionThread(updates);
}});
} catch (RejectedExecutionException e) {
for (ILabelUpdate update : updates) {
handleFailedUpdate(update);
}
}
}
protected void updateLabelInSessionThread(ILabelUpdate[] updates) {
for (final ILabelUpdate update : updates) {
final TimerDMC dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), TimerDMC.class);
if (!checkDmc(dmc, update) || !checkService(TimerService.class, null, update)) continue;
getDMVMProvider().getModelData(
this, update,
getServicesTracker().getService(TimerService.class, null),
dmc,
new DataRequestMonitor<TimerData>(getSession().getExecutor(), null) {
@Override
protected void handleCompleted() {
/*
* Check that the request was evaluated and data is still
* valid. The request could fail if the state of the
* service changed during the request, but the view model
* has not been updated yet.
*/
if (!getStatus().isOK() || !getData().isValid()) {
assert getStatus().isOK() ||
getStatus().getCode() != IDsfService.INTERNAL_ERROR ||
getStatus().getCode() != IDsfService.NOT_SUPPORTED;
handleFailedUpdate(update);
return;
}
/*
* If columns are configured, call the protected methods to
* fill in column values.
*/
String[] localColumns = update.getPresentationContext().getColumns();
if (localColumns == null) localColumns = new String[] { null };
for (int i = 0; i < localColumns.length; i++) {
fillColumnLabel(dmc, getData(), localColumns[i], i, update);
}
update.done();
}
},
getExecutor());
}
}
protected void fillColumnLabel(TimerDMC dmContext, TimerData dmData, String columnId, int idx,
ILabelUpdate update)
{
if (TimersViewColumnPresentation.COL_ID.equals(columnId)) {
update.setLabel( Integer.toString(dmData.getTimerNumber()), idx );
update.setImageDescriptor(
DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(DsfExamplesPlugin.IMG_TIMER), idx);
} else if (TimersViewColumnPresentation.COL_VALUE.equals(columnId)) {
update.setLabel( Integer.toString(dmData.getTimerValue()), idx);
}
}
public int getDeltaFlags(Object e) {
// This node generates delta if the timers have changed, or if the
// label has changed.
if (e instanceof TimerService.TimerTickEvent) {
return IModelDelta.STATE;
} else if (e instanceof TimerService.TimersChangedEvent) {
return IModelDelta.CONTENT;
}
return IModelDelta.NO_CHANGE;
}
public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) {
if (e instanceof TimerService.TimerTickEvent) {
// Add delta indicating that the VMC for the given timer context
// has changed.
parentDelta.addNode( createVMContext(((TimerService.TimerTickEvent)e).getDMContext()), IModelDelta.STATE );
} else if (e instanceof TimerService.TimersChangedEvent) {
// The list of timers has changed, which means that the parent
// node needs to refresh its contents, which in turn will re-fetch the
// elements from this node.
parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
}
requestMonitor.done();
}
}

View file

@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.ui.viewmodel.AbstractVMAdapter;
import org.eclipse.dd.dsf.ui.viewmodel.IRootVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.IVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.RootVMNode;
import org.eclipse.dd.dsf.ui.viewmodel.DefaultVMModelProxyStrategy;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
/**
*
*/
@SuppressWarnings("restriction")
public class TimersVMProvider extends AbstractDMVMProvider {
/**
* The object to be set to the viewer that shows contents supplied by this provider.
* @see org.eclipse.jface.viewers.TreeViewer#setInput(Object)
*/
private final IAdaptable fViewerInputObject =
new IAdaptable() {
/**
* The input object provides the viewer access to the viewer model adapter.
*/
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if ( adapter.isInstance(getVMAdapter()) ) {
return getVMAdapter();
}
return null;
}
@Override
public String toString() {
return "Timers View Root"; //$NON-NLS-1$
}
};
private DefaultVMModelProxyStrategy fModelProxyStrategy;
/** Enumeration of possible layouts for the timers view model */
public enum ViewLayout { ALARMS_AT_TOP, TIMERS_AT_TOP }
public TimersVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) {
super(adapter, presentationContext, session);
setViewLayout(ViewLayout.ALARMS_AT_TOP);
}
public Object getViewerInputObject() {
return fViewerInputObject;
}
/**
* Configures a new layout for the timers view model.
* @param layout New layout to use.
*/
public void setViewLayout(ViewLayout layout) {
if (layout == ViewLayout.ALARMS_AT_TOP) {
IRootVMNode root = new RootVMNode(this);
IVMNode alarmsNode = new AlarmsVMNode(this, getSession());
IVMNode timersNode0 = new TimersVMNode(this, getSession());
addChildNodes(root, new IVMNode[] { alarmsNode, timersNode0 });
IVMNode timersNode = new TimersVMNode(this, getSession());
addChildNodes(alarmsNode, new IVMNode[] { timersNode });
IVMNode alarmStatusNode = new AlarmStatusVMNode(this, getSession());
addChildNodes(timersNode, new IVMNode[] { alarmStatusNode });
setRootNode(root);
} else if (layout == ViewLayout.TIMERS_AT_TOP) {
IRootVMNode root = new RootVMNode(this);
IVMNode timersNode = new TimersVMNode(this, getSession());
addChildNodes(root, new IVMNode[] { timersNode });
IVMNode alarmsNode = new AlarmsVMNode(this, getSession());
addChildNodes(timersNode, new IVMNode[] { alarmsNode });
IVMNode alarmStatusNode = new AlarmStatusVMNode(this, getSession());
addChildNodes(alarmsNode, new IVMNode[] { alarmStatusNode });
setRootNode(root);
}
/* TODO: replace with an event
fModelProxyStrategy.fireModelChanged(
new ModelDelta(getRootElement(), IModelDelta.CONTENT));
*/
}
@Override
public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) {
return new TimersViewColumnPresentation();
}
@Override
public String getColumnPresentationId(IPresentationContext context, Object element) {
return TimersViewColumnPresentation.ID;
}
}

View file

@ -0,0 +1,296 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import java.util.concurrent.ExecutionException;
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.datamodel.IDMContext;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.dd.examples.dsf.timers.TimerService.TimerDMC;
import org.eclipse.dd.examples.dsf.timers.TimersVMProvider.ViewLayout;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.part.ViewPart;
/**
* Example view which displays data from timers and alarms DSF services. This
* starts a new DSF session and configures the services for it. Then it
* configures a data model provider to process the service data and display it
* in a flexible-hierarchy asynchronous viewer.
*/
@SuppressWarnings("restriction")
public class TimersView extends ViewPart {
/** Asynchronous tree viewer from the platform debug.ui plugin. */
private TreeModelViewer fViewer;
/** DSF executor to use for a new session with timers and alarms services */
private DsfExecutor fExecutor;
/** DSF session */
private DsfSession fSession;
/** DSF services tracker used by actions in the viewer. */
private DsfServicesTracker fServices;
/** Adapter used to provide view model for flexible-hierarchy viewer */
private TimersModelAdapter fTimersModelAdapter;
/** Action which toggles the layout in the viewer */
private Action fToggleLayoutAction;
/** Action that adds a new timer */
private Action fAddTimerAction;
/** Action that adds a new alarm */
private Action fAddAlarmAction;
/** Action that removes the selected alarm or timer */
private Action fRemoveAction;
public TimersView() {}
/**
* This is a callback that will allow us to create the viewer and
* initialize it. For this view, it creates the DSF session, along
* with its services. Then it creates the viewer model adapter and
* registers it with the session.
*/
@Override
public void createPartControl(Composite parent) {
/*
* Create the Flexible Hierarchy viewer. Also create a presentation
* context which will be given to the content/label provider adapters
* to distinguish this view from other flex-hierarchy views.
*/
final IPresentationContext presentationContext = new PresentationContext("org.eclipse.dd.examples.dsf.timers"); //$NON-NLS-1$
fViewer = new TreeModelViewer(parent, SWT.VIRTUAL | SWT.FULL_SELECTION, presentationContext);
/*
* Create the executor, which will be used exclusively with this view,
* as well as a session and a services tracker for managing references
* to services.
*/
fExecutor = new DefaultDsfExecutor();
fSession = DsfSession.startSession(fExecutor, "org.eclipse.dd.examples.dsf.timers"); //$NON-NLS-1$
fServices = new DsfServicesTracker(DsfExamplesPlugin.getBundleContext(), fSession.getId());
/*
* Start the services using a sequence. The sequence runs in the
* dispatch thread, so we have to block this thread using Future.get()
* until it completes. The Future.get() will throw an exception if
* the sequence fails.
*/
ServicesStartupSequence startupSeq = new ServicesStartupSequence(fSession);
fSession.getExecutor().execute(startupSeq);
try {
startupSeq.get();
} catch (InterruptedException e) { assert false;
} catch (ExecutionException e) { assert false;
}
/*
* Create the flexible hierarchy content/label adapter. Then register
* it with the session.
*/
fTimersModelAdapter = new TimersModelAdapter(fSession, presentationContext);
fSession.registerModelAdapter(IElementContentProvider.class, fTimersModelAdapter);
fSession.registerModelAdapter(IModelProxyFactory.class, fTimersModelAdapter);
fSession.registerModelAdapter(IColumnPresentationFactory.class, fTimersModelAdapter);
/*
* Set the root element for the timers tree viewer. The root element
* comes from the content provider.
*/
fViewer.setInput(fTimersModelAdapter.getTimersVMProvider().getViewerInputObject());
makeActions();
contributeToActionBars();
}
@Override
public void dispose() {
try {
/*
* First dispose the view model, which is the client of services.
* We are not in the dispatch thread
*/
fSession.getExecutor().submit(new Runnable() {
public void run() {
fSession.unregisterModelAdapter(IElementContentProvider.class);
fSession.unregisterModelAdapter(IModelProxyFactory.class);
fSession.unregisterModelAdapter(IColumnPresentationFactory.class);
fTimersModelAdapter.dispose();
fTimersModelAdapter = null;
}}).get();
// Then invoke the shutdown sequence for the services.
ServicesShutdownSequence shutdownSeq = new ServicesShutdownSequence(fSession);
fSession.getExecutor().execute(shutdownSeq);
try {
shutdownSeq.get();
} catch (InterruptedException e) { assert false;
} catch (ExecutionException e) { assert false;
}
// Finally end the session and the executor:
fSession.getExecutor().submit(new Runnable() {
public void run() {
DsfSession.endSession(fSession);
fSession = null;
fExecutor.shutdown();
fExecutor = null;
}}).get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
//fViewer.dispose();
super.dispose();
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalToolBar(IToolBarManager manager) {
manager.add(fToggleLayoutAction);
manager.add(fAddTimerAction);
manager.add(fAddAlarmAction);
manager.add(fRemoveAction);
manager.add(new Separator());
}
private void makeActions() {
fToggleLayoutAction = new Action("Toggle Layout", IAction.AS_CHECK_BOX) { //$NON-NLS-1$
@Override
public void run() {
// Get the toggle state of the action while on UI thread.
final ViewLayout layout = isChecked() ? ViewLayout.ALARMS_AT_TOP : ViewLayout.TIMERS_AT_TOP;
// Switch to executor thread to perform the change in layout.
fExecutor.submit(new Runnable() { public void run() {
fTimersModelAdapter.getTimersVMProvider().setViewLayout(layout);
}});
}
};
fToggleLayoutAction.setToolTipText("Toggle Layout"); //$NON-NLS-1$
fToggleLayoutAction.setImageDescriptor(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(
DsfExamplesPlugin.IMG_LAYOUT_TOGGLE));
fAddTimerAction = new Action("Add New Timer") { //$NON-NLS-1$
@Override
public void run() {
fExecutor.submit(new Runnable() { public void run() {
// Only need to create the new timer, the events will cause
// the view to refresh.
fServices.getService(TimerService.class).startTimer();
}});
}
};
fAddTimerAction.setToolTipText("Add new timer"); //$NON-NLS-1$
fAddTimerAction.setImageDescriptor(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(
DsfExamplesPlugin.IMG_TIMER));
fAddAlarmAction = new Action("Add New Alarm") { //$NON-NLS-1$
@Override
public void run() {
// Ask user for the new alarm value.
InputDialog inputDialog = new InputDialog(
fViewer.getControl().getShell(),
"New Alarm", //$NON-NLS-1$
"Please enter alarm time", //$NON-NLS-1$
"", //$NON-NLS-1$
new IInputValidator() {
public String isValid(String input) {
try {
int i= Integer.parseInt(input);
if (i <= 0)
return "Please enter a positive integer"; //$NON-NLS-1$
} catch (NumberFormatException x) {
return "Please enter a positive integer"; //$NON-NLS-1$
}
return null;
}
}
);
if (inputDialog.open() != Window.OK) return;
int tmpAlarmValue = -1;
try {
tmpAlarmValue = Integer.parseInt(inputDialog.getValue());
} catch (NumberFormatException x) { assert false; }
final int alarmValue = tmpAlarmValue;
fExecutor.submit(new Runnable() { public void run() {
// Create the new alarm.
fServices.getService(AlarmService.class).createAlarm(alarmValue);
}});
}
};
fAddAlarmAction.setToolTipText("Add new alarm"); //$NON-NLS-1$
fAddAlarmAction.setImageDescriptor(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(
DsfExamplesPlugin.IMG_ALARM));
fRemoveAction = new Action("Remove") { //$NON-NLS-1$
@Override
public void run() {
final Object selectedElement = ((IStructuredSelection)fViewer.getSelection()).getFirstElement();
if (!(selectedElement instanceof IDMVMContext)) return;
final IDMContext selectedDmc = ((IDMVMContext)selectedElement).getDMContext();
// Based on the DMC from the selection, call the appropriate service to
// remove the item.
if (selectedDmc instanceof TimerDMC) {
fExecutor.submit(new Runnable() { public void run() {
fServices.getService(TimerService.class).killTimer(
((TimerDMC)selectedDmc));
}});
} else if (selectedDmc instanceof AlarmService.AlarmDMC) {
fExecutor.submit(new Runnable() { public void run() {
fServices.getService(AlarmService.class).deleteAlarm(
(AlarmService.AlarmDMC)selectedDmc);
}});
}
}
};
fRemoveAction.setToolTipText("Remove selected item"); //$NON-NLS-1$
fRemoveAction.setImageDescriptor(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(
DsfExamplesPlugin.IMG_REMOVE));
}
/**
* Passing the focus request to the viewer's control.
*/
@Override
public void setFocus() {
fViewer.getControl().setFocus();
}
}

View file

@ -0,0 +1,69 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.examples.dsf.timers;
import org.eclipse.dd.examples.dsf.DsfExamplesPlugin;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.jface.resource.ImageDescriptor;
/**
*
*/
@SuppressWarnings("restriction")
public class TimersViewColumnPresentation implements IColumnPresentation {
public static final String ID = DsfExamplesPlugin.PLUGIN_ID + ".TIMER_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$
public static final String COL_ID = ID + ".COL_ID"; //$NON-NLS-1$
public static final String COL_VALUE = ID + ".COL_VALUE"; //$NON-NLS-1$
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#init(org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext)
public void init(IPresentationContext context) {}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#dispose()
public void dispose() {}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getAvailableColumns()
public String[] getAvailableColumns() {
return new String[] { COL_ID, COL_VALUE };
}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getHeader(java.lang.String)
public String getHeader(String id) {
if (COL_ID.equals(id)) {
return "ID"; //$NON-NLS-1$
} else if (COL_VALUE.equals(id)) {
return "Value"; //$NON-NLS-1$
}
return null;
}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getId()
public String getId() {
return ID;
}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getImageDescriptor(java.lang.String)
public ImageDescriptor getImageDescriptor(String id) {
return null;
}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#getInitialColumns()
public String[] getInitialColumns() {
return getAvailableColumns();
}
// @see org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation#isOptional()
public boolean isOptional() {
return true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,28 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="content-type">
<title>Eclipse Device Debug - Debugger Services Framework - Data Model</title>
</head>
<body>
Example demostrationg use of Debugger Services Framework (DSF) data
model interfaces.<br>
<h2>Package Specification</h2>
The example consists of two services, one that monitors a set of timers
(TimerService), and another that tracks a set of alarms
(AlarmService).&nbsp; The timers are incremented at a rate of one per
second, although for visual interest, timers are not synchronous as
each timer is incremented at a slightly different moment.&nbsp; There
is also a "Timers" view that displays the timers and alarms, and has
actions for adding and removing new timers and alarms.&nbsp; Most
interestingly, the view has a toggle action, which switches the layout
of the view from having timers at the root level, with alarms listed
below each timer, to having alarms at the root level with timers listed
below each alarm.<br>
<br>
<img alt="" title="Example diagram" src="doc-files%5Cpackage-1.png"
style="width: 1014px; height: 643px;"><br>
<br>
</body>
</html>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.dd.gdb</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,65 @@
#Thu Jun 07 11:08:01 PDT 2007
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
org.eclipse.jdt.core.compiler.problem.nullReference=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedImport=error
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.5

View file

@ -0,0 +1,22 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: GDB DSF Debugger Integration Core
Bundle-Vendor: Eclipse.org
Bundle-SymbolicName: org.eclipse.dd.gdb;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: org.eclipse.dd.gdb.internal.GdbPlugin
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.dd.dsf,
org.eclipse.dd.dsf.debug,
org.eclipse.dd.mi,
org.eclipse.debug.core,
org.eclipse.cdt.core,
org.eclipse.cdt.launch,
org.eclipse.cdt.debug.core,
org.eclipse.cdt.debug.mi.core
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Export-Package: org.eclipse.dd.gdb.breakpoints,
org.eclipse.dd.gdb.launching,
org.eclipse.dd.gdb.service,
org.eclipse.dd.gdb.service.command

View file

@ -0,0 +1,24 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>About</title></head><body lang="EN-US">
<h2>About This Content</h2>
<p>June 5, 2007</p>
<h3>License</h3>
<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
indicated below, the Content is provided to you under the terms and conditions of the
Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
For purposes of the EPL, "Program" will mean the Content.</p>
<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
being redistributed by another party ("Redistributor") and different terms and conditions may
apply to your use of any object code in the Content. Check the Redistributor's license that was
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
</body></html>

View file

@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml,\
about.html

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension point="org.eclipse.debug.core.launchConfigurationTypes">
<launchConfigurationType
sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
delegate="org.eclipse.dd.gdb.launching.GdbLaunchDelegate"
public="true"
sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer"
name="C/C++ Local Application (Experimental - DSF)"
id="org.eclipse.dd.mi.launch.localCLaunch"
modes="debug">
</launchConfigurationType>
</extension>
<extension
point="org.eclipse.cdt.debug.core.BreakpointExtension">
<breakpointExtension
class="org.eclipse.dd.gdb.breakpoints.CBreakpointGdbThreadsFilterExtension"
debugModelId="org.eclipse.dd.dsf.gdb"
id="org.eclipse.dd.dsf.gdb.threadFilter"
markerType="org.eclipse.cdt.debug.core.cBreakpointMarker">
</breakpointExtension>
</extension>
</plugin>

View file

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems 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.dd.gdb.breakpoints;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dd.dsf.datamodel.DMContexts;
import org.eclipse.dd.dsf.debug.service.IDsfBreakpointExtension;
import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.dd.dsf.debug.service.IRunControl.IExecutionDMContext;
/**
*
*/
public class CBreakpointGdbThreadsFilterExtension implements IDsfBreakpointExtension {
private Map<IContainerDMContext,Set<IExecutionDMContext>> fFilteredThreadsByTarget =
new HashMap<IContainerDMContext,Set<IExecutionDMContext>>(1);
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpointExtension#initialize(org.eclipse.cdt.debug.core.model.ICBreakpoint)
*/
public void initialize(ICBreakpoint breakpoint) {
// TODO: Initialize fFilteredThreadsByTarget with current IContainerDMContext[]
// TODO: IRunControl?
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpoint#getTargetFilters()
*/
public IContainerDMContext[] getTargetFilters() throws CoreException {
Set<IContainerDMContext> set = fFilteredThreadsByTarget.keySet();
return set.toArray( new IContainerDMContext[set.size()] );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpoint#getThreadFilters(org.eclipse.cdt.debug.core.model.ICDebugTarget)
*/
public IExecutionDMContext[] getThreadFilters( IContainerDMContext target ) throws CoreException {
Set<IExecutionDMContext> set = fFilteredThreadsByTarget.get( target );
return ( set != null ) ? set.toArray( new IExecutionDMContext[set.size()] ) : null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpoint#removeTargetFilter(org.eclipse.cdt.debug.core.model.ICDebugTarget)
*/
public void removeTargetFilter( IContainerDMContext target ) throws CoreException {
if ( fFilteredThreadsByTarget.containsKey( target ) ) {
fFilteredThreadsByTarget.remove( target );
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpoint#removeThreadFilters(org.eclipse.cdt.debug.core.model.ICThread[])
*/
public void removeThreadFilters( IExecutionDMContext[] threads ) throws CoreException {
if ( threads != null && threads.length > 0 ) {
IContainerDMContext target = DMContexts.getAncestorOfType(threads[0], IContainerDMContext.class);
if ( fFilteredThreadsByTarget.containsKey( target ) ) {
Set<IExecutionDMContext> set = fFilteredThreadsByTarget.get( target );
if ( set != null ) {
set.removeAll( Arrays.asList( threads ) );
if ( set.isEmpty() ) {
fFilteredThreadsByTarget.remove( target );
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpoint#setTargetFilter(org.eclipse.cdt.debug.core.model.ICDebugTarget)
*/
public void setTargetFilter( IContainerDMContext target ) throws CoreException {
fFilteredThreadsByTarget.put( target, null );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.ICBreakpoint#setThreadFilters(org.eclipse.cdt.debug.core.model.ICThread[])
*/
public void setThreadFilters( IExecutionDMContext[] threads ) throws CoreException {
if ( threads != null && threads.length > 0 ) {
IContainerDMContext target = DMContexts.getAncestorOfType(threads[0], IContainerDMContext.class);
fFilteredThreadsByTarget.put( target, new HashSet<IExecutionDMContext>( Arrays.asList( threads ) ) );
}
}
}

View file

@ -0,0 +1,110 @@
package org.eclipse.dd.gdb.internal;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.Query;
import org.eclipse.dd.gdb.launching.GdbLaunch;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class GdbPlugin extends Plugin {
// The plug-in ID
public static final String PLUGIN_ID = "org.eclipse.dd.gdb"; //$NON-NLS-1$
// The shared instance
private static GdbPlugin plugin;
private static BundleContext fgBundleContext;
/**
* The constructor
*/
public GdbPlugin() {
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(BundleContext context) throws Exception {
fgBundleContext = context;
super.start(context);
plugin = this;
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception {
shutdownActiveLaunches();
plugin = null;
super.stop(context);
fgBundleContext = null;
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static GdbPlugin getDefault() {
return plugin;
}
public static BundleContext getBundleContext() {
return fgBundleContext;
}
/**
* Shuts down any active launches. We must shutdown any active sessions
* and services associated with this plugin before this plugin is stopped.
* Any attempts to use the plugins {@link BundleContext} after the plugin
* is shut down will result in exceptions.
*/
private void shutdownActiveLaunches() {
for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) {
if (launch instanceof GdbLaunch && ((GdbLaunch)launch).getSession().isActive()) {
final GdbLaunch gdbLaunch = (GdbLaunch)launch;
Query<Object> launchShutdownQuery = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
gdbLaunch.shutdownSession(rm);
}
};
try {
gdbLaunch.getSession().getExecutor().execute(launchShutdownQuery);
} catch (RejectedExecutionException e) {
// We can get this exception if the session is shutdown concurrently
// to this method running.
break;
}
// The Query.get() method is a synchronous call which blocks until the
// query completes.
try {
launchShutdownQuery.get();
} catch (InterruptedException e) {
getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "InterruptedException while shutting down PDA debugger launch " + gdbLaunch, e.getCause())); //$NON-NLS-1$
} catch (ExecutionException e) {
getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "Exception while shutting down PDA debugger launch " + gdbLaunch, e.getCause())); //$NON-NLS-1$
}
}
}
}
}

View file

@ -0,0 +1,175 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.gdb.launching;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.dd.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.ImmediateExecutor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.service.DsfServiceEventHandler;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.service.command.GDBControl;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.model.ITerminate;
/**
* The only object in the model that implements the traditional interfaces.
*/
@ThreadSafe
public class GdbLaunch extends Launch
implements ITerminate
{
private DefaultDsfExecutor fExecutor;
private DsfSession fSession;
private DsfServicesTracker fTracker;
private boolean fInitialized = false;
private boolean fShutDown = false;
public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
super(launchConfiguration, mode, locator);
// Create the dispatch queue to be used by debugger control and services
// that belong to this launch
final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID);
dsfExecutor.prestartCoreThread();
fExecutor = dsfExecutor;
fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID);
}
public DsfExecutor getDsfExecutor() { return fExecutor; }
@ConfinedToDsfExecutor("getExecutor")
public void initializeControl()
throws CoreException
{
Runnable initRunnable = new DsfRunnable() {
public void run() {
fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId());
fSession.addServiceEventListener(GdbLaunch.this, null);
fInitialized = true;
fireChanged();
}
};
// Invoke the execution code and block waiting for the result.
try {
fExecutor.submit(initRunnable).get();
} catch (InterruptedException e) {
new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$
} catch (ExecutionException e) {
new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$
}
}
public DsfSession getSession() { return fSession; }
///////////////////////////////////////////////////////////////////////////
// IServiceEventListener
@DsfServiceEventHandler public void eventDispatched(GDBControl.ExitedEvent event) {
shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
}
///////////////////////////////////////////////////////////////////////////
// ITerminate
@Override
public boolean canTerminate() {
return super.canTerminate() && fInitialized && !fShutDown;
}
@Override
public boolean isTerminated() {
return super.isTerminated() || fShutDown;
}
@Override
public void terminate() throws DebugException {
if (fShutDown) return;
super.terminate();
}
// ITerminate
///////////////////////////////////////////////////////////////////////////
/**
* 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 (fShutDown) {
rm.done();
return;
}
fShutDown = true;
Sequence shutdownSeq = new ShutdownSequence(
getDsfExecutor(), fSession.getId(),
new RequestMonitor(fSession.getExecutor(), rm) {
@Override
public void handleCompleted() {
fSession.removeServiceEventListener(GdbLaunch.this);
if (!getStatus().isOK()) {
GdbPlugin.getDefault().getLog().log(new MultiStatus(
GdbPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$
}
// Last order of business, shutdown the dispatch queue.
fTracker.dispose();
fTracker = null;
DsfSession.endSession(fSession);
// endSession takes a full dispatch to distribute the
// session-ended event, finish step only after the dispatch.
fExecutor.shutdown();
fExecutor = null;
fireTerminate();
rm.setStatus(getStatus());
rm.done();
}
});
fExecutor.execute(shutdownSeq);
}
@SuppressWarnings("unchecked")
@Override
public Object getAdapter(Class adapter) {
// Must force adapters to be loaded.
Platform.getAdapterManager().loadAdapter(this, adapter.getName());
return super.getAdapter(adapter);
}
}

View file

@ -0,0 +1,290 @@
/*******************************************************************************
* Copyright (c) 2004, 2006 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
package org.eclipse.dd.gdb.launching;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
import org.eclipse.cdt.launch.internal.ui.LaunchMessages;
import org.eclipse.cdt.launch.internal.ui.LaunchUIPlugin;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.ThreadSafe;
import org.eclipse.dd.dsf.debug.model.DsfMemoryBlockRetrieval;
import org.eclipse.dd.dsf.debug.service.IMemory.IMemoryDMContext;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.service.command.GDBControl;
import org.eclipse.dd.mi.service.command.AbstractCLIProcess;
import org.eclipse.dd.mi.service.command.MIInferiorProcess;
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.IStatusHandler;
import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
import org.eclipse.debug.core.model.IPersistableSourceLocator;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2;
/**
* The launch configuration delegate for the CDI debugger session types.
*/
@ThreadSafe
public class GdbLaunchDelegate extends AbstractCLaunchDelegate
implements ILaunchConfigurationDelegate2
{
public final static String GDB_DEBUG_MODEL_ID = "org.eclipse.dd.dsf.gdb"; //$NON-NLS-1$
/* (non-Javadoc)
* @see org.eclipse.cdt.launch.AbstractCLaunchDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void launch( ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor ) throws CoreException {
if ( monitor == null ) {
monitor = new NullProgressMonitor();
}
if ( mode.equals( ILaunchManager.DEBUG_MODE ) ) {
launchDebugger( config, launch, monitor );
}
}
private void launchDebugger( ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor ) throws CoreException {
monitor.beginTask("Launching debugger session", 10); //$NON-NLS-1$
if ( monitor.isCanceled() ) {
return;
}
try {
String debugMode = config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN );
if ( debugMode.equals( ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ) ) {
launchLocalDebugSession( config, launch, monitor );
}
}
finally {
monitor.done();
}
}
private void launchLocalDebugSession( ILaunchConfiguration config, ILaunch l, IProgressMonitor monitor ) throws CoreException {
if ( monitor.isCanceled() ) {
return;
}
final GdbLaunch launch = (GdbLaunch)l;
monitor.subTask( "Debugging local C/C++ application" ); //$NON-NLS-1$
IPath exePath = verifyProgramPath( config );
ICProject project = verifyCProject( config );
if ( exePath != null ) {
verifyBinary( project, exePath );
}
setDefaultSourceLocator(launch, config);
monitor.worked( 1 );
// Create and invoke the launch sequence to create the debug control and services
final LaunchSequence launchSequence =
new LaunchSequence(launch.getSession(), launch, exePath);
launch.getSession().getExecutor().execute(launchSequence);
try {
launchSequence.get();
} catch (InterruptedException e1) {
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$
} catch (ExecutionException e1) {
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in launch sequence", e1.getCause())); //$NON-NLS-1$
}
launch.initializeControl();
// Add the CLI and "inferior" process objects to the launch.
final AtomicReference<AbstractCLIProcess> cliProcessRef = new AtomicReference<AbstractCLIProcess>();
final AtomicReference<MIInferiorProcess> inferiorProcessRef = new AtomicReference<MIInferiorProcess>();
try {
launch.getDsfExecutor().submit( new Callable<Object>() {
public Object call() throws CoreException {
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), launch.getSession().getId());
GDBControl gdb = tracker.getService(GDBControl.class);
if (gdb != null) {
cliProcessRef.set(gdb.getCLIProcess());
inferiorProcessRef.set(gdb.getInferiorProcess());
}
tracker.dispose();
return null;
}
}).get();
launch.addProcess(DebugPlugin.newProcess(launch, cliProcessRef.get(), "gdb")); //$NON-NLS-1$
launch.addProcess(DebugPlugin.newProcess(launch, inferiorProcessRef.get(), exePath.lastSegment()));
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.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, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
}
// Create a memory retrieval and register it with session
try {
launch.getDsfExecutor().submit( new Callable<Object>() {
public Object call() throws CoreException {
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), launch.getSession().getId());
GDBControl gdbControl = tracker.getService(GDBControl.class);
if (gdbControl != null) {
IMemoryBlockRetrieval memRetrieval = new DsfMemoryBlockRetrieval(
GDB_DEBUG_MODEL_ID, (IMemoryDMContext)gdbControl.getControlDMContext());
launch.getSession().registerModelAdapter(IMemoryBlockRetrieval.class, memRetrieval);
}
tracker.dispose();
return null;
}
}).get();
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.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, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.launch.AbstractCLaunchDelegate#getPluginID()
*/
@Override
protected String getPluginID() {
return LaunchUIPlugin.getUniqueIdentifier();
}
/**
* Performs a runtime exec on the given command line in the context of the
* specified working directory, and returns the resulting process. If the
* current runtime does not support the specification of a working
* directory, the status handler for error code
* <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if
* the exec should be re-executed without specifying a working directory.
*
* @param cmdLine
* the command line
* @param workingDirectory
* the working directory, or <code>null</code>
* @return the resulting process or <code>null</code> if the exec is
* cancelled
* @see Runtime
*/
protected Process exec( String[] cmdLine, String[] environ, File workingDirectory, boolean usePty ) throws CoreException {
Process p = null;
try {
if ( workingDirectory == null ) {
p = ProcessFactory.getFactory().exec( cmdLine, environ );
}
else {
if ( usePty && PTY.isSupported() ) {
p = ProcessFactory.getFactory().exec( cmdLine, environ, workingDirectory, new PTY() );
}
else {
p = ProcessFactory.getFactory().exec( cmdLine, environ, workingDirectory );
}
}
}
catch( IOException e ) {
if ( p != null ) {
p.destroy();
}
abort( "Error starting process.", e, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR ); //$NON-NLS-1$
}
catch( NoSuchMethodError e ) {
// attempting launches on 1.2.* - no ability to set working
// directory
IStatus status = new Status( IStatus.ERROR, LaunchUIPlugin.getUniqueIdentifier(), ICDTLaunchConfigurationConstants.ERR_WORKING_DIRECTORY_NOT_SUPPORTED, LaunchMessages.getString( "LocalDsfLaunchDelegate.9" ), e ); //$NON-NLS-1$
IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler( status );
if ( handler != null ) {
Object result = handler.handleStatus( status, this );
if ( result instanceof Boolean && ((Boolean)result).booleanValue() ) {
p = exec( cmdLine, environ, null, usePty );
}
}
}
return p;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.launch.AbstractCLaunchDelegate#preLaunchCheck(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public boolean preLaunchCheck( ILaunchConfiguration config, String mode, IProgressMonitor monitor ) throws CoreException {
// no pre launch check for core file
if ( mode.equals( ILaunchManager.DEBUG_MODE ) ) {
if ( ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE.equals( config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ) ) )
return true;
}
return super.preLaunchCheck( config, mode, monitor );
}
///////////////////////////////////////////////////////////////////////////
// ILaunchConfigurationDelegate2
@Override
public boolean buildForLaunch(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException {
return false;
}
@Override
public boolean finalLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException {
return true;
}
@Override
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
// Need to configure the source locator before creating the launch
// because once the launch is created and added to launch manager,
// the adapters will be created for the whole session, including
// the source lookup adapter.
ISourceLocator locator = getSourceLocator(configuration);
return new GdbLaunch(configuration, mode, locator);
}
private ISourceLocator getSourceLocator(ILaunchConfiguration configuration) throws CoreException {
String type = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_ID, (String)null);
if (type == null) {
type = configuration.getType().getSourceLocatorId();
}
if (type != null) {
IPersistableSourceLocator locator = DebugPlugin.getDefault().getLaunchManager().newSourceLocator(type);
String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String)null);
if (memento == null) {
locator.initializeDefaults(configuration);
} else {
if(locator instanceof IPersistableSourceLocator2)
((IPersistableSourceLocator2)locator).initializeFromMemento(memento, configuration);
else
locator.initializeFromMemento(memento);
}
return locator;
}
return null;
}
}

View file

@ -0,0 +1,267 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.gdb.launching;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
import org.eclipse.cdt.debug.mi.core.IMILaunchConfigurationConstants;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.debug.service.StepQueueManager;
import org.eclipse.dd.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.dd.dsf.service.DsfServiceEventHandler;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.service.GDBRunControl;
import org.eclipse.dd.gdb.service.command.GDBControl;
import org.eclipse.dd.mi.service.CSourceLookup;
import org.eclipse.dd.mi.service.ExpressionService;
import org.eclipse.dd.mi.service.MIBreakpoints;
import org.eclipse.dd.mi.service.MIBreakpointsManager;
import org.eclipse.dd.mi.service.MIMemory;
import org.eclipse.dd.mi.service.MIModules;
import org.eclipse.dd.mi.service.MIRegisters;
import org.eclipse.dd.mi.service.MIStack;
import org.eclipse.dd.mi.service.command.commands.MIBreakInsert;
import org.eclipse.dd.mi.service.command.commands.MIExecRun;
import org.eclipse.dd.mi.service.command.events.MIStoppedEvent;
import org.eclipse.dd.mi.service.command.output.MIBreakInsertInfo;
import org.eclipse.dd.mi.service.command.output.MIInfo;
import org.eclipse.debug.core.DebugException;
public class LaunchSequence extends Sequence {
public class EntryPointHitEventListener {
boolean fAborted = false;
boolean fFinished = false;
final RequestMonitor fRequestMonitor;
EntryPointHitEventListener(RequestMonitor requestMonitor) {
fRequestMonitor = requestMonitor;
}
@DsfServiceEventHandler
public void eventDispatched(MIStoppedEvent e) {
fFinished = true;
if (!fAborted) {
fSession.removeServiceEventListener(this);
fRequestMonitor.done();
}
}
}
Step[] fSteps = new Step[] {
// Create and initialize the Connection service.
new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
//
// Create the connection.
//
fCommandControl = new GDBControl(
fSession, getGDBPath(), fExecPath, GDBControl.SessionType.RUN, 30);
fCommandControl.initialize(requestMonitor);
}
},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new GDBRunControl(fSession).initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new StepQueueManager(fSession).initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new MIMemory(fSession).initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new MIModules(fSession).initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new MIStack(fSession).initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new ExpressionService(fSession).initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
fSourceLookup = new CSourceLookup(fSession);
fSourceLookup.initialize(requestMonitor);
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
fSourceLookup.setSourceLookupDirector(
fCommandControl.getGDBDMContext(),
((CSourceLookupDirector)fLaunch.getSourceLocator()));
requestMonitor.done();
}},
new Step() { @Override
public void execute(final RequestMonitor requestMonitor) {
// Create the low-level breakpoint service
final MIBreakpoints bpService = new MIBreakpoints(fSession);
bpService.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleOK() {
requestMonitor.done();
}
});
}},
new Step() { @Override
public void execute(final RequestMonitor requestMonitor) {
// Create high-level breakpoint service and install breakpoints
// for the GDB debug context.
final MIBreakpointsManager bpmService = new MIBreakpointsManager(fSession, CDebugCorePlugin.PLUGIN_ID);
bpmService.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleOK() {
bpmService.startTrackingBreakpoints(fCommandControl.getGDBDMContext(), requestMonitor);
}
});
}},
new Step() { @Override
public void execute(RequestMonitor requestMonitor) {
new MIRegisters(fSession).initialize(requestMonitor);
}},
/*
* If needed, insert breakpoint at main and run to it.
*/
new Step() {
private boolean fStopInMain = false;
private String fStopSymbol = null;
/**
* @return The return value actually indicates whether the get operation succeeded,
* not whether to stop.
*/
private boolean readStopAtMain(RequestMonitor requestMonitor) {
try {
fStopInMain = fLaunch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false );
} catch (CoreException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$
requestMonitor.done();
return false;
}
return true;
}
private boolean readStopSymbol(RequestMonitor requestMonitor) {
try {
fStopSymbol = fLaunch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT );
} catch (CoreException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$
requestMonitor.done();
return false;
}
return true;
}
@Override
public void execute(final RequestMonitor requestMonitor) {
if (!readStopAtMain(requestMonitor)) return;
if (!fStopInMain) {
requestMonitor.done();
return;
}
if (!readStopSymbol(requestMonitor)) return;
// Create a listener to wait for the stopped event, and register as even handler.
// This handler will execute the requestMonitor.
final EntryPointHitEventListener entryPointHitListener = new EntryPointHitEventListener(requestMonitor);
fSession.addServiceEventListener(entryPointHitListener, null);
// Create a time-out, to abort if breakpoint not hit.
fSession.getExecutor().schedule(
new Runnable() { public void run() {
// Only process the event if we have not finished yet (hit the breakpoint).
if (!entryPointHitListener.fFinished) {
// Mark the listener as aborted, and unregister it as event listener.
entryPointHitListener.fAborted = true;
fSession.removeServiceEventListener(entryPointHitListener);
// Submit the error result for the step.
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out running to entry point.", null)); //$NON-NLS-1$
requestMonitor.done();
}
}},
60, TimeUnit.SECONDS);
// Insert a breakpoint at the requested stop symbol.
fCommandControl.queueCommand(
new MIBreakInsert(
(IBreakpointsTargetDMContext)fCommandControl.getControlDMContext(),
true, false, null, 0, fStopSymbol, 0),
new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), requestMonitor) {
@Override
protected void handleOK() {
// After the break-insert is done, execute the -exec-run command.
fCommandControl.queueCommand(
new MIExecRun((IContainerDMContext)fCommandControl.getControlDMContext(), new String[0]),
new DataRequestMonitor<MIInfo>(getExecutor(), requestMonitor) {
@Override
protected void handleOK() {
// Note : Do we not need to do something with the original requestMonitor?
// Do nothing. Execution was resumed and the EntryPointHitEventListener
// will resume execution
}
}
);
}
});
}
},
};
DsfSession fSession;
GdbLaunch fLaunch;
IPath fExecPath;
GDBControl fCommandControl;
CSourceLookup fSourceLookup;
public LaunchSequence(DsfSession session, GdbLaunch launch, IPath execPath) {
super(session.getExecutor());
fSession = session;
fLaunch = launch;
fExecPath = execPath;
}
@Override
public Step[] getSteps() {
return fSteps;
}
private IPath getGDBPath() {
IPath retVal = new Path("gdb.exe"); //$NON-NLS-1$
try {
retVal = new Path( fLaunch.getLaunchConfiguration().getAttribute( IMILaunchConfigurationConstants.ATTR_DEBUG_NAME, IMILaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT ) );
} catch (CoreException e) {
}
return retVal;
}
}

View file

@ -0,0 +1,173 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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.dd.gdb.launching;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.service.DsfServicesTracker;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.service.GDBRunControl;
import org.eclipse.dd.gdb.service.command.GDBControl;
import org.eclipse.dd.mi.service.CSourceLookup;
import org.eclipse.dd.mi.service.ExpressionService;
import org.eclipse.dd.mi.service.MIBreakpoints;
import org.eclipse.dd.mi.service.MIBreakpointsManager;
import org.eclipse.dd.mi.service.MIMemory;
import org.eclipse.dd.mi.service.MIModules;
import org.eclipse.dd.mi.service.MIRegisters;
import org.eclipse.dd.mi.service.MIStack;
public class ShutdownSequence extends Sequence {
String fSessionId;
String fApplicationName;
String fDebugModelId;
DsfServicesTracker fTracker;
public ShutdownSequence(DsfExecutor executor, String sessionId, RequestMonitor requestMonitor) {
super(executor, requestMonitor);
fSessionId = sessionId;
}
@Override
public Step[] getSteps() {
return fSteps;
}
private final Step[] fSteps = new Step[] { new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
assert GdbPlugin.getBundleContext() != null;
fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSessionId);
requestMonitor.done();
}
@Override
public void rollBack(RequestMonitor requestMonitor) {
fTracker.dispose();
fTracker = null;
requestMonitor.done();
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(MIRegisters.class, requestMonitor);
}
// TODO: As Pawel about the necessity of this step
// Not clear on the purpose of this step since the next one does it also
// (stopTrackingBreakpoints() is called as part of the shutdown method)
// Besides, the run control is already gone so removing breakpoints from
// the back-end is bound to fail...
// }, new Step() {
// @Override
// public void execute(final RequestMonitor requestMonitor) {
// MIBreakpointsManager bpm = fTracker.getService(MIBreakpointsManager.class);
// GDBControl commandControl = fTracker.getService(GDBControl.class);
// if (bpm != null && commandControl != null) {
// bpm.stopTrackingBreakpoints(
// commandControl.getGDBDMContext(),
// new RequestMonitor(getExecutor(), requestMonitor) {
// @Override
// protected void handleCompleted() {
// // If un-installing breakpoints fails, log the error but continue shutting down.
// if (!getStatus().isOK()) {
// DsfGdbPlugin.getDefault().getLog().log(getStatus());
// }
// requestMonitor.done();
// }
// });
// } else {
// requestMonitor.setStatus(new Status(IStatus.ERROR, DsfGdbPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR,
// "Needed services not found.", null)); //$NON-NLS-1$
// requestMonitor.done();
// }
// }
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(MIBreakpointsManager.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(MIBreakpoints.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(CSourceLookup.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(ExpressionService.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(MIStack.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(MIModules.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(MIMemory.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(GDBRunControl.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
shutdownService(GDBControl.class, requestMonitor);
}
}, new Step() {
@Override
public void execute(RequestMonitor requestMonitor) {
fTracker.dispose();
fTracker = null;
requestMonitor.done();
}
} };
@SuppressWarnings("unchecked")
private void shutdownService(Class clazz, final RequestMonitor requestMonitor) {
IDsfService service = fTracker.getService(clazz);
if (service != null) {
service.shutdown(new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleCompleted() {
if (!getStatus().isOK()) {
GdbPlugin.getDefault().getLog().log(getStatus());
}
requestMonitor.done();
}
});
} else {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfService.INTERNAL_ERROR,
"Service '" + clazz.getName() + "' not found.", null)); //$NON-NLS-1$//$NON-NLS-2$
requestMonitor.done();
}
}
}

View file

@ -0,0 +1,192 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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
* Ericsson AB - Modified for additional functionality
*******************************************************************************/
package org.eclipse.dd.gdb.service;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.datamodel.DMContexts;
import org.eclipse.dd.dsf.debug.service.IRunControl;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.gdb.service.command.GDBControl;
import org.eclipse.dd.gdb.service.command.GDBControlDMContext;
import org.eclipse.dd.mi.service.IMIExecutionDMContext;
import org.eclipse.dd.mi.service.MIRunControl;
import org.eclipse.dd.mi.service.command.commands.CLIInfoThreads;
import org.eclipse.dd.mi.service.command.events.MIEvent;
import org.eclipse.dd.mi.service.command.events.MIThreadExitEvent;
import org.eclipse.dd.mi.service.command.output.CLIInfoThreadsInfo;
public class GDBRunControl extends MIRunControl {
/**
* Implement a custom execution data for threads in order to provide additional
* information. This object can be made separate from IExecutionDMData after
* the deprecated method: IDMService.getModelData() is no longer used.
*/
public static class GDBThreadData {
private final String fId;
private final String fName;
GDBThreadData(String id, String name) {
fId = id;
fName = name;
}
public String getName() {
return fName;
}
public String getId() { return fId; }
public boolean isDebuggerAttached() { return true; }
}
/**
* Implement a custom execution data the process in order to provide additional
* information. This object can be made separate from IExecutionDMData after
* the deprecated method: IDMService.getModelData() is no longer used.
*/
public static class GDBProcessData {
private final String fName;
GDBProcessData(String name) {
fName = name;
}
public String getName() {
return fName;
}
}
private GDBControl fGdb;
// Record list of execution contexts
private IExecutionDMContext[] fOldExecutionCtxts;
public GDBRunControl(DsfSession session) {
super(session);
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(
new RequestMonitor(getExecutor(), requestMonitor) {
@Override
public void handleOK() {
doInitialize(requestMonitor);
}});
}
private void doInitialize(final RequestMonitor requestMonitor) {
fGdb = getServicesTracker().getService(GDBControl.class);
register(new String[]{IRunControl.class.getName(), MIRunControl.class.getName()}, new Hashtable<String,String>());
requestMonitor.done();
}
@Override
public void shutdown(final RequestMonitor requestMonitor) {
unregister();
super.shutdown(requestMonitor);
}
@Override
public void suspend(IExecutionDMContext context, RequestMonitor requestMonitor){
if (canSuspend(context)) {
fGdb.interrupt();
} else {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$
}
requestMonitor.done();
}
/*
* This is a HACK. Remove this method when GDB starts to account exited threads id in -thread-list-id command.
* Exited threads are reported in -thread-list-id command even after an exit event is raised by GDB
* Hence, this method needs a special handling in case of GDB.
* Raises ExitEvent when a thread really exits from the system. This is done by comparing the execution contexts list
* See bug 200615 for details.
*/
@Override
public void getExecutionContexts(IContainerDMContext c, final DataRequestMonitor<IExecutionDMContext[]> rm) {
DataRequestMonitor<IExecutionDMContext[]> rm1 = new DataRequestMonitor<IExecutionDMContext[]>(
getExecutor(), rm) {
@Override
protected void handleOK() {
raiseExitEvents(getData());
fOldExecutionCtxts = getData();
rm.setData(fOldExecutionCtxts);
rm.done();
}
};
super.getExecutionContexts(c, rm1);
}
public void getProcessData(GDBControlDMContext gdbDmc, DataRequestMonitor<GDBProcessData> rm) {
rm.setData( new GDBProcessData(fGdb.getExecutablePath().lastSegment()) );
rm.done();
}
public void getThreadData(final IMIExecutionDMContext execDmc, final DataRequestMonitor<GDBThreadData> rm) {
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(execDmc, IContainerDMContext.class);
assert containerDmc != null; // Every exec context should have a container as an ancestor.
getCache().execute(new CLIInfoThreads(containerDmc),
new DataRequestMonitor<CLIInfoThreadsInfo>(getExecutor(), rm) {
@Override
protected void handleOK() {
rm.setData( createThreadInfo(execDmc, getData()) );
rm.done();
}
});
}
private GDBThreadData createThreadInfo(IMIExecutionDMContext dmc, CLIInfoThreadsInfo info){
for (CLIInfoThreadsInfo.ThreadInfo thread : info.getThreadInfo()) {
if(Integer.parseInt(thread.getId()) == dmc.getThreadId()){
//fMapThreadIds.put(thread.getId(), String.valueOf(dmc.getId()));
return new GDBThreadData(thread.getOsId(), thread.getName());
}
}
return new GDBThreadData("",""); //$NON-NLS-1$ //$NON-NLS-2$
}
private void raiseExitEvents(IExecutionDMContext[] ctxts){
if(ctxts == null || fOldExecutionCtxts == null)
return;
List<IExecutionDMContext> list = Arrays.asList(ctxts);
List<IExecutionDMContext> oldThreadList = Arrays.asList(fOldExecutionCtxts);
Iterator<IExecutionDMContext> iterator = oldThreadList.iterator();
while(iterator.hasNext()){
IExecutionDMContext ctxt = iterator.next();
if(! list.contains(ctxt)){
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(ctxt, IContainerDMContext.class);
MIEvent<?> e = new MIThreadExitEvent(containerDmc, ((IMIExecutionDMContext)ctxt).getThreadId());
// Dispatch DsfMIThreadExitEvent
getSession().dispatchEvent(e, getProperties());
}
}
}
}

View file

@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems 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.dd.gdb.service.command;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.mi.service.command.AbstractCLIProcess;
/**
*
*/
class GDBCLIProcess extends AbstractCLIProcess {
public GDBCLIProcess(GDBControl commandControl, boolean useExecConsole) throws IOException {
super(commandControl, useExecConsole);
}
/**
* @see java.lang.Process#waitFor()
*/
@Override
public int waitFor() throws InterruptedException {
if (!DsfSession.isSessionActive(getSession().getId())) return 0;
Process process = null;
try {
process = getSession().getExecutor().submit(new Callable<Process>() {
public Process call() throws Exception {
if (isDisposed()) return null;
return ((GDBControl)getCommandControl()).getGDBProcess();
}}).get();
} catch (RejectedExecutionException e) {
} catch (ExecutionException e) {
}
if (process == null) return 0;
return process.waitFor();
}
/**
* @see java.lang.Process#exitValue()
*/
@Override
public int exitValue() {
if (!DsfSession.isSessionActive(getSession().getId())) return 0;
try {
return getSession().getExecutor().submit(new Callable<Integer>() {
public Integer call() throws Exception {
if (!DsfSession.isSessionActive(getSession().getId())) {
return new Integer(-1);
} else {
if (isDisposed()) return new Integer(-1);
GDBControl gdb = (GDBControl)getCommandControl();
if (!gdb.isGDBExited()) {
throw new IllegalThreadStateException("GDB Process has not exited"); //$NON-NLS-1$
}
return gdb.getGDBExitCode();
}
}}).get().intValue();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException)e.getCause();
}
}
return 0;
}
/**
* @see java.lang.Process#destroy()
*/
@Override
public void destroy() {
try {
getSession().getExecutor().execute(new DsfRunnable() { public void run() {
if (!DsfSession.isSessionActive(getSession().getId())) return;
if (isDisposed()) return;
GDBControl gdb = (GDBControl)getCommandControl();
gdb.destroy();
}});
} catch (RejectedExecutionException e) {
// Session disposed.
}
}
}

View file

@ -0,0 +1,596 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems 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
* Ericsson - Modified for additional features in DSF Reference implementation
*******************************************************************************/
package org.eclipse.dd.gdb.service.command;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.cdt.utils.spawner.Spawner;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dd.dsf.concurrent.DataRequestMonitor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.RequestMonitor;
import org.eclipse.dd.dsf.concurrent.Sequence;
import org.eclipse.dd.dsf.datamodel.AbstractDMEvent;
import org.eclipse.dd.dsf.debug.service.command.CommandCache;
import org.eclipse.dd.dsf.debug.service.command.ICommandControl;
import org.eclipse.dd.dsf.service.DsfServiceEventHandler;
import org.eclipse.dd.dsf.service.DsfSession;
import org.eclipse.dd.dsf.service.IDsfService;
import org.eclipse.dd.gdb.internal.GdbPlugin;
import org.eclipse.dd.mi.service.command.AbstractCLIProcess;
import org.eclipse.dd.mi.service.command.AbstractMIControl;
import org.eclipse.dd.mi.service.command.CLIEventProcessor;
import org.eclipse.dd.mi.service.command.MIControlDMContext;
import org.eclipse.dd.mi.service.command.MIInferiorProcess;
import org.eclipse.dd.mi.service.command.MIRunControlEventProcessor;
import org.eclipse.dd.mi.service.command.commands.MIGDBExit;
import org.eclipse.dd.mi.service.command.commands.MIGDBShowExitCode;
import org.eclipse.dd.mi.service.command.commands.MIInterpreterExecConsole;
import org.eclipse.dd.mi.service.command.output.MIGDBShowExitCodeInfo;
import org.eclipse.dd.mi.service.command.output.MIInfo;
import org.eclipse.debug.core.DebugException;
import org.osgi.framework.BundleContext;
/**
* GDB Debugger control implementation. This implementation extends the
* base MI control implementation to provide the GDB-specific debugger
* features. This includes:<br * - Launching and monitoring the GDB process,<br>
* - CLI console support,<br>
* - inferior process status tracking.<br>
*/
public class GDBControl extends AbstractMIControl {
/**
* Event indicating that the back end process process has started.
*/
public static class StartedEvent extends AbstractDMEvent<GDBControlDMContext> {
public StartedEvent(GDBControlDMContext context) {
super(context);
}
}
/**
* Event indicating that the back end process has terminated.
*/
public static class ExitedEvent extends AbstractDMEvent<GDBControlDMContext> {
public ExitedEvent(GDBControlDMContext context) {
super(context);
}
}
private static int fgInstanceCounter = 0;
private final GDBControlDMContext fControlDmc;
public enum SessionType { RUN, ATTACH, CORE }
private SessionType fSessionType;
private boolean fConnected = false;
private boolean fUseInterpreterConsole;
private MonitorJob fMonitorJob;
private IPath fGdbPath;
private IPath fExecPath;
private Process fProcess;
private int fGDBExitValue;
final private int fGDBLaunchTimeout;
private CommandCache fCommandCache;
private MIRunControlEventProcessor fMIEventProcessor;
private CLIEventProcessor fCLICommandProcessor;
private AbstractCLIProcess fCLIProcess;
private MIInferiorProcess fInferiorProcess;
public GDBControl(DsfSession session, IPath gdbPath, IPath execPath, SessionType type, int gdbLaunchTimeout) {
super(session);
fSessionType = type;
fGdbPath = gdbPath;
fExecPath = execPath;
fGDBLaunchTimeout = gdbLaunchTimeout;
fControlDmc = new GDBControlDMContext(session.getId(), getClass().getName() + ":" + ++fgInstanceCounter); //$NON-NLS-1$
}
@Override
protected BundleContext getBundleContext() {
return GdbPlugin.getBundleContext();
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize( new RequestMonitor(getExecutor(), requestMonitor) {
@Override
protected void handleOK() {
doInitialize(requestMonitor);
}
});
}
public void doInitialize(final RequestMonitor requestMonitor) {
final Sequence.Step[] initializeSteps = new Sequence.Step[] {
new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING),
new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING),
new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING),
new CheckInterpreterConsoleStep(InitializationShutdownStep.Direction.INITIALIZING),
new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING),
new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING),
};
Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return initializeSteps; }
};
getExecutor().execute(startupSequence);
}
@Override
public void shutdown(final RequestMonitor requestMonitor) {
final Sequence.Step[] shutdownSteps = new Sequence.Step[] {
new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new CommandProcessorsStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new CheckInterpreterConsoleStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new CommandMonitoringStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN),
};
Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) {
@Override public Step[] getSteps() { return shutdownSteps; }
};
getExecutor().execute(shutdownSequence);
}
@Override
public MIControlDMContext getControlDMContext() {
return fControlDmc;
}
/**
* More strongly typed version of {@link #getControlDMContext()}.
*/
public GDBControlDMContext getGDBDMContext() {
return (GDBControlDMContext)getControlDMContext();
}
public SessionType getSessionType() {
return fSessionType;
}
public boolean canInterrupt() {
return fProcess instanceof Spawner;
}
public void interrupt() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.interrupt();
}
}
public void destroy() {
if (fProcess instanceof Spawner) {
Spawner gdbSpawner = (Spawner) fProcess;
gdbSpawner.destroy();
}
}
public void terminate(final RequestMonitor rm) {
// Schedule a runnable to be executed 2 seconds from now.
// If we don't get a response to the quit command, this
// runnable will kill the task.
final Future<?> quitTimeoutFuture = getExecutor().schedule(
new DsfRunnable() {
public void run() {
if (!isGDBExited())
destroy();
rm.done();
}
@Override
protected boolean isExecutionRequired() {
return false;
}
},
2, TimeUnit.SECONDS);
MIGDBExit cmd = new MIGDBExit(fControlDmc);
queueCommand(
cmd,
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
public void handleCompleted() {
// Cancel the time out runnable (if it hasn't run yet).
quitTimeoutFuture.cancel(false);
if (!getStatus().isOK() && !isGDBExited()) {
destroy();
}
rm.done();
}
}
);
}
public boolean isConnected() {
return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED && fConnected;
}
void setConnected(boolean connected) {
fConnected = connected;
}
public Process getGDBProcess() {
return fProcess;
}
public AbstractCLIProcess getCLIProcess() {
return fCLIProcess;
}
public MIInferiorProcess getInferiorProcess() {
return fInferiorProcess;
}
public boolean isGDBExited() {
return fMonitorJob != null && fMonitorJob.fExited;
}
public int getGDBExitCode() {
return fGDBExitValue;
}
public IPath getExecutablePath() { return fExecPath; }
public void getInferiorExitCode(final DataRequestMonitor<Integer> rm) {
fCommandCache.execute(
new MIGDBShowExitCode(fControlDmc),
new DataRequestMonitor<MIGDBShowExitCodeInfo>(getExecutor(), rm) {
@Override
protected void handleOK() {
rm.setData(getData().getCode());
rm.done();
}
});
}
public void getInferiorProcessId(DataRequestMonitor<Integer> rm) {
}
@DsfServiceEventHandler
public void eventDispatched(ExitedEvent e) {
// Handle our "GDB Exited" event and stop processing commands.
stopCommandProcessing();
}
/**
* Monitors a system process, waiting for it to terminate, and
* then notifies the associated runtime process.
*/
private class MonitorJob extends Job {
boolean fExited = false;
DsfRunnable fMonitorStarted;
Process fMonProcess;
@Override
protected IStatus run(IProgressMonitor monitor) {
synchronized(fMonProcess) {
getExecutor().submit(fMonitorStarted);
while (!fExited) {
try {
fMonProcess.waitFor();
fGDBExitValue = fMonProcess.exitValue();
} catch (InterruptedException ie) {
// clear interrupted state
Thread.interrupted();
} finally {
fExited = true;
getSession().dispatchEvent(new ExitedEvent(fControlDmc) {}, getProperties());
}
}
}
return Status.OK_STATUS;
}
MonitorJob(Process process, DsfRunnable monitorStarted) {
super("GDB process monitor job."); //$NON-NLS-1$
fMonProcess = process;
fMonitorStarted = monitorStarted;
setSystem(true);
}
void kill() {
synchronized(fMonProcess) {
if (!fExited) {
getThread().interrupt();
}
}
}
}
public static class InitializationShutdownStep extends Sequence.Step {
public enum Direction { INITIALIZING, SHUTTING_DOWN }
private Direction fDirection;
InitializationShutdownStep(Direction direction) { fDirection = direction; }
@Override
final public void execute(RequestMonitor requestMonitor) {
if (fDirection == Direction.INITIALIZING) {
initialize(requestMonitor);
} else {
shutdown(requestMonitor);
}
}
@Override
final public void rollBack(RequestMonitor requestMonitor) {
if (fDirection == Direction.INITIALIZING) {
shutdown(requestMonitor);
} else {
super.rollBack(requestMonitor);
}
}
protected void initialize(RequestMonitor requestMonitor) {
requestMonitor.done();
}
protected void shutdown(RequestMonitor requestMonitor) {
requestMonitor.done();
}
}
protected class GDBProcessStep extends InitializationShutdownStep {
GDBProcessStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
class GDBLaunchMonitor {
boolean fLaunched = false;
boolean fTimedOut = false;
}
final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor();
final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), null) {
@Override
protected void handleCompleted() {
if (!fGDBLaunchMonitor.fTimedOut) {
fGDBLaunchMonitor.fLaunched = true;
if (!getStatus().isOK()) {
requestMonitor.setStatus(getStatus());
}
requestMonitor.done();
}
}
};
final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
List<String> commandList = new ArrayList<String>();
commandList.add(fGdbPath.toOSString());
if (fExecPath != null) {
commandList.add("--interpreter"); //$NON-NLS-1$
commandList.add("mi"); //$NON-NLS-1$
commandList.add(fExecPath.toOSString());
}
String[] commandLine = commandList.toArray(new String[commandList.size()]);
try {
fProcess = ProcessFactory.getFactory().exec(commandLine);
} catch(IOException e) {
String message = MessageFormat.format("Error while launching command", //$NON-NLS-1$
new Object[]{commandList.toString()});
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e));
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
try {
InputStream stream = fProcess.getInputStream();
Reader r = new InputStreamReader(stream);
BufferedReader reader = new BufferedReader(r);
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
//System.out.println("GDB " + line);
if (line.endsWith("(gdb)")) { //$NON-NLS-1$
break;
}
}
} catch (IOException e) {
gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB STDOUT", e)); //$NON-NLS-1$
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
gdbLaunchRequestMonitor.done();
return Status.OK_STATUS;
}
};
startGdbJob.schedule();
getExecutor().schedule(new Runnable() {
public void run() {
// Only process the event if we have not finished yet (hit the breakpoint).
if (!fGDBLaunchMonitor.fLaunched) {
fGDBLaunchMonitor.fTimedOut = true;
Thread jobThread = startGdbJob.getThread();
if (jobThread != null) {
jobThread.interrupt();
}
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$
requestMonitor.done();
}
}},
fGDBLaunchTimeout, TimeUnit.SECONDS);
}
@Override
protected void shutdown(final RequestMonitor requestMonitor) {
new Job("Terminating GDB process.") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
if (fProcess == null)
fProcess.destroy();
int attempts = 0;
while (attempts < 10) {
try {
// Don't know if we really need the exit value... but what the hell.
fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited
requestMonitor.done();
return Status.OK_STATUS;
} catch (IllegalThreadStateException ie) {
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
attempts++;
}
requestMonitor.setStatus(new Status(
IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfService.REQUEST_FAILED, "Process terminate failed", null)); //$NON-NLS-1$
requestMonitor.done();
return Status.OK_STATUS;
}
}.schedule();
}
}
protected class MonitorJobStep extends InitializationShutdownStep {
MonitorJobStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
fMonitorJob = new MonitorJob(
fProcess,
new DsfRunnable() {
public void run() {
requestMonitor.done();
}
});
fMonitorJob.schedule();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
if (!fMonitorJob.fExited) {
fMonitorJob.kill();
}
requestMonitor.done();
}
}
protected class CommandMonitoringStep extends InitializationShutdownStep {
CommandMonitoringStep(Direction direction) { super(direction); }
@Override
protected void initialize(final RequestMonitor requestMonitor) {
startCommandProcessing(fProcess.getInputStream(), fProcess.getOutputStream());
requestMonitor.done();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
stopCommandProcessing();
requestMonitor.done();
}
}
protected class CheckInterpreterConsoleStep extends InitializationShutdownStep {
CheckInterpreterConsoleStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
MIInterpreterExecConsole<MIInfo> cmd = new MIInterpreterExecConsole<MIInfo>(fControlDmc, "echo"); //$NON-NLS-1$
GDBControl.this.queueCommand(
cmd,
new DataRequestMonitor<MIInfo>(getExecutor(), null) {
@Override
protected void handleCompleted() {
fUseInterpreterConsole = getStatus().isOK();
requestMonitor.done();
}
}
);
}
}
protected class CommandProcessorsStep extends InitializationShutdownStep {
CommandProcessorsStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
try {
fCLIProcess = new GDBCLIProcess(GDBControl.this, fUseInterpreterConsole);
}
catch(IOException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfService.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$
requestMonitor.done();
return;
}
fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fProcess.getOutputStream());
fCLICommandProcessor = new CLIEventProcessor(GDBControl.this, fControlDmc, fInferiorProcess);
fMIEventProcessor = new MIRunControlEventProcessor(GDBControl.this, fControlDmc);
fCommandCache = new CommandCache(GDBControl.this);
requestMonitor.done();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
fCLICommandProcessor.dispose();
fMIEventProcessor.dispose();
fCLIProcess.dispose();
fInferiorProcess.dispose();
requestMonitor.done();
}
}
protected class RegisterStep extends InitializationShutdownStep {
RegisterStep(Direction direction) { super(direction); }
@Override
public void initialize(final RequestMonitor requestMonitor) {
getSession().addServiceEventListener(GDBControl.this, null);
register(new String[]{ ICommandControl.class.getName(), AbstractMIControl.class.getName() }, new Hashtable<String,String>());
getSession().dispatchEvent(new StartedEvent(getGDBDMContext()), getProperties());
requestMonitor.done();
}
@Override
protected void shutdown(RequestMonitor requestMonitor) {
unregister();
getSession().removeServiceEventListener(GDBControl.this);
requestMonitor.done();
}
}
}

View file

@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems 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.dd.gdb.service.command;
import org.eclipse.dd.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
import org.eclipse.dd.dsf.debug.service.IMemory.IMemoryDMContext;
import org.eclipse.dd.dsf.debug.service.IModules.ISymbolDMContext;
import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.dd.dsf.debug.service.ISignals.ISignalsDMContext;
import org.eclipse.dd.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
import org.eclipse.dd.mi.service.command.MIControlDMContext;
/**
*
*/
public class GDBControlDMContext extends MIControlDMContext
implements IContainerDMContext, ISymbolDMContext, IMemoryDMContext, IBreakpointsTargetDMContext, ISourceLookupDMContext,
ISignalsDMContext
{
public GDBControlDMContext(String sessionId, String commandControlId) {
super(sessionId, commandControlId);
}
}

View file

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems 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.dd.gdb.service.command;
import java.io.OutputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.eclipse.dd.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor;
import org.eclipse.dd.gdb.service.command.GDBControl.SessionType;
import org.eclipse.dd.mi.service.command.MIInferiorProcess;
/**
*
*/
class GDBInferiorProcess extends MIInferiorProcess {
public GDBInferiorProcess(GDBControl commandControl, PTY p) {
super(commandControl, p);
}
public GDBInferiorProcess(GDBControl commandControl, OutputStream gdbOutputStream) {
super(commandControl, gdbOutputStream);
}
@Override
@ThreadSafeAndProhibitedFromDsfExecutor("getSession#getExecutor")
public void destroy() {
try {
getSession().getExecutor().submit(new DsfRunnable() {
public void run() {
if (isDisposed() || !getSession().isActive()) return;
GDBControl gdb = (GDBControl)getCommandControl();
if (gdb == null) return;
// An inferior will be destroy():interrupt and kill if
// - For attach session:
// the inferior was not disconnected yet (no need to try
// to kill a disconnected program).
// - For Program session:
// if the inferior was not terminated.
// - For PostMortem(Core): send event
// else noop
if ((gdb.getSessionType() == SessionType.ATTACH && gdb.isConnected()) ||
(gdb.getSessionType() == SessionType.RUN && getState() != State.TERMINATED))
{
// Try to interrupt the inferior, first.
if (getState() == State.RUNNING) {
gdb.interrupt();
}
}
}
}).get();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} finally {
super.destroy();
}
}
}