Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'target_explorer/plugins')
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelRefreshService.java964
1 files changed, 964 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelRefreshService.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelRefreshService.java
new file mode 100644
index 000000000..2676ee496
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelRefreshService.java
@@ -0,0 +1,964 @@
+/*******************************************************************************
+ * Copyright (c) 2013 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.te.tcf.processes.core.model.runtime.services;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IToken;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.services.IProcesses;
+import org.eclipse.tcf.services.ISysMonitor;
+import org.eclipse.tcf.services.ISysMonitor.SysMonitorContext;
+import org.eclipse.tcf.te.core.async.AsyncCallbackCollector;
+import org.eclipse.tcf.te.runtime.callback.Callback;
+import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
+import org.eclipse.tcf.te.runtime.model.interfaces.IContainerModelNode;
+import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode;
+import org.eclipse.tcf.te.runtime.model.interfaces.contexts.IAsyncRefreshableCtx;
+import org.eclipse.tcf.te.runtime.model.interfaces.contexts.IAsyncRefreshableCtx.QueryState;
+import org.eclipse.tcf.te.runtime.model.interfaces.contexts.IAsyncRefreshableCtx.QueryType;
+import org.eclipse.tcf.te.runtime.services.ServiceManager;
+import org.eclipse.tcf.te.runtime.services.interfaces.IDelegateService;
+import org.eclipse.tcf.te.tcf.core.async.CallbackInvocationDelegate;
+import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelChannelService;
+import org.eclipse.tcf.te.tcf.core.model.services.AbstractModelService;
+import org.eclipse.tcf.te.tcf.processes.core.activator.CoreBundleActivator;
+import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode;
+import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode.TYPE;
+import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNodeProperties;
+import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModel;
+import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelRefreshService;
+import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelUpdateService;
+import org.eclipse.tcf.te.tcf.processes.core.nls.Messages;
+
+/**
+ * Runtime model refresh service implementation.
+ * <p>
+ * <b>Service implementation assumptions</b>
+ * <ul>
+ * <li>Refresh operations do not update the model or the existing process context model nodes directly. Any
+ * refresh operation is creating a parallel tree of nodes which is merged into the model <i>at the end</i>
+ * of the refresh operation.</li>
+ * <li>Refresh operations do not modify the status of asynchronous refreshable context of the refresh operation
+ * root. The caller of the refresh service is responsible for handling the asynchronous refreshable context
+ * state of the refresh operation root.</li>
+ * <li>Refresh operations requested for the same root while still running are queued and the callbacks are fired
+ * all at once when the first refresh operation completes.</li>
+ * <li>Auto-refresh operations are walking the whole process context model node tree, starting from the model root,
+ * and triggers an refresh of all process context model nodes found where the child list query marker is set to done.</li>
+ * </ul>
+ */
+public class RuntimeModelRefreshService extends AbstractModelService<IRuntimeModel> implements IRuntimeModelRefreshService {
+ // For each root context to refresh, remember the callbacks to invoke.
+ private final Map<IModelNode, List<ICallback>> ctx2cb = new HashMap<IModelNode, List<ICallback>>();
+ // The default processes runtime model refresh service delegate
+ /* default */ final IRuntimeModelRefreshService.IDelegate defaultDelegate = new DefaultDelegate();
+
+ /**
+ * Default processes runtime model refresh service delegate implementation.
+ */
+ /* default */ final class DefaultDelegate implements IRuntimeModelRefreshService.IDelegate {
+ private final String[] managedPropertyNames = new String[] { IProcessContextNodeProperties.PROPERTY_CMD_LINE };
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelRefreshService.IDelegate#setNodeType(java.lang.String, org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode)
+ */
+ @Override
+ public void setNodeType(String parentContextId, IProcessContextNode node) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(node);
+
+ node.setType(parentContextId == null ? TYPE.Process : TYPE.Thread);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelRefreshService.IDelegate#postRefreshContext(org.eclipse.tcf.protocol.IChannel, org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void postRefreshContext(final IChannel channel, final IProcessContextNode node, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(channel);
+ Assert.isNotNull(node);
+ Assert.isNotNull(callback);
+
+ // The channel must be opened, otherwise the query cannot run
+ if (channel.getState() != IChannel.STATE_OPEN) {
+ IStatus status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), Messages.RuntimeModelRefreshService_error_channelClosed);
+ callback.done(RuntimeModelRefreshService.this, status);
+ return;
+ }
+
+ // Get the required services
+ final ISysMonitor sysMonService = channel.getRemoteService(ISysMonitor.class);
+
+ // The system monitor service must be available
+ if (sysMonService == null) {
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ return;
+ }
+
+ // The context id must be set
+ String contextId = node.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
+ if (contextId == null) {
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ return;
+ }
+
+ // Get the command line of the context
+ sysMonService.getCommandLine(contextId, new ISysMonitor.DoneGetCommandLine() {
+ @Override
+ public void doneGetCommandLine(IToken token, Exception error, String[] cmd_line) {
+ node.setProperty(IProcessContextNodeProperties.PROPERTY_CMD_LINE, error == null ? cmd_line : null);
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelRefreshService.IDelegate#getManagedPropertyNames()
+ */
+ @Override
+ public String[] getManagedPropertyNames() {
+ return managedPropertyNames;
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param model The parent model. Must not be <code>null</code>.
+ */
+ public RuntimeModelRefreshService(IRuntimeModel model) {
+ super(model);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelRefreshService#refresh(org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void refresh(final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ refresh(NONE, callback);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelRefreshService#refresh(int, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void refresh(final int flags, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+
+ // Get the parent model
+ final IRuntimeModel model = getModel();
+ Assert.isNotNull(model);
+
+ // If the parent model is already disposed, the service will drop out immediately
+ if (model.isDisposed()) {
+ if (callback != null) callback.done(this, Status.OK_STATUS);
+ return;
+ }
+
+ // A refresh for the passed in node is already running if the model
+ // is associated with a callback list in 'ctx2cb'.
+ final boolean isRefreshAlreadyRunning = ctx2cb.containsKey(model);
+
+ // Queue the callback to invoke once the refresh is done
+ List<ICallback> callbacks = ctx2cb.get(model);
+ if (callbacks == null) {
+ callbacks = new ArrayList<ICallback>();
+ ctx2cb.put(model, callbacks);
+ }
+ Assert.isNotNull(callbacks);
+ if (callback != null) callbacks.add(callback);
+
+ // If a refresh is already running, drop out. The callback is already
+ // queued and will be invoked once the refresh operation is done.
+ if (isRefreshAlreadyRunning) return;
+
+ // The refresh operation is building up a parallel data tree. Pass in an empty container
+ // to receive the fetched children.
+ final IProcessContextNode container = model.getFactory().newInstance(IProcessContextNode.class);
+ // Mark the container as refresh in progress
+ final IAsyncRefreshableCtx containerRefreshable = (IAsyncRefreshableCtx)container.getAdapter(IAsyncRefreshableCtx.class);
+ Assert.isNotNull(containerRefreshable);
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
+ // Initiate the refresh of the level 1 children
+ refreshChildrenLevel1(null, 2, container, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Mark the container refresh as done
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
+
+ // If the refresh succeeded, update the tree
+ if (status.isOK()) {
+ // Process the new child list and merge it with the model
+ model.getService(IRuntimeModelUpdateService.class).updateChildren(model, container);
+ // Invoke the callbacks
+ invokeCallbacks(model, RuntimeModelRefreshService.this, status);
+ } else {
+ // Invoke the callbacks
+ invokeCallbacks(model, RuntimeModelRefreshService.this, status);
+ }
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelRefreshService#refresh(org.eclipse.tcf.te.runtime.model.interfaces.IModelNode, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void refresh(IModelNode node, ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ refresh(node, NONE, callback);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelRefreshService#refresh(org.eclipse.tcf.te.runtime.model.interfaces.IModelNode, int, org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void refresh(final IModelNode node, final int flags, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(node);
+
+ // Get the parent model
+ final IRuntimeModel model = getModel();
+ Assert.isNotNull(model);
+ // If the model is already disposed, drop out immediately
+ if (model.isDisposed() || !(node instanceof IProcessContextNode)) {
+ if (callback != null) callback.done(this, Status.OK_STATUS);
+ return;
+ }
+
+ // Get the context id
+ final String contextId = node.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
+ if (contextId == null) {
+ if (callback != null) callback.done(this, Status.OK_STATUS);
+ return;
+ }
+
+ // A refresh for the passed in node is already running if the node
+ // is associated with a callback list in 'ctx2cb'.
+ final boolean isRefreshAlreadyRunning = ctx2cb.containsKey(node);
+
+ // Queue the callback to invoke once the refresh is done
+ List<ICallback> callbacks = ctx2cb.get(node);
+ if (callbacks == null) {
+ callbacks = new ArrayList<ICallback>();
+ ctx2cb.put(node, callbacks);
+ }
+ Assert.isNotNull(callbacks);
+ // Add the current callback to the list of callbacks
+ if (callback != null) callbacks.add(callback);
+
+ // If a refresh is already running, drop out. The callback is already
+ // queued and will be invoked once the refresh operation is done.
+ if (isRefreshAlreadyRunning) return;
+
+ // The refresh operation is building up a parallel data tree. Pass in an empty container
+ // to receive the fetched context properties and context children.
+ final IProcessContextNode container = model.getFactory().newInstance(IProcessContextNode.class);
+ // Mark the container as refresh in progress
+ final IAsyncRefreshableCtx containerRefreshable = (IAsyncRefreshableCtx)container.getAdapter(IAsyncRefreshableCtx.class);
+ Assert.isNotNull(containerRefreshable);
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
+ // The context id must be set to the container (asserted by the update service)
+ container.setProperty(IProcessContextNodeProperties.PROPERTY_ID, contextId);
+ // The container has the same type as the original node
+ container.setType(((IProcessContextNode)node).getType());
+ // Initiate the refresh of context
+ refreshContextLevel1(contextId, 2, container, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Mark the container refresh as done
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
+
+ // If the refresh succeeded, update the original node
+ if (status.isOK()) {
+ // Process the new context node and merge it with the original context node
+ model.getService(IRuntimeModelUpdateService.class).update(node, container);
+ // Invoke the callbacks
+ invokeCallbacks(node, RuntimeModelRefreshService.this, status);
+ } else {
+ // Invoke the callbacks
+ invokeCallbacks(node, RuntimeModelRefreshService.this, status);
+ }
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelRefreshService#autoRefresh(org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
+ */
+ @Override
+ public void autoRefresh(ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+
+ // Get the parent model
+ final IRuntimeModel model = getModel();
+
+ // If the parent model is already disposed, the service will drop out immediately
+ if (model.isDisposed()) {
+ if (callback != null) callback.done(this, Status.OK_STATUS);
+ return;
+ }
+
+ // Create the callback collector to fire once all refresh operations are completed
+ final AsyncCallbackCollector collector = new AsyncCallbackCollector(callback, new CallbackInvocationDelegate());
+
+ // Get the first level of children and check if they are need to be refreshed
+ List<IProcessContextNode> children = model.getChildren(IProcessContextNode.class);
+ if (children.size() > 0) {
+ // Initiate the refresh of the children
+ doAutoRefresh(model, children.toArray(new IProcessContextNode[children.size()]), 0, collector);
+ }
+
+ // Mark the collector initialization done
+ collector.initDone();
+ }
+
+ // ----- Non-API refresh methods -----
+
+ /**
+ * Performs the auto refresh of the given nodes.
+ *
+ * @param model The runtime model. Must not be <code>null</code>.
+ * @param nodes The nodes. Must not be <code>null</code>.
+ * @param index The index of the node to refresh within the nodes array. Must be greater or equal than 0 and less than the array length.
+ * @param collector The callback collector. Must not be <code>null</code>.
+ */
+ /* default */ void doAutoRefresh(final IRuntimeModel model, final IProcessContextNode[] nodes, final int index, final AsyncCallbackCollector collector) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(model);
+ Assert.isNotNull(nodes);
+ Assert.isTrue(index >= 0 && index < nodes.length);
+ Assert.isNotNull(collector);
+
+ final IProcessContextNode node = nodes[index];
+
+ // Get the asynchronous refresh context adapter
+ final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)node.getAdapter(IAsyncRefreshableCtx.class);
+ if (refreshable != null) {
+ // Schedule a refresh if the node got refreshed before
+ if (refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.DONE)) {
+ // Create a new callback for the collector to wait for
+ final ICallback callback = new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // We need a reference to the outer callback (== this)
+ final ICallback outerCallback = this;
+ // Create the inner callback
+ final ICallback innerCallback = new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // More nodes to process?
+ int newIndex = index + 1;
+ if (newIndex < nodes.length) {
+ doAutoRefresh(model, nodes, newIndex, collector);
+ }
+ // Remove the outer callback from the collector
+ collector.removeCallback(outerCallback);
+ }
+ };
+
+ // If the node has children, process them first
+ List<IProcessContextNode> children = node.getChildren(IProcessContextNode.class);
+ if (children.size() > 0) {
+ // Create a new callback collector for processing the children
+ final AsyncCallbackCollector childCollector = new AsyncCallbackCollector(innerCallback, new CallbackInvocationDelegate());
+ // Initiate the refresh of the children
+ doAutoRefresh(model, children.toArray(new IProcessContextNode[children.size()]), 0, childCollector);
+ // Mark the collector initialization done
+ childCollector.initDone();
+ } else {
+ // Invoke the inner callback right away
+ innerCallback.done(this, Status.OK_STATUS);
+ }
+ }
+ };
+ collector.addCallback(callback);
+
+ // Get the context id
+ final String contextId = node.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
+ if (contextId == null) {
+ callback.done(this, Status.OK_STATUS);
+ return;
+ }
+
+ // The refresh operation is building up a parallel data tree. Pass in an empty container
+ // to receive the fetched context properties and context children.
+ final IProcessContextNode container = model.getFactory().newInstance(IProcessContextNode.class);
+ // Mark the container as refresh in progress
+ final IAsyncRefreshableCtx containerRefreshable = (IAsyncRefreshableCtx)container.getAdapter(IAsyncRefreshableCtx.class);
+ Assert.isNotNull(containerRefreshable);
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
+ // The context id must be set to the container (asserted by the update service)
+ container.setProperty(IProcessContextNodeProperties.PROPERTY_ID, contextId);
+ // The container has the same type as the original node
+ container.setType(node.getType());
+ // Initiate the refresh of context (only node and the direct children)
+ refreshContextLevel1(contextId, 1, container, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Mark the container refresh as done
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
+
+ // If the refresh succeeded, update the original node
+ if (status.isOK()) {
+ // Auto refresh requires to update the children of any new child found for
+ // the node refreshed. Collect all child nodes not being children of the original node.
+ List<IProcessContextNode> oldChildren = node.getChildren(IProcessContextNode.class);
+ List<IProcessContextNode> newChildren = container.getChildren(IProcessContextNode.class);
+ for (IProcessContextNode child : oldChildren) {
+ // Get the context id of the exiting child
+ String id = child.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
+ if (id == null) continue;
+ // Find the context id in the new children list
+ IProcessContextNode node = null;
+ for (IProcessContextNode candidate : newChildren) {
+ if (id.equals(candidate.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID))) {
+ node = candidate;
+ break;
+ }
+ }
+ // If found in the new children list, remove it
+ if (node != null) newChildren.remove(node);
+ }
+
+ // Process the new context node and merge it with the original context node
+ model.getService(IRuntimeModelUpdateService.class).update(node, container);
+
+ // If there are any new children detected, update the child list of those new children
+ if (newChildren.size() > 0) {
+ // Create the collector firing the final callback at the end
+ final AsyncCallbackCollector collector = new AsyncCallbackCollector(callback, new CallbackInvocationDelegate());
+ // Get the real children list
+ oldChildren = node.getChildren(IProcessContextNode.class);
+
+ for (IProcessContextNode child : newChildren) {
+ // Get the context id of the child
+ String id = child.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
+ if (id == null) continue;
+
+ // Find the real child node
+ IProcessContextNode realChild = null;
+ for (IProcessContextNode candidate : oldChildren) {
+ if (id.equals(candidate.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID))) {
+ realChild = candidate;
+ break;
+ }
+ }
+ if (realChild == null) continue;
+
+ // The refresh operation is building up a parallel data tree. Pass in an empty container
+ // to receive the fetched context properties and context children.
+ final IProcessContextNode container = model.getFactory().newInstance(IProcessContextNode.class);
+ // Mark the container as refresh in progress
+ final IAsyncRefreshableCtx containerRefreshable = (IAsyncRefreshableCtx)container.getAdapter(IAsyncRefreshableCtx.class);
+ Assert.isNotNull(containerRefreshable);
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
+ // The context id must be set to the container (asserted by the update service)
+ container.setProperty(IProcessContextNodeProperties.PROPERTY_ID, contextId);
+ // The container has the same type as the original node
+ container.setType(node.getType());
+
+ // Create the callback to invoke
+ final IProcessContextNode finRealChild = realChild;
+ final ICallback cb = new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Mark the container refresh as done
+ containerRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
+
+ // If the refresh succeeded, update the original child node
+ if (status.isOK()) {
+ model.getService(IRuntimeModelUpdateService.class).updateChildren(finRealChild, container);
+ }
+
+ // Remove the callback from the collector
+ if (status.getException() != null) {
+ collector.handleError(status.getException());
+ } else {
+ collector.removeCallback(this);
+ }
+ }
+ };
+ collector.addCallback(cb);
+
+ // Refresh the children of the new child
+ refreshChildrenLevel1(id, 1, container, cb);
+ }
+
+ collector.initDone();
+ } else {
+ // Invoke the callback
+ callback.done(RuntimeModelRefreshService.this, status);
+ }
+ } else {
+ // Invoke the callback
+ callback.done(RuntimeModelRefreshService.this, status);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Refresh the context properties for the given context id.
+ * <p>
+ * The method fetches the children of the given context id and generates a new set of process
+ * context nodes to represent the children until the max depth is reached.
+ * <p>
+ * The fetched properties are added to the given container.
+ *
+ * @param contextId The context id. Must not be <code>null</code>.
+ * @param maxDepth The max depth of the tree to refresh. Must be greater than 0.
+ * @param container The container. Must not be <code>null</code>.
+ * @param callback The callback to invoke once the operation is completed. Must not be <code>null</code>.
+ */
+ protected void refreshContextLevel1(final String contextId, final int maxDepth, final IProcessContextNode container, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(contextId);
+ Assert.isTrue(maxDepth > 0);
+ Assert.isNotNull(container);
+ Assert.isNotNull(callback);
+
+ // Make sure that the callback is invoked even for unexpected cases
+ try {
+ // Get an open channel
+ IModelChannelService channelService = getModel().getService(IModelChannelService.class);
+ channelService.openChannel(new IModelChannelService.DoneOpenChannel() {
+ @Override
+ public void doneOpenChannel(Throwable error, final IChannel channel) {
+ if (error == null) {
+ // Query the context properties
+ refreshContext(channel, contextId, container, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ if (status.isOK()) {
+ // Query the first level child contexts
+ refreshChildContexts(channel, contextId, container, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Refresh the next level if the depth is still larger than 0
+ if (maxDepth - 1 > 0) {
+ List<IProcessContextNode> children = container.getChildren(IProcessContextNode.class);
+ Assert.isNotNull(children);
+ refreshChildrenLevelN(channel, children.toArray(new IProcessContextNode[children.size()]), maxDepth - 1, callback);
+ } else {
+ // Refresh completed, invoke the callback
+ callback.done(RuntimeModelRefreshService.this, status);
+ }
+ }
+ });
+ } else {
+ callback.done(RuntimeModelRefreshService.this, status);
+ }
+ }
+ });
+ } else {
+ callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), error.getLocalizedMessage(), error));
+ }
+ }
+ });
+ } catch (Throwable e) {
+ callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e));
+ }
+ }
+
+ /**
+ * Fetches the first level of children for the given parent context id.
+ * <p>
+ * The method fetches the first level of children and generates a new set of process
+ * context nodes to represent the children. Each child is refresh itself until the max
+ * depth is reached.
+ * <p>
+ * The fetched first level children are added to the given container.
+ *
+ * @param parentContextId The parent context id or <code>null</code> for the root context.
+ * @param maxDepth The max depth of the tree to refresh. Must be greater than 0.
+ * @param container The container. Must not be <code>null</code>.
+ * @param callback The callback to invoke once the operation is completed. Must not be <code>null</code>.
+ */
+ /* default */ void refreshChildrenLevel1(final String parentContextId, final int maxDepth, final IProcessContextNode container, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isTrue(maxDepth > 0);
+ Assert.isNotNull(container);
+ Assert.isNotNull(callback);
+
+ // Make sure that the callback is invoked even for unexpected cases
+ try {
+ // Get an open channel
+ IModelChannelService channelService = getModel().getService(IModelChannelService.class);
+ channelService.openChannel(new IModelChannelService.DoneOpenChannel() {
+ @Override
+ public void doneOpenChannel(Throwable error, final IChannel channel) {
+ if (error == null) {
+ // Query the first level child contexts
+ refreshChildContexts(channel, parentContextId, container, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Refresh the next level if the depth is still larger than 0
+ if (maxDepth - 1 > 0) {
+ List<IProcessContextNode> children = container.getChildren(IProcessContextNode.class);
+ Assert.isNotNull(children);
+ refreshChildrenLevelN(channel, children.toArray(new IProcessContextNode[children.size()]), maxDepth - 1, callback);
+ } else {
+ // Refresh completed, invoke the callback
+ callback.done(RuntimeModelRefreshService.this, status);
+ }
+ }
+ });
+ } else {
+ callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), error.getLocalizedMessage(), error));
+ }
+ }
+ });
+ } catch (Throwable e) {
+ callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e));
+ }
+ }
+
+ /**
+ * Fetches the children of the given parent context nodes.
+ * <p>
+ * The method calls itself recursively until <code>maxDepth - 1 == 0</code> is true.
+ *
+ * @param channel An open channel. Must not be <code>null</code>.
+ * @param parents The parent contexts. Must not be <code>null</code>.
+ * @param maxDepth The max depth of the tree to refresh. Must be greater than 0.
+ * @param callback The callback to invoke once the operation is completed. Must not be <code>null</code>.
+ */
+ /* default */ void refreshChildrenLevelN(final IChannel channel, final IProcessContextNode[] parents, final int maxDepth, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(channel);
+ Assert.isNotNull(parents);
+ Assert.isTrue(maxDepth > 0);
+ Assert.isNotNull(callback);
+
+ // The channel must be opened, otherwise the query cannot run
+ if (channel.getState() != IChannel.STATE_OPEN) {
+ IStatus status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), Messages.RuntimeModelRefreshService_error_channelClosed);
+ callback.done(RuntimeModelRefreshService.this, status);
+ return;
+ }
+
+ // If the parents list is empty, there is nothing to refresh
+ if (parents.length == 0) {
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ return;
+ }
+
+ // The callback collector to be fired if the children of all parents got fully refreshed
+ final AsyncCallbackCollector collector = new AsyncCallbackCollector(new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Refresh the next level if the depth is still larger than 0
+ if (maxDepth - 1 > 0) {
+ // The callback collector to be fired if the children of all children of all parent contexts got fully refreshed
+ final AsyncCallbackCollector collector2 = new AsyncCallbackCollector(callback, new CallbackInvocationDelegate());
+
+ for (IProcessContextNode parent : parents) {
+ List<IProcessContextNode> children = parent.getChildren(IProcessContextNode.class);
+ Assert.isNotNull(children);
+ refreshChildrenLevelN(channel, children.toArray(new IProcessContextNode[children.size()]), maxDepth - 1, new AsyncCallbackCollector.SimpleCollectorCallback(collector2));
+ }
+
+ collector2.initDone();
+ } else {
+ // Refresh completed, invoke the callback
+ callback.done(RuntimeModelRefreshService.this, status);
+ }
+ }
+ }, new CallbackInvocationDelegate());
+
+ // Loop the parent contexts and refresh the children of each one.
+ for (final IProcessContextNode parent : parents) {
+ // Get the context id of the parent. Must be not null here.
+ String parentContextId = parent.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
+ if (parentContextId == null) continue;
+ // Create the callback
+ final ICallback cb = new AsyncCallbackCollector.SimpleCollectorCallback(collector);
+ // Get the asynchronous refresh context adapter
+ final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)parent.getAdapter(IAsyncRefreshableCtx.class);
+ Assert.isNotNull(refreshable);
+ // If not IN_PROGRESS, initiate the refresh
+ if (!refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.IN_PROGRESS)) {
+ // Mark the refresh as in progress
+ refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
+ // Don't send change events while refreshing
+ final boolean changed = parent.setChangeEventsEnabled(false);
+ // Refresh the children of the parent context
+ refreshChildContexts(channel, parentContextId, parent, new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Mark the refresh as done
+ refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
+ // Re-enable the change events if they had been enabled before
+ if (changed) parent.setChangeEventsEnabled(true);
+ // Invoke the callback
+ cb.done(RuntimeModelRefreshService.this, status);
+ }
+ });
+ } else {
+ // Invoke the callback
+ cb.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ }
+
+ collector.initDone();
+ }
+
+ /**
+ * Refresh the properties of the given context id using the given channel.
+ * All context properties are <i>added</i> to the passed in node.
+ *
+ * @param channel An open channel. Must not be <code>null</code>.
+ * @param contextId The context id. Must not be <code>null</code>.
+ * @param node The node. Must not be <code>null</code>.
+ * @param callback The callback to invoke once the operation is completed. Must not be <code>null</code>.
+ */
+ /* default */ void refreshContext(final IChannel channel, final String contextId, final IProcessContextNode node, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(channel);
+ Assert.isNotNull(contextId);
+ Assert.isNotNull(node);
+ Assert.isNotNull(callback);
+
+ // The channel must be opened, otherwise the query cannot run
+ if (channel.getState() != IChannel.STATE_OPEN) {
+ IStatus status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), Messages.RuntimeModelRefreshService_error_channelClosed);
+ callback.done(RuntimeModelRefreshService.this, status);
+ return;
+ }
+
+ // Get the required services
+ final IProcesses service = channel.getRemoteService(IProcesses.class);
+ final ISysMonitor sysMonService = channel.getRemoteService(ISysMonitor.class);
+
+ // At least the processes and the system monitor service must be available
+ if (service == null || sysMonService == null) {
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ return;
+ }
+
+ // Callback collector to fire once the system monitor and process context queries completed
+ final AsyncCallbackCollector collector = new AsyncCallbackCollector(new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Determine if a delegate is registered
+ IDelegateService service = ServiceManager.getInstance().getService(channel.getRemotePeer(), IDelegateService.class, false);
+ IRuntimeModelRefreshService.IDelegate delegate = service != null ? service.getDelegate(channel.getRemotePeer(), IRuntimeModelRefreshService.IDelegate.class) : null;
+
+ // Run the post refresh context delegate
+ if (delegate == null) delegate = defaultDelegate;
+ Assert.isNotNull(delegate);
+ delegate.postRefreshContext(channel, node, callback);
+ }
+ }, new CallbackInvocationDelegate());
+
+ // Query the system monitor context object
+ final ICallback cb1 = new AsyncCallbackCollector.SimpleCollectorCallback(collector);
+ sysMonService.getContext(contextId, new ISysMonitor.DoneGetContext() {
+ @Override
+ public void doneGetContext(IToken token, Exception error, SysMonitorContext context) {
+ // Ignore errors. Some of the context might be OS context we do not have
+ // permissions to read the properties from.
+ node.setSysMonitorContext(context);
+ // Invoke the callback
+ cb1.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ });
+
+ // Query the process context object
+ final ICallback cb2 = new AsyncCallbackCollector.SimpleCollectorCallback(collector);
+ service.getContext(contextId, new IProcesses.DoneGetContext() {
+ @Override
+ public void doneGetContext(IToken token, Exception error, IProcesses.ProcessContext context) {
+ // Errors are ignored
+ node.setProcessContext(context);
+ // Set the context name from the process context if available
+ if (context != null) node.setProperty(IProcessContextNodeProperties.PROPERTY_NAME, context.getName());
+ // Invoke the callback
+ cb2.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ });
+
+ collector.initDone();
+ }
+
+ /**
+ * Refresh the child contexts of the given parent context id using the given channel.
+ * All child contexts are <i>added</i> to the passed in container.
+ *
+ * @param channel An open channel. Must not be <code>null</code>.
+ * @param parentContextId The parent context id or <code>null</code> for the root context.
+ * @param container The container. Must not be <code>null</code>.
+ * @param callback The callback to invoke once the operation is completed. Must not be <code>null</code>.
+ */
+ /* default */ void refreshChildContexts(final IChannel channel, final String parentContextId, final IContainerModelNode container, final ICallback callback) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(channel);
+ Assert.isNotNull(container);
+ Assert.isNotNull(callback);
+
+ // The channel must be opened, otherwise the query cannot run
+ if (channel.getState() != IChannel.STATE_OPEN) {
+ IStatus status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), Messages.RuntimeModelRefreshService_error_channelClosed);
+ callback.done(RuntimeModelRefreshService.this, status);
+ return;
+ }
+
+ // Get the required services
+ final IProcesses service = channel.getRemoteService(IProcesses.class);
+ final ISysMonitor sysMonService = channel.getRemoteService(ISysMonitor.class);
+
+ // At least the processes and the system monitor service must be available
+ if (service == null || sysMonService == null) {
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ return;
+ }
+
+ // Get the child context id's of the given parent context id
+ sysMonService.getChildren(parentContextId, new ISysMonitor.DoneGetChildren() {
+ @Override
+ public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
+ if (error == null) {
+ if (context_ids != null && context_ids.length > 0) {
+ // Callback collector to fire the passed in callback once all child contexts got fully refreshed
+ final AsyncCallbackCollector collector = new AsyncCallbackCollector(callback, new CallbackInvocationDelegate());
+
+ // Loop the returned context id's and query the context data
+ for (String id : context_ids) {
+ final String contextId = id;
+
+ // Create the context node for the current context id
+ final IProcessContextNode node = createContextNodeFrom(contextId);
+ Assert.isNotNull(node);
+ // Add the node to the container
+ container.add(node);
+
+ // Callback collector to fire once the system monitor and process context queries completed
+ final ICallback innerCallback = new AsyncCallbackCollector.SimpleCollectorCallback(collector);
+ final AsyncCallbackCollector innerCollector = new AsyncCallbackCollector(new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ // Determine if a delegate is registered
+ IDelegateService service = ServiceManager.getInstance().getService(channel.getRemotePeer(), IDelegateService.class, false);
+ IRuntimeModelRefreshService.IDelegate delegate = service != null ? service.getDelegate(channel.getRemotePeer(), IRuntimeModelRefreshService.IDelegate.class) : null;
+
+ // Determine the node type
+ if (delegate != null) delegate.setNodeType(parentContextId, node);
+ // Fallback to the default delegate if node type is not set by delegate
+ if (node.getType() == TYPE.Unknown) defaultDelegate.setNodeType(parentContextId, node);
+
+ // Run the post refresh context delegate
+ if (delegate == null) delegate = defaultDelegate;
+ Assert.isNotNull(delegate);
+ delegate.postRefreshContext(channel, node, innerCallback);
+ }
+ }, new CallbackInvocationDelegate());
+
+ // Query the system monitor context object
+ final ICallback cb1 = new AsyncCallbackCollector.SimpleCollectorCallback(innerCollector);
+ sysMonService.getContext(contextId, new ISysMonitor.DoneGetContext() {
+ @Override
+ public void doneGetContext(IToken token, Exception error, SysMonitorContext context) {
+ // Ignore errors. Some of the context might be OS context we do not have
+ // permissions to read the properties from.
+ node.setSysMonitorContext(context);
+ // Invoke the callback
+ cb1.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ });
+
+ // Query the process context object
+ final ICallback cb2 = new AsyncCallbackCollector.SimpleCollectorCallback(innerCollector);
+ service.getContext(contextId, new IProcesses.DoneGetContext() {
+ @Override
+ public void doneGetContext(IToken token, Exception error, IProcesses.ProcessContext context) {
+ // Errors are ignored
+ node.setProcessContext(context);
+ // Set the context name from the process context if available
+ if (context != null) node.setProperty(IProcessContextNodeProperties.PROPERTY_NAME, context.getName());
+ // Invoke the callback
+ cb2.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ });
+
+ innerCollector.initDone();
+ }
+
+ collector.initDone();
+ } else {
+ callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS);
+ }
+ } else {
+ callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), error.getLocalizedMessage(), error));
+ }
+ }
+ });
+ }
+
+ /**
+ * Create a process context node instance for the given context id.
+ *
+ * @param contextId The context id. Must not be <code>null</code>.
+ * @return The process context node instance.
+ */
+ /* default */ IProcessContextNode createContextNodeFrom(String contextId) {
+ Assert.isNotNull(contextId);
+
+ // Create a context node and associate the given context
+ IProcessContextNode node = getModel().getFactory().newInstance(IProcessContextNode.class);
+
+ // Set the context id
+ node.setProperty(IProcessContextNodeProperties.PROPERTY_ID, contextId);
+
+ return node;
+ }
+
+ // ----- Utility methods ------
+
+ /**
+ * Invoke all pending callbacks for the given context.
+ * <p>
+ * Each callback is invoked as single runnable dispatched to the
+ * TCF event dispatch thread.
+ *
+ * @param context The context. Must not be <code>null</code>.
+ * @param caller The caller of the callback or <code>null</code>.
+ * @param status The status. Must not be <code>null</code>.
+ */
+ protected void invokeCallbacks(final IModelNode context, final Object caller, final IStatus status) {
+ Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
+ Assert.isNotNull(context);
+ Assert.isNotNull(status);
+
+ List<ICallback> callbacks = ctx2cb.remove(context);
+ if (callbacks != null) {
+ for (final ICallback callback : callbacks) {
+ Protocol.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ callback.done(caller, status);
+ }
+ });
+ }
+ }
+ }
+}

Back to the top