[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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|