/******************************************************************************* * Copyright (c) 2011, 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.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ExecutionException; import org.eclipse.tcf.debug.test.services.ResetMap.IResettable; 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.IChannel; 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(IChannel channel, IRunControl service) { super(channel); 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> { RunControlTokenCache() { super(fChannel); } 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) { super(fChannel); 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; } } private class ContextStateCache extends CallbackCache<ContextState> implements IResettable { private class InnerContextStateCache extends TokenCache<ContextState> implements IRunControl.DoneGetState { private final RunControlContext fContext; public InnerContextStateCache(RunControlContext context) { super(fChannel); 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 IdKey<V> { Object fClientKey; ContextCommandKey(Class<V> cacheClass, String contextId, Object clientKey) { super(cacheClass, contextId); 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 class DoneCommandCache extends TokenCache<Object> implements DoneCommand { DoneCommandCache() { super(fChannel); } 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.getID(), 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.getID(), 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.getID(), 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.getID(), 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<?>, Object> 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<?>, Object> 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<?>, Object> 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<?>, Object> 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<?>, Object> 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<?>, Object> 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<?>, Object> 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<?>, Object> 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); } } } } }