aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPawel Piech2012-01-06 20:00:46 (EST)
committerEugene Tarassov2012-01-09 17:51:32 (EST)
commit58086538560ea8e6ac5f9f9c64f9f9f1eed506f4 (patch)
treee26ebf34af61aa0247acb94ad390a03ce5268f04
parent8628e7e335020bf7cc1f5bf5dd39c6a97843464f (diff)
downloadorg.eclipse.tcf-58086538560ea8e6ac5f9f9c64f9f9f1eed506f4.zip
org.eclipse.tcf-58086538560ea8e6ac5f9f9c64f9f9f1eed506f4.tar.gz
org.eclipse.tcf-58086538560ea8e6ac5f9f9c64f9f9f1eed506f4.tar.bz2
Test framework update.
-rw-r--r--plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java7
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF3
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java229
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java2
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java115
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java460
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java83
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java207
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java104
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java2
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java4
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java146
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java43
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java348
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java37
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java136
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java24
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java24
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java24
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java28
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java29
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java159
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java629
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java162
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java422
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java16
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java314
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java2
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java38
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java100
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java5
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java58
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java46
-rw-r--r--tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java254
34 files changed, 3595 insertions, 665 deletions
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java
index bfa8ca8..99bfe77 100644
--- a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java
+++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java
@@ -21,6 +21,7 @@ import java.util.Set;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.InternalVirtualTreeModelViewer;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
@@ -426,7 +427,11 @@ public class TCFModelProxy extends AbstractModelProxy implements IModelProxy, Ru
asyncExec(new Runnable() {
boolean found;
public void run() {
- found = ((InternalTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0;
+ if (getViewer() instanceof InternalTreeModelViewer) {
+ found = ((InternalTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0;
+ } else if (getViewer() instanceof InternalVirtualTreeModelViewer) {
+ found = ((InternalVirtualTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0;
+ }
Protocol.invokeLater(new Runnable() {
public void run() {
if (disposed) return;
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF b/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF
index 48627ce..bbb6687 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF
+++ b/tests/plugins/org.eclipse.tcf.debug.test/META-INF/MANIFEST.MF
@@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.tcf.debug.ui,
org.eclipse.tcf.core,
org.junit;bundle-version="3.8.2",
- org.eclipse.debug.ui;bundle-version="3.7.0"
+ org.eclipse.debug.ui;bundle-version="3.7.0",
+ org.eclipse.cdt.debug.core;bundle-version="7.2.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.eclipse.debug.internal.ui.viewers.model
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java
index efd2294..5876824 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/AbstractTcfUITest.java
@@ -9,10 +9,10 @@ import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
import junit.framework.TestCase;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
@@ -23,25 +23,33 @@ import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.ILaunchesListener2;
import org.eclipse.debug.core.model.IDisconnect;
import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem;
import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tcf.core.TransientPeer;
+import org.eclipse.tcf.debug.test.services.BreakpointsCM;
+import org.eclipse.tcf.debug.test.services.DiagnosticsCM;
+import org.eclipse.tcf.debug.test.services.RunControlCM;
+import org.eclipse.tcf.debug.test.services.RunControlCM.ContextState;
+import org.eclipse.tcf.debug.test.services.StackTraceCM;
+import org.eclipse.tcf.debug.test.services.SymbolsCM;
import org.eclipse.tcf.debug.test.util.AggregateCallback;
import org.eclipse.tcf.debug.test.util.Callback;
+import org.eclipse.tcf.debug.test.util.Callback.ICanceledListener;
import org.eclipse.tcf.debug.test.util.DataCallback;
+import org.eclipse.tcf.debug.test.util.ICache;
import org.eclipse.tcf.debug.test.util.Query;
import org.eclipse.tcf.debug.test.util.Task;
-import org.eclipse.tcf.debug.test.util.Callback.ICanceledListener;
+import org.eclipse.tcf.debug.test.util.Transaction;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
-import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.IBreakpoints;
import org.eclipse.tcf.services.IDiagnostics;
-import org.eclipse.tcf.services.IDiagnostics.ISymbol;
import org.eclipse.tcf.services.IExpressions;
+import org.eclipse.tcf.services.IMemoryMap;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
import org.eclipse.tcf.services.IStackTrace;
@@ -56,6 +64,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
private final static int NUM_CHANNELS = 1;
+
protected IChannel[] channels;
private Query<Object> fMonitorChannelQuery;
@@ -71,15 +80,21 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
protected VariablesVirtualTreeModelViewer fRegistersViewViewer;
protected VirtualViewerUpdatesListener fRegistersViewListener;
+ protected Object fTestRunKey;
+
protected IDiagnostics diag;
protected IExpressions expr;
protected ISymbols syms;
protected IStackTrace stk;
protected IRunControl rc;
protected IBreakpoints bp;
+ protected IMemoryMap fMemoryMap;
- protected TestRunControlListener fRcListener;
-
+ protected RunControlCM fRunControlCM;
+ protected DiagnosticsCM fDiagnosticsCM;
+ protected BreakpointsCM fBreakpointsCM;
+ protected StackTraceCM fStackTraceCM;
+ protected SymbolsCM fSymbolsCM;
private static class RemotePeer extends TransientPeer {
private final ArrayList<Map<String,String>> attrs;
@@ -122,7 +137,9 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}
protected void setUp() throws Exception {
-
+
+ fTestRunKey = new Object();
+
createDebugViewViewer();
createLaunch();
@@ -168,8 +185,6 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
getRemoteServices();
- validateTestAvailable();
-
new Task<Object>() {
@Override
public Object call() throws Exception {
@@ -177,6 +192,8 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
return null;
}
}.get();
+
+ validateTestAvailable();
}
@Override
@@ -198,9 +215,6 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
closeChannels(callback);
}
}.get();
-
- // Check for listener errors at the end of tearDown.
- fRcListener.checkError();
}
protected String getDiagnosticsTestName() {
@@ -208,11 +222,19 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}
protected void setUpServiceListeners() throws Exception{
- fRcListener = new TestRunControlListener(rc);
+ fRunControlCM = new RunControlCM(rc);
+ fDiagnosticsCM = new DiagnosticsCM(diag);
+ fBreakpointsCM = new BreakpointsCM(bp);
+ fStackTraceCM = new StackTraceCM(stk, rc);
+ fSymbolsCM = new SymbolsCM(syms, fRunControlCM, fMemoryMap);
}
protected void tearDownServiceListeners() throws Exception{
- fRcListener.dispose();
+ fSymbolsCM.dispose();
+ fBreakpointsCM.dispose();
+ fStackTraceCM.dispose();
+ fRunControlCM.dispose();
+ fDiagnosticsCM.dispose();
}
private void createDebugViewViewer() {
@@ -248,13 +270,19 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}
- private void createLaunch() throws CoreException {
+ private void createLaunch() throws Exception {
ILaunchManager lManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType lcType = lManager.getLaunchConfigurationType("org.eclipse.tcf.debug.LaunchConfigurationType");
ILaunchConfigurationWorkingCopy lcWc = lcType.newInstance(null, "test");
lcWc.doSave();
+ fDebugViewListener.reset();
+ fDebugViewListener.setDelayContentUntilProxyInstall(true);
fLaunch = lcWc.launch("debug", new NullProgressMonitor());
+ fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | MODEL_PROXIES_INSTALLED | CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES_RUNNING);
Assert.assertTrue( fLaunch instanceof IDisconnect );
+
+ VirtualItem launchItem = fDebugViewListener.findElement(new Pattern[] { Pattern.compile(".*" + fLaunch.getLaunchConfiguration().getName() + ".*") } );
+ Assert.assertTrue( launchItem != null && fLaunch.equals(launchItem.getData()) );
}
private void terminateLaunch() throws DebugException, InterruptedException, ExecutionException {
@@ -299,6 +327,7 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
stk = channels[0].getRemoteService(IStackTrace.class);
rc = channels[0].getRemoteService(IRunControl.class);
bp = channels[0].getRemoteService(IBreakpoints.class);
+ fMemoryMap = channels[0].getRemoteService(IMemoryMap.class);
};
});
}
@@ -430,7 +459,11 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
}
private void validateTestAvailable() throws ExecutionException, InterruptedException {
- String[] testList = getDiagnosticsTestList();
+ String[] testList = new Transaction<String[]>() {
+ protected String[] process() throws InvalidCacheException ,ExecutionException {
+ return validate( fDiagnosticsCM.getTestList() );
+ }
+ }.get();
int i = 0;
for (; i < testList.length; i++) {
@@ -440,166 +473,16 @@ public abstract class AbstractTcfUITest extends TestCase implements IViewerUpdat
Assert.assertTrue("Required test not supported", i != testList.length);
}
- protected String[] getDiagnosticsTestList() throws ExecutionException, InterruptedException {
- assert !Protocol.isDispatchThread();
- return new Query<String[]>() {
+ protected ContextState resumeAndWaitForSuspend(final RunControlContext context, final int mode) throws InterruptedException, ExecutionException {
+ return new Transaction<ContextState>() {
@Override
- protected void execute(final DataCallback<String[]> callback) {
- diag.getTestList(new IDiagnostics.DoneGetTestList() {
- public void doneGetTestList(IToken token, Throwable error, String[] list) {
- callback.setData(list);
- callback.setError(error);
- callback.done();
- }
- });
-
+ protected ContextState process() throws InvalidCacheException, ExecutionException {
+ ICache<Object> waitCache = fRunControlCM.waitForContextSuspended(context.getID(), this);
+ validate( fRunControlCM.resume(context, this, mode, 1) );
+ validate(waitCache);
+ return validate( fRunControlCM.getState(context.getID()) );
}
}.get();
}
- protected void setBreakpoint(final String bp_id, final String location) throws InterruptedException, ExecutionException {
- new Query<Object> () {
- protected void execute(final DataCallback<Object> callback) {
- Map<String,Object> m = new HashMap<String,Object>();
- m.put(IBreakpoints.PROP_ID, bp_id);
- m.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
- m.put(IBreakpoints.PROP_LOCATION, location);
- bp.set(new Map[]{ m }, new IBreakpoints.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- callback.setError(error);
- callback.done();
- }
- });
- }
- }.get();
- }
-
- protected String startDiagnosticsTest() throws InterruptedException, ExecutionException {
- return new Query<String> () {
- protected void execute(final DataCallback<String> callback) {
- diag.runTest(getDiagnosticsTestName(), new IDiagnostics.DoneRunTest() {
- public void doneRunTest(IToken token, Throwable error, String id) {
- callback.setData(id);
- callback.setError(error);
- callback.done();
- }
- });
- }
- }.get();
- }
-
- protected RunControlContext getRunControlContext(final String contextId) throws InterruptedException, ExecutionException {
- return new Query<RunControlContext> () {
- @Override
- protected void execute(final DataCallback<RunControlContext> callback) {
- rc.getContext(contextId, new IRunControl.DoneGetContext() {
- public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext ctx) {
- callback.setData(ctx);
- callback.setError(error);
- callback.done();
- }
- });
- }
- }.get();
- }
-
- protected String getProcessIdFromRunControlContext(final RunControlContext rcContext) throws InterruptedException, ExecutionException {
- return new Task<String>() {
- @Override
- public String call() throws Exception {
- return rcContext.getProcessID();
- }
- }.get();
- }
-
- protected String getSingleThreadIdFromProcess(final String processId) throws InterruptedException, ExecutionException {
- return new Query<String> () {
- protected void execute(final DataCallback<String> callback) {
- rc.getChildren(processId, new IRunControl.DoneGetChildren() {
- public void doneGetChildren(IToken token, Exception error, String[] ids) {
- if (error != null) {
- callback.setError(error);
- }
- else if (ids == null || ids.length == 0) {
- callback.setError(new Exception("Test process has no threads"));
- }
- else if (ids.length != 1) {
- callback.setError(new Exception("Test process has too many threads"));
- }
- else {
- callback.setData(ids[0]);
- }
- callback.done();
- }
- });
- }
- }.get();
- }
-
- protected boolean getRunControlContextHasState(final RunControlContext rcContext) throws InterruptedException, ExecutionException {
- return new Task<Boolean>() {
- @Override
- public Boolean call() throws Exception {
- return rcContext.hasState();
- }
- }.get();
- }
-
- protected ISymbol getDiagnosticsSymbol(final String processId, final String testFunction) throws InterruptedException, ExecutionException {
- return new Query<ISymbol>() {
- @Override
- protected void execute(final DataCallback<ISymbol> callback) {
- diag.getSymbol(processId, testFunction, new IDiagnostics.DoneGetSymbol() {
- public void doneGetSymbol(IToken token, Throwable error, IDiagnostics.ISymbol symbol) {
- if (error != null) {
- callback.setError(error);
- }
- else if (symbol == null) {
- callback.setError(new Exception("Symbol must not be null: tcf_test_func3"));
- }
- else {
- callback.setData(symbol);
- }
- callback.done();
- }
- });
- }
- }.get();
- }
-
- protected Number getSymbolValue(final ISymbol symbol) throws InterruptedException, ExecutionException {
- return new Task<Number>() {
- @Override
- public Number call() throws Exception {
- return symbol.getValue();
- }
- }.get();
- }
-
- protected void resumeContext(final RunControlContext rcContext, final int mode) throws InterruptedException, ExecutionException {
- new Query<Object>() {
- @Override
- protected void execute(final DataCallback<Object> callback) {
- rcContext.resume(mode, 1, new IRunControl.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- callback.setError(error);
- callback.done();
- }
- });
- }
- }.get();
- }
-
- protected void resumeAndWaitForSuspend(final RunControlContext rcContext, final int mode) throws InterruptedException, ExecutionException {
- Query<String> suspendQuery = new Query<String> () {
- @Override
- protected void execute(DataCallback<String> callback) {
- fRcListener.addWaitingForSuspend(rcContext.getID(), callback);
- }
- };
- suspendQuery.invoke();
- resumeContext(rcContext, mode);
- suspendQuery.get();
- }
-
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java
index 5f6ce2d..35dca5f 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/Activator.java
@@ -9,7 +9,7 @@ import org.osgi.framework.BundleContext;
public class Activator extends AbstractUIPlugin {
// The plug-in ID
- public static final String PLUGIN_ID = "org.eclipse.tcf.debug.test"; //$NON-NLS-1$
+ public static final String PLUGIN_ID = "org.eclipse.tm.tcf.debug.test"; //$NON-NLS-1$
// The shared instance
private static Activator plugin;
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java
new file mode 100644
index 0000000..9594baa
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/BreakpointsTest.java
@@ -0,0 +1,115 @@
+package org.eclipse.tcf.debug.test;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+
+import org.eclipse.cdt.debug.core.CDIDebugModel;
+import org.eclipse.tcf.debug.test.util.Transaction;
+import org.eclipse.tcf.services.IBreakpoints;
+import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.junit.Assert;
+
+@SuppressWarnings("restriction")
+public class BreakpointsTest extends AbstractTcfUITest
+{
+ private String fTestId;
+ private RunControlContext fTestCtx;
+ private String fProcessId = "";
+ private String fThreadId = "";
+ private RunControlContext fThreadCtx;
+
+ private void createBreakpoint(final String bpId, final String testFunc) throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+ private Map<String,Object> fBp;
+
+ {
+ fBp = new TreeMap<String,Object>();
+ fBp.put(IBreakpoints.PROP_ID, bpId);
+ fBp.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
+ fBp.put(IBreakpoints.PROP_LOCATION, testFunc);
+ }
+
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ @SuppressWarnings("unchecked")
+ Map<String, Object>[] bps = (Map<String, Object>[])new Map[] { fBp };
+ validate( fBreakpointsCM.set(bps, this) );
+
+ return null;
+ }
+ }.get();
+ }
+
+ private void checkBreakpointForErrors(final String bpId, final String processId) throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ // Wait for breakpoint status event and validate it.
+ Map<String, Object> status = validate( fBreakpointsCM.getStatus(bpId) );
+ String s = (String)status.get(IBreakpoints.STATUS_ERROR);
+ if (s != null) {
+ Assert.fail("Invalid BP status: " + s);
+ }
+ @SuppressWarnings("unchecked")
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
+ if (list != null) {
+ String err = null;
+ for (Map<String,Object> map : list) {
+ String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
+ if (processId.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
+ err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
+ }
+ if (err != null) {
+ Assert.fail("Invalid BP status: " + s);
+ }
+ }
+ return null;
+ }
+ }.get();
+ }
+
+ private void startProcess() throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+ protected Object process() throws Transaction.InvalidCacheException ,ExecutionException {
+ fTestId = validate( fDiagnosticsCM.runTest(getDiagnosticsTestName(), this) );
+ fTestCtx = validate( fRunControlCM.getContext(fTestId) );
+ fProcessId = fTestCtx.getProcessID();
+ // Create the cache to listen for exceptions.
+ fRunControlCM.waitForContextException(fTestId, fTestRunKey);
+
+ if (!fProcessId.equals(fTestId)) {
+ fThreadId = fTestId;
+ } else {
+ String[] threads = validate( fRunControlCM.getChildren(fProcessId) );
+ fThreadId = threads[0];
+ }
+ fThreadCtx = validate( fRunControlCM.getContext(fThreadId) );
+
+ Assert.assertTrue("Invalid thread context", fThreadCtx.hasState());
+ return new Object();
+ };
+ }.get();
+ }
+
+
+ private void initProcessModel(String bpId, String testFunc) throws Exception {
+ createBreakpoint(bpId, testFunc);
+ fDebugViewListener.reset();
+
+ startProcess();
+ fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE | LABEL_UPDATES);
+ }
+
+ public void testCreateBreakpoint() throws Exception {
+ String bpId = "TestStepBP";
+ initProcessModel(bpId, "tcf_test_func0");
+
+
+
+ //CDIDebugModel.createFunctionBreakpoint();
+
+ checkBreakpointForErrors(bpId, fProcessId);
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java
index f370b43..7173f26 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/SampleTest.java
@@ -1,114 +1,239 @@
package org.eclipse.tcf.debug.test;
import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem;
-import org.eclipse.tcf.debug.test.util.DataCallback;
-import org.eclipse.tcf.debug.test.util.Query;
-import org.eclipse.tcf.debug.test.util.Task;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.tcf.debug.test.services.IWaitForEventCache;
+import org.eclipse.tcf.debug.test.services.RunControlCM;
+import org.eclipse.tcf.debug.test.services.RunControlCM.ContextState;
+import org.eclipse.tcf.debug.test.util.Transaction;
+import org.eclipse.tcf.debug.ui.ITCFObject;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.services.IBreakpoints;
import org.eclipse.tcf.services.IDiagnostics.ISymbol;
import org.eclipse.tcf.services.IRunControl;
import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.services.ISymbols.Symbol;
import org.junit.Assert;
@SuppressWarnings("restriction")
public class SampleTest extends AbstractTcfUITest
{
-
- private TestBreakpointsListener fBpListener;
private String fTestId;
private RunControlContext fTestCtx;
- private String fProcessId;
- private String fThreadId;
+ private String fProcessId = "";
+ private String fThreadId = "";
private RunControlContext fThreadCtx;
-
- @Override
- protected void setUpServiceListeners() throws Exception {
- super.setUpServiceListeners();
- fBpListener = new TestBreakpointsListener(bp);
- }
@Override
- protected void tearDownServiceListeners() throws Exception {
- fBpListener.dispose();
- super.tearDownServiceListeners();
+ protected void setUp() throws Exception {
+ super.setUp();
+ clearBreakpoints();
}
- private void createBreakpoint(String bpId, String testFunc) throws InterruptedException, ExecutionException {
- fBpListener.setBreakpointId(bpId);
- setBreakpoint(bpId, testFunc);
- }
-
- private void startProcess() throws InterruptedException, ExecutionException {
- fTestId = startDiagnosticsTest();
- fTestCtx = getRunControlContext(fTestId);
- fProcessId = getProcessIdFromRunControlContext(fTestCtx);
- fBpListener.setProcess(fProcessId);
- fThreadId = null;
- if (!fProcessId.equals(fTestId)) {
- fThreadId = fTestId;
- } else {
- fThreadId = getSingleThreadIdFromProcess(fProcessId);
- }
- fThreadCtx = getRunControlContext(fThreadId);
- Assert.assertTrue("Invalid thread context", getRunControlContextHasState(fThreadCtx));
+ private void clearBreakpoints() throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ // Initialize the event cache for breakpoint status
+ @SuppressWarnings("unchecked")
+ Map<String, Object>[] bps = (Map<String, Object>[])new Map[] { };
+ validate( fBreakpointsCM.set(bps, this) );
+ return null;
+ }
+ }.get();
}
- protected String getProcessNameFromRunControlContext(final RunControlContext rcContext) throws InterruptedException, ExecutionException {
- return new Task<String>() {
+ private void createBreakpoint(final String bpId, final String testFunc) throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+ private Map<String,Object> fBp;
+
+ {
+ fBp = new TreeMap<String,Object>();
+ fBp.put(IBreakpoints.PROP_ID, bpId);
+ fBp.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE);
+ fBp.put(IBreakpoints.PROP_LOCATION, testFunc);
+ }
+
@Override
- public String call() throws Exception {
- return rcContext.getName();
+ protected Object process() throws InvalidCacheException, ExecutionException {
+
+ // Initialize the event cache for breakpoint status
+// ICache<Map<String, Object>> waitStatusCache = fBreakpointsCM.waitStatusChanged(bpId, fTestRunKey);
+
+ validate( fBreakpointsCM.add(fBp, this) );
+
+// // Wait for breakpoint status event and validate it.
+// Map<String, Object> status = validate(waitStatusCache);
+// String s = (String)status.get(IBreakpoints.STATUS_ERROR);
+// if (s != null) {
+// Assert.fail("Invalid BP status: " + s);
+// }
+// @SuppressWarnings("unchecked")
+// Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
+// if (list != null) {
+// String err = null;
+// for (Map<String,Object> map : list) {
+// String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
+// if (processId.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
+// err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
+// }
+// if (err != null) {
+// Assert.fail("Invalid BP status: " + s);
+// }
+// }
+ return null;
}
}.get();
}
-
- private void runToTestEntry(String testFunc) throws InterruptedException, ExecutionException {
- final String suspended_pc = new Query<String> () {
+
+ private void checkBreakpointForErrors(final String bpId, final String processId) throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
@Override
- protected void execute(DataCallback<String> callback) {
- fRcListener.waitForSuspend(fThreadCtx, callback);
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ // Wait for breakpoint status event and validate it.
+ Map<String, Object> status = validate( fBreakpointsCM.getStatus(bpId) );
+ String s = (String)status.get(IBreakpoints.STATUS_ERROR);
+ if (s != null) {
+ Assert.fail("Invalid BP status: " + s);
+ }
+ @SuppressWarnings("unchecked")
+ Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
+ if (list != null) {
+ String err = null;
+ for (Map<String,Object> map : list) {
+ String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
+ if (processId.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
+ err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
+ }
+ if (err != null) {
+ Assert.fail("Invalid BP status: " + s);
+ }
+ }
+ return null;
+ }
+ }.get();
+ }
+
+ private void startProcess() throws InterruptedException, ExecutionException {
+ new Transaction<Object>() {
+ protected Object process() throws Transaction.InvalidCacheException ,ExecutionException {
+ fTestId = validate( fDiagnosticsCM.runTest(getDiagnosticsTestName(), this) );
+ fTestCtx = validate( fRunControlCM.getContext(fTestId) );
+ fProcessId = fTestCtx.getProcessID();
+ // Create the cache to listen for exceptions.
+ fRunControlCM.waitForContextException(fTestId, fTestRunKey);
+
+ if (!fProcessId.equals(fTestId)) {
+ fThreadId = fTestId;
+ } else {
+ String[] threads = validate( fRunControlCM.getChildren(fProcessId) );
+ fThreadId = threads[0];
+ }
+ fThreadCtx = validate( fRunControlCM.getContext(fThreadId) );
+
+ Assert.assertTrue("Invalid thread context", fThreadCtx.hasState());
+ return new Object();
+ };
+ }.get();
+ }
+
+ private boolean runToTestEntry(final String testFunc) throws InterruptedException, ExecutionException {
+ return new Transaction<Boolean>() {
+ Object fWaitForSuspendKey = new Object();
+ boolean fSuspendEventReceived = false;
+ protected Boolean process() throws Transaction.InvalidCacheException ,ExecutionException {
+ ISymbol sym_func0 = validate( fDiagnosticsCM.getSymbol(fProcessId, testFunc) );
+ String sym_func0_value = sym_func0.getValue().toString();
+ ContextState state = validate (fRunControlCM.getState(fThreadId));
+ if (state.suspended) {
+ if ( !new BigInteger(state.pc).equals(new BigInteger(sym_func0_value)) ) {
+ fSuspendEventReceived = true;
+ // We are not at test entry. Create a new suspend wait cache.
+ fWaitForSuspendKey = new Object();
+ fRunControlCM.waitForContextSuspended(fThreadId, fWaitForSuspendKey);
+ // Run to entry point.
+ validate( fRunControlCM.resume(fThreadCtx, fWaitForSuspendKey, IRunControl.RM_RESUME, 1) );
+ }
+ } else {
+ // Wait until we suspend.
+ validate( fRunControlCM.waitForContextSuspended(fThreadId, fWaitForSuspendKey) );
+ }
+
+ return fSuspendEventReceived;
}
}.get();
- ISymbol sym_func0 = getDiagnosticsSymbol(fProcessId, testFunc);
- String sym_func0_value = getSymbolValue(sym_func0).toString();
- if (!new BigInteger(sym_func0_value).equals(new BigInteger(suspended_pc))) {
- resumeAndWaitForSuspend(fThreadCtx, IRunControl.RM_RESUME);
- }
}
private void initProcessModel(String bpId, String testFunc) throws Exception {
createBreakpoint(bpId, testFunc);
-
fDebugViewListener.reset();
- fDebugViewListener.setDelayContentUntilProxyInstall(true);
- startProcess();
- fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | MODEL_PROXIES_INSTALLED | CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES_RUNNING);
+
+ ITCFObject processTCFContext = new ITCFObject() {
+ public String getID() { return fProcessId; }
+ public IChannel getChannel() { return channels[0]; }
+ };
+ ITCFObject threadTCFContext = new ITCFObject() {
+ public String getID() { return fThreadId; }
+ public IChannel getChannel() { return channels[0]; }
+ };
+
+ fDebugViewListener.addLabelUpdate(new TreePath(new Object[] { fLaunch, processTCFContext }));
+ fDebugViewListener.addLabelUpdate(new TreePath(new Object[] { fLaunch, processTCFContext, threadTCFContext }));
+
+ startProcess();
runToTestEntry(testFunc);
+
+ final String topFrameId = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) );
+ Assert.assertTrue("No stack frames" , frameIds.length != 0);
+ return frameIds[frameIds.length - 1];
+ }
+ }.get();
+
+ ITCFObject frameTCFContext = new ITCFObject() {
+ public String getID() { return topFrameId; }
+ public IChannel getChannel() { return channels[0]; }
+ };
+ fDebugViewListener.addLabelUpdate(new TreePath(new Object[] { fLaunch, processTCFContext, threadTCFContext, frameTCFContext }));
+
+ fDebugViewListener.waitTillFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE | LABEL_UPDATES);
}
public void testDebugViewContent() throws Exception {
- initProcessModel("TestStepBP", "tcf_test_func0");
-
+ String bpId = "TestStepBP";
+ initProcessModel(bpId, "tcf_test_func0");
+
VirtualItem launchItem = fDebugViewListener.findElement(new Pattern[] { Pattern.compile(".*" + fLaunch.getLaunchConfiguration().getName() + ".*") } );
Assert.assertTrue(launchItem != null);
- VirtualItem processItem = fDebugViewListener.findElement(launchItem, new Pattern[] { Pattern.compile(".*") } );
+ VirtualItem processItem = fDebugViewListener.findElement(launchItem, new Pattern[] { Pattern.compile(".*agent.*") } );
Assert.assertTrue(processItem != null);
VirtualItem threadItem = fDebugViewListener.findElement(processItem, new Pattern[] { Pattern.compile(".*" + fThreadId + ".*") } );
Assert.assertTrue(threadItem != null);
- VirtualItem frameItem = fDebugViewListener.findElement(threadItem, new Pattern[] { Pattern.compile(".*")});
+ VirtualItem frameItem = fDebugViewListener.findElement(threadItem, new Pattern[] { Pattern.compile(".*tcf_test_func0.*")});
Assert.assertTrue(frameItem != null);
- fBpListener.checkError();
+ checkBreakpointForErrors(bpId, fProcessId);
}
public void testSteppingDebugViewOnly() throws Exception {
- initProcessModel("TestStepBP", "tcf_test_func0");
+ String bpId = "TestStepBP";
+ initProcessModel(bpId, "tcf_test_func0");
// Execute step loop
String previousThreadLabel = null;
@@ -126,7 +251,7 @@ public class SampleTest extends AbstractTcfUITest
previousThreadLabel = topFrameLabel;
}
- fBpListener.checkError();
+ checkBreakpointForErrors(bpId, fProcessId);
}
public void testSteppingWithVariablesAndRegisters() throws Exception {
@@ -155,8 +280,223 @@ public class SampleTest extends AbstractTcfUITest
previousThreadLabel = topFrameLabel;
}
- fBpListener.checkError();
+ checkBreakpointForErrors("TestStepBP", fProcessId);
+ }
+
+ public void testSymbolsCMResetOnContextRemove() throws Exception {
+ createBreakpoint("TestStepBP", "tcf_test_func0");
+ startProcess();
+ runToTestEntry("tcf_test_func0");
+
+ // Retrieve the current PC for use later
+ final String pc = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ return validate(fRunControlCM.getState(fThreadId)).pc;
+ }
+ }.get();
+
+ // Find symbol by name and valide the cache.
+ final String symbolId = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ String symId = validate( fSymbolsCM.find(fProcessId, new BigInteger(pc), "tcf_test_func0") );
+ Symbol sym = validate( fSymbolsCM.getContext(symId) );
+ Assert.assertEquals(ISymbols.UPDATE_ON_MEMORY_MAP_CHANGES, sym.getUpdatePolicy());
+ return symId;
+ }
+ }.get();
+
+ // Find symbol by address and validate its context. Save address for later.
+ final Number symAddr = new Transaction<Number>() {
+ @Override
+ protected Number process() throws InvalidCacheException, ExecutionException {
+ Symbol sym = validate( fSymbolsCM.getContext(symbolId) );
+ String symId2 = validate( fSymbolsCM.findByAddr(fProcessId, sym.getAddress()) );
+ Symbol sym2 = validate( fSymbolsCM.getContext(symId2) );
+ Assert.assertEquals(sym.getAddress(), sym2.getAddress());
+ return sym.getAddress();
+ }
+ }.get();
+
+ // End test, check that all caches were reset and now return an error.
+ new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ validate( fDiagnosticsCM.cancelTest(fTestId, this) );
+ validate( fRunControlCM.waitForContextRemoved(fProcessId, this) );
+ try {
+ validate( fSymbolsCM.getContext(symbolId) );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ validate( fSymbolsCM.find(fProcessId, new BigInteger(pc), "tcf_test_func0") );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ validate( fSymbolsCM.findByAddr(fProcessId, symAddr) );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+
+ return null;
+ }
+ }.get();
+ }
+
+ public void testSymbolsCMResetOnContextStateChange() throws Exception {
+ createBreakpoint("TestStepBP", "tcf_test_func2");
+ startProcess();
+ runToTestEntry("tcf_test_func2");
+
+ // Retrieve the current PC and top frame for use later
+ final String pc = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ return validate(fRunControlCM.getState(fThreadId)).pc;
+ }
+ }.get();
+ final String topFrameId = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ String[] frameIds = validate( fStackTraceCM.getChildren(fThreadId) );
+ return frameIds[frameIds.length - 1];
+ }
+ }.get();
+
+ // Find symbol by name and valide the cache.
+ final String symbolId = new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ String symId = validate( fSymbolsCM.find(topFrameId, new BigInteger(pc), "func2_local1") );
+ Symbol sym = validate( fSymbolsCM.getContext(symId) );
+ Assert.assertEquals(ISymbols.UPDATE_ON_EXE_STATE_CHANGES, sym.getUpdatePolicy());
+ return symId;
+ }
+ }.get();
+
+ // Note: findByAddr doesn't seem to work on a local var.
+// // Find symbol by address and validate its context. Save address for later.
+// final Number symAddr = new Transaction<Number>() {
+// @Override
+// protected Number process() throws InvalidCacheException, ExecutionException {
+// Symbol sym = validate( fSymbolsCM.getContext(symbolId) );
+// String symId2 = validate( fSymbolsCM.findByAddr(topFrameId, sym.getAddress()) );
+// Symbol sym2 = validate( fSymbolsCM.getContext(symId2) );
+// Assert.assertEquals(sym.getAddress(), sym2.getAddress());
+// return sym.getAddress();
+// }
+// }.get();
+
+ // Execute a step.
+ resumeAndWaitForSuspend(fThreadCtx, IRunControl.RM_STEP_OUT);
+
+ // End test, check that all caches were reset and now return an error.
+ new Transaction<Object>() {
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ Assert.assertFalse(
+ "Expected cache to be reset",
+ fSymbolsCM.getContext(symbolId).isValid());
+ Assert.assertFalse(
+ "Expected cache to be reset",
+ fSymbolsCM.find(topFrameId, new BigInteger(pc), "func2_local1").isValid() );
+ return null;
+ }
+ }.get();
}
+ public void testRunControlCMChildrenInvalidation() throws Exception {
+ createBreakpoint("BP1", "tcf_test_func0");
+ startProcess();
+ runToTestEntry("tcf_test_func0");
+
+ // Wait for each threads to start.
+ final String[] threads = new Transaction<String[]>() {
+ List<String> fThreads = new ArrayList<String>();
+ @Override
+ protected String[] process() throws InvalidCacheException, ExecutionException {
+ IWaitForEventCache<RunControlContext[]> waitCache = fRunControlCM.waitForContextAdded(fProcessId, this);
+ validate(fRunControlCM.resume(fTestCtx, this, IRunControl.RM_RESUME, 1));
+ RunControlContext[] addedContexts = validate(waitCache);
+ for (RunControlContext addedContext : addedContexts) {
+ fThreads.add(addedContext.getID());
+ }
+ if (fThreads.size() < 4) {
+ waitCache.reset();
+ validate(waitCache);
+ }
+ // Validate children cache
+ String[] children = validate (fRunControlCM.getChildren(fProcessId));
+ Assert.assertTrue(
+ "Expected children array to contain added ids",
+ Arrays.asList(children).containsAll(fThreads));
+
+ return fThreads.toArray(new String[fThreads.size()]);
+ }
+ }.get();
+
+ // Wait for each thread to suspend, update caches
+ for (final String thread : threads) {
+ new Transaction<Object>() {
+ @Override
+ protected Object process() throws InvalidCacheException, ExecutionException {
+ RunControlCM.ContextState state = validate(fRunControlCM.getState(thread));
+ if (!state.suspended) {
+ validate( fRunControlCM.waitForContextSuspended(thread, this) );
+ }
+ String symId = validate( fSymbolsCM.find(thread, new BigInteger(state.pc), "tcf_test_func0") );
+ Number symAddr = validate( fSymbolsCM.getContext(symId) ).getAddress();
+ Assert.assertEquals("Expected thread to suspend at breakpoint address", symAddr.toString(), state.pc);
+ String[] children = validate( fRunControlCM.getChildren(thread));
+ Assert.assertEquals("Expected thread to have no children contexts", 0, children.length);
+ return null;
+ }
+ }.get();
+ }
+
+ // End test, check for remvoed events and that state caches were cleared
+ new Transaction<String>() {
+ @Override
+ protected String process() throws InvalidCacheException, ExecutionException {
+ validate( fDiagnosticsCM.cancelTest(fTestId, this) );
+ // Create wait caches
+ IWaitForEventCache<?>[] waitCaches = new IWaitForEventCache<?>[threads.length];
+ for (int i = 0; i < threads.length; i++) {
+ waitCaches[i] = fRunControlCM.waitForContextRemoved(threads[i], this);
+ }
+ validate(fRunControlCM.waitForContextRemoved(fProcessId, this));
+
+ try {
+ validate( fRunControlCM.getContext(fProcessId) );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ validate( fRunControlCM.getState(fProcessId) );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ String children[] = validate( fRunControlCM.getChildren(fProcessId) );
+ Assert.assertEquals("Expected no children", 0, children.length);
+ } catch (ExecutionException e) {}
+
+ for (String thread : threads) {
+ try {
+ validate( fRunControlCM.getContext(thread) );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ validate( fRunControlCM.getState(thread) );
+ Assert.fail("Expected error");
+ } catch (ExecutionException e) {}
+ try {
+ String children[] = validate( fRunControlCM.getChildren(fProcessId) );
+ Assert.assertEquals("Expected no children", 0, children.length);
+ } catch (ExecutionException e) {}
+ }
+
+ return null;
+ }
+ }.get();
+ }
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java
deleted file mode 100644
index d84512f..0000000
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestBreakpointsListener.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011 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.tcf.debug.test;
-
-import java.util.Collection;
-import java.util.Map;
-
-import org.eclipse.tcf.services.IBreakpoints;
-
-/**
- * Listener for breakpoint service events.
- */
-public class TestBreakpointsListener implements IBreakpoints.BreakpointsListener{
-
- final private IBreakpoints fBreakpoints;
- private Throwable fError;
- private String bp_id;
- private String process_id;
- private boolean test_done = false;
-
- public TestBreakpointsListener(IBreakpoints bp) {
- fBreakpoints = bp;
- fBreakpoints.addListener(this);
- }
-
- public void dispose() {
- test_done = true;
- fBreakpoints.removeListener(this);
- }
-
- public void setBreakpointId(String bpId) {
- bp_id = bpId;
- }
-
- public void setProcess(String processId) {
- process_id = processId;
- }
-
- public void checkError() throws Exception {
- if (fError != null) {
- throw new Exception(fError);
- }
- }
-
- public void exit(Throwable error) {
- fError = error;
- }
-
- public void breakpointStatusChanged(String id, Map<String, Object> status) {
- if (id.equals(bp_id) && process_id != null && !test_done) {
- String s = (String)status.get(IBreakpoints.STATUS_ERROR);
- if (s != null) exit(new Exception("Invalid BP status: " + s));
- Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)status.get(IBreakpoints.STATUS_INSTANCES);
- if (list == null) return;
- String err = null;
- for (Map<String,Object> map : list) {
- String ctx = (String)map.get(IBreakpoints.INSTANCE_CONTEXT);
- if (process_id.equals(ctx) && map.get(IBreakpoints.INSTANCE_ERROR) != null)
- err = (String)map.get(IBreakpoints.INSTANCE_ERROR);
- }
- if (err != null) exit(new Exception("Invalid BP status: " + err));
- }
- }
-
- public void contextAdded(Map<String, Object>[] bps) {
- }
-
- public void contextChanged(Map<String, Object>[] bps) {
- }
-
- public void contextRemoved(String[] ids) {
- }
-
-
-}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java
deleted file mode 100644
index fb5b047..0000000
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestRunControlListener.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2011 Wind River Systems, Inc. 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.tcf.debug.test;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.debug.internal.ui.actions.RunContextualLaunchAction;
-import org.eclipse.tcf.debug.test.util.DataCallback;
-import org.eclipse.tcf.protocol.IErrorReport;
-import org.eclipse.tcf.protocol.IToken;
-import org.eclipse.tcf.services.IRunControl;
-import org.eclipse.tcf.services.IRunControl.RunControlContext;
-
-/**
- * Run control service listener. Allows client to wait for events with a
- * callback.
- */
-public class TestRunControlListener implements IRunControl.RunControlListener {
-
- private IRunControl fRunControl;
- private Throwable fError;
-
- private final HashMap<String, IRunControl.RunControlContext> ctx_map = new HashMap<String,IRunControl.RunControlContext>();
- private final HashMap<String, String> fSuspendedPCs = new HashMap<String, String>();
- private Map<String, List<DataCallback<String>>> waiting_suspend = new HashMap<String, List<DataCallback<String>>>();
- private String process_id;
- private boolean test_done;
- private String test_ctx_id;
-
-
- public TestRunControlListener(IRunControl rc) {
- fRunControl = rc;
- fRunControl.addListener(this);
- }
-
- public void dispose() throws Exception {
- test_done = true;
- fRunControl.removeListener(this);
- }
-
- public void checkError() throws Exception {
- if (fError != null) {
- throw new Exception(fError);
- }
- }
-
- public void waitForSuspend(RunControlContext context, DataCallback<String> cb) {
- final String contextId = context.getID();
- if (fSuspendedPCs.containsKey(contextId)) {
- String pc = fSuspendedPCs.get(contextId);
- if (pc != null) {
- cb.setData(pc);
- cb.done();
- return;
- } else {
- addWaitingForSuspend(contextId, cb);
- }
- } else {
- getContextState(context, cb);
- }
- }
-
- private void getContextState(RunControlContext context, final DataCallback<String> cb) {
- final String contextId = context.getID();
- context.getState(new IRunControl.DoneGetState() {
- public void doneGetState(IToken token, Exception error,
- boolean suspended, String pc, String reason,
- Map<String,Object> params) {
- if (error != null) {
- exit(error);
- }
- else if (suspended) {
- fSuspendedPCs.put(contextId, pc);
- cb.setData(pc);
- cb.done();
- }
- else {
- fSuspendedPCs.put(contextId, null);
- if (cb != null) {
- addWaitingForSuspend(contextId, cb);
- }
- }
- }
- });
- }
-
- public void addWaitingForSuspend(String contextId, DataCallback<String> cb) {
- List<DataCallback<String>> waitingList = waiting_suspend.get(contextId);
- if (waitingList == null) {
- waitingList = new ArrayList<DataCallback<String>>(1);
- waiting_suspend.put(contextId, waitingList);
- }
- waitingList.add(cb);
- }
-
- private void exit(Throwable e) {
- fError = e;
- }
-
- public void contextAdded(RunControlContext[] contexts) {
- for (IRunControl.RunControlContext ctx : contexts) {
- if (ctx_map.get(ctx.getID()) != null) exit(new Error("Invalid 'contextAdded' event"));
- ctx_map.put(ctx.getID(), ctx);
- }
- }
-
- public void contextChanged(RunControlContext[] contexts) {
- for (IRunControl.RunControlContext ctx : contexts) {
- if (ctx_map.get(ctx.getID()) == null) return;
- ctx_map.put(ctx.getID(), ctx);
- }
- }
-
- public void waitSuspended(DataCallback<String> cb) {
-
- }
-
- public void contextRemoved(String[] context_ids) {
- for (String id : context_ids) {
- ctx_map.remove(id);
- if (id.equals(test_ctx_id)) {
- if (test_done) {
-// bp.set(null, new IBreakpoints.DoneCommand() {
-// public void doneCommand(IToken token, Exception error) {
-// exit(error);
-// }
-// });
- }
- else {
- exit(new Exception("Test process exited too soon"));
- }
- return;
- }
- }
- }
-
- public void contextSuspended(String contextId, String pc, String reason, Map<String, Object> params) {
- if (pc == null || "".equals(pc)) {
- RunControlContext context = ctx_map.get(contextId);
- if (context != null) {
- getContextState(context, null);
- }
- return;
- }
-
- fSuspendedPCs.put(contextId, pc);
- List<DataCallback<String>> waitingList = waiting_suspend.remove(contextId);
- if (waitingList != null) {
- for (DataCallback<String> cb : waitingList) {
- cb.setData(pc);
- cb.done();
- }
- }
- if (test_done) {
- IRunControl.RunControlContext ctx = ctx_map.get(contextId);
- if (ctx != null && process_id != null && process_id.equals(ctx.getParentID())) {
- ctx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() {
- public void doneCommand(IToken token, Exception error) {
- if (error instanceof IErrorReport) {
- int code = ((IErrorReport)error).getErrorCode();
- if (code == IErrorReport.TCF_ERROR_ALREADY_RUNNING) return;
- if (code == IErrorReport.TCF_ERROR_INV_CONTEXT) return;
- }
- if (error != null) exit(error);
- }
- });
- }
- }
- }
-
- public void contextResumed(String context) {
- fSuspendedPCs.put(context, null);
- }
-
- public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
- String[] suspended_ids)
- {
- for (String id : suspended_ids) {
- assert id != null;
- contextSuspended(id, id.equals(context) ? pc : null, null, null);
- }
- }
-
- public void containerResumed(String[] resumed_ids) {
- for (String id : resumed_ids) {
- assert id != null;
- contextResumed(id);
- }
- }
-
- public void contextException(String context, String msg) {
- exit(new Exception("Context exception: " + msg));
- }
-
-
-}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java
deleted file mode 100644
index 5b21d0d..0000000
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/TestViewerInputService.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007, 2009 IBM Corporation 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:
- * IBM Corporation - initial API and implementation
- * Wind River - Pawel Piech - NPE when closing the Variables view (Bug 213719)
- *******************************************************************************/
-package org.eclipse.tcf.debug.test;
-
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
-import org.eclipse.debug.internal.ui.viewers.model.ViewerAdapterService;
-import org.eclipse.debug.internal.ui.viewers.model.ViewerInputUpdate;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputRequestor;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate;
-
-/**
- * Copied from org.eclipse.debug.internal.ui.viewers.model.provisional
- * for customization.
- *
- * Service to compute a viewer input from a source object
- * for a given presentation context.
- */
-@SuppressWarnings("restriction")
-public class TestViewerInputService {
-
- /**
- * An input object which will yield a null input element.
- *
- * @since 3.6
- */
- public final static Object NULL_INPUT = new IViewerInputProvider() {
- public void update(IViewerInputUpdate update) {
- update.setInputElement(null);
- update.done();
- }
- };
-
- // previous update request, cancelled when a new request comes in
- private IViewerInputUpdate fPendingUpdate = null;
-
- private IViewerInputRequestor fRequestor = null;
-
- private ITreeModelViewer fViewer;
-
- private IViewerInputRequestor fProxyRequest = new IViewerInputRequestor() {
- public void viewerInputComplete(final IViewerInputUpdate update) {
- synchronized (TestViewerInputService.this) {
- fPendingUpdate = null;
- }
- fRequestor.viewerInputComplete(update);
- }
- };
-
- /**
- * Constructs a viewer input service for the given requester and presentation context.
- *
- * @param requestor client requesting viewer inputs
- * @param context context for which inputs are required
- */
- public TestViewerInputService(ITreeModelViewer viewer, IViewerInputRequestor requestor) {
- fRequestor = requestor;
- fViewer = viewer;
- }
-
- /**
- * Resolves a viewer input derived from the given source object.
- * Reports the result to the given this service's requester. A requester may be called back
- * in the same or thread, or asynchronously in a different thread. Cancels any previous
- * incomplete request from this service's requester.
- *
- * @param source source from which to derive a viewer input
- */
- public void resolveViewerInput(Object source) {
- IViewerInputProvider provdier = ViewerAdapterService.getInputProvider(source);
- synchronized (this) {
- // cancel any pending update
- if (fPendingUpdate != null) {
- fPendingUpdate.cancel();
- }
- fPendingUpdate = new ViewerInputUpdate(fViewer.getPresentationContext(), fViewer.getInput(), fProxyRequest, source);
- }
- if (provdier == null) {
- fPendingUpdate.setInputElement(source);
- fPendingUpdate.done();
- } else {
- provdier.update(fPendingUpdate);
- }
- }
-
- /**
- * Disposes this viewer input service, canceling any pending jobs.
- */
- public synchronized void dispose() {
- if (fPendingUpdate != null) {
- fPendingUpdate.cancel();
- fPendingUpdate = null;
- }
- }
-}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java
index 439a34b..f41afd0 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VariablesVirtualTreeModelViewer.java
@@ -55,7 +55,7 @@ public class VariablesVirtualTreeModelViewer extends VirtualTreeModelViewer impl
if (fActive) {
setActiveContext(fDebugContextProvider.getActiveContext());
} else {
- fInputService.resolveViewerInput(TestViewerInputService.NULL_INPUT);
+ fInputService.resolveViewerInput(ViewerInputService.NULL_INPUT);
}
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java
index 6001fd2..c175167 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/ViewerUpdatesListener.java
@@ -412,7 +412,7 @@ public class ViewerUpdatesListener
fLabelUpdatesRunning.remove(update);
fLabelUpdatesCompleted.add(update);
fLabelUpdatesCounter--;
- if (!fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) {
+ if (!update.isCanceled() && !fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) {
fRedundantUpdates.add(update);
}
notifyAll();
@@ -443,7 +443,7 @@ public class ViewerUpdatesListener
if (!fModelProxyInstalled) {
delta.accept(new IModelDeltaVisitor() {
public boolean visit(IModelDelta delta, int depth) {
- if ((delta.getFlags() & IModelDelta.EXPAND) != 0) {
+ if ((delta.getFlags() & IModelDelta.INSTALL) != 0) {
fModelProxyInstalled = true;
return false;
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java
index a06fd67..845dfd6 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/VirtualViewerUpdatesListener.java
@@ -10,15 +10,24 @@
*******************************************************************************/
package org.eclipse.tcf.debug.test;
+import java.util.AbstractSet;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
import java.util.regex.Pattern;
+import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualItem;
import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.tcf.debug.ui.ITCFObject;
/**
* Extends base listener to use virtual viewer capabilities.
*/
+@SuppressWarnings("restriction")
public class VirtualViewerUpdatesListener extends ViewerUpdatesListener {
private final VirtualTreeModelViewer fVirtualViewer;
@@ -45,6 +54,141 @@ public class VirtualViewerUpdatesListener extends ViewerUpdatesListener {
}
return item;
}
+
+ @Override
+ protected Set<TreePath> makeTreePathSet() {
+ return new MatchingSet();
+ }
-
+ private final IElementComparer fPatternComparer = new IElementComparer() {
+ public boolean equals(Object a, Object b) {
+ Pattern pattern = null;
+ Object element = null;
+ if (a instanceof Pattern) {
+ pattern = (Pattern) a;
+ element = b;
+ } else if (b instanceof Pattern) {
+ pattern = (Pattern) b;
+ element = a;
+ }
+ if (pattern != null) {
+ return elementMatches(element, pattern);
+ }
+ return false;
+ }
+
+ private boolean elementMatches(Object element, Pattern pattern) {
+ VirtualItem[] items = fVirtualViewer.findItems(element);
+ if (items.length >= 0) {
+ String[] label = (String[])items[0].getData(VirtualItem.LABEL_KEY);
+ if (label != null && label.length >= 1 && label[0] != null && pattern.matcher(label[0]).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int hashCode(Object element) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ private final IElementComparer fTCFContextComparer = new IElementComparer() {
+ public boolean equals(Object a, Object b) {
+ ITCFObject context = null;
+ Object element = null;
+ if (a instanceof ITCFObject) {
+ context = (ITCFObject) a;
+ element = b;
+ } else if (b instanceof ITCFObject) {
+ context = (ITCFObject) b;
+ element = a;
+ }
+ if (context != null) {
+ return elementMatches((ITCFObject)DebugPlugin.getAdapter(element, ITCFObject.class), context);
+ }
+ return false;
+ }
+
+ private boolean elementMatches(ITCFObject element, ITCFObject pattern) {
+ return element != null && element.getID().equals(pattern.getID());
+ }
+
+ public int hashCode(Object element) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ class MatchingSet extends AbstractSet<TreePath> {
+ List<TreePath> fList = new ArrayList<TreePath>(4);
+
+ @Override
+ public Iterator<TreePath> iterator() {
+ return fList.iterator();
+ }
+
+ @Override
+ public int size() {
+ return fList.size();
+ }
+
+ @Override
+ public boolean add(TreePath o) {
+ return fList.add(o);
+ }
+
+ @Override
+ public void clear() {
+ fList.clear();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof TreePath) {
+ return find((TreePath)o) >= 0;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof TreePath) {
+ int index = find((TreePath)o);
+ if (index >= 0) {
+ fList.remove(index);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int find(TreePath path) {
+ for (int i = 0; i < fList.size(); i++) {
+ if (matches(fList.get(i), path)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private boolean matches(TreePath patternPath, TreePath elementPath) {
+ if (patternPath.getSegmentCount() != elementPath.getSegmentCount()) {
+ return false;
+ }
+
+ for (int i = 0; i < patternPath.getSegmentCount(); i++) {
+ Object patternSegment = patternPath.getSegment(i);
+ Object elementSegment = elementPath.getSegment(i);
+ if ( fPatternComparer.equals(elementSegment, patternSegment) ) {
+ continue;
+ } else if (fTCFContextComparer.equals(elementSegment, patternSegment) ) {
+ continue;
+ } else if (patternSegment.equals(elementSegment)) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+ }
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java
new file mode 100644
index 0000000..9094834
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/AbstractCacheManager.java
@@ -0,0 +1,43 @@
+package org.eclipse.tcf.debug.test.services;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.tcf.debug.test.util.ICache;
+
+public class AbstractCacheManager {
+
+ protected Map<Key<?>, ICache<?>> fMap = new LinkedHashMap<Key<?>, ICache<?>>();
+
+ public AbstractCacheManager() {
+ super();
+ }
+
+ public void dispose() {
+ }
+
+ protected <V> V getCache(Key<V> key) {
+ @SuppressWarnings("unchecked")
+ V cache = (V)fMap.get(key);
+ return cache;
+ }
+
+ protected <V> V mapCache(Key<V> key) {
+ @SuppressWarnings("unchecked")
+ V cache = (V)fMap.get(key);
+ if (cache != null) return cache;
+ cache = key.createCache();
+ fMap.put(key, (ICache<?>)cache);
+ return cache;
+ }
+
+ protected boolean contains(Object[] elements, Object toFind) {
+ for (int i = 0; i < elements.length; i++) {
+ if (toFind.equals(elements[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java
new file mode 100644
index 0000000..3efade5
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/BreakpointsCM.java
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.services;
+
+import java.util.Map;
+
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.TokenCache;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IBreakpoints;
+
+/**
+ *
+ */
+public class BreakpointsCM extends AbstractCacheManager implements IBreakpoints.BreakpointsListener {
+
+ private IBreakpoints fService;
+
+ public BreakpointsCM(IBreakpoints service) {
+ fService = service;
+ fService.addListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ fService.removeListener(this);
+ // TODO Auto-generated method stub
+ super.dispose();
+ }
+
+ private abstract static class DoneCommandCache extends TokenCache<Object> implements IBreakpoints.DoneCommand {
+ public void doneCommand(IToken token, Exception error) {
+ set(token, null, error);
+ }
+ }
+
+ public ICache<Object> set(final Map<String,Object>[] properties, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.set(properties, this);
+ }
+ }
+ return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> add(final Map<String,Object> properties, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.add(properties, this);
+ }
+ }
+ return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> change(final Map<String,Object> properties, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.change(properties, this);
+ }
+ }
+ return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> enable(final String[] ids, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.enable(ids, this);
+ }
+ }
+ return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> disable(final String[] ids, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.disable(ids, this);
+ }
+ }
+ return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> remove(final String[] ids, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.remove(ids, this);
+ }
+ }
+ return mapCache( new CommandKey<MyCache>(MyCache.class, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ private class IDsCache extends TokenCache<String[]> implements IBreakpoints.DoneGetIDs {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getIDs(this);
+ }
+
+ public void doneGetIDs(IToken token, Exception error, String[] ids) {
+ set(token, ids, error);
+ }
+
+ public void resetIDs() {
+ // TODO: handle add/remove ids
+ if (isValid()) reset();
+ }
+ }
+
+ private class IDsCacheKey extends Key<IDsCache> {
+ public IDsCacheKey() {
+ super(IDsCache.class);
+ }
+
+ @Override IDsCache createCache() { return new IDsCache(); }
+ }
+
+ public ICache<String[]> getIDs() {
+ return mapCache( new IDsCacheKey() );
+ }
+
+ private class PropertiesCache extends TokenCache<Map<String,Object>> implements IBreakpoints.DoneGetProperties {
+ String fId;
+
+ public PropertiesCache(String id) {
+ fId = id;
+ }
+
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getProperties(fId, this);
+ }
+
+ public void doneGetProperties(IToken token, Exception error, Map<String, Object> properties) {
+ set(token, properties, error);
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ set(null, properties, null);
+ }
+
+ public void resetProperties() {
+ if (isValid()) reset();
+ }
+ }
+
+ private class PropertiesCacheKey extends IdKey<PropertiesCache> {
+ public PropertiesCacheKey(String id) {
+ super(PropertiesCache.class, id);
+ }
+
+ @Override PropertiesCache createCache() { return new PropertiesCache(fId); }
+ };
+
+ public ICache<Map<String,Object>> getProperties(String id) {
+ return mapCache( new PropertiesCacheKey(id) );
+ }
+
+ private class StatusCache extends TokenCache<Map<String,Object>> implements IBreakpoints.DoneGetStatus {
+ String fId;
+
+ public StatusCache(String id) {
+ fId = id;
+ }
+
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getStatus(fId, this);
+ }
+
+ public void doneGetStatus(IToken token, Exception error, Map<String, Object> status) {
+ set(token, status, error);
+ }
+
+ public void setStatus(Map<String, Object> status) {
+ set(null, status, null);
+ }
+ }
+
+ private class StatusCacheKey extends IdKey<StatusCache> {
+ public StatusCacheKey(String id) {
+ super(StatusCache.class, id);
+ }
+ @Override StatusCache createCache() { return new StatusCache(fId); }
+ }
+
+ public ICache<Map<String,Object>> getStatus(String id) {
+ return mapCache( new StatusCacheKey(id) );
+ }
+
+ public ICache<Map<String,Object>> getCapabilities(final String id) {
+ class MyCache extends TokenCache<Map<String,Object>> implements IBreakpoints.DoneGetCapabilities {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getCapabilities(id, this);
+ }
+ public void doneGetCapabilities(IToken token, Exception error, Map<String, Object> capabilities) {
+ set(token, capabilities, error);
+ }
+ }
+ return mapCache( new IdKey<MyCache>(MyCache.class, id) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ private class StatusChangedCache extends WaitForEventCache<Map<String, Object>> {}
+
+ public ICache<Map<String, Object>> waitStatusChanged(String id, Object clientKey) {
+ return mapCache(new IdEventKey<StatusChangedCache>(StatusChangedCache.class, id, clientKey) {
+ @Override
+ StatusChangedCache createCache() {
+ return new StatusChangedCache();
+ }
+ });
+ }
+
+ public void breakpointStatusChanged(String id, final Map<String, Object> status) {
+ StatusCache statusCache = getCache(new StatusCacheKey(id));
+ if (statusCache != null) {
+ statusCache.setStatus(status);
+ }
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( StatusChangedCache.class.equals( eventKey.getCacheClass() ) &&
+ eventKey.fId.equals(id) )
+ {
+ ((StatusChangedCache)entry.getValue()).eventReceived(status);
+ }
+ }
+ }
+ }
+
+ private void setBreakpointsProperties(Map<String, Object>[] bps) {
+ for (Map<String, Object> bp : bps) {
+ Object id = (String)bp.get(IBreakpoints.PROP_ID);
+ if (id instanceof String) {
+ PropertiesCache cache = mapCache(new PropertiesCacheKey((String)id));
+ cache.setProperties(bp);
+ }
+ }
+ }
+
+ private class ContextAddedCache extends WaitForEventCache<Map<String, Object>[]> {}
+
+ public ICache<Map<String, Object>[]> waitContextAdded(Object clientKey) {
+ return mapCache(new EventKey<ContextAddedCache>(ContextAddedCache.class, clientKey) {
+ @Override
+ ContextAddedCache createCache() {
+ return new ContextAddedCache();
+ }
+ });
+ }
+
+ public void contextAdded(Map<String, Object>[] bps) {
+ IDsCache idsCache = getCache(new IDsCacheKey());
+ if (idsCache != null && idsCache.isValid()) {
+ idsCache.resetIDs();
+ }
+
+ setBreakpointsProperties(bps);
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( ContextAddedCache.class.equals( eventKey.getCacheClass() ) ) {
+ ((ContextAddedCache)entry.getValue()).eventReceived(bps);
+ }
+ }
+ }
+ }
+
+ private class ContextChangedCache extends WaitForEventCache<Map<String, Object>[]> {}
+
+ public ICache<Map<String, Object>[]> waitContextChanged(Object clientKey) {
+ return mapCache(new EventKey<ContextChangedCache>(ContextChangedCache.class, clientKey) {
+ @Override
+ ContextChangedCache createCache() {
+ return new ContextChangedCache();
+ }
+ });
+ }
+
+ public void contextChanged(Map<String, Object>[] bps) {
+ setBreakpointsProperties(bps);
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( ContextChangedCache.class.equals( eventKey.getCacheClass() ) ) {
+ ((ContextChangedCache)entry.getValue()).eventReceived(bps);
+ }
+ }
+ }
+ }
+
+ private class ContextRemovedCache extends WaitForEventCache<String[]> {}
+
+ public ICache<String[]> waitContextRemoved(Object clientKey) {
+ return mapCache(new EventKey<ContextRemovedCache>(ContextRemovedCache.class, clientKey) {
+ @Override
+ ContextRemovedCache createCache() {
+ return new ContextRemovedCache();
+ }
+ });
+ }
+
+ public void contextRemoved(String[] ids) {
+ IDsCache idsCache = getCache(new IDsCacheKey());
+ if (idsCache != null) {
+ idsCache.resetIDs();
+ }
+
+ for (String id : ids) {
+ PropertiesCache cache = mapCache(new PropertiesCacheKey(id));
+ cache.resetProperties();
+ }
+ }
+
+
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java
new file mode 100644
index 0000000..51f00c8
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/CommandKey.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.services;
+
+
+/**
+ *
+ */
+public abstract class CommandKey<V> extends Key<V> {
+ Object fClientKey;
+
+ CommandKey(Class<V> cacheClass, Object clientKey) {
+ super(cacheClass);
+ fClientKey = clientKey;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof CommandKey<?>) {
+ return ((CommandKey<?>)obj).fClientKey.equals(fClientKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fClientKey.hashCode();
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java
new file mode 100644
index 0000000..c2a2571
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/DiagnosticsCM.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.services;
+
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.TokenCache;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IDiagnostics;
+import org.eclipse.tcf.services.IDiagnostics.ISymbol;
+
+/**
+ *
+ */
+public class DiagnosticsCM extends AbstractCacheManager{
+ private IDiagnostics fService;
+
+ public DiagnosticsCM(IDiagnostics service) {
+ fService = service;
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+
+ public ICache<String> echo(final String msg, Object clientId) {
+ class MyCache extends TokenCache<String> implements IDiagnostics.DoneEcho {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.echo(msg, this);
+ }
+ public void doneEcho(IToken token, Throwable error, String s) {
+ set(token, s, error);
+ }
+ };
+
+ return mapCache(new CommandKey<MyCache>(MyCache.class, clientId) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<String[]> getTestList() {
+ class MyCache extends TokenCache<String[]> implements IDiagnostics.DoneGetTestList {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getTestList(this);
+ }
+ public void doneGetTestList(IToken token, Throwable error, String[] list) {
+ set(token, list, error);
+ }
+ };
+
+ return mapCache(new Key<MyCache>(MyCache.class) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<String> runTest(final String name, Object clientId) {
+ class MyCache extends TokenCache<String> implements IDiagnostics.DoneRunTest {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.runTest(name, this);
+ }
+ public void doneRunTest(IToken token, Throwable error, String context_id) {
+ set(token, context_id, error);
+ }
+ };
+
+ return mapCache(new CommandKey<MyCache>(MyCache.class, clientId) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> cancelTest(final String context_id, Object clientId) {
+ class MyCache extends TokenCache<Object> implements IDiagnostics.DoneCancelTest {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.cancelTest(context_id, this);
+ }
+ public void doneCancelTest(IToken token, Throwable error) {
+ set(token, null, error);
+ }
+ };
+
+ return mapCache(new CommandKey<MyCache>(MyCache.class, clientId) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ abstract class SymbolKey<V> extends IdKey<V> {
+ String fSymbolName;
+
+ public SymbolKey(Class<V> clazz, String id, String symbolName) {
+ super(clazz, id);
+ fSymbolName = symbolName;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof SymbolKey<?>) {
+ return ((SymbolKey<?>)obj).fSymbolName.equals(fSymbolName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fSymbolName.hashCode();
+ }
+ }
+
+ public ICache<IDiagnostics.ISymbol> getSymbol(final String context_id, final String symbol_name) {
+ class MyCache extends TokenCache<IDiagnostics.ISymbol> implements IDiagnostics.DoneGetSymbol {
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getSymbol(context_id, symbol_name, this);
+ }
+ public void doneGetSymbol(IToken token, Throwable error, ISymbol symbol) {
+ set(token, symbol, error);
+ }
+ };
+
+ return mapCache(new SymbolKey<MyCache>(MyCache.class, context_id, symbol_name) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java
new file mode 100644
index 0000000..20869ff
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/EventKey.java
@@ -0,0 +1,24 @@
+package org.eclipse.tcf.debug.test.services;
+
+
+abstract class EventKey<V> extends Key<V> {
+ private Object fClientKey;
+
+ public EventKey(Class<V> eventClazz, Object clientKey) {
+ super(eventClazz);
+ fClientKey = clientKey;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof EventKey<?>) {
+ return ((EventKey<?>)obj).fClientKey.equals(fClientKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fClientKey.hashCode();
+ }
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java
new file mode 100644
index 0000000..50dd8bf
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IWaitForEventCache.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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.tcf.debug.test.services;
+
+import org.eclipse.tcf.debug.test.util.ICache;
+
+/**
+ *
+ */
+public interface IWaitForEventCache<V> extends ICache<V> {
+
+ /**
+ * Resets the event cache so that it will be called again upon the next event.
+ */
+ public void reset();
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java
new file mode 100644
index 0000000..019d7b6
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdEventKey.java
@@ -0,0 +1,24 @@
+package org.eclipse.tcf.debug.test.services;
+
+
+abstract class IdEventKey<V> extends IdKey<V> {
+ private Object fClientKey;
+
+ public IdEventKey(Class<V> eventClazz, String id, Object clientKey) {
+ super(eventClazz, id);
+ fClientKey = clientKey;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof IdEventKey<?>) {
+ return ((IdEventKey<?>)obj).fClientKey.equals(fClientKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fClientKey.hashCode();
+ }
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java
new file mode 100644
index 0000000..2d355da
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/IdKey.java
@@ -0,0 +1,28 @@
+package org.eclipse.tcf.debug.test.services;
+
+
+abstract class IdKey<V> extends Key<V> {
+ String fId;
+
+ public IdKey(Class<V> clazz, String id) {
+ super(clazz);
+ fId = id;
+ }
+
+ public String getId() {
+ return fId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof IdKey<?>) {
+ return ((IdKey<?>)obj).fId.equals(fId);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fId.hashCode();
+ }
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java
new file mode 100644
index 0000000..91aae61
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/Key.java
@@ -0,0 +1,29 @@
+package org.eclipse.tcf.debug.test.services;
+
+
+abstract class Key<V> {
+ private Class<V> fCacheClass;
+
+ public Key(Class<V> cacheClass) {
+ fCacheClass = cacheClass;
+ }
+
+ abstract V createCache();
+
+ public Class<V> getCacheClass() {
+ return fCacheClass;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Key) {
+ return ((Key<?>)obj).fCacheClass.equals(fCacheClass);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return fCacheClass.hashCode();
+ }
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java
new file mode 100644
index 0000000..56bfc28
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/ResetMap.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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.tcf.debug.test.services;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.eclipse.tcf.debug.test.util.AbstractCache;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ *
+ */
+public class ResetMap {
+
+ public static final String ANY_ID = "";
+
+ private Map<String, List<AbstractCache<?>>> fValid = new TreeMap<String, List<AbstractCache<?>>>();
+ private Map<String, Set<String>> fChildren = new TreeMap<String, Set<String>>();
+ private Map<String, String> fParents = new TreeMap<String, String>();
+ private Map<AbstractCache<?>, Set<String>> fPending = new LinkedHashMap<AbstractCache<?>, Set<String>>();
+
+ public synchronized Set<String> removePending(AbstractCache<?> cache) {
+ Set<String> pendingIds = fPending.remove(cache);
+ if (pendingIds == null) {
+ pendingIds = Collections.emptySet();
+ }
+ return pendingIds;
+ }
+
+ public synchronized void addValid(String id, AbstractCache<?> cache) {
+ assert !fPending.containsKey(cache);
+
+ List<AbstractCache<?>> list = fValid.get(id);
+ if (list == null) {
+ list = new ArrayList<AbstractCache<?>>();
+ fValid.put(id, list);
+ }
+ list.add(cache);
+ }
+
+ public synchronized void addValid(String id, String[] childrenIds, AbstractCache<?> cache) {
+ assert !fPending.containsKey(cache);
+
+ List<AbstractCache<?>> list = fValid.get(id);
+ if (list == null) {
+ list = new ArrayList<AbstractCache<?>>();
+ fValid.put(id, list);
+ }
+ list.add(cache);
+ for (String childId : childrenIds) {
+ fParents.put(childId, id);
+ }
+ }
+
+ public synchronized void addValid(List<String> ids, AbstractCache<?> cache) {
+ assert !fPending.containsKey(cache);
+
+ String id = ids.get(0);
+ List<AbstractCache<?>> list = fValid.get(id);
+ if (list == null) {
+ list = new ArrayList<AbstractCache<?>>();
+ fValid.put(id, list);
+ }
+ list.add(cache);
+
+ for (int i = 0; i < ids.size() - 1; i++) {
+ Set<String> children = fChildren.get(ids.get(i + 1));
+ if (children == null) {
+ children = new TreeSet<String>();
+ fChildren.put(ids.get(i + 1), children);
+ }
+ children.add(ids.get(i));
+ }
+ }
+
+ public synchronized List<AbstractCache<?>> getCaches(String id) {
+ List<AbstractCache<?>> list = fValid.get(id);
+ if (list == null) {
+ list = Collections.emptyList();
+ }
+ return list;
+ }
+
+ public void reset(String id) {
+ reset(id, true, true);
+ }
+
+ public void reset(String id, boolean resetChildren, boolean resetParents) {
+ assert Protocol.isDispatchThread();
+
+ // Do not call reset while holding lock to reset map. Instead collect
+ // caches to reset and reset them outside the lock.
+ List<AbstractCache<?>> anyList = Collections.emptyList();
+ List<AbstractCache<?>> idList = Collections.emptyList();
+ List<AbstractCache<?>> parentList = Collections.emptyList();
+ synchronized (this) {
+ for (Set<String> pendingIds : fPending.values()) {
+ pendingIds.add(id);
+ }
+ anyList = fValid.remove(ANY_ID);
+
+ if (resetChildren && fChildren.containsKey(id)) {
+ idList = new ArrayList<AbstractCache<?>>();
+ collectChildren(id, idList);
+ } else {
+ idList = fValid.remove(id);
+ }
+
+ if (resetParents) {
+ String parentId = fParents.remove(id);
+ if (parentId != null) {
+ parentList = fValid.remove(parentId);
+ }
+ }
+ }
+ resetList(anyList);
+ resetList(idList);
+ resetList(parentList);
+ }
+
+ private void collectChildren(String id, List<AbstractCache<?>> caches) {
+ caches.addAll( fValid.remove(id) );
+ Set<String> children = fChildren.remove(id);
+ if (children != null) {
+ for (String child : children) {
+ collectChildren(child, caches);
+ }
+ }
+ }
+
+ private void resetList(List<AbstractCache<?>> list) {
+ if (list != null) {
+ for (AbstractCache<?> cache : list) {
+ if (cache.isValid()) {
+ cache.reset();
+ }
+ }
+ }
+ }
+
+ public synchronized void addPending(AbstractCache<?> cache) {
+ fPending.put(cache, new TreeSet<String>());
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java
new file mode 100644
index 0000000..ca4b819
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/RunControlCM.java
@@ -0,0 +1,629 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.services;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+
+import org.eclipse.tcf.debug.test.util.CallbackCache;
+import org.eclipse.tcf.debug.test.util.DataCallback;
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.TokenCache;
+import org.eclipse.tcf.debug.test.util.Transaction;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.services.IRunControl.DoneCommand;
+import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tcf.services.IRunControl.RunControlListener;
+
+/**
+ *
+ */
+public class RunControlCM extends AbstractCacheManager implements RunControlListener {
+
+ private final IRunControl fService;
+ private final ResetMap fStateResetMap = new ResetMap();
+ private final ResetMap fChildrenResetMap = new ResetMap();
+ private final List<RunControlListener> fListeners = new ArrayList<RunControlListener>();
+
+ public RunControlCM(IRunControl service) {
+ fService = service;
+ fService.addListener(this);
+ }
+
+ public void dispose() {
+ fService.removeListener(this);
+ super.dispose();
+ }
+
+ public void addListener(RunControlListener listener) {
+ fListeners.add(listener);
+ }
+
+ public void removeListener(RunControlListener listener) {
+ fListeners.remove(listener);
+ }
+
+ public IRunControl getService() {
+ return fService;
+ }
+
+ public ICache<RunControlContext> getContext(final String id) {
+ class MyCache extends RunControlTokenCache<RunControlContext> implements IRunControl.DoneGetContext {
+ @Override
+ protected String getId() {
+ return id;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getContext(id, this);
+ }
+ public void doneGetContext(IToken token, Exception error, RunControlContext context) {
+ set(token, context, error);
+ }
+
+ };
+
+ return mapCache(new IdKey<MyCache>(MyCache.class, id) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ private abstract class RunControlTokenCache<V> extends TokenCache<V> {
+ abstract protected String getId();
+
+ protected void set(IToken token, V data, Throwable error) {
+ fStateResetMap.addValid(getId(), this);
+ super.set(token, data, error);
+ }
+ }
+
+ private class ChildrenCache extends TokenCache<String[]> implements IRunControl.DoneGetChildren {
+ private final String fId;
+ public ChildrenCache(String id) {
+ fId = id;
+ }
+
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getChildren(fId, this);
+ }
+ public void doneGetChildren(IToken token, Exception error, String[] child_ids) {
+ fChildrenResetMap.addValid(fId, child_ids, this);
+ set(token, child_ids, error);
+ }
+ };
+
+ private class ChildrenCacheKey extends IdKey<ChildrenCache> {
+ public ChildrenCacheKey(String id) {
+ super(ChildrenCache.class, id);
+ }
+ @Override ChildrenCache createCache() { return new ChildrenCache(fId); }
+ }
+
+ public ICache<String[]> getChildren(String id) {
+ return mapCache(new ChildrenCacheKey(id));
+ }
+
+ public static class ContextState {
+ public final boolean suspended;
+ public final String pc;
+ public final String reason;
+ public final Map<String, Object> params;
+ ContextState(boolean suspended, String pc, String reason, Map<String, Object> params) {
+ this.suspended = suspended;
+ this.pc = pc;
+ this.reason = reason;
+ this.params = params;
+ }
+
+ }
+
+ protected abstract static class ContextKey<V> extends Key<V> {
+ RunControlContext fContext;
+
+ ContextKey(Class<V> cacheClass, RunControlContext context) {
+ super(cacheClass);
+ fContext = context;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof ContextKey<?>) {
+ return ((ContextKey<?>)obj).fContext.equals(fContext);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fContext.hashCode();
+ }
+ }
+
+
+ private class ContextStateCache extends CallbackCache<ContextState> {
+
+ private class InnerContextStateCache extends TokenCache<ContextState> implements IRunControl.DoneGetState {
+ private final RunControlContext fContext;
+
+ public InnerContextStateCache(RunControlContext context) {
+ fContext = context;
+ }
+
+ public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason,
+ Map<String, Object> params) {
+ set(token, new ContextState(suspended, pc, reason, params), error);
+ }
+
+ @Override
+ protected IToken retrieveToken() {
+ return fContext.getState(this);
+ }
+ }
+
+ private final String fId;
+ private InnerContextStateCache fInnerCache;
+ public ContextStateCache(String id) {
+ fId = id;
+ }
+
+ @Override
+ protected void retrieve(DataCallback<ContextState> rm) {
+ new Transaction<ContextState>() {
+ @Override
+ protected ContextState process() throws InvalidCacheException, ExecutionException
+ {
+ RunControlContext context = validate( getContext(fId) );
+ if (fInnerCache == null || !fInnerCache.fContext.equals(context)) {
+ fInnerCache = new InnerContextStateCache(context);
+ }
+ return validate(fInnerCache);
+ }
+ }.request(rm);
+ }
+
+ @Override
+ protected void handleCompleted(ContextState data, Throwable error, boolean canceled) {
+ if (canceled) return;
+ fStateResetMap.addValid(fId, this);
+ set(data, error, true);
+ }
+
+ public void setState(ContextState state, Throwable error) {
+ fStateResetMap.addValid(fId, this);
+ set(state, error, true);
+ }
+
+ public void reset() {
+ super.reset();
+ if (fInnerCache != null) {
+ fInnerCache.reset();
+ }
+ }
+
+ }
+
+ private class ContextStateKey extends IdKey<ContextStateCache> {
+ public ContextStateKey(String id) {
+ super(ContextStateCache.class, id);
+ }
+ @Override
+ ContextStateCache createCache() {
+ return new ContextStateCache(getId());
+ }
+ }
+
+ public ICache<ContextState> getState(String id) {
+ return mapCache(new ContextStateKey(id));
+ }
+
+ protected abstract static class ContextCommandKey<V> extends ContextKey<V> {
+ Object fClientKey;
+
+ ContextCommandKey(Class<V> cacheClass, RunControlContext context, Object clientKey) {
+ super(cacheClass, context);
+ fClientKey = clientKey;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof ContextCommandKey<?>) {
+ return ((ContextCommandKey<?>)obj).fClientKey.equals(fClientKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fClientKey.hashCode();
+ }
+ }
+
+ private abstract static class DoneCommandCache extends TokenCache<Object> implements DoneCommand {
+ public void doneCommand(IToken token, Exception error) {
+ set(token, null, error);
+ }
+ }
+
+ public ICache<Object> suspend(final RunControlContext context, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return context.suspend(this);
+ }
+ }
+ return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> resume(final RunControlContext context, Object clientKey, final int mode,
+ final int count)
+ {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return context.resume(mode, count, this);
+ }
+ }
+ return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> resume(final RunControlContext context, Object clientKey, final int mode,
+ final int count, final Map<String,Object> params)
+ {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return context.resume(mode, count, params, this);
+ }
+ }
+ return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+ }
+
+ public ICache<Object> terminate(final RunControlContext context, Object clientKey) {
+ class MyCache extends DoneCommandCache {
+ @Override
+ protected IToken retrieveToken() {
+ return context.terminate(this);
+ }
+ }
+ return mapCache( new ContextCommandKey<MyCache>(MyCache.class, context, clientKey) {
+ @Override MyCache createCache() { return new MyCache(); }
+ });
+
+ }
+
+ private class WaitForContainerResumedCache extends WaitForEventCache<String[]> {}
+
+ public IWaitForEventCache<String[]> waitForContainerResumed(String id, Object clientKey) {
+ return mapCache(new IdEventKey<WaitForContainerResumedCache>(WaitForContainerResumedCache.class, id, clientKey) {
+ @Override
+ WaitForContainerResumedCache createCache() {
+ return new WaitForContainerResumedCache();
+ }
+ });
+ }
+
+ public void containerResumed(String[] context_ids) {
+ for (RunControlListener listener : fListeners) {
+ listener.containerResumed(context_ids);
+ }
+
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( WaitForContainerResumedCache.class.equals(eventKey.getCacheClass()) &&
+ contains(context_ids, eventKey.fId) )
+ {
+ ((WaitForContainerResumedCache)entry.getValue()).eventReceived(context_ids);
+ }
+ }
+ }
+ for (String id : context_ids) {
+ doContextResumed(id);
+ }
+ }
+
+ private class WaitForContainerSuspendedCache extends WaitForEventCache<String[]> {}
+
+ public IWaitForEventCache<String[]> waitForContainerSuspended(String id, Object clientKey) {
+ return mapCache(new IdEventKey<WaitForContainerSuspendedCache>(WaitForContainerSuspendedCache.class, id, clientKey) {
+ @Override
+ WaitForContainerSuspendedCache createCache() {
+ return new WaitForContainerSuspendedCache();
+ }
+ });
+ }
+
+ public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
+ String[] suspended_ids)
+ {
+ // Call client listeners first
+ for (RunControlListener listener : fListeners) {
+ listener.containerSuspended(context, pc, reason, params, suspended_ids);
+ }
+
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( WaitForContainerSuspendedCache.class.equals( eventKey.getCacheClass() ) &&
+ eventKey.fId.equals(context) )
+ {
+ ((WaitForContainerSuspendedCache)entry.getValue()).eventReceived(suspended_ids);
+ }
+ }
+ }
+
+ ContextState state = pc == null ? null : new ContextState(true, pc, reason, params);
+ doContextSuspended(context, state);
+
+ for (String id : suspended_ids) {
+ if (!id.equals(context)) {
+ doContextSuspended(id, null);
+ }
+ }
+ }
+
+ private class WaitForContextSuspendedCache extends WaitForEventCache<Object> {}
+
+ public IWaitForEventCache<Object> waitForContextSuspended(String id, Object clientKey) {
+ return mapCache(new IdEventKey<WaitForContextSuspendedCache>(WaitForContextSuspendedCache.class, id, clientKey) {
+ @Override
+ WaitForContextSuspendedCache createCache() {
+ return new WaitForContextSuspendedCache();
+ }
+ });
+ }
+
+ public void contextSuspended(String id, String pc, String reason, Map<String, Object> params) {
+ // Call client listeners first
+ for (RunControlListener listener : fListeners) {
+ listener.contextSuspended(id, pc, reason, params);
+ }
+
+ ContextState state = pc == null ? null : new ContextState(true, pc, reason, params);
+ doContextSuspended(id, state);
+ }
+
+ public void doContextSuspended(String id, ContextState state) {
+ fStateResetMap.reset(id);
+ ContextStateCache stateCache = getCache(new ContextStateKey(id));
+ if (stateCache != null) {
+ if (state != null) {
+ stateCache.setState(state, null);
+ }
+ }
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( WaitForContextSuspendedCache.class.equals( eventKey.getCacheClass() ) &&
+ eventKey.fId.equals(id) )
+ {
+ ((WaitForContextSuspendedCache)entry.getValue()).eventReceived(null);
+ }
+ }
+ }
+ }
+
+ private class WaitForContextResumedCache extends WaitForEventCache<Object> {}
+
+ public IWaitForEventCache<Object> waitForContextResumed(String id, Object clientKey) {
+ return mapCache(new IdEventKey<WaitForContextResumedCache>(WaitForContextResumedCache.class, id, clientKey) {
+ @Override
+ WaitForContextResumedCache createCache() {
+ return new WaitForContextResumedCache();
+ }
+ });
+ }
+
+ private static final ContextState RESUMED_STATE = new ContextState(false, null, null, null);
+
+ public void contextResumed(String id) {
+ for (RunControlListener listener : fListeners) {
+ listener.contextResumed(id);
+ }
+ doContextResumed(id);
+ }
+
+ private void doContextResumed(String id) {
+ for (RunControlListener listener : fListeners) {
+ listener.contextResumed(id);
+ } fStateResetMap.reset(id);
+
+ ContextStateCache stateCache = getCache(new ContextStateKey(id));
+ if (stateCache != null) {
+ stateCache.setState(RESUMED_STATE, null);
+ }
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( WaitForContextResumedCache.class.equals( eventKey.getCacheClass() ) &&
+ eventKey.fId.equals(id) )
+ {
+ ((WaitForContextResumedCache)entry.getValue()).eventReceived(null);
+ }
+ }
+ }
+ }
+
+ private class WaitForContextExceptionCache extends WaitForEventCache<String> {}
+
+ public IWaitForEventCache<String> waitForContextException(String id, Object clientKey) {
+ return mapCache(new IdEventKey<WaitForContextExceptionCache>(WaitForContextExceptionCache.class, id, clientKey) {
+ @Override
+ WaitForContextExceptionCache createCache() {
+ return new WaitForContextExceptionCache();
+ }
+ });
+ }
+
+ public void contextException(String id, String msg) {
+ fStateResetMap.reset(id);
+
+ // TODO: avoid iterating over all entries, use separate list for events.
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( WaitForContextExceptionCache.class.equals( eventKey.getCacheClass() ) &&
+ eventKey.fId.equals(id) )
+ {
+ ((WaitForContextExceptionCache)entry.getValue()).eventReceived(msg);
+ }
+ }
+ }
+ }
+
+ private abstract class ContextEventKey<V> extends IdKey<V> {
+ private Object fClientKey;
+
+ public ContextEventKey(Class<V> eventClazz, String id, Object clientKey) {
+ super(eventClazz, id);
+ fClientKey = clientKey;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof ContextEventKey<?>) {
+ return ((ContextEventKey<?>)obj).fClientKey.equals(fClientKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fClientKey.hashCode();
+ }
+ }
+
+ private class WaitForContextAddedCache extends WaitForEventCache<RunControlContext[]> {}
+
+ public IWaitForEventCache<RunControlContext[]> waitForContextAdded(String parentId, Object clientKey) {
+ return mapCache(new ContextEventKey<WaitForContextAddedCache>(WaitForContextAddedCache.class, parentId, clientKey) {
+ @Override
+ WaitForContextAddedCache createCache() {
+ return new WaitForContextAddedCache();
+ }
+ });
+ }
+
+ public void contextAdded(RunControlContext[] contexts) {
+ for (RunControlListener listener : fListeners) {
+ listener.contextAdded(contexts);
+ }
+
+ for (RunControlContext context : contexts) {
+ fStateResetMap.reset(context.getID());
+ }
+
+ Set<String> parents = new TreeSet<String>();
+ for (RunControlContext context : contexts) {
+ if (context.getParentID() != null) {
+ parents.add(context.getParentID());
+ }
+ }
+ for (String parent : parents) {
+ fChildrenResetMap.reset(parent, false, false);
+ }
+
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof ContextEventKey) {
+ ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey();
+ if ( WaitForContextAddedCache.class.equals( eventKey.getCacheClass()) &&
+ parents.contains(eventKey.getId()) )
+ {
+ ((WaitForContextAddedCache)entry.getValue()).eventReceived(contexts);
+ }
+ }
+ }
+ }
+
+ private class WaitForContextChangedCache extends WaitForEventCache<RunControlContext[]> {}
+
+ public IWaitForEventCache<RunControlContext[]> waitForContextChanged(String id, Object clientKey) {
+ return mapCache(new ContextEventKey<WaitForContextChangedCache>(WaitForContextChangedCache.class, id, clientKey) {
+ @Override
+ WaitForContextChangedCache createCache() {
+ return new WaitForContextChangedCache();
+ }
+ });
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ for (RunControlListener listener : fListeners) {
+ listener.contextChanged(contexts);
+ }
+
+ for (RunControlContext context : contexts) {
+ fStateResetMap.reset(context.getID());
+ fChildrenResetMap.reset(context.getID(), true, false);
+ }
+
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof ContextEventKey) {
+ ContextEventKey<?> eventKey = (ContextEventKey<?>)entry.getKey();
+ if ( WaitForContextChangedCache.class.equals( eventKey.getCacheClass()) &&
+ contains(contexts, eventKey.getId()) )
+ {
+ ((WaitForContextChangedCache)entry.getValue()).eventReceived(null);
+ }
+ }
+ }
+ }
+
+ private class WaitForContextRemovedCache extends WaitForEventCache<String[]> {}
+
+ public IWaitForEventCache<String[]> waitForContextRemoved(String id, Object clientKey) {
+ return mapCache(new IdEventKey<WaitForContextRemovedCache>(WaitForContextRemovedCache.class, id, clientKey) {
+ @Override
+ WaitForContextRemovedCache createCache() {
+ return new WaitForContextRemovedCache();
+ }
+ });
+ }
+
+ public void contextRemoved(String[] context_ids) {
+ // Call client listeners first
+ for (RunControlListener listener : fListeners) {
+ listener.contextRemoved(context_ids);
+ }
+
+ for (String context_id : context_ids) {
+ fChildrenResetMap.reset(context_id, false, true);
+ fStateResetMap.reset(context_id);
+ }
+
+ for (Map.Entry<Key<?>, ICache<?>> entry: fMap.entrySet()) {
+ if (entry.getKey() instanceof IdEventKey) {
+ IdEventKey<?> eventKey = (IdEventKey<?>)entry.getKey();
+ if ( WaitForContextRemovedCache.class.equals( eventKey.getCacheClass()) &&
+ contains(context_ids, eventKey.getId()) )
+ {
+ ((WaitForContextRemovedCache)entry.getValue()).eventReceived(context_ids);
+ }
+ }
+ }
+ }
+
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java
new file mode 100644
index 0000000..5f8b582
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/StackTraceCM.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.services;
+
+import java.util.Map;
+
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.TokenCache;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IRunControl;
+import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tcf.services.IStackTrace;
+import org.eclipse.tcf.services.IStackTrace.StackTraceContext;
+
+/**
+ *
+ */
+public class StackTraceCM extends AbstractCacheManager implements IRunControl.RunControlListener {
+ private IStackTrace fService;
+ private IRunControl fRunControl;
+
+ public StackTraceCM(IStackTrace service, IRunControl runControl) {
+ fService = service;
+ fRunControl = runControl;
+ fRunControl.addListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ fRunControl.removeListener(this);
+ super.dispose();
+ }
+
+ private class ChildrenCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren {
+ private final String fId;
+ public ChildrenCache(String id) {
+ fId = id;
+ }
+
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getChildren(fId, this);
+ }
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ set(token, context_ids, error);
+ }
+ public void resetChildren() {
+ if (isValid()) reset();
+ }
+ };
+
+ private class ChildrenCacheKey extends IdKey<ChildrenCache> {
+ public ChildrenCacheKey(String id) {
+ super(ChildrenCache.class, id);
+ }
+ @Override ChildrenCache createCache() { return new ChildrenCache(fId); }
+ }
+
+ public ICache<String[]> getChildren(String id) {
+ return mapCache(new ChildrenCacheKey(id));
+ }
+
+ class ContextCache extends TokenCache<StackTraceContext> implements IStackTrace.DoneGetContext {
+ private final String fId;
+
+ public ContextCache(String id) {
+ fId = id;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getContext(new String[] { fId }, this);
+ }
+ public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) {
+ StackTraceContext context = contexts != null && contexts.length > 0 ? contexts[0] : null;
+ set(token, context, error);
+ }
+ public void resetContext() {
+ if (isValid()) reset();
+ }
+ }
+
+ private class ContextCacheKey extends IdKey<ContextCache> {
+ public ContextCacheKey(String id) {
+ super(ContextCache.class, id);
+ }
+ @Override ContextCache createCache() { return new ContextCache(fId); }
+ }
+
+ public ICache<StackTraceContext>[] getContext(final String[] ids) {
+ @SuppressWarnings("unchecked")
+ ICache<StackTraceContext>[] caches = (ICache<StackTraceContext>[])new ICache[ids.length];
+ for (int i = 0; i < ids.length; i++) {
+ caches[i] = mapCache(new ContextCacheKey(ids[i]));
+ }
+ return caches;
+ }
+
+ public void contextAdded(RunControlContext[] contexts) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ for (RunControlContext context : contexts) {
+ resetRunControlContext(context.getID());
+ }
+ }
+
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ resetRunControlContext(id);
+ }
+ }
+
+ public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) {
+ resetRunControlContext(context);
+ }
+
+ public void contextResumed(String context) {
+ resetRunControlContext(context);
+ }
+
+ public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
+ String[] suspended_ids)
+ {
+ for (String id : suspended_ids) {
+ resetRunControlContext(id);
+ }
+ }
+
+ public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) {
+ resetRunControlContext(id);
+ }
+ }
+
+ public void contextException(String context, String msg) {
+ resetRunControlContext(context);
+ }
+
+ private void resetRunControlContext(String id) {
+ ChildrenCache childrenCache = getCache(new ChildrenCacheKey(id));
+ if (childrenCache != null && childrenCache.isValid() && childrenCache.getData() != null) {
+ String[] frameIds = childrenCache.getData();
+ for (String frameId : frameIds) {
+ ContextCache contextCache = getCache(new ContextCacheKey(frameId));
+ if (contextCache != null) {
+ contextCache.resetContext();
+ }
+ }
+ childrenCache.resetChildren();
+ }
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java
new file mode 100644
index 0000000..3a1ce11
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/SymbolsCM.java
@@ -0,0 +1,422 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.services;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import org.eclipse.tcf.debug.test.util.AbstractCache;
+import org.eclipse.tcf.debug.test.util.CallbackCache;
+import org.eclipse.tcf.debug.test.util.DataCallback;
+import org.eclipse.tcf.debug.test.util.ICache;
+import org.eclipse.tcf.debug.test.util.TokenCache;
+import org.eclipse.tcf.debug.test.util.Transaction;
+import org.eclipse.tcf.debug.test.util.Transaction.InvalidCacheException;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.services.IMemoryMap;
+import org.eclipse.tcf.services.IMemoryMap.MemoryMapListener;
+import org.eclipse.tcf.services.IRunControl.RunControlContext;
+import org.eclipse.tcf.services.IRunControl.RunControlListener;
+import org.eclipse.tcf.services.ISymbols;
+import org.eclipse.tcf.services.ISymbols.Symbol;
+
+/**
+ *
+ */
+public class SymbolsCM extends AbstractCacheManager {
+
+ private ResetMap fRunControlResetMap = new ResetMap();
+ private ResetMap fMemoryResetMap = new ResetMap();
+
+ private ISymbols fService;
+ private IMemoryMap fMemoryMap;
+ private RunControlCM fRunControlCM;
+
+ public SymbolsCM(ISymbols service, RunControlCM runControl, IMemoryMap memoryMap) {
+ fService = service;
+ fRunControlCM = runControl;
+ fRunControlCM.addListener(fRunControlListener);
+ fMemoryMap = memoryMap;
+ fMemoryMap.addListener(fMemoryListener);
+ }
+
+ @Override
+ public void dispose() {
+ fRunControlCM.removeListener(fRunControlListener);
+ fMemoryMap.removeListener(fMemoryListener);
+ super.dispose();
+ }
+
+ private static final List<String> ANY_ID_PARENTS = new ArrayList<String>(1);
+ {
+ ANY_ID_PARENTS.add(ResetMap.ANY_ID);
+ }
+
+ abstract private class SymbolCache<V> extends CallbackCache<V> {
+ protected final AbstractCache<V> fInner;
+ private Symbol fSymbol;
+ private List<String> fParents = new ArrayList<String>(4);
+
+ public SymbolCache(AbstractCache<V> inner) {
+ fInner = inner;
+ }
+
+ public void reset() {
+ super.reset();
+ if (fInner.isValid()) fInner.reset();
+ }
+
+ abstract protected String getSymbolId();
+
+ @Override
+ protected void retrieve(final DataCallback<V> rm) {
+ fRunControlResetMap.addPending(this);
+ fMemoryResetMap.addPending(this);
+ Transaction<V> transaction = new Transaction<V>() {
+ protected V process() throws InvalidCacheException, ExecutionException {
+ V retVal = processInner(this);
+ fSymbol = processSymbol(this);
+ fParents = processParents(this);
+ return retVal;
+ }
+ };
+ transaction.request(rm);
+ }
+
+ protected V processInner(Transaction<V> t) throws InvalidCacheException, ExecutionException {
+ return t.validate(fInner);
+ }
+
+ protected Symbol processSymbol(Transaction<V> t) throws InvalidCacheException, ExecutionException {
+ return t.validate( getContext(getSymbolId()) );
+ }
+
+ protected List<String> processParents(Transaction<V> t) throws InvalidCacheException, ExecutionException {
+ List<String> parents = new ArrayList<String>(2);
+ String rcContextId = fSymbol.getOwnerID();
+ while( rcContextId != null ) {
+ parents.add(rcContextId);
+ RunControlContext rcContext = t.validate( fRunControlCM.getContext(rcContextId) );
+ rcContextId = rcContext.getParentID();
+ }
+ return parents;
+ }
+
+ @Override
+ protected void handleCompleted(V data, Throwable error, boolean canceled) {
+ if (canceled) return;
+
+ // If we cannot retrieve the symbol's context. Reset the cache on
+ // any rc event.
+ List<String> parents = ANY_ID_PARENTS;
+ int updatePolicy = ISymbols.UPDATE_ON_EXE_STATE_CHANGES;
+ if (error == null) {
+ parents = fParents;
+ updatePolicy = fSymbol.getUpdatePolicy();
+ }
+ updateRunControlResetMap(parents, updatePolicy, data, error);
+ updateMemoryMapResetMap(parents, data, error);
+ }
+
+ private void updateRunControlResetMap(List<String> parents, int updatePolicy, V data, Throwable error) {
+ Set<String> pendingIds = fRunControlResetMap.removePending(this);
+ if (updatePolicy == ISymbols.UPDATE_ON_EXE_STATE_CHANGES) {
+ String ownerId = parents.get(0);
+ if (pendingIds.contains(ownerId) || (ResetMap.ANY_ID.equals(ownerId) && !pendingIds.isEmpty())) {
+ // Reset cache immediately after setting value.
+ set(data, error, false);
+ } else {
+ fRunControlResetMap.addValid(ownerId, this);
+ set(data, error, true);
+ }
+ }
+ }
+
+ private void updateMemoryMapResetMap(List<String> parents, V data, Throwable error) {
+ Set<String> pendingIds = fMemoryResetMap.removePending(this);
+ boolean resetPending = false;
+ if (!pendingIds.isEmpty()) {
+ if (ResetMap.ANY_ID.equals(parents.get(0))) {
+ resetPending = true;
+ } else {
+ for (String parent : parents) {
+ if (pendingIds.contains(parent)) {
+ resetPending = true;
+ }
+ }
+ }
+ }
+
+ if (resetPending) {
+ // Reset cache immediately after setting value.
+ set(data, error, false);
+ } else {
+ fMemoryResetMap.addValid(parents, this);
+ set(data, error, true);
+ }
+ }
+ }
+
+ private class ChildrenCache extends SymbolCache<String[]> {
+ public ChildrenCache(InnerChildrenCache inner) {
+ super(inner);
+ }
+
+ @Override
+ protected String getSymbolId() {
+ return ((InnerChildrenCache)fInner).fId;
+ }
+ }
+
+ private class InnerChildrenCache extends TokenCache<String[]> implements ISymbols.DoneGetChildren {
+ private final String fId;
+ public InnerChildrenCache(String id) {
+ fId = id;
+ }
+
+ @Override
+ protected IToken retrieveToken() {
+ fRunControlResetMap.addPending(this);
+ return fService.getChildren(fId, this);
+ }
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ set(token, context_ids, error);
+ }
+ };
+
+ private class ChildrenCacheKey extends IdKey<ChildrenCache> {
+ public ChildrenCacheKey(String id) {
+ super(ChildrenCache.class, id);
+ }
+ @Override ChildrenCache createCache() { return new ChildrenCache( new InnerChildrenCache(fId) ); }
+ }
+
+ public ICache<String[]> getChildren(String id) {
+ return mapCache(new ChildrenCacheKey(id));
+ }
+
+ private class ContextCache extends SymbolCache<Symbol> {
+ public ContextCache(InnerContextCache inner) {
+ super(inner);
+ }
+ @Override
+ protected String getSymbolId() {
+ return fInner.getData().getID();
+ }
+ @Override
+ protected Symbol processSymbol(Transaction<Symbol> t) throws InvalidCacheException, ExecutionException {
+ return fInner.getData();
+ }
+ }
+
+ class InnerContextCache extends TokenCache<Symbol> implements ISymbols.DoneGetContext {
+ private final String fId;
+
+ public InnerContextCache(String id) {
+ fId = id;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.getContext(fId, this);
+ }
+ public void doneGetContext(IToken token, Exception error, Symbol symbol) {
+ set(token, symbol, error);
+ }
+ public void resetContext() {
+ if (isValid()) reset();
+ }
+ }
+
+ private class ContextCacheKey extends IdKey<ContextCache> {
+ public ContextCacheKey(String id) {
+ super(ContextCache.class, id);
+ }
+ @Override ContextCache createCache() { return new ContextCache( new InnerContextCache(fId)); }
+ }
+
+ public ICache<Symbol> getContext(String id) {
+ return mapCache(new ContextCacheKey(id));
+ }
+
+ private class FindCache extends SymbolCache<String> {
+ public FindCache(InnerFindCache inner) {
+ super(inner);
+ }
+ @Override
+ protected String getSymbolId() {
+ return fInner.getData();
+ }
+ }
+
+ class InnerFindCache extends TokenCache<String> implements ISymbols.DoneFind {
+ private final String fId;
+ private final Number fIp;
+ private final String fName;
+
+ public InnerFindCache(String id, Number ip, String name) {
+ fId = id;
+ fIp = ip;
+ fName = name;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.find(fId, fIp, fName, this);
+ }
+
+ public void doneFind(IToken token, Exception error, String symbol_id) {
+ set(token, symbol_id, error);
+ }
+ }
+
+ private class FindCacheKey extends IdKey<FindCache> {
+ private final Number fIp;
+ private final String fName;
+
+ public FindCacheKey(String id, Number ip, String name) {
+ super(FindCache.class, id);
+ fIp = ip;
+ fName = name;
+ }
+ @Override FindCache createCache() { return new FindCache(new InnerFindCache(fId, fIp, fName)); }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof FindCacheKey) {
+ FindCacheKey other = (FindCacheKey)obj;
+ return fIp.equals(other.fIp) && fName.equals(other.fName);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fIp.hashCode() + fName.hashCode();
+ }
+ }
+
+ public ICache<String> find(String context_id, Number ip, String name) {
+ return mapCache(new FindCacheKey(context_id, ip, name));
+ }
+
+ private class FindByAddrCache extends SymbolCache<String> {
+
+ public FindByAddrCache(InnerFindByAddrCache inner) {
+ super(inner);
+ }
+
+ @Override
+ protected String getSymbolId() {
+ return fInner.getData();
+ }
+ }
+
+ private class InnerFindByAddrCache extends TokenCache<String> implements ISymbols.DoneFind {
+ private final String fId;
+ private final Number fAddr;
+
+ public InnerFindByAddrCache(String id, Number addr) {
+ fId = id;
+ fAddr = addr;
+ }
+ @Override
+ protected IToken retrieveToken() {
+ return fService.findByAddr(fId, fAddr, this);
+ }
+
+ public void doneFind(IToken token, Exception error, String symbol_id) {
+ set(token, symbol_id, error);
+ }
+ }
+
+ private class FindByAddrCacheKey extends IdKey<FindByAddrCache> {
+ private final Number fAddr;
+
+ public FindByAddrCacheKey(String id, Number addr) {
+ super(FindByAddrCache.class, id);
+ fAddr = addr;
+ }
+ @Override FindByAddrCache createCache() { return new FindByAddrCache(new InnerFindByAddrCache(fId, fAddr)); }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj) && obj instanceof FindByAddrCacheKey) {
+ FindByAddrCacheKey other = (FindByAddrCacheKey)obj;
+ return fAddr.equals(other.fAddr);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return super.hashCode() + fAddr.hashCode();
+ }
+ }
+
+ public ICache<String> findByAddr(String context_id, Number addr) {
+ return mapCache(new FindByAddrCacheKey(context_id, addr));
+ }
+
+ private RunControlListener fRunControlListener = new RunControlListener() {
+
+ public void contextAdded(RunControlContext[] contexts) {
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ for (RunControlContext context : contexts) {
+ resetRunControlContext(context.getID());
+ }
+ }
+
+ public void contextRemoved(String[] context_ids) {
+ for (String id : context_ids) {
+ resetRunControlContext(id);
+ fMemoryResetMap.reset(id);
+ }
+ }
+
+ public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) {
+ resetRunControlContext(context);
+ }
+
+ public void contextResumed(String context) {
+ resetRunControlContext(context);
+ }
+
+ public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
+ String[] suspended_ids)
+ {
+ for (String id : suspended_ids) {
+ resetRunControlContext(id);
+ }
+ }
+
+ public void containerResumed(String[] context_ids) {
+ for (String id : context_ids) {
+ resetRunControlContext(id);
+ }
+ }
+
+ public void contextException(String context, String msg) {
+ resetRunControlContext(context);
+ }
+ };
+
+ private void resetRunControlContext(String id) {
+ fRunControlResetMap.reset(id);
+ }
+
+ private MemoryMapListener fMemoryListener = new MemoryMapListener() {
+ public void changed(String context_id) {
+ fMemoryResetMap.reset(context_id);
+ }
+ };
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java
new file mode 100644
index 0000000..3966c58
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/services/WaitForEventCache.java
@@ -0,0 +1,16 @@
+package org.eclipse.tcf.debug.test.services;
+
+import org.eclipse.tcf.debug.test.util.AbstractCache;
+
+class WaitForEventCache<V> extends AbstractCache<V> implements IWaitForEventCache<V> {
+ @Override
+ protected void retrieve() { } // no-op - called by listener
+ @Override
+ protected void canceled() { } // no-op - no command sent
+
+ public void eventReceived(V data) {
+ if (!isValid()) {
+ set(data, null, true); // notify listeners
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java
new file mode 100644
index 0000000..29dbd81
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AbstractCache.java
@@ -0,0 +1,314 @@
+package org.eclipse.tcf.debug.test.util;
+
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, Inc. 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
+ *******************************************************************************/
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.tcf.protocol.Protocol;
+
+/**
+ * A base implementation of a general purpose cache. Sub classes must implement
+ * {@link #retrieve(DataRequestMonitor)} to fetch data from the data source.
+ * Sub-classes are also responsible for calling {@link #set(Object, IStatus)}
+ * and {@link #reset()} to manage the state of the cache in response to events
+ * from the data source.
+ * <p>
+ * This cache requires an executor to use. The executor is used to synchronize
+ * access to the cache state and data.
+ * </p>
+ * @since 2.2
+ */
+public abstract class AbstractCache<V> implements ICache<V> {
+
+ private static final Throwable INVALID_STATUS = new Throwable("Cache invalid"); //$NON-NLS-1$
+
+ private class RequestCanceledListener implements Callback.ICanceledListener {
+ public void requestCanceled(final Callback canceledRm) {
+ invokeInDispatchThread(new Runnable() {
+ public void run() {
+ handleCanceledRm(canceledRm);
+ }
+ });
+ }
+ };
+
+ private RequestCanceledListener fRequestCanceledListener = new RequestCanceledListener();
+
+ private boolean fValid;
+
+ private V fData;
+ private Throwable fError = INVALID_STATUS;
+
+ private Object fWaitingList;
+
+ /**
+ * Sub-classes should override this method to retrieve the cache data from
+ * its source. The implementation should call {@link #set(Object, IStatus)}
+ * to store the newly retrieved data when it arrives (or an error, if one
+ * occurred retrieving the data)
+ *
+ * @param rm
+ * Request monitor for completion of data retrieval.
+ */
+ abstract protected void retrieve();
+
+ protected void invokeInDispatchThread(Runnable runnable) {
+ if (Protocol.isDispatchThread()) {
+ runnable.run();
+ } else {
+ Protocol.invokeLater(runnable);
+ }
+ }
+
+ /**
+ * Called to cancel a retrieve request. This method is called when
+ * clients of the cache no longer need data that was requested. <br>
+ * Sub-classes should cancel and clean up requests to the asynchronous
+ * data source.
+ *
+ * <p>
+ * Note: Called while holding a lock to "this". No new request will start until
+ * this call returns.
+ * </p>
+ */
+ abstract protected void canceled();
+
+ public boolean isValid() {
+ return fValid;
+ }
+
+ public V getData() {
+ if (!fValid) {
+ throw new IllegalStateException("Cache is not valid. Cache data can be read only when cache is valid."); //$NON-NLS-1$
+ }
+ return fData;
+ }
+
+ public Throwable getError() {
+ if (!fValid) {
+ throw new IllegalStateException("Cache is not valid. Cache status can be read only when cache is valid."); //$NON-NLS-1$
+ }
+ return fError;
+ }
+
+ public void update(Callback rm) {
+ assert Protocol.isDispatchThread();
+
+ if (!fValid) {
+ boolean first = false;
+ synchronized (this) {
+ if (fWaitingList == null) {
+ first = true;
+ fWaitingList = rm;
+ } else if (fWaitingList instanceof Callback[]) {
+ Callback[] waitingList = (Callback[])fWaitingList;
+ int waitingListLength = waitingList.length;
+ int i;
+ for (i = 0; i < waitingListLength; i++) {
+ if (waitingList[i] == null) {
+ waitingList[i] = rm;
+ break;
+ }
+ }
+ if (i == waitingListLength) {
+ Callback[] newWaitingList = new Callback[waitingListLength + 1];
+ System.arraycopy(waitingList, 0, newWaitingList, 0, waitingListLength);
+ newWaitingList[waitingListLength] = rm;
+ fWaitingList = newWaitingList;
+ }
+ } else {
+ Callback[] newWaitingList = new Callback[2];
+ newWaitingList[0] = (Callback)fWaitingList;
+ newWaitingList[1] = rm;
+ fWaitingList = newWaitingList;
+ }
+ }
+ rm.addCancelListener(fRequestCanceledListener);
+ if (first) {
+ retrieve();
+ }
+ } else {
+ rm.setError(fError);
+ rm.done();
+ }
+ }
+
+ private void completeWaitingRms() {
+ Object waiting = null;
+ synchronized(this) {
+ waiting = fWaitingList;
+ fWaitingList = null;
+ }
+ if (waiting != null) {
+ if (waiting instanceof Callback) {
+ completeWaitingRm((Callback)waiting);
+ } else if (waiting instanceof Callback[]) {
+ Callback[] waitingList = (Callback[])waiting;
+ for (int i = 0; i < waitingList.length; i++) {
+ if (waitingList[i] != null) {
+ completeWaitingRm(waitingList[i]);
+ }
+ }
+ }
+ waiting = null;
+ }
+ }
+
+ private void completeWaitingRm(Callback rm) {
+ rm.setError(fError);
+ rm.removeCancelListener(fRequestCanceledListener);
+ rm.done();
+ }
+
+ private void handleCanceledRm(final Callback rm) {
+
+ boolean found = false;
+ boolean waiting = false;
+ synchronized (this) {
+ if (rm.equals(fWaitingList)) {
+ found = true;
+ waiting = false;
+ fWaitingList = null;
+ } else if(fWaitingList instanceof Callback[]) {
+ Callback[] waitingList = (Callback[])fWaitingList;
+ for (int i = 0; i < waitingList.length; i++) {
+ if (!found && rm.equals(waitingList[i])) {
+ waitingList[i] = null;
+ found = true;
+ }
+ waiting = waiting || waitingList[i] != null;
+ }
+ }
+ if (found && !waiting) {
+ canceled();
+ }
+ }
+
+ // If we have no clients waiting anymore, cancel the request
+ if (found) {
+ // We no longer need to listen to cancellations.
+ rm.removeCancelListener(fRequestCanceledListener);
+ rm.setError(new CancellationException());
+ rm.done();
+ }
+
+ }
+
+ /**
+ * Returns true if there are no clients waiting for this cache or if the
+ * clients that are waiting, have already canceled their requests.
+ * <p>
+ * Note: Calling this method may cause the client request monitors that were
+ * canceled to be completed with a cancel status. If all the client request
+ * monitors were canceled, this method will also cause the {@link #canceled()}
+ * method to be called. Both of these side effects will only happen
+ * asynchronously after <code>isCanceled()</code> returns.
+ * </p>
+ *
+ * @return <code>true</code> if all clients waiting on this cache have been
+ * canceled, or if there are no clients waiting at all.
+ */
+ protected boolean isCanceled() {
+ boolean canceled;
+ List<Callback> canceledRms = null;
+ synchronized (this) {
+ if (fWaitingList instanceof Callback) {
+ if ( ((Callback)fWaitingList).isCanceled() ) {
+ canceledRms = new ArrayList<Callback>(1);
+ canceledRms.add((Callback)fWaitingList);
+ canceled = true;
+ } else {
+ canceled = false;
+ }
+ } else if(fWaitingList instanceof Callback[]) {
+ canceled = true;
+ Callback[] waitingList = (Callback[])fWaitingList;
+ for (int i = 0; i < waitingList.length; i++) {
+ if (waitingList[i] != null) {
+ if (waitingList[i].isCanceled()) {
+ if (canceledRms == null) {
+ canceledRms = new ArrayList<Callback>(1);
+ }
+ canceledRms.add( waitingList[i] );
+ } else {
+ canceled = false;
+ }
+ }
+ }
+ } else {
+ assert fWaitingList == null;
+ canceled = true;
+ }
+ }
+ if (canceledRms != null) {
+ final List<Callback> _canceledRms = canceledRms;
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ for (Callback canceledRm : _canceledRms) {
+ handleCanceledRm(canceledRm);
+ }
+ }
+ });
+ }
+
+ return canceled;
+ }
+
+ /**
+ * Resets the cache, setting the data to null and the status to
+ * INVALID_STATUS. When in the invalid state, neither the data nor the
+ * status can be queried.
+ */
+ public void reset() {
+ set(null, INVALID_STATUS, false);
+ }
+
+ /**
+ * Sets data and error values into the cache, and optionally puts in valid
+ * state. Note that data may be null and status may be an error status.
+ * 'Valid' simply means that our data is not stale. In other words, if the
+ * request to the source encounters an error, the cache object becomes valid
+ * all the same. The status indicates what error was encountered.
+ *
+ * <p>
+ * This method is called internally, typically in response to having
+ * obtained the result from the asynchronous request to the source. The
+ * data/status will remain valid until the cache object receives an event
+ * notification from the source indicating otherwise.
+ *
+ * @param data
+ * The data that should be returned to any clients waiting for
+ * cache data and for clients requesting data until the cache is
+ * invalidated.
+ * @param error The status that should be returned to any clients waiting for
+ * cache data and for clients requesting data until the cache is
+ * invalidated
+ * @param valid Whether the cache should bet set in valid state. If false,
+ * any callback waiting for data are completed but the cache
+ * is moved back to invalid state.
+ *
+ * @see #reset
+ */
+ public void set(V data, Throwable error, boolean valid) {
+ assert Protocol.isDispatchThread();
+
+ fData = data;
+ fError = error;
+ fValid = valid;
+
+ completeWaitingRms();
+ }
+
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java
index 1e3281d..25313f1 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/AggregateCallback.java
@@ -100,7 +100,7 @@ public class AggregateCallback extends Callback {
@Override
public synchronized void setError(Throwable error) {
if ((getError() == null)) {
- setError(new AggregateError("") {
+ super.setError(new AggregateError("") {
private static final long serialVersionUID = 1L;
@Override
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java
index 78c7e47..227112f 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Callback.java
@@ -251,6 +251,11 @@ public class Callback {
}
}
+ public void done(Throwable error) {
+ setError(error);
+ done();
+ }
+
/**
* Marks this request as completed. Once this method is called, the
* monitor submits a runnable to the DSF Executor to call the
@@ -261,6 +266,8 @@ public class Callback {
* </p>
*/
public synchronized void done() {
+ assert Protocol.getEventQueue().isDispatchThread();
+
if (fDone) {
throw new IllegalStateException("Callback: " + this + ", done() method called more than once"); //$NON-NLS-1$//$NON-NLS-2$
}
@@ -276,19 +283,7 @@ public class Callback {
fParentCallback.removeCancelListener(fCanceledListener);
}
- try {
- Protocol.invokeLater(new Runnable() {
- public void run() {
- Callback.this.handleCompleted();
- }
- @Override
- public String toString() {
- return "Completed: " + Callback.this.toString(); //$NON-NLS-1$
- }
- });
- } catch (IllegalStateException e) {
- handleEventQueueShutDown(e);
- }
+ handleCompleted();
}
@Override
@@ -404,21 +399,4 @@ public class Callback {
fParentCallback.done();
}
}
-
- /**
- * Default handler for when the executor supplied in the constructor
- * rejects the runnable that is submitted invoke this request monitor.
- * This usually happens only when the executor is shutting down.
- * <p>
- * The default handler creates a new error status for the rejected
- * execution and propagates it to the client or logs it.
- */
- protected void handleEventQueueShutDown(IllegalStateException e) {
- if (fParentCallback != null) {
- fParentCallback.setError(e);
- fParentCallback.done();
- } else {
- Protocol.log("In callback " + toString() + ", unhandled event queue shut down.", e);
- }
- }
}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java
new file mode 100644
index 0000000..9275118
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/CallbackCache.java
@@ -0,0 +1,100 @@
+package org.eclipse.tcf.debug.test.util;
+
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, Inc. 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
+ *******************************************************************************/
+
+
+/**
+ * A general purpose cache, which caches the result of a single request.
+ * Sub classes need to implement {@link #retrieve(DataRequestMonitor)} to fetch
+ * data from the data source. Clients are responsible for calling
+ * {@link #disable()} and {@link #reset()} to manage the state of the cache in
+ * response to events from the data source.
+ * <p>
+ * This cache requires an executor to use. The executor is used to synchronize
+ * access to the cache state and data.
+ * </p>
+ * @since 2.2
+ */
+public abstract class CallbackCache<V> extends AbstractCache<V> {
+
+ protected DataCallback<V> fRm;
+
+ @Override
+ protected final void retrieve() {
+ // Make sure to cancel the previous rm. This may lead to the rm being
+ // canceled twice, but that's not harmful.
+ if (fRm != null) {
+ fRm.cancel();
+ }
+
+ fRm = new DataCallback<V>(null) {
+ @Override
+ protected void handleCompleted() {
+ if (this == fRm) {
+ fRm = null;
+ CallbackCache.this.handleCompleted(getData(), getError(), isCanceled());
+ }
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return super.isCanceled() || CallbackCache.this.isCanceled();
+ };
+ };
+ retrieve(fRm);
+ }
+
+ /**
+ * Sub-classes should override this method to retrieve the cache data
+ * from its source.
+ *
+ * @param rm Request monitor for completion of data retrieval.
+ */
+ protected abstract void retrieve(DataCallback<V> rm);
+
+ protected void handleCompleted(V data, Throwable error, boolean canceled) {
+ // If the requestor canceled the request, then leave the
+ // cache as is, regardless of how the retrieval completes.
+ // We want the cache to stay in the invalid state so that
+ // it remains functional. The request may have completed
+ // successfully, but using it may produce inconsistent
+ // results.
+ if (!canceled) {
+ set(data, error, true);
+ }
+ }
+
+ @Override
+ protected synchronized void canceled() {
+ if (fRm != null) {
+ fRm.cancel();
+ }
+ }
+
+ @Override
+ public void set(V data, Throwable error, boolean valid) {
+ if (fRm != null) {
+ fRm.cancel();
+ fRm = null;
+ }
+ super.set(data, error, valid);
+ }
+
+ @Override
+ public void reset() {
+ if (fRm != null) {
+ fRm.cancel();
+ fRm = null;
+ }
+ super.reset();
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java
index e9822fa..8127d9e 100644
--- a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/DataCallback.java
@@ -46,6 +46,11 @@ public class DataCallback<V> extends Callback {
*/
public synchronized V getData() { return fData; }
+ public void done(V data, Throwable error) {
+ setData(data);
+ done(error);
+ }
+
@Override
public String toString() {
if (getData() != null) {
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java
new file mode 100644
index 0000000..eca1bb7
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/ICache.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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.tcf.debug.test.util;
+
+
+/**
+ * The interface for a general purpose cache that caches the result of a single
+ * request. Implementations need to provide the logic to fetch data from an
+ * asynchronous data source.
+ * <p>
+ * This cache requires an executor to use. The executor is used to synchronize
+ * access to the cache state and data.
+ * </p>
+ * @since 2.2
+ */
+public interface ICache<V> {
+
+ /**
+ * Returns the current data value held by this cache. Clients should first
+ * call isValid() to determine if the data is up to date. Calling this when
+ * in the invalid state will throw an IllegalStateException
+ */
+ public V getData();
+
+ /**
+ * Returns the status of the source request held by this cache. Clients
+ * should first call isValid() to determine if the data is up to date.
+ * Calling this when in the invalid state will throw an
+ * IllegalStateException
+ */
+ public Throwable getError();
+
+ /**
+ * Asks the cache to update its value from the source. If the cache is
+ * already valid, the request is completed immediately, otherwise data will
+ * first be retrieved from the source. Typically, this method is called by a
+ * client after it discovers the cache is invalid via {@link #isValid()}
+ *
+ * @param rm
+ * RequestMonitor that is called when cache becomes valid.
+ */
+ public void update(Callback rm);
+
+ /**
+ * Returns <code>true</code> if the cache is currently valid. I.e.
+ * whether the cache can return a value immediately without first
+ * retrieving it from the data source.
+ */
+ public boolean isValid();
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java
new file mode 100644
index 0000000..e19b734
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/TokenCache.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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.tcf.debug.test.util;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.tcf.protocol.IToken;
+
+/**
+ *
+ */
+public abstract class TokenCache<V> extends AbstractCache<V> {
+
+ private AtomicReference<IToken> fToken = new AtomicReference<IToken>();
+
+ @Override
+ final protected void retrieve() {
+ fToken.set(retrieveToken());
+ }
+
+ protected boolean checkToken(IToken token) {
+ return fToken.compareAndSet(token, null);
+ }
+
+ abstract protected IToken retrieveToken();
+
+ protected void set(IToken token, V data, Throwable error) {
+ if (checkToken(token) ) {
+ set(data, error, true);
+ }
+ }
+
+ @Override
+ protected void canceled() {
+ IToken token = fToken.getAndSet(null);
+ token.cancel();
+ }
+}
diff --git a/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java
new file mode 100644
index 0000000..d27281e
--- /dev/null
+++ b/tests/plugins/org.eclipse.tcf.debug.test/src/org/eclipse/tcf/debug/test/util/Transaction.java
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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.tcf.debug.test.util;
+
+import java.util.Arrays;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * @since 2.2
+ */
+public abstract class Transaction<V> implements Future<V> {
+
+ /**
+ * The exception we throw when the client transaction logic asks us to
+ * validate a cache object that is stale (or has never obtained a value from
+ * the source)
+ */
+ private static final InvalidCacheException INVALID_CACHE_EXCEPTION = new InvalidCacheException();
+
+ /** The request object we've been given to set the transaction results in */
+ private DataCallback<V> fRm;
+
+ private Query<V> fQuery;
+
+ public static class InvalidCacheException extends Exception {
+ private static final long serialVersionUID = 1L;
+ }
+
+ /**
+ * Kicks off the transaction. We'll either complete the request monitor
+ * immediately if all the data points the transaction needs are cached and
+ * valid, or we'll end up asynchronously completing the monitor if and when
+ * either (a) all the data points are available and up-to-date, or (b)
+ * obtaining them from the source encountered an error. Note that there is
+ * potential in (b) for us to never complete the monitor. If one or more
+ * data points are perpetually becoming stale, then we'll indefinitely wait
+ * for them to stabilize. The caller should cancel its request monitor in
+ * order to get us to stop waiting.
+ *
+ * @param rm Request completion monitor.
+ */
+ public void request(DataCallback<V> rm) {
+ if (fRm != null) {
+ assert fRm.isCanceled();
+ fRm.done();
+ }
+ fRm = rm;
+ assert fRm != null;
+ execute();
+ }
+
+ /**
+ * The transaction logic--code that tries to synchronously make use of,
+ * usually, multiple data points that are normally obtained asynchronously.
+ * Each data point is represented by a cache object. The transaction logic
+ * must check the validity of each cache object just prior to using it
+ * (calling its getData()). It should do that check by calling one of our
+ * validate() methods. Those methods will throw InvalidCacheException if the
+ * cached data is invalid (stale, e.g.,) or CoreException if an error was
+ * encountered the last time it got data form the source. The exception will
+ * abort the transaction, but in the case of InvalidCacheException, we
+ * schedule an asynchronous call that will re-invoke the transaction
+ * logic once the cache object has been updated from the source.
+ *
+ * @return the cached data if it's valid, otherwise an exception is thrown
+ * @throws Transaction.InvalidCacheException Exception indicating that a
+ * cache is not valid and transaction will need to be rescheduled.
+ * @throws CoreException Exception indicating that one of the caches is
+ * in error state and transaction cannot be processed.
+ */
+ abstract protected V process() throws InvalidCacheException, ExecutionException;
+
+ /**
+ * Method which invokes the transaction logic and handles any exception that
+ * may result. If that logic encounters a stale/unset cache object, then we
+ * simply do nothing. This method will be called again once the cache
+ * objects tell us it has obtained an updated value form the source.
+ */
+ private void execute() {
+ if (fRm.isCanceled()) {
+ fRm.done();
+ fRm = null;
+ return;
+ }
+
+ try {
+ // Execute the transaction logic
+ V data = process();
+
+ // No exception means all cache objects used by the transaction
+ // were valid and up to date. Complete the request
+ fRm.setData(data);
+ fRm.done();
+ fRm = null;
+ }
+ catch (InvalidCacheException e) {
+ // At least one of the cache objects was stale/unset. Keep the
+ // request monitor in the incomplete state, thus leaving our client
+ // "waiting" (asynchronously). We'll get called again once the cache
+ // objects are updated, thus re-starting the whole transaction
+ // attempt.
+ }
+ catch (Throwable e) {
+ // At least one of the cache objects encountered a failure obtaining
+ // the data from the source. Complete the request.
+ fRm.setError(e);
+ fRm.done();
+ fRm = null;
+ }
+
+ }
+
+ /**
+ * Clients must call one of our validate methods prior to using (calling
+ * getData()) on data cache object.
+ *
+ * @param cache
+ * the object being validated
+ * @throws InvalidCacheException
+ * if the data is stale/unset
+ * @throws ExecutionException
+ * if an error was encountered getting the data from the source
+ */
+ public <T> T validate(ICache<T> cache) throws InvalidCacheException, ExecutionException {
+ if (cache.isValid()) {
+ if (cache.getError() != null) {
+ throw new ExecutionException(cache.getError());
+ }
+ return cache.getData();
+ } else {
+ // Throw the invalid cache exception, but first ask the cache to
+ // update itself from its source, and schedule a re-attempt of the
+ // transaction logic to occur when the stale/unset cache has been
+ // updated
+ cache.update(new Callback(fRm) {
+ @Override
+ protected void handleCompleted() {
+ execute();
+ }
+ });
+ throw INVALID_CACHE_EXCEPTION;
+ }
+ }
+
+ /**
+ * See {@link #validate(RequestCache)}. This variant simply validates
+ * multiple cache objects.
+ */
+ public <T> void validate(ICache<?> ... caches) throws InvalidCacheException, ExecutionException {
+ validate(Arrays.asList(caches));
+ }
+
+ /**
+ * See {@link #validate(RequestCache)}. This variant simply validates
+ * multiple cache objects.
+ */
+ public void validate(@SuppressWarnings("rawtypes") Iterable caches) throws InvalidCacheException, ExecutionException {
+ // Check if any of the caches have errors:
+ boolean allValid = true;
+
+ for (Object cacheObj : caches) {
+ ICache<?> cache = (ICache<?>)cacheObj;
+ if (cache.isValid()) {
+ if (cache.getError() != null) {
+ throw new ExecutionException(cache.getError());
+ }
+ } else {
+ allValid = false;
+ }
+ }
+ if (!allValid) {
+ // Throw the invalid cache exception, but first schedule a
+ // re-attempt of the transaction logic, to occur when the
+ // stale/unset cache objects have been updated
+ AggregateCallback countringRm = new AggregateCallback(fRm) {
+ @Override
+ protected void handleCompleted() {
+ execute();
+ }
+ };
+ int count = 0;
+ for (Object cacheObj : caches) {
+ ICache<?> cache = (ICache<?>)cacheObj;
+ if (!cache.isValid()) {
+ cache.update(countringRm);
+ count++;
+ }
+ }
+ countringRm.setDoneCount(count);
+ throw INVALID_CACHE_EXCEPTION;
+ }
+ }
+
+ private synchronized Query<V> getQuery(boolean create) {
+ if (fQuery == null && create) {
+ fQuery = new Query<V>() {
+ @Override
+ protected void execute(DataCallback<V> callback) {
+ request(callback);
+ }
+ };
+ fQuery.invoke();
+ }
+
+ return fQuery;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ Query<V> query = getQuery(false);
+ if (query != null) {
+ return query.cancel(mayInterruptIfRunning);
+ }
+ return false;
+ }
+
+ public boolean isCancelled() {
+ Query<V> query = getQuery(false);
+ if (query != null) {
+ return query.isCancelled();
+ }
+ return false;
+ }
+
+ public boolean isDone() {
+ Query<V> query = getQuery(false);
+ if (query != null) {
+ return query.isDone();
+ }
+ return false;
+ }
+
+ public V get() throws InterruptedException, ExecutionException {
+ return getQuery(true).get();
+ }
+
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return getQuery(true).get(timeout, unit);
+ }
+
+
+}