Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e2c0f7b9fbef914af6338181a161e3325fbc40dc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/*******************************************************************************
 * 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.tcf.te.tcf.locator;

import java.net.ConnectException;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.tcf.core.Command;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.ILocator;
import org.eclipse.tcf.te.tcf.core.Tcf;
import org.eclipse.tcf.te.tcf.locator.interfaces.IScanner;
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.interfaces.nodes.IPeerModelProperties;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelLookupService;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.ILocatorModelUpdateService;
import org.eclipse.tcf.te.tcf.locator.nodes.PeerModel;
import org.eclipse.tcf.te.tcf.locator.nodes.PeerRedirector;


/**
 * Scanner runnable to be executed for each peer to probe within the
 * TCF event dispatch thread.
 */
public class ScannerRunnable implements Runnable, IChannel.IChannelListener {

	// Reference to the parent model scanner
	private final IScanner parentScanner;
	// Reference to the peer model node to update
	/* default */ final IPeerModel peerNode;
	// Reference to the channel
	/* default */ IChannel channel = null;
	// Mark if the used channel is a shared channel instance
	/* default */ boolean sharedChannel = false;

	/**
	 * Constructor.
	 *
	 * @param scanner The parent model scanner or <code>null</code> if the runnable is constructed from outside a scanner.
	 * @param peerNode The peer model instance. Must not be <code>null</code>.
	 */
	public ScannerRunnable(IScanner scanner, IPeerModel peerNode) {
		super();

		parentScanner = scanner;

		Assert.isNotNull(peerNode);
		this.peerNode = peerNode;
	}

