From fe4801d772c40bfe3684f487cc67dc41d601c2ef Mon Sep 17 00:00:00 2001 From: Uwe Stieber Date: Thu, 31 Oct 2013 10:18:11 +0100 Subject: Target Explorer: Rework processes runtime model refresh services/RuntimeModelRefreshService.java - Fix: Refreshed context properties and children not merged reliable into model - Introduced runtime model refresh service delegates. Allows to refresh target specific properties. - Separate non-UI delegates from UI delegates. Introduced IDelegateService for non-UI delegates. --- .../META-INF/MANIFEST.MF | 1 + .../core/activator/CoreBundleActivator.java | 35 ++ .../core/model/interfaces/IProcessContextNode.java | 2 +- .../runtime/IRuntimeModelRefreshService.java | 45 ++ .../runtime/IRuntimeModelUpdateService.java | 30 + .../core/model/listener/ModelListener.java | 37 ++ .../core/model/nodes/ProcessContextNode.java | 2 +- .../processes/core/model/runtime/RuntimeModel.java | 6 +- .../services/RuntimeModelRefreshService.java | 655 --------------------- .../services/RuntimeModelUpdateService.java | 264 ++++++++- .../tcf/te/tcf/processes/core/nls/Messages.java | 2 + .../te/tcf/processes/core/nls/Messages.properties | 2 + 12 files changed, 393 insertions(+), 688 deletions(-) create mode 100644 target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelUpdateService.java create mode 100644 target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/listener/ModelListener.java delete mode 100644 target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelRefreshService.java (limited to 'target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core') diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/META-INF/MANIFEST.MF b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/META-INF/MANIFEST.MF index 180fede13..18705b77a 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/META-INF/MANIFEST.MF +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/META-INF/MANIFEST.MF @@ -30,6 +30,7 @@ Export-Package: org.eclipse.tcf.te.tcf.processes.core.activator;x-internal:=true org.eclipse.tcf.te.tcf.processes.core.model.interfaces, org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime, org.eclipse.tcf.te.tcf.processes.core.model.internal.factory;x-internal:=true, + org.eclipse.tcf.te.tcf.processes.core.model.listener, org.eclipse.tcf.te.tcf.processes.core.model.nodes, org.eclipse.tcf.te.tcf.processes.core.model.properties, org.eclipse.tcf.te.tcf.processes.core.model.runtime, diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/activator/CoreBundleActivator.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/activator/CoreBundleActivator.java index ec80202ba..aed90e597 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/activator/CoreBundleActivator.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/activator/CoreBundleActivator.java @@ -10,7 +10,12 @@ package org.eclipse.tcf.te.tcf.processes.core.activator; import org.eclipse.core.runtime.Plugin; +import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.te.runtime.tracing.TraceHandler; +import org.eclipse.tcf.te.tcf.locator.interfaces.IModelListener; +import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel; +import org.eclipse.tcf.te.tcf.locator.model.Model; +import org.eclipse.tcf.te.tcf.processes.core.model.listener.ModelListener; import org.osgi.framework.BundleContext; /** @@ -21,6 +26,8 @@ public class CoreBundleActivator extends Plugin { private static BundleContext context; // The trace handler instance private static volatile TraceHandler traceHandler; + // The locator model listener instance + /* default */ IModelListener listener; // The shared instance private static CoreBundleActivator plugin; @@ -72,6 +79,20 @@ public class CoreBundleActivator extends Plugin { public void start(BundleContext bundleContext) throws Exception { CoreBundleActivator.context = bundleContext; plugin = this; + + // Create the model listener instance + listener = new ModelListener(); + + Runnable runnable = new Runnable() { + @Override + public void run() { + if (listener == null) return; + // Register the model listener with the locator model + Model.getModel().addListener(listener); + } + }; + + Protocol.invokeLater(runnable); } /* (non-Javadoc) @@ -81,6 +102,20 @@ public class CoreBundleActivator extends Plugin { public void stop(BundleContext bundleContext) throws Exception { CoreBundleActivator.context = null; plugin = null; + + // Remove the model listener from the locator model + if (listener != null) { + Runnable runnable = new Runnable() { + @Override + public void run() { + ILocatorModel model = Model.getModel(true); + if (model != null) model.removeListener(listener); + listener = null; + } + }; + if (Protocol.isDispatchThread()) runnable.run(); + else Protocol.invokeAndWait(runnable); + } } } diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/IProcessContextNode.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/IProcessContextNode.java index 13cd5d500..0f4828c4c 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/IProcessContextNode.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/IProcessContextNode.java @@ -20,7 +20,7 @@ public interface IProcessContextNode extends IContainerModelNode { /** * Process context node types */ - public enum TYPE { Process, Thread } + public enum TYPE { Unknown, Process, Thread } /** * Set the type of the context node. diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelRefreshService.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelRefreshService.java index 58966d510..ff12de9f7 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelRefreshService.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelRefreshService.java @@ -9,14 +9,59 @@ *******************************************************************************/ package org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime; +import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelRefreshService; +import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode; /** * Interface to be implemented by processes runtime model refresh services. */ public interface IRuntimeModelRefreshService extends IModelRefreshService { + /** + * Interface to be implemented by processes runtime model refresh service delegates. + */ + public static interface IDelegate { + + /** + * Called from the processes runtime model refresh service after having queried the + * system monitor and the process context objects. Can be used to initialize the node + * type. + *

