/*******************************************************************************
* 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.nodes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.jobs.Job;
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.core.interfaces.listeners.IChannelStateChangeListener;
import org.eclipse.tm.te.tcf.locator.Scanner;
import org.eclipse.tm.te.tcf.locator.activator.CoreBundleActivator;
import org.eclipse.tm.te.tcf.locator.interfaces.IModelListener;
import org.eclipse.tm.te.tcf.locator.interfaces.IScanner;
import org.eclipse.tm.te.tcf.locator.interfaces.ITracing;
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.ILocatorModelService;
import org.eclipse.tm.te.tcf.locator.interfaces.services.ILocatorModelUpdateService;
import org.eclipse.tm.te.tcf.locator.listener.ChannelStateChangeListener;
import org.eclipse.tm.te.tcf.locator.listener.LocatorListener;
import org.eclipse.tm.te.tcf.locator.services.LocatorModelLookupService;
import org.eclipse.tm.te.tcf.locator.services.LocatorModelRefreshService;
import org.eclipse.tm.te.tcf.locator.services.LocatorModelUpdateService;
import org.eclipse.tm.te.tcf.locator.utils.IPAddressUtil;
/**
* Default locator model implementation.
*/
public class LocatorModel extends PlatformObject implements ILocatorModel {
// The unique model id
private final UUID uniqueId = UUID.randomUUID();
// Flag to mark the model disposed
private boolean disposed;
// The list of known peers
private final Map<String, IPeerModel> peers = new HashMap<String, IPeerModel>();
// Reference to the scanner
private IScanner scanner = null;
// Reference to the model locator listener
private ILocator.LocatorListener locatorListener = null;
// Reference to the model channel state change listener
private IChannelStateChangeListener channelStateChangeListener = null;
// The list of registered model listeners
private final List<IModelListener> modelListener = new ArrayList<IModelListener>();
// Reference to the refresh service
private final ILocatorModelRefreshService refreshService = new LocatorModelRefreshService(this);
// Reference to the lookup service
private final ILocatorModelLookupService lookupService = new LocatorModelLookupService(this);
// Reference to the update service
private final ILocatorModelUpdateService updateService = new LocatorModelUpdateService(this);
/**
* Constructor.
*/
public LocatorModel() {
super();
disposed = false;
channelStateChangeListener = new ChannelStateChangeListener(this);
Tcf.addChannelStateChangeListener(channelStateChangeListener);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#addListener(org.eclipse.tm.te.tcf.locator.core.interfaces.IModelListener)
*/
@Override
public void addListener(IModelListener listener) {
Assert.isNotNull(listener);
Assert.isTrue(Protocol.isDispatchThread());
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.addListener( " + listener + " )", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$ //$NON-NLS-2$
}
if (!modelListener.contains(listener)) modelListener.add(listener);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#removeListener(org.eclipse.tm.te.tcf.locator.core.interfaces.IModelListener)
*/
@Override
public void removeListener(IModelListener listener) {
Assert.isNotNull(listener);
Assert.isTrue(Protocol.isDispatchThread());
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.removeListener( " + listener + " )", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$ //$NON-NLS-2$
}
modelListener.remove(listener);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.interfaces.nodes.ILocatorModel#getListener()
*/
@Override
public IModelListener[] getListener() {
return modelListener.toArray(new IModelListener[modelListener.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#dispose()
*/
@Override
public void dispose() {
Assert.isTrue(Protocol.isDispatchThread());
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.dispose()", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$
}
// If already disposed, we are done immediately
if (disposed) return;
disposed = true;
final IModelListener[] listeners = getListener();
if (listeners.length > 0) {
Protocol.invokeLater(new Runnable() {
@Override
public void run() {
for (IModelListener listener : listeners) {
listener.locatorModelDisposed(LocatorModel.this);
}
}
});
}
modelListener.clear();
if (locatorListener != null) {
Protocol.getLocator().removeListener(locatorListener);
locatorListener = null;
}
if (channelStateChangeListener != null) {
Tcf.removeChannelStateChangeListener(channelStateChangeListener);
channelStateChangeListener = null;
}
if (scanner != null) {
stopScanner();
scanner = null;
}
peers.clear();
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#isDisposed()
*/
@Override
public boolean isDisposed() {
return disposed;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#getPeers()
*/
@Override
public IPeerModel[] getPeers() {
return peers.values().toArray(new IPeerModel[peers.values().size()]);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(ILocator.LocatorListener.class)) {
return locatorListener;
}
if (adapter.isAssignableFrom(IScanner.class)) {
return scanner;
}
if (adapter.isAssignableFrom(ILocatorModelRefreshService.class)) {
return refreshService;
}
if (adapter.isAssignableFrom(ILocatorModelLookupService.class)) {
return lookupService;
}
if (adapter.isAssignableFrom(ILocatorModelUpdateService.class)) {
return updateService;
}
if (adapter.isAssignableFrom(Map.class)) {
return peers;
}
return super.getAdapter(adapter);
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public final boolean equals(Object obj) {
if (obj instanceof LocatorModel) {
return uniqueId.equals(((LocatorModel)obj).uniqueId);
}
return super.equals(obj);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#getService(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public <V extends ILocatorModelService> V getService(Class<V> serviceInterface) {
Assert.isNotNull(serviceInterface);
return (V)getAdapter(serviceInterface);
}
/**
* Check if the locator listener has been created and registered
* to the global locator service.
* <p>
* <b>Note:</b> This method is not intended to be call from clients.
*/
public void checkLocatorListener() {
Assert.isTrue(Protocol.isDispatchThread());
Assert.isNotNull(Protocol.getLocator());
if (locatorListener == null) {
locatorListener = doCreateLocatorListener(this);
Protocol.getLocator().addListener(locatorListener);
}
}
/**
* Creates the locator listener instance.
*
* @param model The parent model. Must not be <code>null</code>.
* @return The locator listener instance.
*/
protected ILocator.LocatorListener doCreateLocatorListener(ILocatorModel model) {
Assert.isNotNull(model);
return new LocatorListener(model);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#getScanner()
*/
@Override
public IScanner getScanner() {
if (scanner == null) scanner = new Scanner(this);
return scanner;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#startScanner(long, long)
*/
@Override
public void startScanner(long delay, long schedule) {
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.startScanner( " + delay + ", " + schedule + " )", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
IScanner scanner = getScanner();
if (scanner != null) {
// Pass on the schedule parameter
Map<String, Object> config = new HashMap<String, Object>(scanner.getConfiguration());
config.put(IScanner.PROP_SCHEDULE, Long.valueOf(schedule));
scanner.setConfiguration(config);
}
// The default scanner implementation is a job.
// -> schedule here if it is a job
if (scanner instanceof Job) {
Job job = (Job)scanner;
job.setSystem(true);
job.setPriority(Job.DECORATE);
job.schedule(delay);
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#stopScanner()
*/
@Override
public void stopScanner() {
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.stopScanner()", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$
}
if (scanner != null) {
// Terminate the scanner
scanner.terminate();
// Reset the scanner reference
scanner = null;
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.ILocatorModel#validatePeerNodeForAdd(org.eclipse.tm.te.tcf.locator.core.interfaces.nodes.IPeerModel)
*/
@Override
public IPeerModel validatePeerNodeForAdd(IPeerModel node) {
Assert.isNotNull(node);
Assert.isTrue(Protocol.isDispatchThread());
// Get the peer from the peer node
IPeer peer = node.getPeer();
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd( " + (peer != null ? peer.getID() : null) + " )", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$ //$NON-NLS-2$
}
IPeerModel result = node;
// Check on the filtered by preference settings what to do
boolean isFilterByAgentId = Platform.getPreferencesService().getBoolean(CoreBundleActivator.getUniqueIdentifier(),
IPreferenceKeys.PREF_FILTER_BY_AGENT_ID,
false, null);
if (isFilterByAgentId) {
// Peers are filtered by agent id. Don't add the peer node
// if we have another peer node already having the same agent id
String agentId = peer.getAgentID();
IPeerModel[] previousNodes = agentId != null ? getService(ILocatorModelLookupService.class).lkupPeerModelByAgentId(agentId) : new IPeerModel[0];
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd: agentId=" + agentId + ", Matching peer nodes " //$NON-NLS-1$ //$NON-NLS-2$
+ (previousNodes.length > 0 ? "found (" + previousNodes.length +")" : "not found --> DONE") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
, ITracing.ID_TRACE_LOCATOR_MODEL, this);
}
boolean fireListener = false;
for (IPeerModel previousNode : previousNodes) {
// Get the peer for the previous node
IPeer previousPeer = previousNode.getPeer();
if (previousPeer != null) {
// We prefer to use the peer node for the canonical IP address before
// the loop back address before any other address.
String loopback = IPAddressUtil.getInstance().getIPv4LoopbackAddress();
String canonical = IPAddressUtil.getInstance().getCanonicalAddress();
String peerIP = peer.getAttributes().get(IPeer.ATTR_IP_HOST);
String previousPeerIP = previousPeer.getAttributes().get(IPeer.ATTR_IP_HOST);
String peerPort = peer.getAttributes().get(IPeer.ATTR_IP_PORT);
if (peerPort == null || "".equals(peerPort)) peerPort = "1534"; //$NON-NLS-1$ //$NON-NLS-2$
String previousPeerPort = previousPeer.getAttributes().get(IPeer.ATTR_IP_PORT);
if (previousPeerPort == null || "".equals(previousPeerPort)) previousPeerPort = "1534"; //$NON-NLS-1$ //$NON-NLS-2$
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd: loopback=" + loopback + ", canonical=" + canonical //$NON-NLS-1$ //$NON-NLS-2$
+ ", peerIP=" + peerIP + ", previousPeerIP=" + previousPeerIP //$NON-NLS-1$ //$NON-NLS-2$
+ ", peerPort=" + peerPort + ", previousPeerPort=" + previousPeerPort //$NON-NLS-1$ //$NON-NLS-2$
, ITracing.ID_TRACE_LOCATOR_MODEL, this);
}
// If the ports of the agent instances are identical,
// than try to find the best representation of the agent instance
if (peerPort.equals(previousPeerPort)) {
if (canonical != null && canonical.equals(peerIP) && !canonical.equals(previousPeerIP)) {
// Remove the old node and replace it with the new new
peers.remove(previousNode.getPeer().getID());
fireListener = true;
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd: Previous peer node replaced (canonical overwrite)" //$NON-NLS-1$
, ITracing.ID_TRACE_LOCATOR_MODEL, this);
}
} else if (loopback != null && loopback.equals(peerIP) && !loopback.equals(previousPeerIP)
&& (canonical == null || !canonical.equals(previousPeerIP))) {
// Remove the old node and replace it with the new new
peers.remove(previousNode.getPeer().getID());
fireListener = true;
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd: Previous peer node replaced (loopback overwrite)" //$NON-NLS-1$
, ITracing.ID_TRACE_LOCATOR_MODEL, this);
}
} else {
// Drop the current node
result = null;
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd: Previous peer node kept, new peer node dropped" //$NON-NLS-1$
, ITracing.ID_TRACE_LOCATOR_MODEL, this);
}
}
// Break the loop if the ports matched
break;
}
if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) {
CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeerNodeForAdd: Previous peer node kept, new peer node added (Port mismatch)" //$NON-NLS-1$
, ITracing.ID_TRACE_LOCATOR_MODEL, this);
}
}
}
if (fireListener) {
final IModelListener[] listeners = getListener();
if (listeners.length > 0) {
Protocol.invokeLater(new Runnable() {
@Override
public void run() {
for (IModelListener listener : listeners) {
listener.locatorModelChanged(LocatorModel.this);
}
}
});
}
}
}
return result;
}
}