	/**
	 * Returns the parent scanner instance.
	 *
	 * @return The parent scanner instance or <code>null</code>.
	 */
	protected final IScanner getParentScanner() {
		return parentScanner;
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		if (peerNode != null && peerNode.getPeer() != null) {
			// Check if there is a shared channel available which is still in open state
			channel = Tcf.getChannelManager().getChannel(peerNode.getPeer());
			if (channel == null || channel.getState() != IChannel.STATE_OPEN) {
				sharedChannel = false;
				// Open the channel
				channel = peerNode.getPeer().openChannel();
				// Add ourself as channel listener
				channel.addChannelListener(this);
			} else {
				sharedChannel = true;
				// Shared channel is in open state -> use it
				onChannelOpened();
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.protocol.IChannel.IChannelListener#onChannelOpened()
	 */
	@SuppressWarnings("unused")
    @Override
	public void onChannelOpened() {
		// Peer is reachable
		if (channel != null && !sharedChannel) {
			// Remove ourself as channel listener
			channel.removeChannelListener(this);
		}

		// Set the peer state property
		if (peerNode != null) {
			int counter = peerNode.getIntProperty(IPeerModelProperties.PROP_CHANNEL_REF_COUNTER);
			peerNode.setProperty(IPeerModelProperties.PROP_STATE, counter > 0 ? IPeerModelProperties.STATE_CONNECTED : IPeerModelProperties.STATE_REACHABLE);
			peerNode.setProperty(IPeerModelProperties.PROP_LAST_SCANNER_ERROR, null);
		}

		if (channel != null && channel.getState() == IChannel.STATE_OPEN) {
			// Keep the channel open as long as the query for the remote peers is running.
			boolean keepOpen = false;

			// Get the parent model from the model mode
			final ILocatorModel model = (ILocatorModel)peerNode.getAdapter(ILocatorModel.class);
			if (model != null) {
				// Get the local service
				Collection<String> localServices = new ArrayList<String>(channel.getLocalServices());
				// Get the remote services
				Collection<String> remoteServices = new ArrayList<String>(channel.getRemoteServices());

				// Get the update service
				ILocatorModelUpdateService updateService = model.getService(ILocatorModelUpdateService.class);
				if (updateService != null) {
					// Update the services nodes
					updateService.updatePeerServices(peerNode, localServices, remoteServices);
				}

				// Use the open channel to ask the remote peer what other peers it knows
				ILocator locator = channel.getRemoteService(ILocator.class);
				if (locator != null) {
					// Channel must be kept open as long as the command runs
					keepOpen = true;
					// Issue the command
		            new Command(channel, locator, "getPeers", null) { //$NON-NLS-1$
		                @Override
		                public void done(Exception error, Object[] args) {
		                    if (error == null) {
		                        assert args.length == 2;
		                        error = toError(args[0]);
		                    }
		                    // If the error is still null here, process the returned peers
		                    if (error == null && args[1] != null) {
	                        	// Get the parent peer
	                        	IPeer parentPeer = channel.getRemotePeer();
	                        	// Get the old child list
	                        	List<IPeerModel> oldChildren = new ArrayList<IPeerModel>(model.getChildren(parentPeer.getID()));

		                    	// "getPeers" returns a collection of peer attribute maps
		                        @SuppressWarnings("unchecked")
                                Collection<Map<String,String>> peerAttributesList = (Collection<Map<String,String>>)args[1];
		                        for (Map<String,String> attributes : peerAttributesList) {
		                        	// Get the peer id
		                        	String peerId = attributes.get(IPeer.ATTR_ID);
		                        	// Create a peer instance
		                        	IPeer peer = new PeerRedirector(parentPeer, attributes);
									// Try to find an existing peer node first
									IPeerModel peerNode = model.getService(ILocatorModelLookupService.class).lkupPeerModelById(parentPeer.getID(), peerId);
									if (peerNode == null) {
										// Not yet known -> add it
										peerNode = new PeerModel(model, peer);
										peerNode.setParentNode(ScannerRunnable.this.peerNode);
										// Validate the peer node before adding
										peerNode = model.validateChildPeerNodeForAdd(peerNode);
										if (peerNode != null) {
											// Add the child peer node to model
											model.getService(ILocatorModelUpdateService.class).addChild(peerNode);
											// And schedule for immediate status update
											Runnable runnable = new ScannerRunnable(getParentScanner(), peerNode);
											Protocol.invokeLater(runnable);
										}
									} else {
										// The parent node should be set and match
										Assert.isTrue(peerNode.getParentNode() != null && peerNode.getParentNode().equals(ScannerRunnable.this.peerNode));
										// Peer node found, update the peer instance
										peerNode.setProperty(IPeerModelProperties.PROP_INSTANCE, peer);
										// And remove it from the old child list
										oldChildren.remove(peerNode);
									}
		                        }

		                        // Everything left in the old child list is not longer known to the remote peer
		                        for (IPeerModel child : oldChildren) {
		                        	// Remove the child peer node from the model
									model.getService(ILocatorModelUpdateService.class).removeChild(child);
		                        }
		                    }

		                    // Once everything is processed, close the channel
		                    if (!sharedChannel) channel.close();
		                }
		            };
				}

				// If we don't queried the DNS name of the peer, or the peer IP changed,
				// trigger a query (can run in any thread, outside TCF dispatch and UI
				// thread). This make sense only if there is an IP address to query at all.
				final String ip = channel.getRemotePeer().getAttributes().get(IPeer.ATTR_IP_HOST);
				if (ip != null && !"".equals(ip)) { //$NON-NLS-1$
					if (peerNode.getStringProperty("dns.name.transient") == null || !ip.equals(peerNode.getStringProperty("dns.lastIP.transient"))) { //$NON-NLS-1$ //$NON-NLS-2$
						// If the IP address changed, reset the "do not query again" marker
						if (!ip.equals(peerNode.getStringProperty("dns.lastIP.transient"))) { //$NON-NLS-1$
							peerNode.setProperty("dns.lastIP.transient", ip); //$NON-NLS-1$
							peerNode.setProperty("dns.skip.transient", false); //$NON-NLS-1$
						}

						if (!peerNode.getBooleanProperty("dns.skip.transient")) { //$NON-NLS-1$
							Runnable runnable = new Runnable() {
								@Override
								public void run() {
									try {
										InetAddress address = InetAddress.getByName(ip);
										final String name = address.getCanonicalHostName();
										Protocol.invokeLater(new Runnable() {
											@Override
											public void run() {
												if (name != null && !"".equals(name) && !ip.equals(name)) { //$NON-NLS-1$
													String dnsName = name.indexOf('.') != -1 ? name.substring(0, name.indexOf('.')) : name;
													if (!ip.equalsIgnoreCase(dnsName)) {
														peerNode.setProperty("dns.name.transient", dnsName.toLowerCase()); //$NON-NLS-1$
													}
												}
											}
										});
									}
									catch (UnknownHostException e) {
										Protocol.invokeLater(new Runnable() {
											@Override
                                            public void run() {
												peerNode.setProperty("dns.skip.transient", true); //$NON-NLS-1$
											}
										});
									}
								}
							};

							Thread thread = new Thread(runnable, "DNS Query Thread for " + ip); //$NON-NLS-1$
							thread.start();
						}
					}
				}
			}

			// And close the channel
			if (!sharedChannel && !keepOpen) channel.close();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.protocol.IChannel.IChannelListener#onChannelClosed(java.lang.Throwable)
	 */
	@Override
	public void onChannelClosed(Throwable error) {
		// Peer is not reachable

		if (channel != null) {
			// Remove ourself as channel listener
			channel.removeChannelListener(this);
		}

		// Set the peer state property, if the scanner the runnable
		// has been scheduled from is still active.
		if (peerNode != null && (parentScanner == null || parentScanner != null && !parentScanner.isTerminated())) {
			peerNode.setProperty(IPeerModelProperties.PROP_CHANNEL_REF_COUNTER, null);
			boolean timeout = error instanceof SocketTimeoutException || (error instanceof ConnectException && error.getMessage() != null && error.getMessage().startsWith("Connection timed out:")); //$NON-NLS-1$
			peerNode.setProperty(IPeerModelProperties.PROP_STATE, timeout ? IPeerModelProperties.STATE_NOT_REACHABLE : IPeerModelProperties.STATE_ERROR);
			peerNode.setProperty(IPeerModelProperties.PROP_LAST_SCANNER_ERROR, error instanceof SocketTimeoutException ? null : error);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.tcf.protocol.IChannel.IChannelListener#congestionLevel(int)
	 */
	@Override
	public void congestionLevel(int level) {
	}

}

Back to the top