[179102] Re-aligned plugin and package names to confirm with Eclipse policy.
7
plugins/org.eclipse.dd.examples.dsf/.classpath
Normal 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>
|
28
plugins/org.eclipse.dd.examples.dsf/.project
Normal 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>
|
|
@ -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
|
18
plugins/org.eclipse.dd.examples.dsf/META-INF/MANIFEST.MF
Normal 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
|
24
plugins/org.eclipse.dd.examples.dsf/about.html
Normal 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>
|
7
plugins/org.eclipse.dd.examples.dsf/build.properties
Normal file
|
@ -0,0 +1,7 @@
|
|||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
plugin.xml,\
|
||||
plugin.properties,\
|
||||
about.html
|
BIN
plugins/org.eclipse.dd.examples.dsf/icons/alarm.gif
Normal file
After Width: | Height: | Size: 85 B |
BIN
plugins/org.eclipse.dd.examples.dsf/icons/alarm_triggered.gif
Normal file
After Width: | Height: | Size: 206 B |
BIN
plugins/org.eclipse.dd.examples.dsf/icons/layout.gif
Normal file
After Width: | Height: | Size: 857 B |
BIN
plugins/org.eclipse.dd.examples.dsf/icons/remove.gif
Normal file
After Width: | Height: | Size: 163 B |
BIN
plugins/org.eclipse.dd.examples.dsf/icons/sample.gif
Normal file
After Width: | Height: | Size: 983 B |
BIN
plugins/org.eclipse.dd.examples.dsf/icons/timer.gif
Normal file
After Width: | Height: | Size: 153 B |
13
plugins/org.eclipse.dd.examples.dsf/plugin.properties
Normal 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
|
||||
|
57
plugins/org.eclipse.dd.examples.dsf/plugin.xml
Normal 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>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}});
|
||||
}});
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 6.1 KiB |
|
@ -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: "Times New Roman";">Version
|
||||
1.0<br>
|
||||
Pawel Piech<br>
|
||||
© 2006, Wind River Systems.<span style=""> </span>Release
|
||||
under EPL version 1.0.</span></b><b><span
|
||||
style="font-size: 18pt; font-family: "Times New Roman";"><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. Suppose there is a viewer which needs to
|
||||
show data that originates from a remote "provider". There is a
|
||||
considerable delay in transmitting the data to and from the provider,
|
||||
and some delay in processing the data. 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. 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. It has an associated content
|
||||
provider, SlowDataProviderContentProvider) which handles all the
|
||||
interactions with the data provider. 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>.
|
||||
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. 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<Integer> 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. 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. 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. 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. 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. 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. <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. <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 -> 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. 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. 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. The result of this is visible to the user as
|
||||
lines of data are filled in one-by-one in the table. 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. 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. <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. 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. 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. When the service implementation receives a
|
||||
request for a single item, it buffers the request, and waits for other
|
||||
requests to come in. 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>).
|
||||
<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. 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. </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<String> 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 < 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. 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. 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. For the table viewer
|
||||
content provider, this means that additional features have to be
|
||||
added. 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 >= topIdx && index <= 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. To make the canceling feature useful,
|
||||
the data provider service has to limit the size of the request
|
||||
queue. 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. 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. 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() >= 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. 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. 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. Neither
|
||||
of these optimizations affected the original interface of the service,
|
||||
and one of them only needed a service-side modification. 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. </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. 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. The perfect example of this is a Variables
|
||||
service, which is responsible for calculating the value of expressions
|
||||
shown in the Variables view. 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. But as
|
||||
soon as a resumed event is issued by Run Control, the Variables service
|
||||
needs to cancel the pending evaluation requests.<br>
|
||||
</p>
|
||||
<br>
|
||||
<br>
|
||||
</body>
|
||||
</html>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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. Such layout nodes are
|
||||
needed to represents elements which themselves have a natural tree
|
||||
structures. This example uses filesystem folders as the
|
||||
tree-structured data, which is retrieved directly from the java.io.File
|
||||
class. This example also demonstrates a viewer model
|
||||
implementation which does not retrieve data using DSF services and
|
||||
associated data model interfaces. <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. 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. 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). <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. 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}. 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. <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. 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. 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. <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.
|
||||
<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. <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. 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. <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. <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 -> 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->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>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 14 KiB |
|
@ -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). 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. There
|
||||
is also a "Timers" view that displays the timers and alarms, and has
|
||||
actions for adding and removing new timers and alarms. 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>
|
7
plugins/org.eclipse.dd.gdb/.classpath
Normal 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>
|
28
plugins/org.eclipse.dd.gdb/.project
Normal 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>
|
|
@ -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
|
22
plugins/org.eclipse.dd.gdb/META-INF/MANIFEST.MF
Normal 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
|
24
plugins/org.eclipse.dd.gdb/about.html
Normal 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>
|
6
plugins/org.eclipse.dd.gdb/build.properties
Normal file
|
@ -0,0 +1,6 @@
|
|||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
plugin.xml,\
|
||||
about.html
|
26
plugins/org.eclipse.dd.gdb/plugin.xml
Normal 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>
|
|
@ -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 ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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$
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|