/*******************************************************************************
* Copyright (c) 2011 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tm.te.tcf.locator.services;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.tm.tcf.core.TransientPeer;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.ILocator;
import org.eclipse.tm.te.tcf.core.Tcf;
import org.eclipse.tm.te.tcf.locator.ScannerRunnable;
import org.eclipse.tm.te.tcf.locator.activator.CoreBundleActivator;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.ILocatorModel;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel;
import org.eclipse.tm.te.tcf.locator.interfaces.preferences.IPreferenceKeys;
import org.eclipse.tm.te.tcf.locator.interfaces.services.ILocatorModelLookupService;
import org.eclipse.tm.te.tcf.locator.interfaces.services.ILocatorModelRefreshService;
import org.eclipse.tm.te.tcf.locator.interfaces.services.ILocatorModelUpdateService;
import org.eclipse.tm.te.tcf.locator.nodes.LocatorModel;
import org.eclipse.tm.te.tcf.locator.nodes.PeerModel;
/**
* 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);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.services.ILocatorModelRefreshService#refresh()
*/
@Override
public void refresh() {
Assert.isTrue(Protocol.isDispatchThread());
// Get the parent locator model
ILocatorModel model = getLocatorModel();
// If the parent model is already disposed, the service will drop out immediately
if (model.isDisposed()) return;
// If the TCF framework isn't initialized yet, the service will drop out immediately
if (!Tcf.isRunning()) return;
// Get the list of old children (update node instances where possible)
final List<IPeerModel> oldChildren = new ArrayList<IPeerModel>(Arrays.asList(model.getPeers()));
// Get the locator service
ILocator locatorService = Protocol.getLocator();
if (locatorService != null) {
// Check for the locator listener to be created and registered
if (model instanceof LocatorModel) ((LocatorModel)model).checkLocatorListener();
// Get the map of peers known to the locator service.
Map<String, IPeer> peers = locatorService.getPeers();
// Process the peers
processPeers(peers, oldChildren, model);
}
// Refresh the static peer definitions
refreshStaticPeers(oldChildren, model);
// If there are remaining old children, remove them from the model (non-recursive)
for (IPeerModel oldChild : oldChildren) model.getService(ILocatorModelUpdateService.class).remove(oldChild);
}
/**
* 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<IPeerModel> oldChildren, ILocatorModel model) {
Assert.isNotNull(peers);
Assert.isNotNull(oldChildren);
Assert.isNotNull(model);
for (String peerId : peers.keySet()) {
// Get the peer instance for the current peer id
IPeer peer = peers.get(peerId);
// Try to find an existing peer node first
IPeerModel peerNode = model.getService(ILocatorModelLookupService.class).lkupPeerModelById(peerId);
// And create a new one if we cannot find it
if (peerNode == null) peerNode = new PeerModel(model, peer);
else oldChildren.remove(peerNode);
// Validate the peer node before adding
peerNode = model.validatePeerNodeForAdd(peerNode);
if (peerNode != null) {
// Add the peer node to model
model.getService(ILocatorModelUpdateService.class).add(peerNode);
// And schedule for immediate status update
Runnable runnable = new ScannerRunnable(model.getScanner(), peerNode);
Protocol.invokeLater(runnable);
}
}
}
/**
* Refresh the static peer definitions.
*
* @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 refreshStaticPeers(List<IPeerModel> oldChildren, ILocatorModel model) {
Assert.isNotNull(oldChildren);
Assert.isNotNull(model);
// Get the root locations to lookup the static peer definitions
File[] roots = getStaticPeerLookupDirectories();
if (roots.length > 0) {
// The map of peers created from the static definitions
Map<String, IPeer> peers = new HashMap<String, IPeer>();
// Process the root locations
for (File root : roots) {
// List all "*.ini" 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("ini"); //$NON-NLS-1$
}
});
// If there are ini files to read, process them
if (candidates != null && candidates.length > 0) {
for (File candidate : candidates) {
try {
Properties properties = new Properties();
// Load the properties found in the candidate file into
// the Properties object. The input stream passed to
// the Properties.load(...) method needs to be closed
// manually afterwards.
InputStream is = null;
try {
is = new FileInputStream(candidate);
properties.load(is);
} finally {
if (is != null) {
try { is.close(); } catch (IOException e) { /* ignored on purpose */ }
}
}
// Remember the file path within the properties
properties.setProperty("Path", candidate.getAbsolutePath()); //$NON-NLS-1$
// Validate the name attribute. If not set, set
// it to the file name without the .ini extension.
String name = properties.getProperty(IPeer.ATTR_NAME);
if (name == null || "".equals(name.trim())) { //$NON-NLS-1$
name = new Path(candidate.getAbsolutePath()).removeFileExtension().lastSegment();
properties.setProperty(IPeer.ATTR_NAME, name);
}
// Validate the id attribute. If not set, generate one.
String id = properties.getProperty(IPeer.ATTR_ID);
if (id == null || "".equals(id.trim()) || "USR:".equals(id.trim())) { //$NON-NLS-1$ //$NON-NLS-2$
String transport = properties.getProperty(IPeer.ATTR_TRANSPORT_NAME);
String host = properties.getProperty(IPeer.ATTR_IP_HOST);
String port = properties.getProperty(IPeer.ATTR_IP_PORT);
if (transport != null && host != null && !(id != null && "USR:".equals(id.trim()))) { //$NON-NLS-1$
id = transport.trim() + ":" + host.trim(); //$NON-NLS-1$
id += port != null ? ":" + port.trim() : ":1534"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
id = "USR:" + System.currentTimeMillis(); //$NON-NLS-1$
// If the key is not unique, we have to wait a little bit an try again
while (peers.containsKey(id)) {
try { Thread.sleep(20); } catch (InterruptedException e) { /* ignored on purpose */ }
id = "USR:" + System.currentTimeMillis(); //$NON-NLS-1$
}
}
properties.put(IPeer.ATTR_ID, id);
}
// Copy all string attributes
Map<String, String> attrs = new HashMap<String, String>();
for (Object key : properties.keySet()) {
if (key instanceof String && properties.get(key) instanceof String) {
attrs.put((String)key, (String)properties.get(key));
}
}
// Construct the peer from the attributes
IPeer peer = new TransientPeer(attrs);
// Add the constructed peer to the peers map
peers.put(peer.getID(), peer);
} catch (IOException e) {
/* ignored on purpose */
}
}
}
}
// Process the read peers
if (!peers.isEmpty()) processPeers(peers, oldChildren, model);
}
}
/**
* Returns the list of root locations to lookup for static peers definitions.
*
* @return The list of root locations or an empty list.
*/
protected File[] getStaticPeerLookupDirectories() {
// The list defining the root locations
List<File> rootLocations = new ArrayList<File>();
// Check on the peers root locations preference setting
String roots = Platform.getPreferencesService().getString(CoreBundleActivator.getUniqueIdentifier(),
IPreferenceKeys.PREF_STATIC_PEERS_ROOT_LOCATIONS,
null, null);
// If set, split it in its single components
if (roots != null) {
String[] candidates = roots.split(File.pathSeparator);
// Check on each candidate to denote an existing directory
for (String candidate : candidates) {
File file = new File(candidate);
if (file.canRead() && file.isDirectory() && !rootLocations.contains(file)) {
rootLocations.add(file);
}
}
} else {
// Try the bundles state location first (not available if launched with -data @none).
try {
File file = CoreBundleActivator.getDefault().getStateLocation().append(".peers").toFile(); //$NON-NLS-1$
if (file.canRead() && file.isDirectory() && !rootLocations.contains(file)) {
rootLocations.add(file);
}
} catch (IllegalStateException e) {
/* ignored on purpose */
}
// The users local peers lookup directory is $HOME/.tcf/.peers.
File file = new Path(System.getProperty("user.home")).append(".tcf/.peers").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
if (file.canRead() && file.isDirectory() && !rootLocations.contains(file)) {
rootLocations.add(file);
}
}
return rootLocations.toArray(new File[rootLocations.size()]);
}
}