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) {
}
}
|