/*******************************************************************************
* Copyright (c) 2011, 2014 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.locator.services;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.tcf.core.Command;
import org.eclipse.tcf.core.TransientPeer;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.ILocator;
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.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.persistence.interfaces.IPersistableNodeProperties;
import org.eclipse.tcf.te.runtime.persistence.interfaces.IURIPersistenceService;
import org.eclipse.tcf.te.runtime.services.ServiceManager;
import org.eclipse.tcf.te.tcf.core.Tcf;
import org.eclipse.tcf.te.tcf.core.interfaces.IChannelManager;
import org.eclipse.tcf.te.tcf.core.interfaces.IPeerProperties;
import org.eclipse.tcf.te.tcf.core.peers.Peer;
import org.eclipse.tcf.te.tcf.core.util.persistence.PeerDataHelper;
import org.eclipse.tcf.te.tcf.locator.interfaces.ILocatorModelListener;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorNode;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProperties;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelLookupService;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelRefreshService;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelUpdateService;
import org.eclipse.tcf.te.tcf.locator.model.ModelLocationUtil;
import org.eclipse.tcf.te.tcf.locator.model.ModelManager;
import org.eclipse.tcf.te.tcf.locator.nodes.LocatorNode;
/**
* Default locator model refresh service implementation.
*/
public class LocatorModelRefreshService extends AbstractLocatorModelService implements ILocatorModelRefreshService {
/**
* Constructor.
*
* @param parentModel The parent locator model instance. Must not be <code>null</code>.
*/
public LocatorModelRefreshService(ILocatorModel parentModel) {
super(parentModel);
}
/**
* Asynchronously invoke the callback within the TCF dispatch thread.
*
* @param callback The callback to invoke or <code>null</code>.
*/
protected final void invokeCallback(final ICallback callback) {
Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
if (callback != null) {
Protocol.invokeLater(new Runnable() {
@Override
public void run() {
callback.done(LocatorModelRefreshService.this, Status.OK_STATUS);
}
});
}
}
/*
* (non-Javadoc)
* @see
* org.eclipse.tcf.te.tcf.locator.interfaces.services.IPeerModelRefreshService#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$
// Get the parent peer model
ILocatorModel model = getLocatorModel();
// If the parent model is already disposed, the service will drop out immediately
if (model.isDisposed()) {
invokeCallback(callback);
return;
}
// If the TCF framework isn't initialized yet, the service will drop out immediately
if (!Tcf.isRunning()) {
invokeCallback(callback);
return;
}
refreshStaticPeers();
// Get the list of old children (update node instances where possible)
ILocatorNode[] locatorNodes = model.getLocatorNodes();
List<IPeer> oldChildren = new ArrayList<IPeer>();
for (ILocatorNode node : locatorNodes) {
if (!node.isStatic()) {
oldChildren.add(node.getPeer());
}
}
// Refresh the static peer definitions
processPeers(Protocol.getLocator().getPeers(), oldChildren, model);
// If there are remaining old children, remove them from the model (non-recursive)
for (IPeer oldChild : oldChildren) {
model.getService(ILocatorModelUpdateService.class).remove(oldChild);
}
ILocatorModelListener[] listeners = model.getListener();
for (ILocatorModelListener listener : listeners) {
listener.modelChanged(model, null, false);
}
// Invoke the callback
invokeCallback(callback);
}
/**
* Process the given map of peers and update the given locator model.
*
* @param peers The map of peers to process. Must not be <code>null</code>.
* @param oldChildren The list of old children. Must not be <code>null</code>.
* @param model The locator model. Must not be <code>null</code>.
*/
protected void processPeers(Map<String, IPeer> peers, List<IPeer> oldChildren, ILocatorModel model) {
Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
Assert.isNotNull(peers);
Assert.isNotNull(oldChildren);
Assert.isNotNull(model);
for (Entry<String, IPeer> entry : peers.entrySet()) {
// Get the peer instance for the current peer id
IPeer peer = entry.getValue();
// Check if the peer is filtered
if (isFiltered(peer)) continue;
// Try to find an existing peer node first
IPeer lkupPeer = model.getService(ILocatorModelLookupService.class)
.lkupPeerById(entry.getKey());
// And create a new one if we cannot find it
if (lkupPeer == null) {
// Validate peer before adding
lkupPeer = model.validatePeer(peer);
if (lkupPeer != null) model.getService(ILocatorModelUpdateService.class)
.add(lkupPeer);
}
else {
oldChildren.remove(peer);
}
}
}
/*
* (non-Javadoc)
* @see
* org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelRefreshService#refresh(org
* .eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorNode,
* org.eclipse.tcf.te.runtime.interfaces.callback.ICallback)
*/
@Override
public void refresh(final ILocatorNode locatorNode, ICallback callback) {
Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$
// Get the parent peer model
final ILocatorModel model = getLocatorModel();
// If the parent model is already disposed, the service will drop out immediately
if (model.isDisposed()) {
invokeCallback(callback);
return;
}
// If the TCF framework isn't initialized yet, the service will drop out immediately
if (!Tcf.isRunning()) {
invokeCallback(callback);
return;
}
final IAsyncRefreshableCtx refreshCtx = (IAsyncRefreshableCtx) locatorNode
.getAdapter(IAsyncRefreshableCtx.class);
refreshCtx.setQueryState(QueryType.CONTEXT, QueryState.IN_PROGRESS);
refreshCtx.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
Map<String, Boolean> flags = new HashMap<String, Boolean>();
flags.put(IChannelManager.FLAG_FORCE_NEW, Boolean.TRUE);
flags.put(IChannelManager.FLAG_NO_PATH_MAP, Boolean.TRUE);
flags.put(IChannelManager.FLAG_NO_VALUE_ADD, Boolean.TRUE);
final ICallback finCb = new Callback(callback) {
@Override
protected void internalDone(Object caller, IStatus status) {
refreshCtx.setQueryState(QueryType.CONTEXT, QueryState.DONE);
refreshCtx.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
final ILocatorModel model = ModelManager.getLocatorModel();
final ILocatorModelListener[] listeners = model.getListener();
if (listeners.length > 0) {
Protocol.invokeLater(new Runnable() {
@Override
public void run() {
for (ILocatorModelListener listener : listeners) {
listener.modelChanged(model, locatorNode, false);
}
}
});
}
}
};
Tcf.getChannelManager().openChannel(locatorNode.getPeer(), flags, new IChannelManager.DoneOpenChannel() {
@Override
public void doneOpenChannel(Throwable error, final IChannel channel) {
if (error != null || channel == null) {
locatorNode.removeAll(ILocatorNode.class);
refreshCtx.setQueryState(QueryType.CONTEXT, QueryState.DONE);
refreshCtx.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
if (channel != null) {
Tcf.getChannelManager().closeChannel(channel);
}
if (locatorNode.isStatic()) {
locatorNode.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, locatorNode.getProperty(ILocatorNode.PROPERTY_STATIC_INSTANCE));
}
else {
ILocatorModelUpdateService update = model.getService(ILocatorModelUpdateService.class);
update.remove(locatorNode.getPeer());
}
invokeCallback(finCb);
}
else {
onDoneOpenChannelRefreshLocatorNode(channel, locatorNode, new Callback(finCb) {
@Override
protected void internalDone(Object caller, org.eclipse.core.runtime.IStatus status) {
Tcf.getChannelManager().closeChannel(channel);
}
});
}
}
});
}
protected void onDoneOpenChannelRefreshLocatorNode(final IChannel channel, final ILocatorNode locatorNode, final ICallback callback) {
final ILocator locator = channel.getRemoteService(ILocator.class);
final IAsyncRefreshableCtx refreshCtx = (IAsyncRefreshableCtx) locatorNode
.getAdapter(IAsyncRefreshableCtx.class);
if (locator == null) {
locatorNode.removeAll(ILocatorNode.class);
refreshCtx.setQueryState(QueryType.CONTEXT, QueryState.DONE);
refreshCtx.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
invokeCallback(callback);
}
else {
locator.getAgentID(new ILocator.DoneGetAgentID() {
@Override
public void doneGetAgentID(IToken token, Exception error, String agentID) {
if (error == null) {
locatorNode.setProperty(IPeer.ATTR_AGENT_ID, agentID);
}
refreshCtx.setQueryState(QueryType.CONTEXT, QueryState.DONE);
getPeers(locator, channel, locatorNode, callback);
}
});
}
}
protected void getPeers(final ILocator locator, final IChannel channel, final ILocatorNode locatorNode, final ICallback callback) {
@SuppressWarnings("unused")
Command cmd = new Command(channel, locator, "getPeers", null) { //$NON-NLS-1$
@Override
public void done(Exception error, Object[] args) {
if (error == null) {
Assert.isTrue(args.length == 2);
error = toError(args[0]);
}
// If the error is still null here, process the returned peers
if (error == null && args[1] != null) {
// calculate proxies
String parentProxies = locatorNode.getPeer().getAttributes()
.get(IPeerProperties.PROP_PROXIES);
IPeer[] proxies = PeerDataHelper.decodePeerList(parentProxies);
List<IPeer> proxiesList = new ArrayList<IPeer>(Arrays.asList(proxies));
proxiesList.add(locatorNode.getPeer());
proxies = proxiesList.toArray(new IPeer[proxiesList.size()]);
String encProxies = PeerDataHelper.encodePeerList(proxies);
List<ILocatorNode> oldChildren = locatorNode.getChildren(ILocatorNode.class);
ILocatorModelLookupService lkup = getLocatorModel()
.getService(ILocatorModelLookupService.class);
String parentAgentId = locatorNode.getPeer().getAgentID();
if (parentAgentId == null) {
parentAgentId = locatorNode.getStringProperty(IPeer.ATTR_AGENT_ID);
}
String parentId = normalizeId(locatorNode.getPeer().getID());
@SuppressWarnings("unchecked")
Collection<Map<String, String>> peerAttributesList = (Collection<Map<String, String>>) args[1];
for (Map<String, String> attributes : peerAttributesList) {
String agentId = attributes.get(IPeer.ATTR_AGENT_ID);
String id = attributes.get(IPeer.ATTR_ID);
String normalizedId = normalizeId(id);
ILocatorNode existing = null;
ILocatorNode[] lkupNodes = agentId != null ? lkup
.lkupLocatorNodeByAgentId(locatorNode, agentId) : new ILocatorNode[0];
if (lkupNodes.length == 0) {
lkupNodes = id != null ? lkup.lkupLocatorNodeById(locatorNode, id) : new ILocatorNode[0];
}
for (ILocatorNode node : lkupNodes) {
if (normalizeId(node.getPeer().getID()).equals(normalizedId)) {
oldChildren.remove(node);
existing = node;
break;
}
}
if (agentId != null && !agentId.equals(parentAgentId)) {
ILocatorNode parent = locatorNode.getParent(ILocatorNode.class);
ILocatorNode[] parentNodes = lkup
.lkupLocatorNodeByAgentId(parent, agentId);
while (parentNodes.length == 0 && parent != null) {
parent = parent.getParent(ILocatorNode.class);
parentNodes = lkup.lkupLocatorNodeByAgentId(parent, agentId);
}
attributes = new HashMap<String, String>(attributes);
attributes.put(IPeerProperties.PROP_PROXIES, encProxies);
IPeer peer = new TransientPeer(attributes);
if (existing == null) {
if (parentNodes.length == 0 && !isFiltered(peer)) {
locatorNode.add(new LocatorNode(peer));
}
}
else {
if (parentNodes.length == 0 && !isFiltered(peer)) {
existing.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, peer);
}
else {
locatorNode.remove(existing, true);
}
}
}
else if (locatorNode.isStatic()) {
attributes = new HashMap<String, String>(attributes);
attributes.put(IPeerProperties.PROP_PROXIES, parentProxies);
attributes.putAll(((IPeer)locatorNode.getProperty(ILocatorNode.PROPERTY_STATIC_INSTANCE)).getAttributes());
IPeer peer = new TransientPeer(attributes);
locatorNode.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, peer);
}
}
for (ILocatorNode old : oldChildren) {
if (!old.isStatic()) {
locatorNode.remove(old, true);
}
}
for (ILocatorNode child : locatorNode.getChildren(ILocatorNode.class)) {
String childAgentId = child.getPeer().getAgentID();
String childId = child.getPeer().getID();
if (parentAgentId.equals(childAgentId)) {
locatorNode.remove(child, true);
}
else if (parentId != null) {
if (parentId.equals(normalizeId(childId))) {
locatorNode.remove(child, true);
}
}
}
}
else {
List<ILocatorNode> oldChildren = locatorNode.getChildren(ILocatorNode.class);
for (ILocatorNode old : oldChildren) {
if (old.isStatic()) {
old.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, locatorNode
.getProperty(ILocatorNode.PROPERTY_STATIC_INSTANCE));
}
else {
locatorNode.remove(old, true);
}
}
}
IAsyncRefreshableCtx refreshCtx = (IAsyncRefreshableCtx) locatorNode
.getAdapter(IAsyncRefreshableCtx.class);
refreshCtx.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
// Invoke the callback
invokeCallback(callback);
}
};
}
protected String normalizeId(String id) {
if (id != null) {
id = id.toLowerCase().replaceAll(":localhost:", "::"); //$NON-NLS-1$ //$NON-NLS-2$
id = id.replaceAll(":127\\.0+\\.0+\\.0*1:", "::"); //$NON-NLS-1$ //$NON-NLS-2$
}
return id;
}
/**
* Returns if or if not the given peer is filtered.
*
* @param peer The peer or <code>null</code>.
* @return <code>True</code> if the given peer is filtered, <code>false</code> otherwise.
*/
protected boolean isFiltered(IPeer peer) {
boolean filtered = peer == null;
if (!filtered) {
String value = peer.getAttributes().get("ValueAdd"); //$NON-NLS-1$
boolean isValueAdd = value != null && ("1".equals(value.trim()) || Boolean.parseBoolean(value.trim())); //$NON-NLS-1$
boolean isCli = peer.getName() != null && (peer.getName().endsWith("Command Server") || peer.getName().endsWith("CLI Server")); //$NON-NLS-1$ //$NON-NLS-2$
filtered = isValueAdd || isCli;
}
return filtered;
}
/**
* Returns the list of root locations to lookup for static locator definitions.
*
* @return The list of root locations or an empty list.
*/
protected File[] getStaticLocatorsLookupDirectories() {
// The list defining the root locations
List<File> rootLocations = new ArrayList<File>();
// always add default root location
IPath defaultPath = ModelLocationUtil.getStaticLocatorsRootLocation();
if (defaultPath != null) {
File file = defaultPath.toFile();
if (file.canRead() && file.isDirectory() && !rootLocations.contains(file)) {
rootLocations.add(file);
}
}
return rootLocations.toArray(new File[rootLocations.size()]);
}
/**
* Refresh the static locator definitions.
*/
protected void refreshStaticPeers() {
// Get the root locations to lookup the static peer definitions
File[] roots = getStaticLocatorsLookupDirectories();
if (roots.length > 0) {
// The lst of locator peers created from the static definitions
List<IPeer> peers = new ArrayList<IPeer>();
// Process the root locations
for (File root : roots) {
// List all "*.locator" files within the root location
File[] candidates = root.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
IPath path = new Path(pathname.getAbsolutePath());
return path.getFileExtension() != null && path.getFileExtension()
.toLowerCase().equals("locator"); //$NON-NLS-1$
}
});
// If there are ini files to read, process them
if (candidates != null && candidates.length > 0) {
for (File candidate : candidates) {
try {
IURIPersistenceService service = ServiceManager.getInstance()
.getService(IURIPersistenceService.class);
IPeer tempPeer = (IPeer) service.read(IPeer.class, candidate
.getAbsoluteFile().toURI());
Map<String, String> attrs = new HashMap<String, String>(tempPeer.getAttributes());
// Remember the file path within the properties
attrs.put(IPersistableNodeProperties.PROPERTY_URI, candidate
.getAbsoluteFile().toURI().toString());
// Construct the peer from the attributes
IPeer peer = new Peer(attrs);
peers.add(peer);
}
catch (IOException e) {
/* ignored on purpose */
}
}
}
}
for (IPeer peer : peers) {
ILocatorModelUpdateService update = getLocatorModel()
.getService(ILocatorModelUpdateService.class);
update.add(peer, true);
}
}
}
}