/******************************************************************************* * 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 * Michael Jahn - [452466] Running multiple agents on localhost *******************************************************************************/ package org.eclipse.tcf.te.tcf.locator.nodes; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.tcf.protocol.IPeer; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.ILocator; import org.eclipse.tcf.te.runtime.utils.net.IPAddressUtil; import org.eclipse.tcf.te.tcf.core.interfaces.ITransportTypes; import org.eclipse.tcf.te.tcf.locator.activator.CoreBundleActivator; import org.eclipse.tcf.te.tcf.locator.interfaces.ILocatorModelListener; import org.eclipse.tcf.te.tcf.locator.interfaces.ITracing; 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.services.ILocatorModelLookupService; import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelRefreshService; import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelService; import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelUpdateService; import org.eclipse.tcf.te.tcf.locator.listener.LocatorListener; import org.eclipse.tcf.te.tcf.locator.services.LocatorModelLookupService; import org.eclipse.tcf.te.tcf.locator.services.LocatorModelRefreshService; import org.eclipse.tcf.te.tcf.locator.services.LocatorModelUpdateService; /** * 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 locator nodes /* default */ final Map<String, ILocatorNode> locatorNodes = new HashMap<String, ILocatorNode>(); // The list of registered model listeners private final List<ILocatorModelListener> modelListener = new ArrayList<ILocatorModelListener>(); // Reference to the model locator listener private ILocator.LocatorListener locatorListener = null; // 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; } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.core.interfaces.nodes.ILocatorModel#addListener(org.eclipse.tcf.te.tcf.locator.core.interfaces.IModelListener) */ @Override public void addListener(ILocatorModelListener listener) { Assert.isNotNull(listener); 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.tcf.te.tcf.locator.core.interfaces.nodes.ILocatorModel#removeListener(org.eclipse.tcf.te.tcf.locator.core.interfaces.IModelListener) */ @Override public void removeListener(ILocatorModelListener listener) { Assert.isNotNull(listener); 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.tcf.te.tcf.locator.interfaces.nodes.IPeerModel#getListener() */ @Override public ILocatorModelListener[] getListener() { Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ return modelListener.toArray(new ILocatorModelListener[modelListener.size()]); } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.core.interfaces.nodes.ILocatorModel#dispose() */ @Override public void dispose() { Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ 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; if (locatorListener != null) { Protocol.getLocator().removeListener(locatorListener); locatorListener = null; } final ILocatorModelListener[] listeners = getListener(); if (listeners.length > 0) { Protocol.invokeLater(new Runnable() { @Override public void run() { for (ILocatorModelListener listener : listeners) { listener.modelDisposed(LocatorModel.this); } } }); } modelListener.clear(); locatorNodes.clear(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.core.interfaces.nodes.ILocatorModel#isDisposed() */ @Override public boolean isDisposed() { return disposed; } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel#getLocatorNodes() */ @Override public ILocatorNode[] getLocatorNodes() { return locatorNodes.values().toArray(new ILocatorNode[locatorNodes.size()]); } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel#getPeers() */ @Override public IPeer[] getPeers() { List<IPeer> peers = new ArrayList<IPeer>(); for (ILocatorNode locatorNode : locatorNodes.values()) { peers.add(locatorNode.getPeer()); } return peers.toArray(new IPeer[peers.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(ILocatorModelRefreshService.class)) { return refreshService; } if (adapter.isAssignableFrom(ILocatorModelLookupService.class)) { return lookupService; } if (adapter.isAssignableFrom(ILocatorModelUpdateService.class)) { return updateService; } if (adapter.isAssignableFrom(Map.class)) { return locatorNodes; } return super.getAdapter(adapter); } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return uniqueId.hashCode(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.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); } /* (non-Javadoc) * @see org.eclipse.tcf.te.tcf.locator.interfaces.nodes.ILocatorModel#validatePeer(org.eclipse.tcf.protocol.IPeer) */ @Override public IPeer validatePeer(IPeer peer) { Assert.isNotNull(peer); Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ // Skip validation if the transport type is not TCP or SSL String transport = peer.getTransportName(); if (transport == null || !ITransportTypes.TRANSPORT_TYPE_TCP.equals(transport) && !ITransportTypes.TRANSPORT_TYPE_SSL.equals(transport)) { return peer; } if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) { CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeer( " + peer.getID() + " )", ITracing.ID_TRACE_LOCATOR_MODEL, this); //$NON-NLS-1$ //$NON-NLS-2$ } IPeer result = peer; // Get the loopback address String loopback = IPAddressUtil.getInstance().getIPv4LoopbackAddress(); // Get the canonical address String canonical = IPAddressUtil.getInstance().getIPv4CanonicalAddress(); // Get the peer IP and port String peerIP = peer.getAttributes().get(IPeer.ATTR_IP_HOST); String peerPort = peer.getAttributes().get(IPeer.ATTR_IP_PORT); // If the new peer IP is not a local host address, we are done checking if (!IPAddressUtil.getInstance().isLocalHost(peerIP)) return result; // The list of peers to remove from the model final List<String> toRemove = new ArrayList<String>(); // Loop the discovered peers and find previous local host nodes for (Entry<String, ILocatorNode> entry : locatorNodes.entrySet()) { // Get the IP address from peers with transport type TCP or SSL IPeer candidate = entry.getValue().getPeer(); if (ITransportTypes.TRANSPORT_TYPE_TCP.equals(candidate.getTransportName()) || ITransportTypes.TRANSPORT_TYPE_SSL.equals(candidate.getTransportName())) { String ip = candidate.getAttributes().get(IPeer.ATTR_IP_HOST); Assert.isNotNull(ip); String port = candidate.getAttributes().get(IPeer.ATTR_IP_PORT); Assert.isNotNull(port); // If the IP is for localhost, we have to do additional checking if (IPAddressUtil.getInstance().isLocalHost(ip) && port.equals(peerPort)) { // If the IP of the peer already in the model is the loopback address, // ignore all other. if (ip.equals(loopback)) { result = null; break; } // If the IP of the new peer IP is the loopback address, remove this peer if (peerIP.equals(loopback)) { toRemove.add(entry.getKey()); continue; } // None of the IP's are matching the loopback address, keep the one which is the canonical address if (ip.equals(canonical)) { result = null; break; } if (peerIP.equals(canonical)) { toRemove.add(entry.getKey()); continue; } // None of the IP's are matching the loopback nor the canonical address, // keep the one already in the model result = null; break; } } } // Remove any node identified to get removed. Do it via the update // service to make sure the notifications are send out. for (String candidate : toRemove) { if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) { CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeer( " + peer.getID() + " ): Remove old peer with id " + candidate, //$NON-NLS-1$ //$NON-NLS-2$ ITracing.ID_TRACE_LOCATOR_MODEL, this); } ILocatorNode node = locatorNodes.get(candidate); if (node != null) getService(ILocatorModelUpdateService.class).remove(node.getPeer()); } if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_LOCATOR_MODEL)) { CoreBundleActivator.getTraceHandler().trace("LocatorModel.validatePeer( " + peer.getID() + " ): result = " + (result != null ? result.getID() : "null"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ ITracing.ID_TRACE_LOCATOR_MODEL, this); } // Return the result return result; } /** * 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(), "Illegal Thread Access"); //$NON-NLS-1$ 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); Assert.isTrue(Protocol.isDispatchThread(), "Illegal Thread Access"); //$NON-NLS-1$ return new LocatorListener(model); } }