+ * This method is invoked before {@link #postRefreshContext(IChannel, IProcessContextNode, ICallback)}. + *

+ * Note: The method must be called in the TCF event dispatch thread. + * + * @param parentContextId The parent context id or null for the root context. + * @param node The process context node. Must not be null. + */ + public void setNodeType(String parentContextId, IProcessContextNode node); + + /** + * Called from the processes runtime model refresh service after having queried the + * system monitor and the process context objects. Can be used to initiate additional + * context node specific queries. + *

+ * This method is invoked after {@link #setNodeType(IProcessContextNode)}. + *

+ * Note: The method must be called in the TCF event dispatch thread. + * + * @param channel An open channel. Must not be null. + * @param node The process context node. Must not be null. + * @param callback The callback to invoke once the operation is completed. Must not be null. + */ + public void postRefreshContext(IChannel channel, IProcessContextNode node, ICallback callback); + + /** + * Returns the list of property names the delegate may add to a given process + * context node while executing {@link #postRefreshContext(IChannel, IProcessContextNode, ICallback)}. + * + * @return The list of managed property name or null. + */ + public String[] getManagedPropertyNames(); + } + /** * Auto refresh the content of the model from the top. It search for * all nodes with query state "done" and refresh them one by one. diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelUpdateService.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelUpdateService.java new file mode 100644 index 000000000..51ba24c0e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/interfaces/runtime/IRuntimeModelUpdateService.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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.interfaces.runtime; + +import org.eclipse.tcf.te.runtime.model.interfaces.IContainerModelNode; +import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelUpdateService; + +/** + * Interface to be implemented by processes runtime model update services. + */ +public interface IRuntimeModelUpdateService extends IModelUpdateService { + + /** + * Merge the child tree of the given source container with the child tree + * of the given destination container. + * + * @param dst The destination container. Must not be null. + * @param src The source container. Must not be null. + * + * @return True if the destination container changed, false otherwise. + */ + public void updateChildren(IContainerModelNode dst, IContainerModelNode src); +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/listener/ModelListener.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/listener/ModelListener.java new file mode 100644 index 000000000..e5d979468 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/listener/ModelListener.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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.listener; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel; +import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerModel; +import org.eclipse.tcf.te.tcf.locator.listener.ModelAdapter; +import org.eclipse.tcf.te.tcf.processes.core.model.ModelManager; + +/** + * Locator model listener implementation. + */ +public class ModelListener extends ModelAdapter { + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.tcf.locator.listener.ModelAdapter#locatorModelChanged(org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel, org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerModel, boolean) + */ + @Override + public void locatorModelChanged(ILocatorModel model, IPeerModel peer, boolean added) { + Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ + if (peer == null) return; + + if (!added) { + // Dispose possibly associated models + ModelManager.disposeRuntimeModel(peer); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/nodes/ProcessContextNode.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/nodes/ProcessContextNode.java index 97937c75f..0dc33b65f 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/nodes/ProcessContextNode.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/nodes/ProcessContextNode.java @@ -40,7 +40,7 @@ public class ProcessContextNode extends ContainerModelNode implements IProcessCo private ISysMonitor.SysMonitorContext sContext = null; // The node type - private TYPE type = TYPE.Process; + private TYPE type = TYPE.Unknown; // Context nodes needs asynchronous refreshes private final IAsyncRefreshableCtx refreshableCtxAdapter = new AsyncRefreshableCtxAdapter(); diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/RuntimeModel.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/RuntimeModel.java index 3d321e24b..bb328915c 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/RuntimeModel.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/RuntimeModel.java @@ -33,6 +33,7 @@ import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerModel; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModel; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModelLookupService; 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.model.runtime.services.RuntimeModelChannelService; import org.eclipse.tcf.te.tcf.processes.core.model.runtime.services.RuntimeModelLookupService; import org.eclipse.tcf.te.tcf.processes.core.model.runtime.services.RuntimeModelRefreshService; @@ -57,7 +58,7 @@ public final class RuntimeModel extends ContainerModelNode implements IRuntimeMo // Reference to the lookup service private final IRuntimeModelLookupService lookupService = new RuntimeModelLookupService(this); // Reference to the update service - private final IModelUpdateService updateService = new RuntimeModelUpdateService(this); + private final IRuntimeModelUpdateService updateService = new RuntimeModelUpdateService(this); // Reference to the channel service private final IModelChannelService channelService = new RuntimeModelChannelService(this); @@ -144,6 +145,9 @@ public final class RuntimeModel extends ContainerModelNode implements IRuntimeMo if (IModelLookupService.class.equals(adapter)) { return lookupService; } + if (IRuntimeModelUpdateService.class.equals(adapter)) { + return updateService; + } if (IModelUpdateService.class.equals(adapter)) { return updateService; } 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 deleted file mode 100644 index d61cf2803..000000000 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelRefreshService.java +++ /dev/null @@ -1,655 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012, 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.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -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.IProcessesV1; -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.events.ChangeEvent; -import org.eclipse.tcf.te.runtime.events.EventManager; -import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; -import org.eclipse.tcf.te.runtime.model.PendingOperationModelNode; -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.tcf.core.async.CallbackInvocationDelegate; -import org.eclipse.tcf.te.tcf.core.model.interfaces.IModel; -import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelChannelService; -import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelLookupService; -import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelUpdateService; -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.nodes.PendingOperationNode; - -/** - * Runtime model refresh service implementation. - */ -public class RuntimeModelRefreshService extends AbstractModelService implements IRuntimeModelRefreshService { - - /** - * Constructor. - * - * @param model The parent model. Must not be null. - */ - 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(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(); - - // 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; - } - - final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)model.getAdapter(IAsyncRefreshableCtx.class); - final AtomicBoolean resetPendingNode = new AtomicBoolean(false); - if (refreshable != null && refreshable.getQueryState(QueryType.CHILD_LIST) != QueryState.IN_PROGRESS) { - resetPendingNode.set(true); - // Mark the refresh as in progress - refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS); - // Create a new pending operation node and associate it with the refreshable - PendingOperationModelNode pendingNode = new PendingOperationNode(); - pendingNode.setParent(model); - refreshable.setPendingOperationNode(pendingNode); - // Trigger a refresh of the view content. - ChangeEvent event = new ChangeEvent(model, IContainerModelNode.NOTIFY_CHANGED, null, null); - EventManager.getInstance().fireEvent(event); - } - - // Get the list of old children (update node instances where possible) - final List oldChildren = model.getChildren(IProcessContextNode.class); - - // Refresh the process contexts from the agent - refreshContextChildren(oldChildren, model, null, 2, new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - final AtomicBoolean isDisposed = new AtomicBoolean(); - Runnable runnable = new Runnable() { - @Override - public void run() { - isDisposed.set(model.isDisposed()); - } - }; - if (Protocol.isDispatchThread()) runnable.run(); - else Protocol.invokeAndWait(runnable); - - if (!isDisposed.get()) { - // If there are remaining old children, remove them from the model (non-recursive) - for (IProcessContextNode oldChild : oldChildren) model.getService(IModelUpdateService.class).remove(oldChild); - } - - if (refreshable != null && resetPendingNode.get()) { - resetPendingNode.set(false); - // Mark the refresh as done - refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE); - // Reset the pending operation node - refreshable.setPendingOperationNode(null); - // Trigger a refresh of the view content. - ChangeEvent event = new ChangeEvent(model, IContainerModelNode.NOTIFY_CHANGED, null, null); - EventManager.getInstance().fireEvent(event); - } - - // Invoke the callback - if (callback != null) callback.done(this, Status.OK_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(IModelNode node, int flags, 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() || !(node instanceof IProcessContextNode)) { - if (callback != null) callback.done(this, Status.OK_STATUS); - return; - } - - // Perform the refresh of the node - doRefresh(model, node, 2, callback); - } - - /* (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 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(); - } - - /** - * Performs the auto refresh of the given nodes. - * - * @param model The runtime model. Must not be null. - * @param nodes The nodes. Must not be null. - * @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 null. - */ - protected 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 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); - // Refresh the node (only node and the direct children) - doRefresh(model, node, 1, callback); - } - } - } - - /** - * Performs the refresh of the given model node. - * - * @param model The runtime model. Must not be null. - * @param node The node. Must not be null. - * @param depth Until which depth the tree gets refreshed. - * @param callback The callback to invoke once the refresh operation finished, or null. - */ - protected void doRefresh(final IRuntimeModel model, final IModelNode node, final int depth, final ICallback callback) { - Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ - Assert.isNotNull(model); - Assert.isNotNull(node); - - // Refresh the process context from the agent - refreshContext(model, node, new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - if (status.getSeverity() == IStatus.ERROR) { - if (callback != null) callback.done(caller, status); - return; - } - - // Get the list of old children (update node instances where possible) - final List oldChildren = ((IProcessContextNode)node).getChildren(IProcessContextNode.class); - - // Refresh the children of the process context node from the agent - refreshContextChildren(oldChildren, model, (IProcessContextNode)node, depth, new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - final AtomicBoolean isDisposed = new AtomicBoolean(); - Runnable runnable = new Runnable() { - @Override - public void run() { - isDisposed.set(model.isDisposed()); - } - }; - if (Protocol.isDispatchThread()) runnable.run(); - else Protocol.invokeAndWait(runnable); - - if (!isDisposed.get()) { - // If there are remaining old children, remove them from the parent node (recursive) - for (IProcessContextNode oldChild : oldChildren) ((IProcessContextNode)node).remove(oldChild, true); - } - - // Invoke the callback - if (callback != null) callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - }); - } - }); - } - - /** - * Process the given map of process contexts and update the given model. - * - * @param contexts The map of contexts to process. Must not be null. - * @param oldChildren The list of old children. Must not be null. - * @param model The model. Must not be null. - * @param parent The parent context node or null. - */ - protected void processContexts(Map contexts, List oldChildren, IModel model, IProcessContextNode parent) { - Assert.isNotNull(contexts); - Assert.isNotNull(oldChildren); - Assert.isNotNull(model); - - for (Entry entry : contexts.entrySet()) { - // Get the context instance for the current id - IProcessContextNode candidate = entry.getValue(); - // Try to find an existing context node first - IModelNode[] nodes = model.getService(IModelLookupService.class).lkupModelNodesById(candidate.getStringProperty(IModelNode.PROPERTY_ID)); - // If found, update the context node properties from the new one - if (nodes.length > 0) { - for (IModelNode node : nodes) { - model.getService(IModelUpdateService.class).update(node, candidate); - oldChildren.remove(node); - } - } else { - if (parent == null) { - model.getService(IModelUpdateService.class).add(candidate); - } else { - // Validate the the children are added to the real parent node - nodes = model.getService(IModelLookupService.class).lkupModelNodesById(parent.getStringProperty(IModelNode.PROPERTY_ID)); - if (nodes.length > 0) { - // In fact we should have found just one parent - Assert.isTrue(nodes.length == 1); - Assert.isTrue(nodes[0] instanceof IProcessContextNode); - // Add to the real parent node - ((IProcessContextNode)nodes[0]).add(candidate); - } else { - // Add to the passed in parent node - parent.add(candidate); - } - } - } - } - } - - /** - * Refresh the given process context node. - * - * @param model The runtime model. Must not be null. - * @param node The node. Must not be null. - * @param callback The callback to invoke once the refresh operation finished, or null. - */ - protected void refreshContext(final IRuntimeModel model, final IModelNode node, final ICallback callback) { - Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ - Assert.isNotNull(model); - Assert.isNotNull(node); - - // 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) { - final IProcesses service = channel.getRemoteService(IProcesses.class); - Assert.isNotNull(service); - final IProcessesV1 serviceV1 = channel.getRemoteService(IProcessesV1.class); - final ISysMonitor sysMonService = channel.getRemoteService(ISysMonitor.class); - Assert.isNotNull(sysMonService); - final String contextId = ((IProcessContextNode)node).getStringProperty(IModelNode.PROPERTY_ID); - sysMonService.getContext(contextId, new ISysMonitor.DoneGetContext() { - @Override - public void doneGetContext(IToken token, Exception error, SysMonitorContext context) { - ((IProcessContextNode)node).setSysMonitorContext(context); - - // 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); - - // Get the process context - service.getContext(contextId, new IProcesses.DoneGetContext() { - @Override - public void doneGetContext(IToken token, Exception error, IProcesses.ProcessContext context) { - ((IProcessContextNode)node).setProcessContext(context); - if (serviceV1 != null && context != null) { - serviceV1.getCapabilities(context.getID(), new IProcessesV1.DoneGetCapabilities() { - @Override - public void doneGetCapabilities(IToken token, Exception error, Map properties) { - ((IProcessContextNode)node).setProperty(IProcessContextNodeProperties.PROPERTY_CAPABILITIES, properties); - - callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - }); - } - else - callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - }); - } - }); - } - }); - } else { - callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), error.getLocalizedMessage(), error)); - } - } - }); - } - - /** - * Refresh the children of the given process context node. - * - * @param oldChildren The list of old children. Must not be null. - * @param model The model. Must not be null. - * @param parent The parent context node or null. - * @param depth Until which depth the tree gets refreshed. - * @param callback The callback to invoke at the end of the operation. Must not be null. - */ - protected void refreshContextChildren(final List oldChildren, final IModel model, final IProcessContextNode parent, final int depth, final ICallback callback) { - Assert.isNotNull(oldChildren); - Assert.isNotNull(model); - Assert.isNotNull(callback); - - // Make sure that the callback is invoked even for unexpected cases - try { - // The map of contexts created from the agents response - final Map contexts = new HashMap(); - - // 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) { - // Determine the parent context id - String parentContextId = null; - if (parent != null) parentContextId = parent.getStringProperty(IModelNode.PROPERTY_ID); - - // Get the required services - final IProcesses service = channel.getRemoteService(IProcesses.class); - final IProcessesV1 serviceV1 = channel.getRemoteService(IProcessesV1.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; - } - - 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) { - final AsyncCallbackCollector collector = new AsyncCallbackCollector(new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ - if (status.getSeverity() == IStatus.OK) { - // Process the read process contexts - if (!contexts.isEmpty()) processContexts(contexts, oldChildren, model, parent); - callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } else { - callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), status.getMessage(), status.getException())); - } - } - }, new CallbackInvocationDelegate()); - - // Loop the returned context id's and query the context data - for (String id : context_ids) { - final String contextId = id; - final ICallback innerCallback = 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. - if (context != null) { - final IProcessContextNode node = createContextNodeFrom(context); - Assert.isNotNull(node); - node.setType(parent == null ? TYPE.Process : TYPE.Thread); - contexts.put(node.getUUID(), node); - - // 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); - - // Query the corresponding process context - service.getContext(contextId, new IProcesses.DoneGetContext() { - @Override - public void doneGetContext(IToken token, Exception error, IProcesses.ProcessContext context) { - // Errors are ignored - node.setProcessContext(context); - - if (context != null) node.setProperty(IProcessContextNodeProperties.PROPERTY_NAME, context.getName()); - - if (serviceV1 != null && context != null) { - serviceV1.getCapabilities(context.getID(), new IProcessesV1.DoneGetCapabilities() { - @Override - public void doneGetCapabilities(IToken token, Exception error, Map properties) { - node.setProperty(IProcessContextNodeProperties.PROPERTY_CAPABILITIES, properties); - // Get the asynchronous refresh context adapter - final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)node.getAdapter(IAsyncRefreshableCtx.class); - - // Refresh the children of the node if the depth is still larger than 0 - if (depth - 1 > 0 && (refreshable == null || !refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.IN_PROGRESS))) { - if (refreshable != null) { - // Mark the refresh as in progress - refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS); - // Create a new pending operation node and associate it with the refreshable - PendingOperationModelNode pendingNode = new PendingOperationNode(); - pendingNode.setParent(node); - refreshable.setPendingOperationNode(pendingNode); - } - - // Don't send change events while refreshing - final boolean changed = node.setChangeEventsEnabled(false); - // Initiate the refresh - List oldChildren = node.getChildren(IProcessContextNode.class); - refreshContextChildren(oldChildren, model, node, depth - 1, new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - // Mark the refresh as done - refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE); - // Reset the pending operation node - refreshable.setPendingOperationNode(null); - // Re-enable the change events if they had been enabled before - if (changed) node.setChangeEventsEnabled(true); - // Trigger a refresh of the view content - ChangeEvent event = new ChangeEvent(node, IContainerModelNode.NOTIFY_CHANGED, null, null); - EventManager.getInstance().fireEvent(event); - // Finally invoke the callback - innerCallback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - }); - } else { - innerCallback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - } - }); - } - else { - // Get the asynchronous refresh context adapter - final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)node.getAdapter(IAsyncRefreshableCtx.class); - - // Refresh the children of the node if the depth is still larger than 0 - if (depth - 1 > 0 && (refreshable == null || !refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.IN_PROGRESS))) { - if (refreshable != null) { - // Mark the refresh as in progress - refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS); - // Create a new pending operation node and associate it with the refreshable - PendingOperationModelNode pendingNode = new PendingOperationNode(); - pendingNode.setParent(node); - refreshable.setPendingOperationNode(pendingNode); - } - - // Don't send change events while refreshing - final boolean changed = node.setChangeEventsEnabled(false); - // Initiate the refresh - List oldChildren = node.getChildren(IProcessContextNode.class); - refreshContextChildren(oldChildren, model, node, depth - 1, new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - // Mark the refresh as done - refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE); - // Reset the pending operation node - refreshable.setPendingOperationNode(null); - // Re-enable the change events if they had been enabled before - if (changed) node.setChangeEventsEnabled(true); - // Trigger a refresh of the view content - ChangeEvent event = new ChangeEvent(node, IContainerModelNode.NOTIFY_CHANGED, null, null); - EventManager.getInstance().fireEvent(event); - // Finally invoke the callback - innerCallback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - }); - } else { - innerCallback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - - } - } - }); - } - }); - } else { - innerCallback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - } - }); - } - - collector.initDone(); - } else { - callback.done(RuntimeModelRefreshService.this, Status.OK_STATUS); - } - } else { - callback.done(RuntimeModelRefreshService.this, new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), error.getLocalizedMessage(), error)); - } - } - }); - } 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)); - } - } - - /** - * Create a context node instance from the given process context. - * - * @param context The system monitor context. Must not be null. - * @return The context node instance. - */ - public IProcessContextNode createContextNodeFrom(SysMonitorContext context) { - Assert.isNotNull(context); - - // Create a context node and associate the given context - IProcessContextNode node = getModel().getFactory().newInstance(IProcessContextNode.class); - node.setSysMonitorContext(context); - - // Re-create the context properties from the context - node.setProperty(IProcessContextNodeProperties.PROPERTY_ID, context.getID()); - - return node; - } -} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelUpdateService.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelUpdateService.java index ed28c16d2..2db351e91 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelUpdateService.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/model/runtime/services/RuntimeModelUpdateService.java @@ -9,22 +9,32 @@ *******************************************************************************/ package org.eclipse.tcf.te.tcf.processes.core.model.runtime.services; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.eclipse.core.runtime.Assert; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IProcesses; +import org.eclipse.tcf.services.ISysMonitor; 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.tcf.core.model.interfaces.services.IModelUpdateService; +import org.eclipse.tcf.te.runtime.services.ServiceManager; +import org.eclipse.tcf.te.runtime.services.interfaces.IDelegateService; import org.eclipse.tcf.te.tcf.core.model.services.AbstractModelService; import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode; 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; /** * Runtime model update service implementation. */ -public class RuntimeModelUpdateService extends AbstractModelService implements IModelUpdateService { +public class RuntimeModelUpdateService extends AbstractModelService implements IRuntimeModelUpdateService { /** * Constructor. @@ -40,6 +50,7 @@ public class RuntimeModelUpdateService extends AbstractModelServicenull. + * @param list The list. Must not be null. + * + * @return The matching process context node or null if not found. + */ + /* default */ IProcessContextNode findInList(String id, List list) { + Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ + Assert.isNotNull(id); + Assert.isNotNull(list); + + IProcessContextNode node = null; + + for (IProcessContextNode candidate : list) { + if (id.equals(candidate.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID))) { + node = candidate; + break; + } + } + + return node; + } + + /** + * Update the child tree of the destination container from the given source container. + * + * @param dst The destination container. Must not be null. + * @param src The source container. Must not be null. + * + * @return True if the destination container changed, false otherwise. + */ + /* default */ boolean __updateChildren(IContainerModelNode dst, IContainerModelNode src) { + Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ + Assert.isNotNull(dst); + Assert.isNotNull(src); + + boolean dstNodeChanged = false; + // Get the asynchronous refreshable contexts of the nodes IAsyncRefreshableCtx dstRefreshable = (IAsyncRefreshableCtx)dst.getAdapter(IAsyncRefreshableCtx.class); + Assert.isNotNull(dstRefreshable); IAsyncRefreshableCtx srcRefreshable = (IAsyncRefreshableCtx)src.getAdapter(IAsyncRefreshableCtx.class); - if (dstRefreshable != null && srcRefreshable != null) { - if (srcRefreshable.getQueryState(QueryType.CONTEXT) == QueryState.IN_PROGRESS || srcRefreshable.getQueryState(QueryType.CHILD_LIST) == QueryState.IN_PROGRESS) { - dstRefreshable.setPendingOperationNode(srcRefreshable.getPendingOperationNode()); - } - Assert.isTrue(srcRefreshable.getQueryState(QueryType.CONTEXT) != QueryState.IN_PROGRESS, "Context query of node '" + src.getName() + "' in progress while updating model."); //$NON-NLS-1$ //$NON-NLS-2$ - if (srcRefreshable.getQueryState(QueryType.CONTEXT) == QueryState.DONE) { - dstRefreshable.setQueryState(QueryType.CONTEXT, srcRefreshable.getQueryState(QueryType.CONTEXT)); - } - Assert.isTrue(srcRefreshable.getQueryState(QueryType.CHILD_LIST) != QueryState.IN_PROGRESS, "Child list query of node '" + src.getName() + "' in progress while updating model."); //$NON-NLS-1$ //$NON-NLS-2$ - if (srcRefreshable.getQueryState(QueryType.CHILD_LIST) == QueryState.DONE) { - dstRefreshable.setQueryState(QueryType.CHILD_LIST, srcRefreshable.getQueryState(QueryType.CHILD_LIST)); - if (dst instanceof IContainerModelNode) { - ((IContainerModelNode)dst).clear(); - if (src instanceof IContainerModelNode) { - for (IModelNode child : ((IContainerModelNode)src).getChildren()) { - ((IContainerModelNode)dst).add(child); - } + Assert.isNotNull(srcRefreshable); + + // Synchronize the refreshable states + Assert.isTrue(srcRefreshable.getQueryState(QueryType.CONTEXT) != QueryState.IN_PROGRESS, "Context query of node '" + src.getName() + "' in progress while updating model."); //$NON-NLS-1$ //$NON-NLS-2$ + if (srcRefreshable.getQueryState(QueryType.CONTEXT) == QueryState.DONE && dstRefreshable.getQueryState(QueryType.CONTEXT) != QueryState.DONE) { + dstRefreshable.setQueryState(QueryType.CONTEXT, QueryState.DONE); + dstNodeChanged |= true; + } + Assert.isTrue(srcRefreshable.getQueryState(QueryType.CHILD_LIST) != QueryState.IN_PROGRESS, "Child list query of node '" + src.getName() + "' in progress while updating model."); //$NON-NLS-1$ //$NON-NLS-2$ + if (srcRefreshable.getQueryState(QueryType.CHILD_LIST) == QueryState.DONE && dstRefreshable.getQueryState(QueryType.CHILD_LIST) != QueryState.DONE) { + dstRefreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE); + dstNodeChanged |= true; + } + + // If the refreshable state of the source container is PENDING, than we are done here + if (srcRefreshable.getQueryState(QueryType.CHILD_LIST) == QueryState.PENDING) { + return dstNodeChanged; + } + + // Get the list of old children (update node instances where possible) + final List oldChildren = dst.getChildren(IProcessContextNode.class); + + // Disable notifications while updating the child list + boolean eventEnablementChanged = dst.setChangeEventsEnabled(false); + + // Get the list of new children + final List newChildren = src.getChildren(IProcessContextNode.class); + + // Loop the list of new children and lookup a matching node in the list of old children + for (IProcessContextNode candidate : newChildren) { + String id = candidate.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID); + if (id == null) continue; + // Find the old process context node + IProcessContextNode oldNode = findInList(id, oldChildren); + if (oldNode != null) { + // Remove the old node from the old children list + oldChildren.remove(oldNode); + // Update the properties of the old node from the new node + dstNodeChanged |= __updateProperties(oldNode, candidate); + // If the child list of the new node is valid, update the child list + IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)candidate.getAdapter(IAsyncRefreshableCtx.class); + Assert.isNotNull(refreshable); + if (refreshable.getQueryState(QueryType.CONTEXT) == QueryState.DONE) { + // Update the child tree of the old node from the new node + dstNodeChanged |= __updateChildren(oldNode, candidate); + } + } else { + if (candidate.getParent() == null) { + // Parent not set -> Add the new child node + dstNodeChanged |= dst.add(candidate); + } else { + // Parent set -> Create a copy of the new node + IProcessContextNode copy = getModel().getFactory().newInstance(IProcessContextNode.class); + __updateProperties(copy, candidate); + IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)candidate.getAdapter(IAsyncRefreshableCtx.class); + Assert.isNotNull(refreshable); + if (refreshable.getQueryState(QueryType.CONTEXT) == QueryState.DONE) { + __updateChildren(copy, candidate); } + // Add the copy of the new node + dstNodeChanged |= dst.add(copy); } } } - if (dst instanceof IProcessContextNode && src instanceof IProcessContextNode) { - ((IProcessContextNode)dst).setSysMonitorContext(((IProcessContextNode)src).getSysMonitorContext()); - ((IProcessContextNode)dst).setProcessContext(((IProcessContextNode)src).getProcessContext()); - dst.setProperty(IProcessContextNodeProperties.PROPERTY_CMD_LINE, src.getProperty(IProcessContextNodeProperties.PROPERTY_CMD_LINE)); + // If there are remaining old children, remove them (non-recursive) + for (IProcessContextNode oldChild : oldChildren) { + dstNodeChanged |= dst.remove(oldChild, false); + } + + // Re-enable the change events + if (eventEnablementChanged) dst.setChangeEventsEnabled(true); + + return dstNodeChanged; + } + + /** + * Update the destination node properties from the given source node. + *

+ * Note: This method does not update the child tree. Use {@link #updateChildren(IContainerModelNode, IContainerModelNode)} + * for updating the child tree. + * + * @param dst The destination node. Must not be null. + * @param src The source node. Must not be null. + * + * @return True if the properties of the destination node changed, false otherwise. + */ + /* default */ boolean __updateProperties(IProcessContextNode dst, IProcessContextNode src) { + Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ + Assert.isNotNull(dst); + Assert.isNotNull(src); + + boolean eventEnablementChanged = dst.setChangeEventsEnabled(false); + boolean dstNodeChanged = false; + + // Update the properties of the destination node from the source node + for (String key : src.getProperties().keySet()) { + dstNodeChanged |= dst.setProperty(key, src.getProperty(key)); + } + + // Make sure that old properties are removed from the destination node. + // Collect the list of property names to check for removal + List managedPropertyNames = new ArrayList(); + managedPropertyNames.add(IProcessContextNodeProperties.PROPERTY_ID); + managedPropertyNames.add(IProcessContextNodeProperties.PROPERTY_NAME); + + // Determine if a delegate is registered + IDelegateService service = ServiceManager.getInstance().getService(dst, IDelegateService.class, false); + IRuntimeModelRefreshService.IDelegate delegate = service != null ? service.getDelegate(dst, IRuntimeModelRefreshService.IDelegate.class) : null; + + if (delegate == null && getModel().getService(IRuntimeModelRefreshService.class) instanceof RuntimeModelRefreshService) { + delegate = ((RuntimeModelRefreshService)getModel().getService(IRuntimeModelRefreshService.class)).defaultDelegate; + } + if (delegate != null) { + String[] candidates = delegate.getManagedPropertyNames(); + if (candidates != null) managedPropertyNames.addAll(Arrays.asList(candidates)); + } + + // Clean up the destination node + for (String managedPropertyName : managedPropertyNames) { + if (src.isProperty(managedPropertyName, null)) { + dstNodeChanged |= dst.setProperty(managedPropertyName, null); + } + } + + // Update the system monitor context object (if necessary) + ISysMonitor.SysMonitorContext s1 = dst.getSysMonitorContext(); + ISysMonitor.SysMonitorContext s2 = src.getSysMonitorContext(); + if ((s1 == null && s2 != null) || (s1 != null && s2 == null) || !s1.equals(s2)) { + dst.setSysMonitorContext(src.getSysMonitorContext()); + dstNodeChanged |= true; + } + + // Update the process context object (if necessary) + IProcesses.ProcessContext p1 = dst.getProcessContext(); + IProcesses.ProcessContext p2 = src.getProcessContext(); + if ((p1 == null && p2 != null) || (p1 != null && p2 == null) || !p1.equals(p2)) { + dst.setProcessContext(src.getProcessContext()); + dstNodeChanged |= true; + } + + // Update the node type (if necessary) + if (dst.getType() != src.getType()) { + dst.setType(src.getType()); + dstNodeChanged |= true; } // Re-enable the change events if (eventEnablementChanged) dst.setChangeEventsEnabled(true); + return dstNodeChanged; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelUpdateService#update(org.eclipse.tcf.te.runtime.model.interfaces.IModelNode, org.eclipse.tcf.te.runtime.model.interfaces.IModelNode) + */ + @Override + public void update(IModelNode dst, IModelNode src) { + Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ + Assert.isNotNull(dst); + Assert.isNotNull(src); + + // The nodes to update must be process context nodes + if (!(dst instanceof IProcessContextNode) || !(src instanceof IProcessContextNode)) { + return; + } + + // Update the nodes only if the id's are matching + String dstContextId = dst.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID); + String srcContextId = src.getStringProperty(IProcessContextNodeProperties.PROPERTY_ID); + if ((dstContextId == null && srcContextId != null) || (dstContextId != null && srcContextId == null) || !dstContextId.equals(srcContextId)) { + return; + } + + boolean dstNodeChanged = __updateProperties((IProcessContextNode)dst, (IProcessContextNode)src); + if (dst instanceof IContainerModelNode && src instanceof IContainerModelNode) { + dstNodeChanged |= __updateChildren((IContainerModelNode)dst, (IContainerModelNode)src); + } + // Fire a properties changed event if the destination node changed if (dstNodeChanged) { - dst.fireChangeEvent("properties", null, dst.getProperties()); //$NON-NLS-1$ + dst.fireChangeEvent(IContainerModelNode.NOTIFY_CHANGED, null, null); } } } diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.java index 231162619..84040c5c9 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.java @@ -69,4 +69,6 @@ public class Messages extends NLS { public static String TerminateStep_error_terminate; public static String TerminateStep_error_getContext; public static String TerminateStep_error_openChannel; + + public static String RuntimeModelRefreshService_error_channelClosed; } diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.properties index dd06d09dc..bbbec851f 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.properties +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/nls/Messages.properties @@ -48,3 +48,5 @@ TerminateStep_error_title=Error TerminateStep_error_terminate=Failed to terminate context: {0} TerminateStep_error_getContext=Failed to get context: {0} TerminateStep_error_openChannel=Failed to open channel: {0} + +RuntimeModelRefreshService_error_channelClosed=Refresh failed. Channel is closed. -- cgit v1.2.3