-
@@ -780,17 +776,764 @@ module.
+
Timers Example
+
The
Timers example, found in the
org.eclipse.dd.examples.dsf.timers
+package, is used as a reference throughout the following
+sections. It is useful to get familiar with this example at this
+time.
+
Timer example defines the following two services:
+
+
+ - TimerService -
+This service manages a set of timers where each timer's value is
+incremented every second. The timer service contains the
+following features:
+
+
+ - startTimer() method -
+Allows user to create a new timer. It returns the Data Model
+context for the new timer.
+
+ - killTimer() method -
+Allows the user to delete the given timer. It requires a timer
+context.
+
+ - getTimers() method -
+Returns the array of contexts for existing timers.
+
+ - getTimerValue() method
+- Returns the current value for the given timer context.
+
+ - TimerTickEvent event
+class - An event that is generated for every timer, every time its
+value changes (once per second). The event contains the timer's
+context.
+
+
+ - AlarmService -
+This service manages a set of triggers and alarms. Triggers can
+be created and destroyed independently. Alarms represent a timer
+and a trigger combined. The Alarm service has the following
+features:
+
+ - createTrigger() method
+- Creates a new trigger with a given value. It returns a context
+to the new trigger.
+
+ - deleteTrigger() method
+- Deletes the trigger for the given context.
+
+ - setTriggerValue()
+method - Sets the value of a trigger to the given value.
+
+ - getAlarm() method -
+Gets the alarm for the specified timer and trigger contexts. It
+returns an alarm context object.
+
+ - AlarmTriggeredDMEvent
+event class - An event that is generated for every timer that trips the
+given trigger by surpassing its value. The event contains an
+alarm context.
+
+
+
+The Timers example also features a user interface for displaying and
+manipulating the data in the example's services. The principal
+component of this UI is a view that can be opened by following the
+menus:
Window->Show View->Other,
+then selecting
DSF
+Examples->Timers View in the selection dialog. This
+view contains a tree viewer which displays the timers, triggers, and
+alarms in a hierarchy. The alarms are only shown when triggered
+and are automatically selected upon a triggered event.
+
+
+
+ 
+ |
+
+
+ Image 4: Screen shot of the Timers view.
+ |
+
+
+
+Other features of the Timers example UI include:
+
+ - New Timer action
+- Adds a new timer, which immediately shows up in the view.
+ - New Trigger action
+- Opens a dialog where the user enters the value of the new
+trigger. Upon OK, the dialog creates a new trigger that is added
+to the view.
+ - Remove action -
+Removes a timer or a trigger, whichever is currently selected in the
+viewer.
+
+ - Toggle Layout action
+- Switches the hierarchy in the tree to either Timers->Triggers->Alarm or Triggers->Timers->Alarm
+ - Edit Trigger Value cell
+editor - changes the value of the selected trigger.
+
+
Services
OSGi
+DSF builds on top of OSGi services APIs. OSGi offers a rich
+API for managing services and it is important to understand some of the
+OSGi service API basics in order to use DSF
+
+ - Registration -
+Services need to register and unregister themselves with OSGi framework
+
+ - BundleContext.registerService()
+- registers a service, it returns a ServiceRegistration object which
+should be retained by the caller.
+ - ServiceRegistration.unregister()
+- unregisters a service.
+
+ - References -
+Clients wishing to use a service, need to obtain a reference to the
+service. OSGi features reference counting for services.
+
+ - BundleContext.getServiceReference(),
+BundleContext.getServiceReferences(),
+BundleContext.getAllServiceReferences() - methods for retrieving a
+reference to a service using a class name and/or a property filter.
+ - BundleContext.ungetService() - Releases a service reference and
+decrements its use count.
+
+ - Events - Clients
+using services should listen to service events. Events are issued
+when services are added/removed/modified.
+
+ - org.osgi.framework.ServiceListener - interface for a service
+listener. Objects implementing this interface can be registered
+with the BundleContext
+
+
+
+
+
+
+ Note: The service APIs all use the
+BundleContext and they require the BundleContext to be active.
+This means DSF-based debugger integrations initialize after the plugin
+is started, but that they also shut down before the plugin is
+stopped. The first part is not difficult, but the second part
+usually requires that the plugin's BundleActivator.stop() method shuts
+down the debugger.
+ |
+
+
+
+
Session
+DSF-based debugger integrations can register many services and there
+can be multiple instances of debuggers registering services with the
+same interfaces. To help coordinate services in a give debugger
+instance and distinguish the services between the instances of
+debuggers, DSF services are organized into sessions.
+
DSF Session features include:
+
+
+ - Unique Session ID
+- This ID is used to distinguish services from different
+sessions. Clients may also obtain a session instance using an ID
+through a static method.
+
+ - Session Lifecycle Events - Clients may register to listen when
+sessions are started and ended.
+
+ - DSF Executor -
+Eash session has a (single-threaded) DSF Executor associated with
+it, though multiple sessions could share a single executor. More
+about session executor in the next section.
+ - Service Events -
+The session is used to dispatch service events. More on events in
+following sections.
+ - Model Adapters -
+A session allws an adapter to be registered, which will be returned by
+all Data Model contexts in a given session for a given adapter
+type. More information about Data Model is described in the Data
+Model section.
+
+
+
Executor
+All the services registered with the same session share a single DSF
+Executor. By convention, all public service interfaces should be
+restricted to being called in this executor thread. This point
+goes back to the primary synchronization mechanism of DSF.
+Following this rule greatly simplifies the task of protecting the
+integrity of service state information.
+
+
+
+
+ Note: All service public methods
+should be called using the session's DSF executor. |
+
+
+
+
Tracker
+Using the OSGi APIs for accessing services directly can be very
+cumbersome. A client retrieving a service reference is
+responsible for retaining the ServiceReference object and for calling
+BundleContext.ungetService() to avoid leaking the refernce. Also,
+since a service may be un-registered at any time, the clients need to
+listen for events indicating when a service is unregistered.
+Fortunately there are two utilities which help with this task
+
+
+
+
+
+ |
+ org.osgi.util.tracker.ServiceTracker
+ |
+ org.eclipse.dd.dsf.service.DsfServicesTracker
+ |
+
+
+ Services
+tracked
+ |
+ Tracks all services with a given
+class name or filter.
+ |
+ Tracks all services within a
+given DSF session.
+ |
+
+
+ Thread
+safety |
+ Thread safe |
+ Restricted to the session
+executor thread. |
+
+
+ Accessor
+methods
+ |
+
+
+ - getService() -
+return the first service instance matching the class/filter
+ - getServices() -
+returns all references matching the specified class/filter.
+
+
+ |
+
+
+ - getService(Class)
+- Returns the first service instance matching given class
+ - getService(Class, String)
+- Returns the first service instance matching given class and filter.
+
+
+ |
+
+
+ Activation/Disposal
+methods
+ |
+
+
+ - open() - Starts
+tracking maching services.
+ - close() - Shuts
+down and un-gets all service references.
+
+ |
+
+
+ - <constructor>
+- DSF services tracker can be used immediately after being constucted.
+ - dispose() -
+Disposes and un-gets all service references held by the tracker.
+
+
+ |
+
+
+
+
+
+
+ Note: All service trackers must be
+disposed (or closed). Failing to dispose a tracker results in a
+service reference leak.
+ |
+
+
+
+
Initialization / Shutdown
+
Every DSF service must
+implement the IDsfService.initialize() and IDsfService.shutdown()
+methods. These methods can only be called in the session executor
+thread and are asynchronous. As the last step in
+initialization, a service should register itself. Likewise as the
+first step of shut-down a service should unregister itself. Also
+during initialization, each service should call
+DsfSession.getAndIncrementServiceStartupCounter(), in order to obtain
+the startup number of the service. This number is used in
+prioritizing the service events.
+
Starting up a large number of DSF services requires calling a number
+of asynchronous method in a pre-defined sequence. Implementing
+this startup code can be cumbersome and DSF provides a quitility for
+implementing it: org.eclipse.dd.dsf.concurrent.Sequence.
+
+
Here's
+an example of how the Sequence is extended to perform the task of
+shutting down the services in the
+Timers example:
+
+
+
+
+
+ org.eclipse.dd.examples.dsf.timers.ServicesStartupSequence
+ |
+
+
+
+ |
+
+ 25: public class ServicesShutdownSequence extends Sequence {
27: // Session to that the services are running in. 28: final private DsfSession fSession; 29: 30: // DSF Services is created as the first step of the sequence. It 31: // cannot be created by the constructor because it can only be called 32: // in the session thread. 33: DsfServicesTracker fTracker;
35: public ServicesShutdownSequence(DsfSession session) { 36: super(session.getExecutor()); 37: fSession = session; 38: } 39: 40: Step[] fSteps = new Step[] { 41: new Step() { 42: @Override 43: public void execute(RequestMonitor requestMonitor) { 44: fTracker = new DsfServicesTracker(DsfExamplesPlugin.getBundleContext(), fSession.getId()); 45: requestMonitor.done(); 46: } 47: 48: @Override 49: public void rollBack(RequestMonitor requestMonitor) { 50: // Dispose the tracker in case shutdown sequence is aborted 51: // and is rolled back. 52: fTracker.dispose(); 53: fTracker = null; 54: requestMonitor.done(); 55: } 56: }, 57: new Step() { 58: @Override 59: public void execute(RequestMonitor requestMonitor) { 60: shutdownService(AlarmService.class, requestMonitor); 61: } 62: }, 63: new Step() { 64: @Override 65: public void execute(RequestMonitor requestMonitor) { 66: shutdownService(TimerService.class, requestMonitor); 67: } 68: }, 69: new Step() { 70: @Override 71: public void execute(RequestMonitor requestMonitor) { 72: // Dispose the tracker after the services are shut down. 73: fTracker.dispose(); 74: fTracker = null; 75: requestMonitor.done(); 76: } 77: } 78: }; 79: 80: @Override 81: public Step[] getSteps() { return fSteps; }
83: // A convenience method that shuts down given service. Only service class 84: // is used to identify the service. 85: private <V extends IDsfService> void shutdownService(Class<V> clazz, RequestMonitor requestMonitor) { 86: IDsfService service = fTracker.getService(clazz); 87: if (service != null) { 88: service.shutdown(requestMonitor); 89: } 90: else { 91: requestMonitor.setStatus(new Status( 92: IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, 93: IDsfService.INTERNAL_ERROR, 94: "Service '" + clazz.getName() + "' not found.", null)); 95: requestMonitor.done(); 96: } 97: } 99: }
+ |
+
+
+
+
+
+ - Line 40 initializes an array of Step objects which are invoked by
+the Sequence logic. Each Step class is an inner class with access
+to
+shared data in the ServicesShutdownSequence class.
+ - Line 81 implements the protected method used by the Sequence
+class to access the steps.
+ - Line 85 encapsulates the repetitive logic of finding and shutting
+down a given service.
+ - Line 73 disposes the DsfServicesTracker used by the sequence.
+
+
+Below is the code snipped that invokes the ServicesShutdownSequence in
+the Timers example:
+
+
+
+
+
+ org.eclipse.dd.examples.dsf.timers.TimersView
+ |
+
+
+
+ |
+
+ 181: ServicesShutdownSequence shutdownSeq = 182: new ServicesShutdownSequence(fSession); 183: fSession.getExecutor().execute(shutdownSeq); 184: try { 185: shutdownSeq.get(); 186: } catch (InterruptedException e) { assert false; 187: } catch (ExecutionException e) { assert false; 188: }
+ |
+
+
+
+
+
+ - Line 183 submits the sequence to the session executor.
+
+ - Line 185 calls the Future.get() method of the sequence to block
+the calling thread until the sequence completes.
+
+
+
+
+ Note: Sequence implements the
+java.util.concurrent.Future interface just like the DSF Query
+object. However, if the sequence needs to be invoked from the
+executor thread, the Future.get() method cannot be used (or a deadlock
+would occur). Instead the sequence should be constructed with a
+custom request monitor to be invoked at the completion of the sequence.
+ |
+
+
+
+
+
Events
+DSF provides a somewhat unusual event mechanism, where event listeners
+do not implement any particular listener interface. Instead,
+event listeners use the
DsfServiceEventHandler
+annotation to identify listener methods. DSF finds the annotated
+listener methods using reflection.
+
To generate an event a service must:
+
+
+ - Call DsfSession.dispatchEvent(Object
+event, Dictionary<String, String> serviceProperties)
+method. The second parameter allows service listeners to filter
+events using specific service properties.
+
+In order to receive DSF events a client must:
+
+ - Declare a public event
+listener method (method name is not important), which takes an event parameter. The type of the
+event parameter depends on the event, where the listener will receive
+all service events which can be cast to the declared type. A
+second optional parameter of type Dictionary<String,
+String> allows the event listener to examine the properties
+of the service that is sending the event.
+ - Add itself as a service event listener by calling DsfSession.addServiceEventListener().
+
+
+
+
+ Note: DsfSession.dispatchEvent()
+calls event listeners in a separate Runnable submitted to the session
+executor. This is significant because the event listeners may
+call other service methods changing the overall state of the
+system. It also implies that the event listeners are always
+called in the session executor thread.
+ |
+
+
+
+
+
+
+
+ Note: Service events are
+prioritized. Listeners which themselves are services are called
+first, in the order that they were initialized. All other
+listenres are called after the services.
+ |
+
+
+
Data Model
-
View Model
-
Adapter, Provider, Node
-
Timers
+The term
Data Model refers to
+the natural structure of data that is being retrieved by the DSF
+services. One of the great challanges of creating an user
+interface for a debugger is that the amount of of data that is
+available on the target is much greater than what can be practially
+presented to the user. Therefere the debugger services need to
+break up the data into chunks with appropriate granularity to achieve
+maximum performance and usability.
+
IDMContext
+The IDMContext represents a handle to a chunk of data in the Data
+Model. This interface is a minimal, yet central feature of the
+Data Model API.
+
What a Data Model context is:
+
+
+ - It is hierarchical.
+Contexts can have other contexts as parents. The hierarchy of
+contexts in a given system roughly defines that system's overall Data
+Model. More on context hierarchy
+
+ - It extends the org.eclipse.core.runtime.IAdaptable interface. This
+allows decorators, retargetable actions, etc. to be associated with a
+context.
+ - It is associated with a
+single DSF session. The IDMContext.getSessionID() returns
+the session ID of the given context. This allows all clients to
+get a handle on the session and the executor needed to access the DSF
+services that the context originated from.
+
+ - It is thread safe.
+This allows context objects to be stored and compared in viewers,
+caches, and other clients which may implement their own threading model.
+
+ - It is light-weight and
+preferably immutable. This allows contexts to be stored by
+clients that may persist beyond the life of the services that
+originated them. If a context holds references to a lot of data
+or it may prevent that data from being garbage collected.
+
+What a Data Model context is NOT:
+
+ - It is NOT a reference
+to a service. Context should not return a reference to a
+service directly because clients should use the appropriate OSGi APIs
+to obtain references to DSF services.
+
+ - It is NOT persistable.
+Since a context returns a context ID, it is valid only for the life of
+a single DSF session.
+
+
+
+
+
+ Note: An IDMContext object can be
+used to retrieve any
+type of data object from the service. Although there is an
+IDMData marker interface defined, its presence it historical and its
+use is optional.
+ |
+
+
+
+
Context Hierarchy
+One of the most powerful features of the IDMContext interface is that
+is is hierarchical. The
IDMContext.getParents()
+method returns the immediate ancestors of a given context and following
+the parents' parents allows clients to traverse the full hierarchy of a
+context.
+
The use of the context hierarchy may be best explained with use of
+the Timers example. In the timers example there are three
+contexts that are used:
+
+
+
+ - Timer - no
+parent contexts
+
+ - Trigger - no
+parent contexts
+
+ - Alarm - requires
+both a timer and a trigger as parent contexts
+
+
+From these, only the third one has any parents (and any hierarchy), the
+code snippet below shows how these parents are used in the AlarmService:
-
+
+
+ org.eclipse.dd.examples.dsf.timers.AlarmService.isAlarmTriggered()
+ |
+
+
+
+ |
+
+ 209: public boolean isAlarmTriggered(AlarmDMContext alarmCtx) { 210: // Extract the timer and trigger contexts. They should always be part 211: // of the alarm. 212: TimerService.TimerDMContext timerCtx = DMContexts.getAncestorOfType( 213: alarmCtx, TimerService.TimerDMContext.class); 214: TriggerDMContext triggerCtx = DMContexts.getAncestorOfType( 215: alarmCtx, TriggerDMContext.class);
217: assert triggerCtx != null && timerCtx != null;
219: // Find the trigger and check whether the timers value has surpassed it. 220: if (fTriggers.containsKey(triggerCtx)) { 221: int timerValue = getServicesTracker().getService(TimerService.class). 222: getTimerValue(timerCtx); 223: 224: return timerValue >= fTriggers.get(triggerCtx); 225: } 226: 227: return false; 228: }
+ |
+
+
+
+
+
+ - Lines 212 and 214 search the context hierarchy of the alarm
+context for the timer and trigger contexts.
+
+
+
+
+
+ Note: Methods that take a context
+as an argument can specify the generic IDMContext as the argument type,
+then search this context for a specific context type. The benefit
+of this technique is increased flexibility, at the cost of compile-time
+type checking, and it is used throughout DSF to avoid dependencies
+between service interfaces.
+ |
+
+
+
+
DMContexts
+Searching the context hierarchy can be tedious to implement, the
+DMContexts utility class contains a few static methods to simplify this
+task:
+
+ - getAncestorOfType()
+- Searches for a context of a specific type in the hierarchy of the
+given context.
+ - isAncestorOf() -
+Checks whether the one of the argument contexts is in the hierarchy of
+the other.
+ - toList() -
+Converts all the contexts in a hierarchy of the give context into a
+list.
+
+
View Model
+View Model refers to the ideal
user-presentable
+structure of the data. This is in contrast to the Data Model,
+which refers to the
natural
+data structure, although the two often end up being the same.
+Never the less, the needs of the user presentation often change so the
+central feature of the View Model is the ability to customize it.
+
Flexible Hierarchy
+View Model builds on the
flexible
+hierarchy API introduced by Debug
+Platform team in release 3.2. The flexible hierarchy API has a
+few distinguishing features:
+
+ - There are provider interfaces for every aspect of data
+presentation in the viewer (content, label, columns, etc.).
+
+ - The provider interfaces are retrieved by the viewer for each element in the
+viewer. This allows the
+view content to be populated from multiple sources.
+ - Provider interfaces are asynchronous.
+
+
+
+
+
+ Note: Flexible Hierarchy is still
+a provisional API in Eclipse Platform 3.4. This virtually
+guarantees that DSF will break backward API compatibility in future
+releases. However, these APIs have now been widely used by open
+source projects such as DD and CDT and also by many commercial Eclipse
+integrations, so the API changes are likely to be small and mostly
+related to packaging.
+ |
+
+
+
+
Adapter Problem
+
+The number two feature of flexible hierarchy API is implemented using
+the adapter pattern. One down-side of the adapter pattern is that
+there can only be one instance of an adapter of a particular type
+registered for a given element. For flexible hierarchy providers,
+it means that each provider must implement the element presentation
+logic for every view that the element appears in, and as a result
+adding a new view can force changing a large number of modules.
+
TODO: add a diagram of the
+adapter pattern used by multiple views.
+
+
To overcome the adapter pattern limitation, the View Model uses
+wrapper objects. The wrapper objects are held by the viewer and
+they redirect the requests for different flexible hierarchy providers
+to the appropriate modules.
+
+
TODO: add a diagram of the View
+Model hierarchy adapter pattern use
+
+
IVMAdapter -> IVMProvider -> IVMNode -> IVMContext
+
+
+
+
+
+
diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.dia b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.dia
new file mode 100644
index 00000000000..c2bd2b8b805
Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.dia differ
diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.png
new file mode 100644
index 00000000000..f9fec21cd3e
Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/request_monitor_1.png differ
diff --git a/plugins/org.eclipse.dd.doc.dsf/docs/common/timers_1.png b/plugins/org.eclipse.dd.doc.dsf/docs/common/timers_1.png
new file mode 100644
index 00000000000..172f5e1f279
Binary files /dev/null and b/plugins/org.eclipse.dd.doc.dsf/docs/common/timers_1.png differ