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 
00085 
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         
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                         
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 
00124 
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 
00137 
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 
00150 
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 
00163 
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 
00177 
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 
00186 
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 
00195 
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 
00204 
00205 
00206     @Override
00207     public ServiceInfo[] getServiceInfos(final String type, final String name, final boolean persistent, final long timeout) {
00208         
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 
00233 
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 
00242 
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 
00251 
00252 
00253     @Override
00254     public void requestServiceInfo(String type, String name, long timeout) {
00255         this.requestServiceInfo(type, name, false, timeout);
00256     }
00257 
00258     
00259 
00260 
00261 
00262     @Override
00263     public void requestServiceInfo(final String type, final String name, final boolean persistent, final long timeout) {
00264         
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 
00280 
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 
00291 
00292 
00293     @Override
00294     public void removeServiceTypeListener(ServiceTypeListener listener) {
00295         for (JmDNS mDNS : _knownMDNS.values()) {
00296             mDNS.removeServiceTypeListener(listener);
00297         }
00298     }
00299 
00300     
00301 
00302 
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 
00322 
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 
00333 
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 
00351 
00352 
00353     @Override
00354     public void registerService(ServiceInfo info) throws IOException {
00355         
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 
00367 
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 
00382 
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 
00396 
00397 
00398     @Override
00399     public void registerServiceType(String type) {
00400         for (JmDNS mDNS : _knownMDNS.values()) {
00401             mDNS.registerServiceType(type);
00402         }
00403     }
00404 
00405     
00406 
00407 
00408 
00409     @Override
00410     public ServiceInfo[] list(String type) {
00411         return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT);
00412     }
00413 
00414     
00415 
00416 
00417 
00418     @Override
00419     public ServiceInfo[] list(final String type, final long timeout) {
00420         
00421  
00422         
00423         
00424         
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                 
00431                         
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                                         
00440                                         duplicate = true;
00441                                 }
00442                         }
00443                         if ( !duplicate ) {
00444                                 
00445                                 
00446                                 
00447                                 
00448                         single_mdns_list.add(service_info);
00449                         }
00450                 }
00451             
00452                 final List<ServiceInfo> service_list = new ArrayList<ServiceInfo>(single_mdns_list);
00453             executor.submit(new Runnable() {
00457                 @Override
00458                 public void run() {
00459                     
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 
00475 
00476 
00477     @Override
00478     public Map<String, ServiceInfo[]> listBySubtype(String type) {
00479         return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT);
00480     }
00481 
00482     
00483 
00484 
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 
00508 
00509 
00510     @Override
00511     public void addNetworkTopologyListener(NetworkTopologyListener listener) {
00512         _networkListeners.add(listener);
00513     }
00514 
00515     
00516 
00517 
00518 
00519     @Override
00520     public void removeNetworkTopologyListener(NetworkTopologyListener listener) {
00521         _networkListeners.remove(listener);
00522     }
00523 
00524     
00525 
00526 
00527 
00528     @Override
00529     public NetworkTopologyListener[] networkListeners() {
00530         return _networkListeners.toArray(new NetworkTopologyListener[_networkListeners.size()]);
00531     }
00532 
00533     
00534 
00535 
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 
00565 
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 }