Zeroconf.java
Go to the documentation of this file.
00001 package ros.zeroconf.jmdns;
00002 
00003 import java.io.IOException;
00004 import java.lang.Thread;
00005 import java.lang.Boolean;
00006 import java.net.InetAddress;
00007 import java.net.Inet4Address;
00008 import java.net.Inet6Address;
00009 import java.net.NetworkInterface;
00010 import java.util.Arrays;
00011 import java.util.Iterator;
00012 import java.util.HashMap;
00013 import java.util.HashSet;
00014 import java.util.List;
00015 import java.util.ArrayList;
00016 import java.util.Map;
00017 import java.util.Set;
00018 import javax.jmdns.JmmDNS;
00019 import javax.jmdns.NetworkTopologyEvent;
00020 import javax.jmdns.NetworkTopologyListener;
00021 import javax.jmdns.ServiceEvent;
00022 import javax.jmdns.ServiceInfo;
00023 import javax.jmdns.ServiceListener;
00024 import javax.jmdns.ServiceTypeListener;
00025 import org.ros.message.zeroconf_comms.DiscoveredService;
00026 
00041 public class Zeroconf implements ServiceListener, ServiceTypeListener, NetworkTopologyListener {
00042 
00043         private class DefaultLogger implements ZeroconfLogger {
00044                 public void println(String msg) {}
00045         }
00046         
00047     JmmDNS jmmdns;
00048     Set<String> listeners;
00049     Set<ServiceInfo> services;
00050     ZeroconfLogger logger;
00051     Map<String, ZeroconfDiscoveryHandler> listener_callbacks;
00052     ZeroconfDiscoveryHandler default_listener_callback;
00053 
00054     public Zeroconf() {
00055         /********************
00056          * Variables
00057          *******************/
00058         this.jmmdns = JmmDNS.Factory.getInstance();
00059         this.listeners = new HashSet<String>();
00060         this.services = new HashSet<ServiceInfo>();
00061         this.logger = new DefaultLogger();
00062         this.listener_callbacks = new HashMap<String, ZeroconfDiscoveryHandler>();
00063         this.default_listener_callback = null;
00064 
00065         /********************
00066          * Methods
00067          *******************/
00068         // be nice to get rid of this completely - and have it in the jmdns library itself.
00069         this.jmmdns.addNetworkTopologyListener(this);
00070     }
00071 
00072     public Zeroconf(ZeroconfLogger logger) {
00073         /********************
00074          * Variables
00075          *******************/
00076         this.jmmdns = JmmDNS.Factory.getInstance();
00077         this.listeners = new HashSet<String>();
00078         this.services = new HashSet<ServiceInfo>();
00079         this.logger = logger;
00080         this.listener_callbacks = new HashMap<String, ZeroconfDiscoveryHandler>();
00081         this.default_listener_callback = null;
00082 
00083         /********************
00084          * Methods
00085          *******************/
00086         // be nice to get rid of this completely - and have it in the jmdns library itself.
00087         this.jmmdns.addNetworkTopologyListener(this);
00088     }
00089 
00090         /*************************************************************************
00091          * User Interface
00092          ************************************************************************/
00093     public void setDefaultDiscoveryCallback(ZeroconfDiscoveryHandler listener_callback) {
00094         this.default_listener_callback = listener_callback;
00095     }
00096     public void addListener(String service_type, String domain) {
00097         addListener(service_type, domain, this.default_listener_callback);
00098     }
00105     public void addListener(String service_type, String domain, ZeroconfDiscoveryHandler listener_callback) {
00106         String service = service_type + "." + domain + ".";
00107         logger.println("Activating listener: " + service);
00108         listeners.add(service);
00109         if ( listener_callback != null ) { 
00110                 listener_callbacks.put(service, listener_callback);
00111         }
00112         // add to currently established interfaces
00113         jmmdns.addServiceListener(service, this);
00114     }
00115     
00119     public void removeListener(String service_type, String domain) {
00120         String listener_to_remove = service_type + "." + domain + ".";
00121         for ( Iterator<String> listener = listeners.iterator(); listener.hasNext(); ) {
00122                 String this_listener = listener.next().toString();
00123                 if ( this_listener.equals(listener_to_remove) ) { 
00124                         logger.println("Deactivating listener: " + this_listener);
00125                 listener.remove();
00126                 // remove from currently established interfaces
00127                                 jmmdns.removeServiceListener(listener_to_remove, this);
00128                                 break;
00129                 }
00130         }
00131         listener_callbacks.remove(listener_to_remove);
00132     }
00145     public void addService(String name, String type, String domain, int port, String description) {
00146         String full_service_type = type + "." + domain + ".";
00147         logger.println("Registering service: " + full_service_type);
00148         String service_key = "description"; // Max 9 chars
00149         HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
00150         properties.put(service_key,description.getBytes());
00151         ServiceInfo service_info = ServiceInfo.create(full_service_type, name, port, 0, 0, true, properties);
00152         // we need much better logic here to handle duplications.
00153         if ( services.add(service_info) ) {
00154                 try {
00155                         jmmdns.registerService(service_info);
00156             } catch (IOException e) {
00157                 e.printStackTrace();
00158             }
00159         }
00160         
00161         // this is broken - it adds it, but fails to resolve it on other systems
00162         // https://sourceforge.net/tracker/?func=detail&aid=3435220&group_id=93852&atid=605791
00163         // services.add(ServiceInfo.create(service_type, service_name, service_port, 0, 0, true, text));
00164     }
00165 
00172     public List<DiscoveredService> listDiscoveredServices() {
00173         List<ServiceInfo> service_infos = new ArrayList<ServiceInfo>();
00174         for(String service : listeners ) {
00175                 service_infos.addAll(Arrays.asList(this.jmmdns.list(service)));
00176         }
00177         // At this point, we have a real problem - quite often they are duplicated
00178         // but have different addresses resolved to each, in other words, we need
00179         // to uniquely resolve them since jmdns doesn't do us that favour!
00180         // Todo: Maybe get jmdns to patch this?
00181         List<DiscoveredService> discovered_services = new ArrayList<DiscoveredService>();
00182         for(ServiceInfo service_info : service_infos ) {
00183                 Boolean service_found = false;
00184                 for ( DiscoveredService discovered_service : discovered_services ) {
00185                         if ( service_info.getQualifiedName().equals(discovered_service.name+"."+discovered_service.type+"."+discovered_service.domain+".") ) {
00186                                 for ( InetAddress inet_address : service_info.getInetAddresses() ) {
00187                                         if ( inet_address instanceof Inet4Address) {
00188                                                 Boolean address_found = false;
00189                                         for ( String unique_address : discovered_service.ipv4_addresses ) {
00190                                                 if ( inet_address.getHostAddress().equals(unique_address) ) {
00191                                                         address_found = true;
00192                                                         break;
00193                                                 }
00194                                         }
00195                                         if ( !address_found ) {
00196                                                 discovered_service.ipv4_addresses.add(inet_address.getHostAddress());
00197                                         }
00198                                         } else { // Inet6Address
00199                                                 Boolean address_found = false;
00200                                         for ( String unique_address : discovered_service.ipv6_addresses ) {
00201                                                 if ( inet_address.getHostAddress().equals(unique_address) ) {
00202                                                         address_found = true;
00203                                                         break;
00204                                                 }
00205                                         }
00206                                         if ( !address_found ) {
00207                                                 discovered_service.ipv6_addresses.add(inet_address.getHostAddress());
00208                                         }
00209                                         }
00210                                 }
00211                                 service_found = true;
00212                                 break;
00213                         }
00214                 }
00215                 if ( !service_found ) {
00216                         discovered_services.add(toDiscoveredService(service_info));
00217                 }
00218                 
00219         }
00220         return discovered_services;
00221     }
00222 
00227     public void removeAllServices() {
00228         logger.println("Removing all services");
00229         jmmdns.unregisterAllServices();
00230         services.clear();
00231     }
00232     
00233     public void shutdown() throws IOException {
00234         removeAllServices();
00235         logger.println("Shutdown");
00236         jmmdns.close();
00237     }
00238     
00239         /*************************************************************************
00240          * Listener Callbacks - from ServiceListener and ServiceTypeListener
00241          ************************************************************************/
00242     @Override
00243     public void serviceAdded(ServiceEvent event) {
00244         final ServiceInfo service_info = event.getInfo();
00245         // might need to add a timeout as a last arg here
00246         // true tells it to keep resolving when new, new info comes in (persistent).
00247         jmmdns.getServiceInfos(service_info.getType(), service_info.getName(), true);
00248         ZeroconfDiscoveryHandler callback = listener_callbacks.get(service_info.getType());
00249         if ( callback != null ) {
00250                 callback.serviceAdded(toDiscoveredService(service_info));
00251         } else {
00252             logger.println("[+] Service         : " + service_info.getQualifiedName());
00253         }
00254     }
00255 
00256     @Override
00257     public void serviceRemoved(ServiceEvent event) {
00258         final String name = event.getName();
00259         final ServiceInfo service_info = event.getInfo();
00260         ZeroconfDiscoveryHandler callback = listener_callbacks.get(service_info.getType());
00261         if ( callback != null ) {
00262                 callback.serviceRemoved(toDiscoveredService(service_info));
00263         } else {
00264             logger.println("[-] Service         : " + name);
00265         }
00266     }
00267 
00268     @Override
00277     public void serviceResolved(ServiceEvent event) {
00278         final ServiceInfo service_info = event.getInfo();
00279         ZeroconfDiscoveryHandler callback = listener_callbacks.get(service_info.getType());
00280         if ( callback != null ) {
00281                 callback.serviceResolved(toDiscoveredService(service_info));
00282         } else {
00283             logger.println("[=] Resolved        : " + service_info.getQualifiedName());
00284                 logger.println("      Port          : " + service_info.getPort() );
00285                 for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00286                 logger.println("      Address       : " + service_info.getInetAddresses()[i].getHostAddress() );
00287                 }
00288         }
00289     }
00290 
00291     @Override
00292     public void serviceTypeAdded(ServiceEvent event) {
00293 //        final String aType = event.getType();
00294 //        logger.println("TYPE: " + aType);
00295     }
00296 
00297     @Override
00298     public void subTypeForServiceTypeAdded(ServiceEvent event) {
00299 //        logger.println("SUBTYPE: " + event.getType());
00300     }
00301 
00302         /******************************
00303          * Network Topology Callbacks 
00304          *****************************/
00305     @Override
00306         public void inetAddressAdded(NetworkTopologyEvent event) {
00307                 try {
00308                         logger.println("[+] NetworkInterface: " + event.getInetAddress().getHostAddress() + " [" + NetworkInterface.getByInetAddress(event.getInetAddress()).getDisplayName() + "]");
00309                 } catch (IOException e) {
00310                 e.printStackTrace();
00311         }
00312         try {
00313                 event.getDNS().addServiceTypeListener(this);
00314                 for(String listener : listeners ) {
00315                         logger.println("      Adding service listener '" + listener + "'");
00316                 event.getDNS().addServiceListener(listener, this);
00317                 }
00318                 for (ServiceInfo service : services ) {
00319                         logger.println("Publishing Service on " + event.getInetAddress().getHostAddress());
00320                         logger.println("  Name   : " + service.getName() );
00321                         logger.println("  Type   : " + service.getType() );
00322                         logger.println("  Port   : " + service.getPort() );
00323                 event.getDNS().registerService(service.clone()); // if you don't clone it, it falls over badly!
00324                 }
00325         } catch (IOException e) {
00326                 e.printStackTrace();
00327         }
00328         }
00329         
00330     @Override
00331     public void inetAddressRemoved(NetworkTopologyEvent event) {
00332         String event_address_str = event.getInetAddress().getHostAddress();
00333         // can't get the display name like above, as the interface is no longer available.
00334         // if we really want the display name, need to store it somewhere when the network interface
00335         // is added.
00336                 logger.println("[-] NetworkInterface: " + event_address_str);
00337                 // Trigger service info callbacks - this is fairly brutal. It might be better to
00338                 // check here if that service is no longer supplied on all interfaces, then call
00339                 // serviceRemoved. If it is still supplied, provide a serviceResolved callback with
00340                 // the updated addresses.
00341 
00342                 event.getDNS().removeServiceTypeListener(this);
00343         for(String listener : listeners ) {
00344                 logger.println("      Removing service listener '" + listener + "'");
00345                 event.getDNS().removeServiceListener(listener, this);
00346         }
00347         for (ServiceInfo service : services ) {
00348                 logger.println("Unpublishing Service:");
00349                 logger.println("  Name   : " + service.getName() );
00350                 logger.println("  Type   : " + service.getType() );
00351                 logger.println("  Port   : " + service.getPort() );
00352                 event.getDNS().unregisterService(service); // this may not work because we're cloning it.
00353         }
00354         }
00355 
00356     /******************************
00357          * Utility Functions 
00358          *****************************/
00359     public void display(DiscoveredService discovered_service) {
00360         logger.println("Discovered Service:");
00361         logger.println("  Name   : " + discovered_service.name );
00362         logger.println("  Type   : " + discovered_service.type );
00363         logger.println("  Port   : " + discovered_service.port );
00364         for ( String address : discovered_service.ipv4_addresses ) {
00365                 logger.println("  Address: " + address );
00366         }
00367         for ( String address : discovered_service.ipv6_addresses ) {
00368                 logger.println("  Address: " + address );
00369         }
00370     }
00371 
00372     public String toString(DiscoveredService discovered_service) {
00373         String result = "Service Info:\n";
00374         result += "  Name   : " + discovered_service.name + "\n";
00375         result += "  Type   : " + discovered_service.type + "\n";
00376         result += "  Port   : " + discovered_service.port + "\n";
00377         for ( String address : discovered_service.ipv4_addresses ) {
00378                 result += "  Address: " + address + "\n";
00379         }
00380         for ( String address : discovered_service.ipv6_addresses ) {
00381                 result += "  Address: " + address + "\n";
00382         }
00383         return result;
00384     }
00385 
00386     /*************************************************************************
00387          * Private 
00388          ************************************************************************/
00389 //    /******************************
00390 //       * Discovery 
00391 //       *****************************/
00392 //    /**
00393 //     * If you try calling this immediately after a service added callback
00394 //     * occurred, you probably wont see anything - it needs some time to resolve.
00395 //     * 
00396 //     * It will block if it needs to resolve services (and aren't in its cache yet).
00397 //     * 
00398 //     * @sa listDiscoveredServices
00399 //     * 
00400 //     * @return service_infos : an array of discovered ServiceInfo objects.
00401 //     */
00402 //    private List<ServiceInfo> listJmdnsDiscoveredServices() {
00403 //      List<ServiceInfo> service_infos = new ArrayList<ServiceInfo>();
00404 //      for(String service : listeners ) {
00405 //              service_infos.addAll(Arrays.asList(this.jmmdns.list(service)));
00406 //      }
00407 //        return service_infos;
00408 //    }
00409 
00410     /******************************
00411          * Utility Functions 
00412          *****************************/
00413     private DiscoveredService toDiscoveredService(ServiceInfo service_info) {
00414         DiscoveredService discovered_service = new DiscoveredService();
00415                 discovered_service.name = service_info.getName();
00416                 String[] type_domain_str = service_info.getType().split("\\.");
00417                 discovered_service.type = type_domain_str[0] + "." + type_domain_str[1];
00418                 discovered_service.domain = service_info.getDomain();
00419                 discovered_service.hostname = service_info.getServer();
00420                 discovered_service.port = service_info.getPort();
00421                 for ( InetAddress inet_address : service_info.getInetAddresses() ) {
00422                         if ( inet_address instanceof Inet4Address) {
00423                                 discovered_service.ipv4_addresses.add(inet_address.getHostAddress());
00424                         } else { // Inet6Address
00425                                 discovered_service.ipv6_addresses.add(inet_address.getHostAddress());
00426                         }
00427                 }
00428         return discovered_service;
00429     }
00430 
00431 //    private String toString(ServiceInfo service_info) {
00432 //      String result = "Service Info:\n";
00433 //      result += "  Name   : " + service_info.getName() + "\n";
00434 //      result += "  Type   : " + service_info.getType() + "\n";
00435 //      result += "  Port   : " + service_info.getPort() + "\n";
00436 //      for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00437 //              result += "  Address: " + service_info.getInetAddresses()[i].getHostAddress() + "\n";
00438 //      }
00439 //      return result;
00440 //    }
00441 //
00442 //    private void display(ServiceInfo service_info) {
00443 //      logger.println("Service Info:");
00444 //      logger.println("  Name   : " + service_info.getName() );
00445 //      logger.println("  Type   : " + service_info.getType() );
00446 //      logger.println("  Port   : " + service_info.getPort() );
00447 //      for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00448 //              logger.println("  Address: " + service_info.getInetAddresses()[i].getHostAddress() );
00449 //      }
00450 //    }
00451 
00452 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends


zeroconf_jmdns
Author(s): Daniel Stonier
autogenerated on Tue Nov 6 2012 14:18:57