JmmDNSImpl.java
Go to the documentation of this file.
00001 
00004 package javax.jmdns.impl;
00005 
00006 import java.io.IOException;
00007 import java.net.InetAddress;
00008 import java.util.ArrayList;
00009 import java.util.Arrays;
00010 import java.util.Collections;
00011 import java.util.HashMap;
00012 import java.util.HashSet;
00013 import java.util.List;
00014 import java.util.Map;
00015 import java.util.Set;
00016 import java.util.Timer;
00017 import java.util.TimerTask;
00018 import java.util.concurrent.ConcurrentHashMap;
00019 import java.util.concurrent.ConcurrentMap;
00020 import java.util.concurrent.ExecutorService;
00021 import java.util.concurrent.Executors;
00022 import java.util.concurrent.TimeUnit;
00023 import java.util.logging.Level;
00024 import java.util.logging.Logger;
00025 
00026 import javax.jmdns.JmDNS;
00027 import javax.jmdns.JmmDNS;
00028 import javax.jmdns.NetworkTopologyDiscovery;
00029 import javax.jmdns.NetworkTopologyEvent;
00030 import javax.jmdns.NetworkTopologyListener;
00031 import javax.jmdns.ServiceInfo;
00032 import javax.jmdns.ServiceListener;
00033 import javax.jmdns.ServiceTypeListener;
00034 import javax.jmdns.impl.constants.DNSConstants;
00035 
00041 public class JmmDNSImpl implements JmmDNS, NetworkTopologyListener, ServiceInfoImpl.Delegate {
00042     private static Logger                            logger = Logger.getLogger(JmmDNSImpl.class.getName());
00043 
00044     private final Set<NetworkTopologyListener>       _networkListeners;
00045 
00049     private final ConcurrentMap<InetAddress, JmDNS>  _knownMDNS;
00050 
00054     private final ConcurrentMap<String, ServiceInfo> _services;
00055 
00056     private final ExecutorService                    _ListenerExecutor;
00057 
00058     private final ExecutorService                    _jmDNSExecutor;
00059 
00060     private final Timer                              _timer;
00061     
00062     private boolean is_closed;
00063 
00067     public JmmDNSImpl() {
00068         super();
00069         _networkListeners = Collections.synchronizedSet(new HashSet<NetworkTopologyListener>());
00070         _knownMDNS = new ConcurrentHashMap<InetAddress, JmDNS>();
00071         _services = new ConcurrentHashMap<String, ServiceInfo>(20);
00072         _ListenerExecutor = Executors.newSingleThreadExecutor();
00073         _jmDNSExecutor = Executors.newCachedThreadPool();
00074         _timer = new Timer("Multihommed mDNS.Timer", true);
00075         boolean is_closed = false;
00076         (new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance())).start(_timer);
00077     }
00078 
00079     public boolean isClosed() {
00080         return is_closed;
00081     }
00082     
00083     /*
00084      * (non-Javadoc)
00085      * @see java.io.Closeable#close()
00086      */
00087     @Override
00088     public void close() throws IOException {
00089         is_closed = true;
00090         if (logger.isLoggable(Level.FINER)) {
00091             logger.finer("Cancelling JmmDNS: " + this);
00092         }
00093         _timer.cancel();
00094         _ListenerExecutor.shutdown();
00095         // We need to cancel all the DNS
00096         ExecutorService executor = Executors.newCachedThreadPool();
00097         for (final JmDNS mDNS : _knownMDNS.values()) {
00098             executor.submit(new Runnable() {
00102                 @Override
00103                 public void run() {
00104                     try {
00105                         mDNS.close();
00106                     } catch (IOException exception) {
00107                         // JmDNS never throws this is only because of the closeable interface
00108                     }
00109                 }
00110             });
00111         }
00112         executor.shutdown();
00113         try {
00114             executor.awaitTermination(DNSConstants.CLOSE_TIMEOUT, TimeUnit.MILLISECONDS);
00115         } catch (InterruptedException exception) {
00116             logger.log(Level.WARNING, "Exception ", exception);
00117         }
00118         _knownMDNS.clear();
00119     }
00120 
00121     
00122     /*
00123      * (non-Javadoc)
00124      * @see javax.jmdns.JmmDNS#getNames()
00125      */
00126     @Override
00127     public String[] getNames() {
00128         Set<String> result = new HashSet<String>();
00129         for (JmDNS mDNS : _knownMDNS.values()) {
00130             result.add(mDNS.getName());
00131         }
00132         return result.toArray(new String[result.size()]);
00133     }
00134 
00135     /*
00136      * (non-Javadoc)
00137      * @see javax.jmdns.JmmDNS#getHostNames()
00138      */
00139     @Override
00140     public String[] getHostNames() {
00141         Set<String> result = new HashSet<String>();
00142         for (JmDNS mDNS : _knownMDNS.values()) {
00143             result.add(mDNS.getHostName());
00144         }
00145         return result.toArray(new String[result.size()]);
00146     }
00147 
00148     /*
00149      * (non-Javadoc)
00150      * @see javax.jmdns.JmmDNS#getInetAddresses()
00151      */
00152     @Override
00153     public InetAddress[] getInetAddresses() throws IOException {
00154         Set<InetAddress> result = new HashSet<InetAddress>();
00155         for (JmDNS mDNS : _knownMDNS.values()) {
00156             result.add(mDNS.getInetAddress());
00157         }
00158         return result.toArray(new InetAddress[result.size()]);
00159     }
00160 
00161     /*
00162      * (non-Javadoc)
00163      * @see javax.jmdns.JmmDNS#getInterfaces()
00164      */
00165     @Override
00166     @Deprecated
00167     public InetAddress[] getInterfaces() throws IOException {
00168         Set<InetAddress> result = new HashSet<InetAddress>();
00169         for (JmDNS mDNS : _knownMDNS.values()) {
00170             result.add(mDNS.getInterface());
00171         }
00172         return result.toArray(new InetAddress[result.size()]);
00173     }
00174 
00175     /*
00176      * (non-Javadoc)
00177      * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String)
00178      */
00179     @Override
00180     public ServiceInfo[] getServiceInfos(String type, String name) {
00181         return this.getServiceInfos(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
00182     }
00183 
00184     /*
00185      * (non-Javadoc)
00186      * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, long)
00187      */
00188     @Override
00189     public ServiceInfo[] getServiceInfos(String type, String name, long timeout) {
00190         return this.getServiceInfos(type, name, false, timeout);
00191     }
00192 
00193     /*
00194      * (non-Javadoc)
00195      * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, boolean)
00196      */
00197     @Override
00198     public ServiceInfo[] getServiceInfos(String type, String name, boolean persistent) {
00199         return this.getServiceInfos(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
00200     }
00201 
00202     /*
00203      * (non-Javadoc)
00204      * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, boolean, long)
00205      */
00206     @Override
00207     public ServiceInfo[] getServiceInfos(final String type, final String name, final boolean persistent, final long timeout) {
00208         // We need to run this in parallel to respect the timeout.
00209         final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size()));
00210         ExecutorService executor = Executors.newCachedThreadPool();
00211         for (final JmDNS mDNS : _knownMDNS.values()) {
00212             executor.submit(new Runnable() {
00216                 @Override
00217                 public void run() {
00218                     result.add(mDNS.getServiceInfo(type, name, persistent, timeout));
00219                 }
00220             });
00221         }
00222         executor.shutdown();
00223         try {
00224             executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
00225         } catch (InterruptedException exception) {
00226             logger.log(Level.WARNING, "Exception ", exception);
00227         }
00228         return result.toArray(new ServiceInfo[result.size()]);
00229     }
00230 
00231     /*
00232      * (non-Javadoc)
00233      * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String)
00234      */
00235     @Override
00236     public void requestServiceInfo(String type, String name) {
00237         this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
00238     }
00239 
00240     /*
00241      * (non-Javadoc)
00242      * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean)
00243      */
00244     @Override
00245     public void requestServiceInfo(String type, String name, boolean persistent) {
00246         this.requestServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
00247     }
00248 
00249     /*
00250      * (non-Javadoc)
00251      * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, long)
00252      */
00253     @Override
00254     public void requestServiceInfo(String type, String name, long timeout) {
00255         this.requestServiceInfo(type, name, false, timeout);
00256     }
00257 
00258     /*
00259      * (non-Javadoc)
00260      * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean, long)
00261      */
00262     @Override
00263     public void requestServiceInfo(final String type, final String name, final boolean persistent, final long timeout) {
00264         // We need to run this in parallel to respect the timeout.
00265         for (final JmDNS mDNS : _knownMDNS.values()) {
00266             _jmDNSExecutor.submit(new Runnable() {
00270                 @Override
00271                 public void run() {
00272                     mDNS.requestServiceInfo(type, name, persistent, timeout);
00273                 }
00274             });
00275         }
00276     }
00277 
00278     /*
00279      * (non-Javadoc)
00280      * @see javax.jmdns.JmmDNS#addServiceTypeListener(javax.jmdns.ServiceTypeListener)
00281      */
00282     @Override
00283     public void addServiceTypeListener(ServiceTypeListener listener) throws IOException {
00284         for (JmDNS mDNS : _knownMDNS.values()) {
00285             mDNS.addServiceTypeListener(listener);
00286         }
00287     }
00288 
00289     /*
00290      * (non-Javadoc)
00291      * @see javax.jmdns.JmmDNS#removeServiceTypeListener(javax.jmdns.ServiceTypeListener)
00292      */
00293     @Override
00294     public void removeServiceTypeListener(ServiceTypeListener listener) {
00295         for (JmDNS mDNS : _knownMDNS.values()) {
00296             mDNS.removeServiceTypeListener(listener);
00297         }
00298     }
00299 
00300     /*
00301      * (non-Javadoc)
00302      * @see javax.jmdns.JmmDNS#addServiceListener(java.lang.String, javax.jmdns.ServiceListener)
00303      */
00304     @Override
00305     public void addServiceListener(String type, ServiceListener listener) {
00306         for (JmDNS mDNS : _knownMDNS.values()) {
00307                 try {
00308                         System.out.println("Adding listener:");
00309                             System.out.printf("  Service type  : %s\n", type);
00310                             System.out.printf("  mDNS Name     : %s\n", mDNS.getName());
00311                             System.out.printf("  mDNS Localhost: %s\n", mDNS.getHostName());
00312                         System.out.printf("  mDNS Address  : %s\n", mDNS.getInetAddress().getHostAddress());
00313                     } catch (IOException e) {
00314                         e.printStackTrace();
00315                     }
00316             mDNS.addServiceListener(type, listener);
00317         }
00318     }
00319 
00320     /*
00321      * (non-Javadoc)
00322      * @see javax.jmdns.JmmDNS#removeServiceListener(java.lang.String, javax.jmdns.ServiceListener)
00323      */
00324     @Override
00325     public void removeServiceListener(String type, ServiceListener listener) {
00326         for (JmDNS mDNS : _knownMDNS.values()) {
00327             mDNS.removeServiceListener(type, listener);
00328         }
00329     }
00330 
00331     /*
00332      * (non-Javadoc)
00333      * @see javax.jmdns.impl.ServiceInfoImpl.Delegate#textValueUpdated(javax.jmdns.ServiceInfo, byte[])
00334      */
00335     @Override
00336     public void textValueUpdated(ServiceInfo target, byte[] value) {
00337         synchronized (_services) {
00338             for (JmDNS mDNS : _knownMDNS.values()) {
00339                 ServiceInfo info = ((JmDNSImpl) mDNS).getServices().get(target.getQualifiedName());
00340                 if (info != null) {
00341                     info.setText(value);
00342                 } else {
00343                     logger.warning("We have a mDNS that does not know about the service info being updated.");
00344                 }
00345             }
00346         }
00347     }
00348 
00349     /*
00350      * (non-Javadoc)
00351      * @see javax.jmdns.JmmDNS#registerService(javax.jmdns.ServiceInfo)
00352      */
00353     @Override
00354     public void registerService(ServiceInfo info) throws IOException {
00355         // This is really complex. We need to clone the service info for each DNS but then we loose the ability to update it.
00356         synchronized (_services) {
00357             for (JmDNS mDNS : _knownMDNS.values()) {
00358                 mDNS.registerService(info.clone());
00359             }
00360             ((ServiceInfoImpl) info).setDelegate(this);
00361             _services.put(info.getQualifiedName(), info);
00362         }
00363     }
00364 
00365     /*
00366      * (non-Javadoc)
00367      * @see javax.jmdns.JmmDNS#unregisterService(javax.jmdns.ServiceInfo)
00368      */
00369     @Override
00370     public void unregisterService(ServiceInfo info) {
00371         synchronized (_services) {
00372             for (JmDNS mDNS : _knownMDNS.values()) {
00373                 mDNS.unregisterService(info);
00374             }
00375             ((ServiceInfoImpl) info).setDelegate(null);
00376             _services.remove(info.getQualifiedName());
00377         }
00378     }
00379 
00380     /*
00381      * (non-Javadoc)
00382      * @see javax.jmdns.JmmDNS#unregisterAllServices()
00383      */
00384     @Override
00385     public void unregisterAllServices() {
00386         synchronized (_services) {
00387             for (JmDNS mDNS : _knownMDNS.values()) {
00388                 mDNS.unregisterAllServices();
00389             }
00390             _services.clear();
00391         }
00392     }
00393 
00394     /*
00395      * (non-Javadoc)
00396      * @see javax.jmdns.JmmDNS#registerServiceType(java.lang.String)
00397      */
00398     @Override
00399     public void registerServiceType(String type) {
00400         for (JmDNS mDNS : _knownMDNS.values()) {
00401             mDNS.registerServiceType(type);
00402         }
00403     }
00404 
00405     /*
00406      * (non-Javadoc)
00407      * @see javax.jmdns.JmmDNS#list(java.lang.String)
00408      */
00409     @Override
00410     public ServiceInfo[] list(String type) {
00411         return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT);
00412     }
00413 
00414     /*
00415      * (non-Javadoc)
00416      * @see javax.jmdns.JmmDNS#list(java.lang.String, long)
00417      */
00418     @Override
00419     public ServiceInfo[] list(final String type, final long timeout) {
00420         // We need to run this in parallel to respect the timeout.
00421  
00422         // The .equals for ServiceInfo only compares qualified names, no good for resolved 
00423         // service info comparison here.
00424         //final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size() * 5));
00425         final List<ServiceInfo> result = new ArrayList<ServiceInfo>(_knownMDNS.size() * 5);
00426         
00427         ExecutorService executor = Executors.newCachedThreadPool();
00428         for (final JmDNS mDNS : _knownMDNS.values()) {
00429                 List<ServiceInfo> single_mdns_list = new ArrayList<ServiceInfo>();
00430                 // try {
00431                         // System.out.printf("jmmdns.list(): %s\n",mDNS.getInetAddress().getHostAddress());
00432                 for (ServiceInfo service_info : Arrays.asList(mDNS.list(type, timeout)) ) {
00433                         boolean duplicate = false;
00434                         for ( ServiceInfo existing_service_info : single_mdns_list ) {
00435                                 if ( ( existing_service_info.getQualifiedName().equals(service_info.getQualifiedName() ) ) &&
00436                                          ( existing_service_info.getPort() == service_info.getPort() ) &&
00437                                          ( Arrays.equals(existing_service_info.getInetAddresses(),service_info.getInetAddresses()) ) 
00438                                    ) {
00439                                         // System.out.println("  jmmdns.list(): found duplicate service info");
00440                                         duplicate = true;
00441                                 }
00442                         }
00443                         if ( !duplicate ) {
00444                                 // System.out.printf("  Service: %s\n", service_info.getQualifiedName());
00445                                 // for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00446                                 // System.out.printf("    Address: %s\n", service_info.getInetAddresses()[i].getHostAddress() );
00447                                 // }
00448                         single_mdns_list.add(service_info);
00449                         }
00450                 }
00451             // } catch (IOException exception) {}
00452                 final List<ServiceInfo> service_list = new ArrayList<ServiceInfo>(single_mdns_list);
00453             executor.submit(new Runnable() {
00457                 @Override
00458                 public void run() {
00459                     //result.addAll(Arrays.asList(mDNS.list(type, timeout)));
00460                     result.addAll(service_list);
00461                 }
00462             });
00463         }
00464         executor.shutdown();
00465         try {
00466             executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
00467         } catch (InterruptedException exception) {
00468             logger.log(Level.WARNING, "Exception ", exception);
00469         }
00470         return result.toArray(new ServiceInfo[result.size()]);
00471     }
00472 
00473     /*
00474      * (non-Javadoc)
00475      * @see javax.jmdns.JmmDNS#listBySubtype(java.lang.String)
00476      */
00477     @Override
00478     public Map<String, ServiceInfo[]> listBySubtype(String type) {
00479         return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT);
00480     }
00481 
00482     /*
00483      * (non-Javadoc)
00484      * @see javax.jmdns.JmmDNS#listBySubtype(java.lang.String, long)
00485      */
00486     @Override
00487     public Map<String, ServiceInfo[]> listBySubtype(final String type, final long timeout) {
00488         Map<String, List<ServiceInfo>> map = new HashMap<String, List<ServiceInfo>>(5);
00489         for (ServiceInfo info : this.list(type, timeout)) {
00490             String subtype = info.getSubtype();
00491             if (!map.containsKey(subtype)) {
00492                 map.put(subtype, new ArrayList<ServiceInfo>(10));
00493             }
00494             map.get(subtype).add(info);
00495         }
00496 
00497         Map<String, ServiceInfo[]> result = new HashMap<String, ServiceInfo[]>(map.size());
00498         for (String subtype : map.keySet()) {
00499             List<ServiceInfo> infoForSubType = map.get(subtype);
00500             result.put(subtype, infoForSubType.toArray(new ServiceInfo[infoForSubType.size()]));
00501         }
00502 
00503         return result;
00504     }
00505 
00506     /*
00507      * (non-Javadoc)
00508      * @see javax.jmdns.JmmDNS#addNetworkTopologyListener(javax.jmdns.NetworkTopologyListener)
00509      */
00510     @Override
00511     public void addNetworkTopologyListener(NetworkTopologyListener listener) {
00512         _networkListeners.add(listener);
00513     }
00514 
00515     /*
00516      * (non-Javadoc)
00517      * @see javax.jmdns.JmmDNS#removeNetworkTopologyListener(javax.jmdns.NetworkTopologyListener)
00518      */
00519     @Override
00520     public void removeNetworkTopologyListener(NetworkTopologyListener listener) {
00521         _networkListeners.remove(listener);
00522     }
00523 
00524     /*
00525      * (non-Javadoc)
00526      * @see javax.jmdns.JmmDNS#networkListeners()
00527      */
00528     @Override
00529     public NetworkTopologyListener[] networkListeners() {
00530         return _networkListeners.toArray(new NetworkTopologyListener[_networkListeners.size()]);
00531     }
00532 
00533     /*
00534      * (non-Javadoc)
00535      * @see javax.jmdns.NetworkTopologyListener#inetAddressAdded(javax.jmdns.NetworkTopologyEvent)
00536      */
00537     @Override
00538     public void inetAddressAdded(NetworkTopologyEvent event) {
00539         InetAddress address = event.getInetAddress();
00540         try {
00541             synchronized (this) {
00542                 if (!_knownMDNS.containsKey(address)) {
00543                     _knownMDNS.put(address, JmDNS.create(address));
00544                     final NetworkTopologyEvent jmdnsEvent = new NetworkTopologyEventImpl(_knownMDNS.get(address), address);
00545                     for (final NetworkTopologyListener listener : this.networkListeners()) {
00546                         _ListenerExecutor.submit(new Runnable() {
00550                             @Override
00551                             public void run() {
00552                                 listener.inetAddressAdded(jmdnsEvent);
00553                             }
00554                         });
00555                     }
00556                 }
00557             }
00558         } catch (Exception e) {
00559             logger.warning("Unexpected unhandled exception: " + e);
00560         }
00561     }
00562 
00563     /*
00564      * (non-Javadoc)
00565      * @see javax.jmdns.NetworkTopologyListener#inetAddressRemoved(javax.jmdns.NetworkTopologyEvent)
00566      */
00567     @Override
00568     public void inetAddressRemoved(NetworkTopologyEvent event) {
00569         InetAddress address = event.getInetAddress();
00570         try {
00571             synchronized (this) {
00572                 if (_knownMDNS.containsKey(address)) {
00573                     JmDNS mDNS = _knownMDNS.remove(address);
00574                     mDNS.close();
00575                     final NetworkTopologyEvent jmdnsEvent = new NetworkTopologyEventImpl(mDNS, address);
00576                     for (final NetworkTopologyListener listener : this.networkListeners()) {
00577                         _ListenerExecutor.submit(new Runnable() {
00581                             @Override
00582                             public void run() {
00583                                 listener.inetAddressRemoved(jmdnsEvent);
00584                             }
00585                         });
00586                     }
00587                 }
00588             }
00589         } catch (Exception e) {
00590             logger.warning("Unexpected unhandled exception: " + e);
00591         }
00592     }
00593 
00598     static class NetworkChecker extends TimerTask {
00599         private static Logger                  logger1 = Logger.getLogger(NetworkChecker.class.getName());
00600 
00601         private final NetworkTopologyListener  _mmDNS;
00602 
00603         private final NetworkTopologyDiscovery _topology;
00604 
00605         private Set<InetAddress>               _knownAddresses;
00606 
00607         public NetworkChecker(NetworkTopologyListener mmDNS, NetworkTopologyDiscovery topology) {
00608             super();
00609             this._mmDNS = mmDNS;
00610             this._topology = topology;
00611             _knownAddresses = Collections.synchronizedSet(new HashSet<InetAddress>());
00612         }
00613 
00614         public void start(Timer timer) {
00615             timer.schedule(this, 0, DNSConstants.NETWORK_CHECK_INTERVAL);
00616         }
00617 
00621         @Override
00622         public void run() {
00623             try {
00624                 InetAddress[] curentAddresses = _topology.getInetAddresses();
00625                 Set<InetAddress> current = new HashSet<InetAddress>(curentAddresses.length);
00626                 for (InetAddress address : curentAddresses) {
00627                     current.add(address);
00628                     if (!_knownAddresses.contains(address)) {
00629                         final NetworkTopologyEvent event = new NetworkTopologyEventImpl(_mmDNS, address);
00630                         _mmDNS.inetAddressAdded(event);
00631                     }
00632                 }
00633                 for (InetAddress address : _knownAddresses) {
00634                     if (!current.contains(address)) {
00635                         final NetworkTopologyEvent event = new NetworkTopologyEventImpl(_mmDNS, address);
00636                         _mmDNS.inetAddressRemoved(event);
00637                     }
00638                 }
00639                 _knownAddresses = current;
00640             } catch (Exception e) {
00641                 logger1.warning("Unexpected unhandled exception: " + e);
00642             }
00643         }
00644 
00645     }
00646 
00647 }


zeroconf_jmdns_suite
Author(s): Daniel Stonier
autogenerated on Thu Aug 27 2015 15:50:27