diff options
author | Pawel Piech | 2006-07-28 16:01:39 +0000 |
---|---|---|
committer | Pawel Piech | 2006-07-28 16:01:39 +0000 |
commit | 9d95f568dbd6b45c659909dc849eb0808c1f590b (patch) | |
tree | 0c116b49e0f88742e56a6ccfecdc45aa662318f8 | |
download | org.eclipse.cdt-9d95f568dbd6b45c659909dc849eb0808c1f590b.tar.gz org.eclipse.cdt-9d95f568dbd6b45c659909dc849eb0808c1f590b.tar.xz org.eclipse.cdt-9d95f568dbd6b45c659909dc849eb0808c1f590b.zip |
Initial checkin.
30 files changed, 2104 insertions, 0 deletions
diff --git a/plugins/org.eclipse.dd.dsf/.classpath b/plugins/org.eclipse.dd.dsf/.classpath new file mode 100644 index 00000000000..751c8f2e504 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/.classpath @@ -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> diff --git a/plugins/org.eclipse.dd.dsf/.cvsignore b/plugins/org.eclipse.dd.dsf/.cvsignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/plugins/org.eclipse.dd.dsf/.project b/plugins/org.eclipse.dd.dsf/.project new file mode 100644 index 00000000000..63ae142e1be --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/.project @@ -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> diff --git a/plugins/org.eclipse.dd.dsf/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.dd.dsf/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..93a2ad59b5e --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/.settings/org.eclipse.jdt.core.prefs @@ -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 diff --git a/plugins/org.eclipse.dd.dsf/META-INF/MANIFEST.MF b/plugins/org.eclipse.dd.dsf/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..300d22e0fe2 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/META-INF/MANIFEST.MF @@ -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 diff --git a/plugins/org.eclipse.dd.dsf/build.properties b/plugins/org.eclipse.dd.dsf/build.properties new file mode 100644 index 00000000000..e9863e281ea --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/plugins/org.eclipse.dd.dsf/plugin.xml b/plugins/org.eclipse.dd.dsf/plugin.xml new file mode 100644 index 00000000000..33ca93ceb1b --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/plugin.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.2"?> +<plugin> + +</plugin> diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java new file mode 100644 index 00000000000..a87ce47e211 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/DsfPlugin.java @@ -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; + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java new file mode 100644 index 00000000000..3bd4e996661 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DefaultDsfExecutor.java @@ -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)); + } + } +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java new file mode 100644 index 00000000000..e36dbb23cee --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/Done.java @@ -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; + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneTracker.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneTracker.java new file mode 100644 index 00000000000..3805a0e0b89 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DoneTracker.java @@ -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; } +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutor.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutor.java new file mode 100644 index 00000000000..a3243f4cbb8 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfExecutor.java @@ -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(); +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java new file mode 100644 index 00000000000..0d58590c65a --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfQuery.java @@ -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)); + } +} + diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfRunnable.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfRunnable.java new file mode 100644 index 00000000000..caf610210ae --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfRunnable.java @@ -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 { +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java new file mode 100644 index 00000000000..b8f852585b2 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/DsfSequence.java @@ -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); + } + }}); + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/GetDataDone.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/GetDataDone.java new file mode 100644 index 00000000000..3a8784d10ee --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/GetDataDone.java @@ -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; } +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/GetDataDoneWithClientDone.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/GetDataDoneWithClientDone.java new file mode 100644 index 00000000000..0affa1ff7ae --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/concurrent/GetDataDoneWithClientDone.java @@ -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(); +}
\ No newline at end of file diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/AbstractDMC.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/AbstractDMC.java new file mode 100644 index 00000000000..08e03569c18 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/AbstractDMC.java @@ -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; + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/DMCs.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/DMCs.java new file mode 100644 index 00000000000..5aa60ef4d2b --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/DMCs.java @@ -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; + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/DataModelEvent.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/DataModelEvent.java new file mode 100644 index 00000000000..7a41740ea07 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/DataModelEvent.java @@ -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; + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelContext.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelContext.java new file mode 100644 index 00000000000..362182961ab --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelContext.java @@ -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(); +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelData.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelData.java new file mode 100644 index 00000000000..5d3df186db0 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelData.java @@ -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(); +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelEvent.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelEvent.java new file mode 100644 index 00000000000..1806112cac9 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelEvent.java @@ -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(); +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelService.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelService.java new file mode 100644 index 00000000000..da587a61fe2 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/IDataModelService.java @@ -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(); +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/package.html b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/package.html new file mode 100644 index 00000000000..2ea664a1ee4 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/model/package.html @@ -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. 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. 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. 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> diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/AbstractDsfService.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/AbstractDsfService.java new file mode 100644 index 00000000000..3c4d6b95487 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/AbstractDsfService.java @@ -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; } +}
\ No newline at end of file diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfServiceEventHandler.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfServiceEventHandler.java new file mode 100644 index 00000000000..d82886fae2f --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfServiceEventHandler.java @@ -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 { + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfServicesTracker.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfServicesTracker.java new file mode 100644 index 00000000000..5821e8796a6 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfServicesTracker.java @@ -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(); + } + } + +} diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfSession.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfSession.java new file mode 100644 index 00000000000..aa273865fa9 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/DsfSession.java @@ -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; + } + +}
\ No newline at end of file diff --git a/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java new file mode 100644 index 00000000000..82e5c310e32 --- /dev/null +++ b/plugins/org.eclipse.dd.dsf/src/org/eclipse/dd/dsf/service/IDsfService.java @@ -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(); +} |