1
2
3
4 package javax.jmdns.impl;
5
6 import java.io.IOException;
7 import java.net.InetAddress;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.Timer;
17 import java.util.TimerTask;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.ConcurrentMap;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.TimeUnit;
23 import java.util.logging.Level;
24 import java.util.logging.Logger;
25
26 import javax.jmdns.JmDNS;
27 import javax.jmdns.JmmDNS;
28 import javax.jmdns.NetworkTopologyDiscovery;
29 import javax.jmdns.NetworkTopologyEvent;
30 import javax.jmdns.NetworkTopologyListener;
31 import javax.jmdns.ServiceInfo;
32 import javax.jmdns.ServiceListener;
33 import javax.jmdns.ServiceTypeListener;
34 import javax.jmdns.impl.constants.DNSConstants;
35
36
37
38
39
40
41 public class JmmDNSImpl implements JmmDNS, NetworkTopologyListener, ServiceInfoImpl.Delegate {
42 private static Logger logger = Logger.getLogger(JmmDNSImpl.class.getName());
43
44 private final Set<NetworkTopologyListener> _networkListeners;
45
46
47
48
49 private final ConcurrentMap<InetAddress, JmDNS> _knownMDNS;
50
51
52
53
54 private final ConcurrentMap<String, ServiceInfo> _services;
55
56 private final ExecutorService _ListenerExecutor;
57
58 private final ExecutorService _jmDNSExecutor;
59
60 private final Timer _timer;
61
62
63
64
65 public JmmDNSImpl() {
66 super();
67 _networkListeners = Collections.synchronizedSet(new HashSet<NetworkTopologyListener>());
68 _knownMDNS = new ConcurrentHashMap<InetAddress, JmDNS>();
69 _services = new ConcurrentHashMap<String, ServiceInfo>(20);
70 _ListenerExecutor = Executors.newSingleThreadExecutor();
71 _jmDNSExecutor = Executors.newCachedThreadPool();
72 _timer = new Timer("Multihommed mDNS.Timer", true);
73 (new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance())).start(_timer);
74 }
75
76
77
78
79
80 @Override
81 public void close() throws IOException {
82 if (logger.isLoggable(Level.FINER)) {
83 logger.finer("Cancelling JmmDNS: " + this);
84 }
85 _timer.cancel();
86 _ListenerExecutor.shutdown();
87
88 ExecutorService executor = Executors.newCachedThreadPool();
89 for (final JmDNS mDNS : _knownMDNS.values()) {
90 executor.submit(new Runnable() {
91
92
93
94 @Override
95 public void run() {
96 try {
97 mDNS.close();
98 } catch (IOException exception) {
99
100 }
101 }
102 });
103 }
104 executor.shutdown();
105 try {
106 executor.awaitTermination(DNSConstants.CLOSE_TIMEOUT, TimeUnit.MILLISECONDS);
107 } catch (InterruptedException exception) {
108 logger.log(Level.WARNING, "Exception ", exception);
109 }
110 _knownMDNS.clear();
111 }
112
113
114
115
116
117 @Override
118 public String[] getNames() {
119 Set<String> result = new HashSet<String>();
120 for (JmDNS mDNS : _knownMDNS.values()) {
121 result.add(mDNS.getName());
122 }
123 return result.toArray(new String[result.size()]);
124 }
125
126
127
128
129
130 @Override
131 public String[] getHostNames() {
132 Set<String> result = new HashSet<String>();
133 for (JmDNS mDNS : _knownMDNS.values()) {
134 result.add(mDNS.getHostName());
135 }
136 return result.toArray(new String[result.size()]);
137 }
138
139
140
141
142
143 @Override
144 public InetAddress[] getInterfaces() throws IOException {
145 Set<InetAddress> result = new HashSet<InetAddress>();
146 for (JmDNS mDNS : _knownMDNS.values()) {
147 result.add(mDNS.getInterface());
148 }
149 return result.toArray(new InetAddress[result.size()]);
150 }
151
152
153
154
155
156 @Override
157 public ServiceInfo[] getServiceInfos(String type, String name) {
158 return this.getServiceInfos(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
159 }
160
161
162
163
164
165 @Override
166 public ServiceInfo[] getServiceInfos(String type, String name, long timeout) {
167 return this.getServiceInfos(type, name, false, timeout);
168 }
169
170
171
172
173
174 @Override
175 public ServiceInfo[] getServiceInfos(String type, String name, boolean persistent) {
176 return this.getServiceInfos(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
177 }
178
179
180
181
182
183 @Override
184 public ServiceInfo[] getServiceInfos(final String type, final String name, final boolean persistent, final long timeout) {
185
186 final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size()));
187 ExecutorService executor = Executors.newCachedThreadPool();
188 for (final JmDNS mDNS : _knownMDNS.values()) {
189 executor.submit(new Runnable() {
190
191
192
193 @Override
194 public void run() {
195 result.add(mDNS.getServiceInfo(type, name, persistent, timeout));
196 }
197 });
198 }
199 executor.shutdown();
200 try {
201 executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
202 } catch (InterruptedException exception) {
203 logger.log(Level.WARNING, "Exception ", exception);
204 }
205 return result.toArray(new ServiceInfo[result.size()]);
206 }
207
208
209
210
211
212 @Override
213 public void requestServiceInfo(String type, String name) {
214 this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
215 }
216
217
218
219
220
221 @Override
222 public void requestServiceInfo(String type, String name, boolean persistent) {
223 this.requestServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
224 }
225
226
227
228
229
230 @Override
231 public void requestServiceInfo(String type, String name, long timeout) {
232 this.requestServiceInfo(type, name, false, timeout);
233 }
234
235
236
237
238
239 @Override
240 public void requestServiceInfo(final String type, final String name, final boolean persistent, final long timeout) {
241
242 for (final JmDNS mDNS : _knownMDNS.values()) {
243 _jmDNSExecutor.submit(new Runnable() {
244
245
246
247 @Override
248 public void run() {
249 mDNS.requestServiceInfo(type, name, persistent, timeout);
250 }
251 });
252 }
253 }
254
255
256
257
258
259 @Override
260 public void addServiceTypeListener(ServiceTypeListener listener) throws IOException {
261 for (JmDNS mDNS : _knownMDNS.values()) {
262 mDNS.addServiceTypeListener(listener);
263 }
264 }
265
266
267
268
269
270 @Override
271 public void removeServiceTypeListener(ServiceTypeListener listener) {
272 for (JmDNS mDNS : _knownMDNS.values()) {
273 mDNS.removeServiceTypeListener(listener);
274 }
275 }
276
277
278
279
280
281 @Override
282 public void addServiceListener(String type, ServiceListener listener) {
283 for (JmDNS mDNS : _knownMDNS.values()) {
284 mDNS.addServiceListener(type, listener);
285 }
286 }
287
288
289
290
291
292 @Override
293 public void removeServiceListener(String type, ServiceListener listener) {
294 for (JmDNS mDNS : _knownMDNS.values()) {
295 mDNS.removeServiceListener(type, listener);
296 }
297 }
298
299
300
301
302
303 @Override
304 public void textValueUpdated(ServiceInfo target, byte[] value) {
305 synchronized (_services) {
306 for (JmDNS mDNS : _knownMDNS.values()) {
307 ServiceInfo info = ((JmDNSImpl) mDNS).getServices().get(target.getQualifiedName());
308 if (info != null) {
309 info.setText(value);
310 } else {
311 logger.warning("We have a mDNS that does not know about the service info being updated.");
312 }
313 }
314 }
315 }
316
317
318
319
320
321 @Override
322 public void registerService(ServiceInfo info) throws IOException {
323
324 synchronized (_services) {
325 for (JmDNS mDNS : _knownMDNS.values()) {
326 mDNS.registerService(info.clone());
327 }
328 ((ServiceInfoImpl) info).setDelegate(this);
329 _services.put(info.getQualifiedName(), info);
330 }
331 }
332
333
334
335
336
337 @Override
338 public void unregisterService(ServiceInfo info) {
339 synchronized (_services) {
340 for (JmDNS mDNS : _knownMDNS.values()) {
341 mDNS.unregisterService(info);
342 }
343 ((ServiceInfoImpl) info).setDelegate(null);
344 _services.remove(info.getQualifiedName());
345 }
346 }
347
348
349
350
351
352 @Override
353 public void unregisterAllServices() {
354 synchronized (_services) {
355 for (JmDNS mDNS : _knownMDNS.values()) {
356 mDNS.unregisterAllServices();
357 }
358 _services.clear();
359 }
360 }
361
362
363
364
365
366 @Override
367 public void registerServiceType(String type) {
368 for (JmDNS mDNS : _knownMDNS.values()) {
369 mDNS.registerServiceType(type);
370 }
371 }
372
373
374
375
376
377 @Override
378 public ServiceInfo[] list(String type) {
379 return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT);
380 }
381
382
383
384
385
386 @Override
387 public ServiceInfo[] list(final String type, final long timeout) {
388
389 final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size() * 5));
390 ExecutorService executor = Executors.newCachedThreadPool();
391 for (final JmDNS mDNS : _knownMDNS.values()) {
392 executor.submit(new Runnable() {
393
394
395
396 @Override
397 public void run() {
398 result.addAll(Arrays.asList(mDNS.list(type, timeout)));
399 }
400 });
401 }
402 executor.shutdown();
403 try {
404 executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
405 } catch (InterruptedException exception) {
406 logger.log(Level.WARNING, "Exception ", exception);
407 }
408 return result.toArray(new ServiceInfo[result.size()]);
409 }
410
411
412
413
414
415 @Override
416 public Map<String, ServiceInfo[]> listBySubtype(String type) {
417 return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT);
418 }
419
420
421
422
423
424 @Override
425 public Map<String, ServiceInfo[]> listBySubtype(final String type, final long timeout) {
426 Map<String, List<ServiceInfo>> map = new HashMap<String, List<ServiceInfo>>(5);
427 for (ServiceInfo info : this.list(type, timeout)) {
428 String subtype = info.getSubtype();
429 if (!map.containsKey(subtype)) {
430 map.put(subtype, new ArrayList<ServiceInfo>(10));
431 }
432 map.get(subtype).add(info);
433 }
434
435 Map<String, ServiceInfo[]> result = new HashMap<String, ServiceInfo[]>(map.size());
436 for (String subtype : map.keySet()) {
437 List<ServiceInfo> infoForSubType = map.get(subtype);
438 result.put(subtype, infoForSubType.toArray(new ServiceInfo[infoForSubType.size()]));
439 }
440
441 return result;
442 }
443
444
445
446
447
448 @Override
449 public void addNetworkTopologyListener(NetworkTopologyListener listener) {
450 _networkListeners.add(listener);
451 }
452
453
454
455
456
457 @Override
458 public void removeNetworkTopologyListener(NetworkTopologyListener listener) {
459 _networkListeners.remove(listener);
460 }
461
462
463
464
465
466 @Override
467 public NetworkTopologyListener[] networkListeners() {
468 return _networkListeners.toArray(new NetworkTopologyListener[_networkListeners.size()]);
469 }
470
471
472
473
474
475 @Override
476 public void inetAddressAdded(NetworkTopologyEvent event) {
477 InetAddress address = event.getInetAddress();
478 try {
479 synchronized (this) {
480 if (!_knownMDNS.containsKey(address)) {
481 _knownMDNS.put(address, JmDNS.create(address));
482 final NetworkTopologyEvent jmdnsEvent = new NetworkTopologyEventImpl(_knownMDNS.get(address), address);
483 for (final NetworkTopologyListener listener : this.networkListeners()) {
484 _ListenerExecutor.submit(new Runnable() {
485
486
487
488 @Override
489 public void run() {
490 listener.inetAddressAdded(jmdnsEvent);
491 }
492 });
493 }
494 }
495 }
496 } catch (Exception e) {
497 logger.warning("Unexpected unhandled exception: " + e);
498 }
499 }
500
501
502
503
504
505 @Override
506 public void inetAddressRemoved(NetworkTopologyEvent event) {
507 InetAddress address = event.getInetAddress();
508 try {
509 synchronized (this) {
510 if (_knownMDNS.containsKey(address)) {
511 JmDNS mDNS = _knownMDNS.remove(address);
512 mDNS.close();
513 final NetworkTopologyEvent jmdnsEvent = new NetworkTopologyEventImpl(mDNS, address);
514 for (final NetworkTopologyListener listener : this.networkListeners()) {
515 _ListenerExecutor.submit(new Runnable() {
516
517
518
519 @Override
520 public void run() {
521 listener.inetAddressRemoved(jmdnsEvent);
522 }
523 });
524 }
525 }
526 }
527 } catch (Exception e) {
528 logger.warning("Unexpected unhandled exception: " + e);
529 }
530 }
531
532
533
534
535
536 static class NetworkChecker extends TimerTask {
537 private static Logger logger1 = Logger.getLogger(NetworkChecker.class.getName());
538
539 private final NetworkTopologyListener _mmDNS;
540
541 private final NetworkTopologyDiscovery _topology;
542
543 private Set<InetAddress> _knownAddresses;
544
545 public NetworkChecker(NetworkTopologyListener mmDNS, NetworkTopologyDiscovery topology) {
546 super();
547 this._mmDNS = mmDNS;
548 this._topology = topology;
549 _knownAddresses = Collections.synchronizedSet(new HashSet<InetAddress>());
550 }
551
552 public void start(Timer timer) {
553 timer.schedule(this, 0, DNSConstants.NETWORK_CHECK_INTERVAL);
554 }
555
556
557
558
559 @Override
560 public void run() {
561 try {
562 InetAddress[] curentAddresses = _topology.getInetAddresses();
563 Set<InetAddress> current = new HashSet<InetAddress>(curentAddresses.length);
564 for (InetAddress address : curentAddresses) {
565 current.add(address);
566 if (!_knownAddresses.contains(address)) {
567 final NetworkTopologyEvent event = new NetworkTopologyEventImpl(_mmDNS, address);
568 _mmDNS.inetAddressAdded(event);
569 }
570 }
571 for (InetAddress address : _knownAddresses) {
572 if (!current.contains(address)) {
573 final NetworkTopologyEvent event = new NetworkTopologyEventImpl(_mmDNS, address);
574 _mmDNS.inetAddressRemoved(event);
575 }
576 }
577 _knownAddresses = current;
578 } catch (Exception e) {
579 logger1.warning("Unexpected unhandled exception: " + e);
580 }
581 }
582
583 }
584
585 }