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