1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-16 21:45:22 +02:00

Initial checkin.

This commit is contained in:
Pawel Piech 2006-07-28 16:01:39 +00:00
commit 9d95f568db
30 changed files with 2104 additions and 0 deletions

View file

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

View file

@ -0,0 +1 @@
bin

View file

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

View file

@ -0,0 +1,12 @@
#Thu Jul 27 15:22:22 PDT 2006
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.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.5

View file

@ -0,0 +1,14 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Riverbed Plug-in
Bundle-SymbolicName: org.eclipse.dd.dsf
Bundle-Version: 1.0.0
Bundle-Activator: org.eclipse.dd.dsf.DsfPlugin
Bundle-Localization: plugin
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.debug.core
Eclipse-LazyStart: true
Export-Package: org.eclipse.dd.dsf.concurrent,
org.eclipse.dd.dsf.debug,
org.eclipse.dd.dsf.model,
org.eclipse.dd.dsf.service

View file

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

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
</plugin>

View file

@ -0,0 +1,68 @@
/*******************************************************************************
* 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.dsf;
import org.eclipse.core.runtime.Plugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class DsfPlugin extends Plugin {
// The plug-in ID
public static final String PLUGIN_ID = "org.eclipse.dd.dsf";
// The shared instance
private static DsfPlugin fgPlugin;
private static BundleContext fgBundleContext;
/**
* The constructor
*/
public DsfPlugin() {
fgPlugin = this;
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
fgBundleContext = context;
super.start(context);
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
fgPlugin = null;
fgBundleContext = null;
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static DsfPlugin getDefault() {
return fgPlugin;
}
public static BundleContext getBundleContext() {
return fgBundleContext;
}
}

View file

@ -0,0 +1,60 @@
/*******************************************************************************
* 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.dsf.concurrent;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.DsfPlugin;
/**
* Default implementation of a Riverbed executor interfaces, based on the
* standard java.util.concurrent.ThreadPoolExecutor.
*/
public class DefaultDsfExecutor extends ScheduledThreadPoolExecutor
implements DsfExecutor
{
static class DsfThreadFactory implements ThreadFactory {
Thread fThread;
public Thread newThread(Runnable r) {
assert fThread == null; // Should be called only once.
fThread = new Thread(new ThreadGroup("Riverbed Thread Group"), r, "Riverbed Dispatch Thread", 0);
return fThread;
}
}
public DefaultDsfExecutor() {
super(1, new DsfThreadFactory());
}
public boolean isInExecutorThread() {
return Thread.currentThread().equals( ((DsfThreadFactory)getThreadFactory()).fThread );
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
// FIXME: Unfortunately this is not enough to catch runnable exceptions, because
// FutureTask implementation swallows exceptions when they're thrown by runnables.
// Need to override the FutureTask class, and the AbstractExecutorService.submit()
// methods in order to provide access to these exceptions.
super.afterExecute(r, t);
if (t != null) {
DsfPlugin.getDefault().getLog().log(new Status(
IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Uncaught exception in dispatch thread.", t));
}
}
}

View file

@ -0,0 +1,48 @@
/*******************************************************************************
* 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.dsf.concurrent;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
/**
* Base class for Riverbed service method-completion callbacks. By default
* all callbacks that indicate a complition of a method contain the status
* of the result.
* <br>NOTE: Access to the status data is not synchronized, so
* clients have to make sure that access to this object is thread safe if
* it's used outside of the caller's dispatch thread.
*/
abstract public class Done extends DsfRunnable {
private IStatus fStatus = Status.OK_STATUS;
/** Sets the status of the called method. */
public void setStatus(IStatus status) { fStatus = status; }
/** Returns the status of the completed method. */
public IStatus getStatus() { return fStatus; }
/**
* Convenience method for setting the status using a status object of a
* sub-command.
* @param pluginId plugin id of the invoked method
* @param code status code
* @param message message to include
* @param subStatus status object to base the Done status on
*/
public void setErrorStatus(String pluginId, int code, String message, final IStatus subStatus) {
MultiStatus status = new MultiStatus(pluginId, code, message, null);
status.merge(subStatus);
fStatus = status;
}
}

View file

@ -0,0 +1,104 @@
/*******************************************************************************
* 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.dsf.concurrent;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.dd.dsf.DsfPlugin;
/**
* Utility class to track multiple done (callback) results of commands
* that are initiated simultaneously. The usage is as follows:
* <pre>
* final DoneTracker doneTracker = new DoneTracker() {
* public void run() {
* System.out.println("All complete, errors=" + !getStatus().isOK());
* }
* };
* for (int i = 0; i < 10; i++) {
* service.call(i, doneTracker.addDone(new Done() {
* public void run() {
* System.out.println(Integer.toString(i) + " complete");
* doneTracker.doneDone(this);
* }
* }));
* }
* </pre>
*/
public abstract class DoneTracker extends Done {
private Map<Done,Boolean> fDones = new HashMap<Done,Boolean>();
private int fDoneCounter;
/**
* No-arg constructor.
* <br>
* Note: this runnable will be executed following
* execution of the last done, and in the same dispatch loop.
*
*/
public DoneTracker() {
setStatus(new MultiStatus(DsfPlugin.PLUGIN_ID, 0, "Collective status for set of sub-operations.", null));
}
/**
* Adds a new Done callback to this tracker's list.
* @param <V> Service-specific class of the Done callback, to avoid an
* unnecessary cast.
* @param done callback object to add to the tracker
* @return the done that was just added, it allows this method to be used
* inlined in service method calls
*/
public <V extends Done> V add(V done) {
assert !fDones.containsKey(done);
fDones.put(done, false);
fDoneCounter++;
return done;
}
/**
* Adds a Done which performs no actions. This is useful if all work
* is performed inside DoneTracker.run().
* @return Done which is to be passed as an argument to a service method.
*/
public Done addNoActionDone() {
return add(new Done() { public void run() {
doneDone(this);
}});
}
/**
* Marks the given Done callback as completed. Client implementations of
* the Done callback have to call this method in order for the tracker
* to complete.
* <br>
* Note: funniest method signature ever!
* @param done
*/
public void doneDone(Done done) {
((MultiStatus)getStatus()).merge(done.getStatus());
fDones.put(done, true);
fDoneCounter--;
if (fDoneCounter == 0) {
assert !fDones.containsValue(false);
run();
}
}
/**
* Returns the map of Done callbacks. Access to this data is provided
* in case overriding classes need access to the collected data in the
* done callbacks.
* @return map of the done callbacks.
*/
public Map<Done,Boolean> getDones() { return fDones; }
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* 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.dsf.concurrent;
import java.util.concurrent.ScheduledExecutorService;
/**
* DSF executor service. Implementations of this executor must ensure
* that all runnables and callables are executed in the same thread: the
* executor's single dispatch thread.
* <br>Note: A DSF executor dispatch thread does not necessarily have
* to be exclusive to the executor, it could be shared with
* another event dispatch service, such as the SWT display dispatch thread.
*/
public interface DsfExecutor extends ScheduledExecutorService
{
/**
* Checks if the thread that this method is called in is the same as the
* executor's dispatch thread.
* @return true if in DSF executor's dispatch thread
*/
public boolean isInExecutorThread();
}

View file

@ -0,0 +1,161 @@
/*******************************************************************************
* 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.dsf.concurrent;
import java.util.concurrent.Future;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.DsfPlugin;
/**
* A convenience class that allows a client to retrieve data from services
* synchronously from a non-dispatch thread. This class is different from
* a Callable<V> in that it allows the implementation code to calculate
* the result in several dispatches, rather than requiring it to return the
* data at end of Callable#call method.
*
* @see java.util.concurrent.Callable
* FIXME: make this class implement the Future<V> interface.
*/
abstract public class DsfQuery {
private Object fResult;
private boolean fValid;
private DsfExecutor fExecutor;
private Future fFuture;
private boolean fWaiting;
private IStatus fStatus = Status.OK_STATUS;
public DsfQuery(DsfExecutor executor) {
fExecutor = executor;
}
/**
* Start data retrieval.
* Client must implement this method to do whatever is needed to retrieve data.
* Retrieval can be (but does not have to be) asynchronious - it meas this method can return
* before data is retrieved. When data is ready Proxy must be notified by calling done() method.
*/
protected abstract void execute();
/**
* Allows deriving classes to implement their own snipped additional
* cancellation code.
*/
protected void revokeChildren(Object result) {};
/**
* Get data associated with this proxy. This method is thread safe and
* it will block until data is ready. Because it's a blocking call and it waits
* for commands to be processed on the dispatch thread, this methods itself
* CANNOT be called on the dispatch thread.
*/
public synchronized Object get() {
assert !fExecutor.isInExecutorThread();
if(!fValid) {
if (!fWaiting) {
fFuture = fExecutor.submit(new DsfRunnable() {
public void run() {
// TODO: not sure if this try-catch is desirable. It might encourage
// implementors to not catch its own exceptions. If the query code takes
// more than one dispatch, then this code will not be helpful anyway.
try {
DsfQuery.this.execute();
} catch (Throwable t) {
doneException(t);
}
}
});
}
fWaiting = true;
try {
while(fWaiting) {
wait();
}
} catch (InterruptedException e) {
fStatus = new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1,
"Interrupted exception while waiting for result.", e);
fValid = true;
}
assert fValid;
}
return fResult;
}
/**
* Same as get(), but with code to automatically re-threw the exception if one
* was reported by the run() method.
*/
public Object getWithThrows() throws CoreException {
Object retVal = get();
if (!getStatus().isOK()) {
throw new CoreException(getStatus());
}
return retVal;
}
public IStatus getStatus() { return fStatus; }
/** Abort current operation and keep old proxy data */
public synchronized void cancel() {
assert fExecutor.isInExecutorThread();
assert !fWaiting || !fValid;
if (fWaiting) {
fFuture.cancel(false);
fWaiting = false;
notifyAll();
} else if (fValid) {
revokeChildren(fResult);
}
fValid = true;
}
/** Abort current operation and set proxy data to 'result' */
public synchronized void cancel(Object newResult) {
fResult = newResult;
cancel();
}
public Object getCachedResult() {
return fResult;
}
public boolean isValid() { return fValid; }
public synchronized void done(Object result) {
// Valid could be true if request was cancelled while data was
// being retrieved, and then done() was called.
if (fValid) return;
fResult = result;
fValid = true;
if (fWaiting) {
fWaiting = false;
notifyAll();
}
}
public synchronized void doneError(IStatus errorStatus) {
if (fValid) return;
fStatus = errorStatus;
done(null);
}
public synchronized void doneException(Throwable t) {
if (fValid) return;
doneError(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1,
"Exception while computing result.", t));
}
}

View file

@ -0,0 +1,21 @@
/*******************************************************************************
* 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.dsf.concurrent;
/**
* A DSF-instrumented alternative to the Runnable interface.
* <p>
* While it is perfectly fine for clients to call the Riverbed executor with
* an object only implementing the Runnable interface, the RbRunnable is a
* place holder for future tracing enhancments for Riverbed.
*/
abstract public class DsfRunnable implements Runnable {
}

View file

@ -0,0 +1,249 @@
/*******************************************************************************
* 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.dsf.concurrent;
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.DsfPlugin;
/**
* Convenience class for implementing a series of commands that need to be
* executed asynchronously.
* <p>
* Certain complex tasks require multiple commands to be executed in a chain,
* because for example result of one command is used as input into another
* command. The typical Riverbed pattern of solving this problem is the following:
* <li>
* <br> 1. original caller passes a Done callback to a method and invokes it
* <br> 2. the method is executed by a subsystem
* <br> 3. when the method is finished it calls another method and passes
* the original callback to it
* <br> 4. steps 2-3 are repeated number of times
* <br> 5. when the last method in a chain is executed, it submits the original
* Done callback
* </li>
* <p>
* This pattern is very useful in itself, but it proves very difficult to follow
* because the methods can be scattered accross many classes and systems. Also
* if progress reporting, cancellability, and roll-back ability is required, it
* has to be re-implemented every time. The Sequence class tries to address
* this problem by containing this pattern in a single class.
*
* <br>TODO: should a sequence be re-entrant. I.e. should the arguments be
* passed in through a map, and the return values returned back in the done?
* <br>FIXME: convert to implement Future interface
*/
abstract public class DsfSequence {
/**
* The abstract class that each step has to implement
* <br>FIXME: convert execute() and rollBacl() to take "done" as an argument.
* This way we can make step a static class, and make its paradigm
* more consistent with rest of Riverbed.
*/
abstract public class Step {
public void execute() { stepFinished(); }
public void rollBack() { stepRolledBack(); }
public int getTicks() { return 1; }
}
private DsfExecutor fExecutor;
private Step[] fSteps;
private Done fDoneQC;
private String fTaskName;
private String fRollbackTaskName;
private IProgressMonitor fProgressMonitor = new NullProgressMonitor();
private int fCurrentStepIdx = 0;
boolean fCancelled = false;
/**
* Default constructor. If this constructor is used, the steps need to be initialized
* before the sequence can be invoked.
* @param executor the Riverbed executor which will be used to invoke all steps
*/
public DsfSequence(DsfExecutor executor) {
this(executor, null);
}
/**
* Constructor that initialized the steps.
* @param executor the Riverbed executor which will be used to invoke all steps
* @param steps sequence steps
*/
public DsfSequence(DsfExecutor executor, Step[] steps) {
fExecutor = executor;
fSteps = steps;
}
/** Returns the riverbed executor for this sequence */
public DsfExecutor getExecutor() { return fExecutor; }
/**
* Sets the done callback to be submitted when the sequence is finished.
* If the sequence is submitted by a caller in the dispatch thread, this is
* the way that the original caller can be notified of the sequence
* completion. If the caller blocks and waits for the sequence
* completion, the Done callback is not necessary.
* @param doneQC callback to submit when sequence completes, can be null
*/
public void setDone(Done doneQC) {
fDoneQC = doneQC;
}
/**
* Returns the Done callback that is registered with the Sequence
* @param doneQC callback that will be submitted when sequence completes,
* null if there is no callback configured
*/
public Done getDone() { return fDoneQC; }
/** Sets the steps to be executed. */
public void setSteps(Step[] steps) {
assert fCurrentStepIdx == 0;
fSteps = steps;
}
/** Returns the steps to be executed. */
public Step[] getSteps() { return fSteps; }
/**
* Returns index of the step that is currently being executed.
* <br>NOTE: After sequence is invoked, this method should be called
* only in the Riverbed executor thread.
* @return
*/
public int getCurrentIdx() { return fCurrentStepIdx; }
/**
* Sets the progress monitor that will be called by the sequence with udpates.
* @param pm
*/
public void setProgressMonitor(IProgressMonitor pm) { fProgressMonitor = pm; }
/**
* Sets the task name for this sequence. To be used with progress monitor;
* @param taskName
*/
public void setTaskName(String taskName) { fTaskName = taskName; }
/**
* Sets the task name to be used with progress monitor, if this sequence needs
* to be rolled back as result of cancellation or error.
* @param taskName
*/
public void setRollBackTaskName(String n) { fRollbackTaskName = n; }
/** Submits this sequence to the executor. */
public void invokeLater() {
getExecutor().submit( new DsfRunnable() { public void run() { doInvoke(); } });
}
/**
* Submits this sequence to the Riverbed executor, and blocks waiting for the
* sequence to complete.
* <br>NOTE: This method is NOT to be called on the Riverbed executor thread.
*/
public synchronized void invoke() {
assert !fExecutor.isInExecutorThread() :
"Cannot be called on dispatch thread: " + this;
setDone(new Done() {
public void run() {
synchronized(DsfSequence.this) { DsfSequence.this.notifyAll(); }
}
});
invokeLater();
try {
wait();
} catch (InterruptedException e) {
// TODO: error handling?
}
}
private void doInvoke() {
assert fCurrentStepIdx == 0;
if (fTaskName != null) {
fProgressMonitor.subTask(fTaskName);
}
fSteps[fCurrentStepIdx].execute();
}
/**
* Cancells the execution of this sequence. The roll-back will start when
* the current step completes.
*
*/
public void cancel() {
fCancelled = true;
}
/**
* To be called only by the step implementation, Tells the sequence to
* submit the next step.
*/
public void stepFinished() {
getExecutor().submit(new DsfRunnable() { public void run() {
fProgressMonitor.worked(getSteps()[fCurrentStepIdx].getTicks());
fCurrentStepIdx++;
if (fCurrentStepIdx < fSteps.length) {
if (fCancelled) {
abort(new Status(
IStatus.CANCEL, DsfPlugin.PLUGIN_ID, -1,
"Cancelled" + fTaskName != null ? ": " + fTaskName : "",
null));
}
fSteps[fCurrentStepIdx].execute();
} else {
if (fDoneQC != null) getExecutor().submit(fDoneQC);
}
}});
}
/**
* To be called only by the step implementation. Tells the sequence to
* roll back next step.
*/
public void stepRolledBack() {
getExecutor().submit(new DsfRunnable() { public void run() {
fProgressMonitor.worked(getSteps()[fCurrentStepIdx].getTicks());
fCurrentStepIdx--;
if (fCurrentStepIdx >= 0) {
fSteps[fCurrentStepIdx].rollBack();
} else {
if (fDoneQC != null) getExecutor().submit(fDoneQC);
}
}});
}
/**
* To be called only by step implementation. Tells the sequence
* that its execution is to be aborted and it should start rolling back
* the sequence as if it was cancelled by user.
* @param error
*/
public void abort(final IStatus error) {
getExecutor().submit(new DsfRunnable() { public void run() {
if (fRollbackTaskName != null) {
fProgressMonitor.subTask(fRollbackTaskName);
}
fDoneQC.setStatus(error);
fCurrentStepIdx--;
if (fCurrentStepIdx >= 0) {
fSteps[fCurrentStepIdx].rollBack();
} else {
if (fDoneQC != null) getExecutor().submit(fDoneQC);
}
}});
}
}

View file

@ -0,0 +1,33 @@
/*******************************************************************************
* 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.dsf.concurrent;
/**
* Asynchronous method callback which returns a data object (in addition to
* the base class's getStatus().
* @param <V>
*/
public abstract class GetDataDone<V> extends Done {
/** Data object reference */
private V fData;
/**
* Sets the data object to specified value. To be called by the
* asynchronous method implementor.
* @param data Data value to set.
*/
public void setData(V data) { fData = data; }
/**
* Returns the data value, null if not set.
*/
public V getData() { return fData; }
}

View file

@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2006 Wind River Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.dd.dsf.concurrent;
import org.eclipse.core.runtime.IStatus;
/**
* Convenience extension to GetDataDone, which handles posting of the client's
* <code>Done</code> upon the completion of this <code>GetDataDone</code>.
* @param <V> Class type of data.
*/
public abstract class GetDataDoneWithClientDone<V> extends GetDataDone<V> {
private DsfExecutor fExecutor;
private Done fClientDone;
/**
* Constructor requires the Done to be posted as well as the executor to
* post it with.
*/
public GetDataDoneWithClientDone(DsfExecutor executor, Done clientDone) {
fExecutor = executor;
fClientDone = clientDone;
}
/**
* The run method checks the client done for cancellation, and this done
* for errors. It calls doRun() for the sub-class execution, and posts
* the client done when finished.
*/
public final void run() {
if (fClientDone.getStatus().getSeverity() == IStatus.CANCEL) return;
if (!getStatus().isOK()) {
fClientDone.setStatus(getStatus());
} else {
doRun();
}
fExecutor.execute(fClientDone);
}
/**
* Method to perform the actual work. It should not post the client done
* because it will be posted by this class in run().
*/
protected abstract void doRun();
}

View file

@ -0,0 +1,119 @@
/*******************************************************************************
* 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.dsf.model;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.dd.dsf.service.AbstractDsfService;
import org.eclipse.dd.dsf.service.DsfSession;
/**
* Base implementation of the DMC object. Most functionality here is centered
* around comparing DMCs, as this is a critical to make the views work
* correctly.
* @param <V> Data model data that this context is for.
*/
public class AbstractDMC<V extends IDataModelData> extends PlatformObject
implements IDataModelContext
{
private final String fSessionId;
private final String fServiceFilter;
private final IDataModelContext[] fParents;
/**
* Main constructor provides all data needed to implement the IModelContext
* interface.
*/
public AbstractDMC(String sessionId, String filter, IDataModelContext[] parents) {
fSessionId = sessionId;
fServiceFilter = filter;
fParents = parents;
}
/** Convenience constructor */
public AbstractDMC(AbstractDsfService service, IDataModelContext parent) {
this(service.getSession().getId(),
service.getServiceFilter(),
parent == null ? new IDataModelContext[] {} : new IDataModelContext[] { parent });
}
/**
* Should be used by the deriving class to compare the basic context object
* information.
* @param other the other service to compare to
* @return true if contexts are equal
*/
protected boolean baseEquals(Object other) {
if (other == null) return false;
if ( !(other.getClass().equals(getClass()))) return false;
IDataModelContext otherCtx = (IDataModelContext)other;
return getSessionId().equals(otherCtx.getSessionId()) &&
getServiceFilter().equals(otherCtx.getServiceFilter()) &&
areParentsEqual(otherCtx.getParents());
}
private boolean areParentsEqual(IDataModelContext[] otherParents) {
if ( !(fParents.length == otherParents.length) ) return false;
for (int i = 0; i < fParents.length; i++) {
if (!fParents[i].equals(otherParents[i])) {
return false;
}
}
return true;
}
protected int baseHashCode() {
int parentsHash = 0;
for (Object parent : getParents()) {
parentsHash += parent.hashCode();
}
return getSessionId().hashCode() + getServiceFilter().hashCode() + parentsHash;
}
protected String baseToString() {
StringBuffer retVal = new StringBuffer();
for (IDataModelContext parent : fParents) {
retVal.append(parent);
}
return retVal.toString();
}
public String getSessionId() { return fSessionId; }
public String getServiceFilter() { return fServiceFilter; }
public IDataModelContext[] getParents() { return fParents; }
/**
* Overrides the standard platform getAdapter to provide session-specific
* adapters.
* <p>
* ModelContext is intended to be used in views, which call the
* contexts.getAdapter() method to retrieve model-specific content and
* label providers. But since many different sessions could be active
* at the same time, each requiring different content providers, the
* standard platform <code>IAdapterManager</code> is not sufficient in
* handling adapters for the model context object. This is because
* <code>IAdapterManager</code> uses only the class of the adaptable to
* select the correct adapter factoru, while for model context, the
* session is equally important.
* @see org.eclipse.runtime.IAdapterManager
*/
public Object getAdapter(Class adapterType) {
Object retVal = null;
DsfSession session = DsfSession.getSession(fSessionId);
if (session != null) {
retVal = session.getModelAdapter(adapterType);
}
if (retVal == null) {
retVal = super.getAdapter(adapterType);
}
return retVal;
}
}

View file

@ -0,0 +1,44 @@
/*******************************************************************************
* 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.dsf.model;
/**
* Holder for utility static methods for manipulating IDataModelContext
* objects.
*/
public class DMCs {
/**
* Finds a data model context of given type among ancestors of the
* specified context.
* @param ctx DMC to search.
* @param ancestorType Class type of the desired DMC ancestor.
* @return Returns the ancestor if found, null otherwise.
*/
@SuppressWarnings("unchecked")
public static <V extends IDataModelContext> V getAncestorOfType(IDataModelContext ctx, Class<V> ancestorType) {
for (IDataModelContext parent : ctx.getParents()) {
if (parent.getClass().equals(ancestorType)) {
return (V)parent;
}
}
for (IDataModelContext parent : ctx.getParents()) {
if (parent.getClass().equals(ancestorType)) {
V ancestor = getAncestorOfType(parent, ancestorType);
if (ancestor != null) return ancestor;
}
}
return null;
}
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* 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.dsf.model;
/**
* Base implementation of the IDataModelContext interface.
* <p>
* TODO: consider merging the event interface with this class.
*/
public class DataModelEvent<V extends IDataModelContext> implements IDataModelEvent<V> {
private V fModelContext;
public DataModelEvent(V context) {
fModelContext = context;
}
public V getDMC() {
return fModelContext;
}
}

View file

@ -0,0 +1,75 @@
/*******************************************************************************
* 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.dsf.model;
import org.eclipse.core.runtime.IAdaptable;
/**
* The base class for data model objects.
* <p>
* DSF services need to return objects to clients which can be used as
* handles to track data stored in the service. Clients such as lazy-loading
* tree and table views retrieve a list of handles, then as needed, they
* retrieve the children and label information for these handles. Because of
* this pattern, services need to be able to return a set of handle objects,
* then as needed clients can retrieve data corresponding to these handles.
* The DMC object is the interface that DSF services should use
* to represent the handle objects that are to be referenced by view model.
* <p>
* <i>Note: DMC objects are meant to be immutable and thus accessible from
* any thread instead of just the services dispatch thread. This is because
* clients may need to call context objects' methods on non-dispatch thread,
* especially equals and hashCode.</i>
* <p>
* <i>Note #2: DMCs should also avoid holding references to service
* instances or other large chunks of data, because some of the clients may
* hold onto these objects for longer time than the life of the service.
* This may prevent the service from being garbage collected, possibly keeping
* a lot of resources tied up.
*
* @param <V> For each context object there is a corresponding data object
* which will contain information about that context. This template argument
* allows the clients to avoid casting the data class when retrieving data
* for a context object.
*
* @see IDataModelData
*/
public interface IDataModelContext<V extends IDataModelData> extends IAdaptable
{
/**
* Each model context object needs to track the session from which it
* originated. The session ID allows clients to choose the correct
* dispatch thread with which to access the service, and it allows the
* service to be uniquely identified among other sessions.
* @return Session ID of the service that originated the cotnext.
*/
public String getSessionId();
/**
* Returns the service filter object which can be used to uniquely identify
* a service. For most services, it's sufficient to know the service class
* and the session-id to find the service, but some services may have
* multiple instances running in the same session. For those services, this
* filter string can be used to find the correct service instance.
* @see org.osgi.framework.BundleContext#getServiceReferences
* @return
*/
public String getServiceFilter();
/**
* Returns the parent context of this context. ModelContext objects can be
* chained this way to allow methods that require context from multiple
* services to retrieve this context from a single handle that comes from
* the client.
* @return parent context of this context.
*/
public IDataModelContext[] getParents();
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* 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.dsf.model;
/**
* Data object containing information regarding a model context. Unlike the
* context object, this object does have to be accessed on the dispatch thread,
* unless other-wise noted. And it does not need to be immutable or free of
* references to the service.
* <p>
* This interface is intended primarily to allow for future development of
* a generic API to parametrize data model data.
*
*/
public interface IDataModelData {
/**
* Returns true if the data represented by this object is still valid.
* Data may become invalid if, for example the cache object backing this
* data was cleared.
*/
public boolean isValid();
}

View file

@ -0,0 +1,21 @@
/*******************************************************************************
* 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.dsf.model;
/**
* Common interface for events that signify changes in the data model.
* The sub-classes should contain specific information about the event, while
* this base class only identifies the DMC that is affected.
* @param <V> DMC that is affected by this event.
*/
public interface IDataModelEvent <V extends IDataModelContext> {
V getDMC();
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* 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.dsf.model;
import org.eclipse.dd.dsf.service.IDsfService;
/**
* Interface for Riverbed services that provide model data to clients.
* <p>
* For completeness this service interface derives from <code>IDataModelData</data>
* and has a method which allows clients to retrieve the DMC that represents the
* service data.
*/
public interface IDataModelService extends IDsfService, IDataModelData {
/**
* Returns the context representing the service in the data model. It is
* usually used in events to indicate that lists of contexts in this
* service are changed.
*/
IDataModelContext getServiceContext();
}

View file

@ -0,0 +1,26 @@
<!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>
Provides a base API and utilities for expoding data model through DSF
services.<br>
<br>
<h2>Package Specification</h2>
Practically speaking, all state data held by the DSF services makes up
the "data mode" of the service session.&nbsp; However, to make it easy
to present this data in standard debug views, as well as customizable
views, it is useful to present the data using a consisten pattern and
with a set of published APIs and utilities.&nbsp; This package aims to
provide these APIs and utilities.<br>
<h3>Development Plans</h3>
This package is a work in progress and it is missing a major
feature.&nbsp; This feature is being able to automatically parametrize
the contents of the data model in order to generically traverse it, and
to write data-driven framework for populating views with model data.<br>
<br>
</body>
</html>

View file

@ -0,0 +1,139 @@
/*******************************************************************************
* 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.dsf.service;
import java.util.Dictionary;
import java.util.Enumeration;
import org.eclipse.dd.dsf.concurrent.Done;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
/**
* Standard base implementation of the Riverbed service. This is a convinience
* class that provides the basic functionality that all Riverbed services have
* to implement.
*/
abstract public class AbstractDsfService
implements IDsfService
{
/** Reference to the session that this service belongs to. */
private DsfSession fSession;
/** Startup order number of this service. */
private int fStartupNumber;
/** Registration object for this service. */
private ServiceRegistration fRegistration;
/** Tracker for services that this service depends on. */
private DsfServicesTracker fTracker;
/** Properties that this service was registered with */
private Dictionary fProperties;
/** Properties that this service was registered with */
private String fFilter;
/**
* Only constructor, requires a reference to the session that this
* service belongs to.
* @param session
*/
public AbstractDsfService(DsfSession session) {
fSession = session;
}
public DsfExecutor getExecutor() { return fSession.getExecutor(); }
public Dictionary getProperties() { return fProperties; }
public String getServiceFilter() { return fFilter; }
public int getStartupNumber() { return fStartupNumber; }
public void initialize(Done done) {
fTracker = new DsfServicesTracker(getBundleContext(), fSession.getId());
fStartupNumber = fSession.getAndIncrementServiceStartupCounter();
getExecutor().submit(done);
}
public void shutdown(Done done) {
fTracker.dispose();
fTracker = null;
getExecutor().submit(done);
}
/** Returns the session object for this service */
public DsfSession getSession() { return fSession; }
/**
* Sub-classes should return the bundle context of the plugin, which the
* service belongs to.
*/
abstract protected BundleContext getBundleContext();
/** Returns the tracker for the services that this service depends on. */
protected DsfServicesTracker getServicesTracker() { return fTracker; }
/**
* Registers this service.
* <br> FIXME: Move registering call to default initialize()/shutdown(). Add a new
* protected method calcProperties() to get the initial list of properties.
*/
@SuppressWarnings("unchecked")
protected void register(String[] classes, Dictionary properties) {
String[] newClasses = new String[classes.length + 2];
System.arraycopy(classes, 0, newClasses, 2, classes.length);
newClasses[0] = IDsfService.class.getName();
newClasses[1] = getClass().getName();
properties.put(PROP_SESSION_ID, getSession().getId());
fProperties = properties;
fRegistration = getBundleContext().registerService(newClasses, this, properties);
fRegistration.getReference().getProperty(Constants.OBJECTCLASS);
fFilter = generateFilter(fProperties);
fProperties.put(Constants.OBJECTCLASS, fRegistration.getReference().getProperty(Constants.OBJECTCLASS));
}
private String generateFilter(Dictionary properties) {
StringBuffer filter = new StringBuffer();
filter.append("(&");
// Add the service class to the filter.
filter.append('(');
filter.append(Constants.OBJECTCLASS);
filter.append('=');
filter.append(this.getClass().getName());
filter.append(')');
for (Enumeration keys = properties.keys(); keys.hasMoreElements();) {
Object key = keys.nextElement();
filter.append('(');
filter.append(key.toString());
filter.append('=');
filter.append(properties.get(key).toString());
filter.append(')');
}
filter.append(')');
return filter.toString();
}
/**
* De-registers this service.
*
*/
protected void unregister() {
fRegistration.unregister();
}
/** Returns the registration object that was obtained when this service was registered */
protected ServiceRegistration getServiceRegistration() { return fRegistration; }
}

View file

@ -0,0 +1,45 @@
/*******************************************************************************
* 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.dsf.service;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation for service event handler methods. The name of the event
* handler method is irrelevant, only the annotation is checked.
* <p>
* Each service event handler method should have one or two parameters:
* <li>
* <br> First argument is required and it should be the event object, with
* type with the event class desired.
* <br> Second argument is optional, and it has to be of type Dictionary<String,String>.
* If this parameter is declared, the handler will be passed the properties
* dictionary of the service that submitted the event.
* </li>
* <p>
* It is expected that service event classes are hierarchical. So that if a
* handler is registered for a super-class of another event, this handler
* will be called every time one of the sub-class events is invoked.
* If a listener declares a handler for an event AND a superclass of that event,
* both handlers will be invoked when the event is dispatched.
*
* <br>TODO: Handling of second argument is not yet implemented.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DsfServiceEventHandler {
}

View file

@ -0,0 +1,163 @@
/*******************************************************************************
* 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.dsf.service;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
/**
* Convenience class to help track Riverbed services that a given
* client needs to use. This class is similar to the standard OSGI
* org.osgi.util.tracker.ServiceTracker class, with a few differences:
* <br>1. This class is assumed to be accessed by a single thread hence it
* has no synchronization built in, while OSGI ServiceTracker synchronized
* access to its data.
* <br>2. This class is primarily designed to track multiple services of
* different type (class), while OSGI ServiceTracker is designed to work with
* single class type, with optional filtering options.
* <br>3. This class uses knowledge of Riverbed sessions to help narrow down
* service references.
* <br>4. OSGI Service tracker explicitly listens to OSGI service
* startup/shutdown events and it will clear a reference to a service as
* soon as it's shut down. This class leaves it up to the client to make
* sure that it doesn't access a service once that service has been shut down.
* <p>
* That said, it might be more convenient for certain types of clients to use
* OSGI Service tracker for the additional features it provides.
*
* @see org.osgi.util.tracker.ServiceTracker
*/
public class DsfServicesTracker {
private static String getServiceFilter(String sessionId) {
return ("(" + IDsfService.PROP_SESSION_ID + "=" + sessionId + ")").intern();
}
private static class ServiceKey
{
String fClassString;
String fFilter;
public ServiceKey(String classString, String filter) {
fClassString = classString;
fFilter = filter;
}
public boolean equals(Object other) {
// I guess this doesn't have to assume fFilter can be null, but oh well.
return other instanceof ServiceKey &&
((ServiceKey)other).fClassString.equals(fClassString) &&
((fFilter == null && ((ServiceKey)other).fFilter == null) ||
(fFilter != null && fFilter.equals(((ServiceKey)other).fFilter)));
}
public int hashCode() {
return fClassString.hashCode() + (fFilter == null ? 0 : fFilter.hashCode());
}
}
private BundleContext fBundleContext;
private Map<ServiceKey,ServiceReference> fServiceReferences = new HashMap<ServiceKey,ServiceReference>();
private Map<ServiceReference,IDsfService> fServices = new HashMap<ServiceReference,IDsfService>();
private String fServiceFilter;
/**
* Only constructor.
* @param bundleContext Context of the plugin that the client lives in.
* @param sessionId The Riverbed session that this tracker will be used for.
*/
public DsfServicesTracker(BundleContext bundleContext, String sessionId) {
fBundleContext = bundleContext;
fServiceFilter = getServiceFilter(sessionId);
}
/**
* Retrieves a service reference for given service class and optional filter.
* Filter should be used if there are multiple instances of the desired service
* running within the same session.
* @param serviceClass class of the desired service
* @param custom filter to use when searching for the service, this filter will
* be used instead of the standard filter so it should also specify the desired
* session-ID
* @return OSGI service reference object to the desired service, null if not found
*/
public ServiceReference getServiceReference(Class serviceClass, String filter) {
ServiceKey key = new ServiceKey(serviceClass.getName().intern(), filter != null ? filter : fServiceFilter);
if (fServiceReferences.containsKey(key)) {
return fServiceReferences.get(key);
}
try {
ServiceReference[] references = fBundleContext.getServiceReferences(key.fClassString, key.fFilter);
assert references == null || references.length <= 1;
if (references == null || references.length == 0) {
return null;
} else {
fServiceReferences.put(key, references[0]);
return references[0];
}
} catch(InvalidSyntaxException e) {
assert false : "Invalid session ID syntax";
}
return null;
}
/**
* Convenience class to retrieve a service based on class name only.
* @param serviceClass class of the desired service
* @return instance of the desired service, null if not found
*/
public <V extends IDsfService> V getService(Class<V> serviceClass) {
return getService(serviceClass, null);
}
/**
* Retrieves the service given service class and optional filter.
* Filter should be used if there are multiple instances of the desired service
* running within the same session.
* @param serviceClass class of the desired service
* @param custom filter to use when searching for the service, this filter will
* be used instead of the standard filter so it should also specify the desired
* session-ID
* @return instance of the desired service, null if not found
*/
@SuppressWarnings("unchecked")
public <V extends IDsfService> V getService(Class<V> serviceClass, String filter) {
ServiceReference serviceRef = getServiceReference(serviceClass, filter);
if (serviceRef == null) {
return null;
} else {
if (fServices.containsKey(serviceRef)) {
return (V)fServices.get(serviceRef);
} else {
V service = (V)fBundleContext.getService(serviceRef);
fServices.put(serviceRef, service);
return service;
}
}
}
/**
* Un-gets all the serferences held by this tracker. Must be called
* to avoid leaking OSGI service references.
*/
public void dispose() {
for (Iterator itr = fServices.keySet().iterator(); itr.hasNext();) {
fBundleContext.ungetService((ServiceReference)itr.next());
itr.remove();
}
}
}

View file

@ -0,0 +1,395 @@
/*******************************************************************************
* 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.dsf.service;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dd.dsf.DsfPlugin;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
import org.eclipse.dd.dsf.concurrent.DsfRunnable;
import org.osgi.framework.Filter;
/**
* Class to manage Riverbed sessions. A Riverbed session is a way to
* associate a set of Riverbed services that are running simultaneously and
* are interacting with each other to provide a complete set of functionality.
* <p>
* Properties of a session are following:
* <br>1. Each session is associated with a single Riverbed executor, although there
* could be multiple sessions using the same executor.
* <br>2. Each session has a unique String identifier, which has to be used by
* the services belonging to this session when registering with OSGI services.
* <br>3. Each session has its set of service event listeners.
* <br>4. Start and end of each session is announced by events, which are always
* sent on that session's executor dispatch thread.
*
* @see org.eclipse.dd.dsf.concurrent.DsfExecutor
*/
public class DsfSession
{
/**
* Listener for session started events. This listener is always going to be
* called in the dispatch thread of the session's executor.
*
*/
public static interface SessionStartedListener {
/**
* Called when a new session is started. It is always called in the
* dispatch thread of the new session.
*/
public void sessionStarted(DsfSession session);
}
/**
* Listener for session ended events. This listener is always going to be
* called in the dispatch thread of the session's executor.
*/
public static interface SessionEndedListener {
/**
* Called when a session is ended. It is always called in the
* dispatch thread of the session.
*/
public void sessionEnded(DsfSession session);
}
private static int fgSessionIdCounter = 0;
private static Set<DsfSession> fgActiveSessions = Collections.synchronizedSet(new HashSet<DsfSession>());
private static List<SessionStartedListener> fSessionStartedListeners = Collections.synchronizedList(new ArrayList<SessionStartedListener>());
private static List<SessionEndedListener> fSessionEndedListeners = Collections.synchronizedList(new ArrayList<SessionEndedListener>());
/** Returns true if given session is currently active */
public static boolean isSessionActive(String sessionId) {
return getSession(sessionId) != null;
}
/** Returns a session instance for given session identifier */
public static DsfSession getSession(String sessionId) {
for (DsfSession session : fgActiveSessions) {
if (session.getId().equals(sessionId)) {
return session;
}
}
return null;
}
/**
* Registers a listener for session started events.
* Can be called on any thread.
*/
public static void addSessionStartedListener(SessionStartedListener listener) {
assert !fSessionStartedListeners.contains(listener);
fSessionStartedListeners.add(listener);
}
/**
* Un-registers a listener for session started events.
* Can be called on any thread.
*/
public static void removeSessionStartedListener(SessionStartedListener listener) {
assert fSessionStartedListeners.contains(listener);
fSessionStartedListeners.remove(listener);
}
/**
* Registers a listener for session ended events.
* Can be called on any thread.
*/
public static void addSessionEndedListener(SessionEndedListener listener) {
assert !fSessionEndedListeners.contains(listener);
fSessionEndedListeners.add(listener);
}
/**
* Un-registers a listener for session ended events.
* Can be called on any thread.
*/
public static void removeSessionEndedListener(SessionEndedListener listener) {
assert fSessionEndedListeners.contains(listener);
fSessionEndedListeners.remove(listener);
}
/**
* Starts and returns a new session instance. This method can be called on any
* thread, but the session-started listeners will be called using the session's
* executor.
* @param executor The Riverbed executor to use for this session.
* @return instance object of the new session
*/
public static DsfSession startSession(DsfExecutor executor) {
synchronized(fgActiveSessions) {
final DsfSession newSession = new DsfSession(executor, Integer.toString(fgSessionIdCounter++));
fgActiveSessions.add(newSession);
executor.submit( new DsfRunnable() { public void run() {
SessionStartedListener[] listeners = fSessionStartedListeners.toArray(
new SessionStartedListener[fSessionStartedListeners.size()]);
for (int i = 0; i < listeners.length; i++) {
listeners[i].sessionStarted(newSession);
}
}});
return newSession;
}
}
/**
* Terminates the given session. This method can be also called on any
* thread, but the session-ended listeners will be called using the session's
* executor.
* @param session session to terminate
*/
public static void endSession(final DsfSession session) {
synchronized(fgActiveSessions) {
if (!fgActiveSessions.contains(session)) {
throw new IllegalArgumentException();
}
fgActiveSessions.remove(session);
session.getExecutor().submit( new DsfRunnable() { public void run() {
SessionEndedListener[] listeners = fSessionEndedListeners.toArray(
new SessionEndedListener[fSessionEndedListeners.size()]);
for (int i = 0; i < listeners.length; i++) {
listeners[i].sessionEnded(session);
}
}});
}
}
private static class ListenerEntry {
Object fListener;
Filter fFilter;
ListenerEntry(Object listener, Filter filter) {
fListener = listener;
fFilter = filter;
}
public boolean equals(Object other) {
return other instanceof ListenerEntry && fListener.equals(((ListenerEntry)other).fListener);
}
public int hashCode() { return fListener.hashCode(); }
}
/** Session ID of this session. */
private String fId;
/** Dispatch-thread executor for this session */
private DsfExecutor fExecutor;
/** Service start-up counter for this session */
private int fServiceInstanceCounter;
/** Map of registered event listeners. */
private Map<ListenerEntry,Method[]> fListeners = new HashMap<ListenerEntry,Method[]>();
/**
* Map of registered adapters, for implementing the
* IModelContext.getAdapter() method.
* @see org.eclipse.dd.dsf.model.AbstractDMC#getAdapter
*/
private Map<Class,Object> fAdapters = Collections.synchronizedMap(new HashMap<Class,Object>());
/** Returns the ID of this session */
public String getId() { return fId; }
/** Returns the Riverbed executor of this session */
public DsfExecutor getExecutor() { return fExecutor; }
/**
* Adds a new listener for service events in this session.
* @param listener the listener that will receive service events
* @param filter optional filter to restrict the services that the
* listener will receive events from
*/
public void addServiceEventListener(Object listener, Filter filter) {
ListenerEntry entry = new ListenerEntry(listener, filter);
assert !fListeners.containsKey(entry);
fListeners.put(entry, getEventHandlerMethods(listener));
}
/**
* Removes the given listener.
* @param listener listener to remove
*/
public void removeServiceEventListener(Object listener) {
ListenerEntry entry = new ListenerEntry(listener, null);
assert fListeners.containsKey(entry);
fListeners.remove(entry);
}
/**
* Retrieves and increments the startup counter for services in this session.
* Riverbed services should retrieve this counter when they are initialized,
* and should return it through IService.getStartupNumber(). This number is then
* used to prioritize service events.
* @return current startup counter value
*/
public int getAndIncrementServiceStartupCounter() { return fServiceInstanceCounter++; }
/**
* Dispatches the given event to service event listeners. The event is submitted to
* the executor to be dispatched.
* @param event to be sent out
* @param serviceProperties properties of the service requesting the event to be dispatched
*/
public void dispatchEvent(final Object event, final Dictionary serviceProperties) {
getExecutor().submit(new DsfRunnable() { public void run() {
// TED added FIXME otherwise no way to detect!!!
try {
doDispatchEvent(event, serviceProperties);
} catch(Throwable e) { e.printStackTrace(); }
}});
}
/**
* Registers a IModelContext adapter of given type.
* @param adapterType class type to register the adapter for
* @param adapter adapter instance to register
* @see org.eclipse.dsdp.model.AbstractDMC#getAdapter
*/
public void registerModelAdapter(Class adapterType, Object adapter) {
fAdapters.put(adapterType, adapter);
}
/**
* Un-registers a IModelContext adapter of given type.
* @param adapterType adapter type to unregister
* @see org.eclipse.dsdp.model.AbstractDMC#getAdapter
*/
public void unregisterModelAdapter(Class adapterType) {
fAdapters.remove(adapterType);
}
/**
* Retrieves an adapter for given type for IModelContext.
* @param adapterType adapter type to look fors
* @return adapter object for given type, null if none is registered with the session
* @see org.eclipse.dsdp.model.AbstractDMC#getAdapter
*/
public Object getModelAdapter(Class adapterType) {
return fAdapters.get(adapterType);
}
public boolean equals(Object other) {
return other instanceof DsfSession && fId.equals(((DsfSession)other).fId);
}
public int hashCode() { return fId.hashCode(); }
private void doDispatchEvent(Object event, Dictionary serviceProperties) {
// Build a list of listeners;
SortedMap<ListenerEntry,List<Method>> listeners = new TreeMap<ListenerEntry,List<Method>>(new Comparator<ListenerEntry>() {
public int compare(ListenerEntry o1, ListenerEntry o2) {
if (o1.fListener == o2.fListener) {
return 0;
} if (o1.fListener instanceof IDsfService && !(o2.fListener instanceof IDsfService)) {
return Integer.MAX_VALUE;
} else if (o2.fListener instanceof IDsfService && !(o1.fListener instanceof IDsfService)) {
return Integer.MIN_VALUE;
} else if ( (o1.fListener instanceof IDsfService) && (o2.fListener instanceof IDsfService) ) {
return ((IDsfService)o1.fListener).getStartupNumber() - ((IDsfService)o2.fListener).getStartupNumber();
}
return 1;
};
public boolean equals(Object obj) {
return obj == this;
};
});
// Build a list of listeners and methods that are registered for this event class.
Class<?> eventClass = event.getClass();
for (Map.Entry<ListenerEntry,Method[]> entry : fListeners.entrySet()) {
if (entry.getKey().fFilter != null && !entry.getKey().fFilter.match(serviceProperties)) {
// Dispatching service doesn't match the listener's filter, skip it.
continue;
}
Method[] allMethods = entry.getValue();
List<Method> matchingMethods = new ArrayList<Method>();
for (Method method : allMethods) {
assert method.getParameterTypes().length > 0 : eventClass.getName() + "." + method.getName()
+ " signature contains zero parameters";
if ( method.getParameterTypes()[0].isAssignableFrom(eventClass) ) {
matchingMethods.add(method);
}
}
if (!matchingMethods.isEmpty()) {
listeners.put(entry.getKey(), matchingMethods);
}
}
// Call the listeners
for (Map.Entry<ListenerEntry,List<Method>> entry : listeners.entrySet()) {
for (Method method : entry.getValue()) {
try {
method.invoke(entry.getKey().fListener, new Object[] { event } );
}
catch (IllegalAccessException e) {
DsfPlugin.getDefault().getLog().log(new Status(
IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Security exception when calling a service event handler method", e));
assert false : "IServiceEventListener.ServiceHandlerMethod method not accessible, is listener declared public?";
}
catch (InvocationTargetException e) {
DsfPlugin.getDefault().getLog().log(new Status(
IStatus.ERROR, DsfPlugin.PLUGIN_ID, -1, "Invocation exception when calling a service event handler method", e));
assert false : "Exception thrown by a IServiceEventListener.ServiceHandlerMethod method";
}
}
}
}
private Method[] getEventHandlerMethods(Object listener)
{
List<Method> retVal = new ArrayList<Method>();
try {
Method[] methods = listener.getClass().getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(DsfServiceEventHandler.class)) {
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length > 2) {
throw new IllegalArgumentException("ServiceEventHandler method has incorrect number of parameters");
}
retVal.add(method);
}
}
} catch(SecurityException e) {
throw new IllegalArgumentException("No permission to access ServiceEventHandler method");
}
if (retVal.isEmpty()) {
throw new IllegalArgumentException("No methods marked with @ServiceEventHandler in listener, is listener declared public?");
}
return retVal.toArray(new Method[retVal.size()]);
}
/**
* Class to be instanciated only using startSession()
*/
private DsfSession(DsfExecutor executor, String id) {
fId = id;
fExecutor = executor;
}
}

View file

@ -0,0 +1,88 @@
/*******************************************************************************
* 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.dsf.service;
import java.util.Dictionary;
import org.eclipse.dd.dsf.concurrent.Done;
import org.eclipse.dd.dsf.concurrent.DsfExecutor;
/**
* The inteface that all Riverbed services must implement. It only privides a
* few features to help manage and identify the servies using the OSGI services
* framework.
* <p>
* Each service should register itself with OSGI services framework using
* the BundleContext.registerService() method. And each service should use the
* session ID that it is registering with as one of the service properties. If there
* is more than one instance of the service to be instanciated for a given session,
* additional properties should be used when registering the service to allow clients
* to uniquely identify the services.
* <p>
* By convention, all methods of Riverbed services can be called only on the dispatch
* thread of the Riverbed executor that is associated with the service. If a
* service exposes a method that is to be called on non-dispatch thread, it should
* be documented so.
*
* TODO: Add IStatus error code constants for common service related failures.
*
* @see org.osgi.framework.BundleContext#registerService(String[], Object, Dictionary)
*/
public interface IDsfService {
/**
* Property name for the session-id of this service. This property should be set by
* all Riverbed services when they are registered with OSGI service framework.
*/
static String PROP_SESSION_ID = "org.eclipse.dd.dsf.service.IService.session_id";
/**
* Returns the executor that should be used to call methods of this service.
* @return
*/
DsfExecutor getExecutor();
/**
* Returns a filter string that can be used to uniquely identify this
* service. This filter string should be based on the properties and class
* name, which were used to register this service.
* @see org.osgi.framework.BundleContext#getServiceReferences
*/
String getServiceFilter();
/**
* Performs initialization and registration of the given service. Implementation
* should initialize the service, so that all methods and events belonging to this
* service can be used following the initialization.
* <br>Note: Since service initializaiton should be performed by an external
* logic, if this service depends on other services, the implementaion should
* assume that these services are already present, and if they are not, the
* initializaiton should fail.
* @param done callback to be submitted when the initialization is complete
*/
void initialize(Done done);
/**
* Performs shutdown and de-registration of the given service.
* @param done callback to be submitted when shutdown is complete
*/
void shutdown(Done done);
/**
* Returns the startup order number of this service among services in the same session.
* Implementations should get this number during initialization by calling
* Session.getAndIncrementServiceStartupCounter(). This counter is used to Session
* objects to prioritize the listeners of service events.
* @return startup order number of this service
* @see org.eclipse.dd.dsf.service.DsfSession#getAndIncrementServiceStartupCounter()
*/
int getStartupNumber();
}