Zeroconf.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Yujin Robot.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
00005  * use this file except in compliance with the License. You may obtain a copy of
00006  * the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00013  * License for the specific language governing permissions and limitations under
00014  * the License.
00015  */
00016 package org.rosbuilding.zeroconf;
00017 
00018 import java.io.IOException;
00019 import java.lang.Boolean;
00020 import java.net.InetAddress;
00021 import java.net.Inet4Address;
00022 //import java.net.Inet6Address;
00023 import java.net.NetworkInterface;
00024 import java.util.Arrays;
00025 import java.util.Collections;
00026 import java.util.Enumeration;
00027 import java.util.Iterator;
00028 import java.util.HashMap;
00029 import java.util.HashSet;
00030 import java.util.List;
00031 import java.util.ArrayList;
00032 import java.util.Map;
00033 import java.util.Set;
00034 
00035 import javax.jmdns.JmmDNS;
00036 import javax.jmdns.NetworkTopologyEvent;
00037 import javax.jmdns.NetworkTopologyListener;
00038 import javax.jmdns.ServiceEvent;
00039 import javax.jmdns.ServiceInfo;
00040 import javax.jmdns.ServiceListener;
00041 import javax.jmdns.ServiceTypeListener;
00042 
00043 import com.github.rosjava.zeroconf_jmdns_suite.jmdns.ZeroconfDiscoveryHandler;
00044 import com.github.rosjava.zeroconf_jmdns_suite.jmdns.ZeroconfLogger;
00045 
00046 
00061 public class Zeroconf implements ServiceListener, ServiceTypeListener, NetworkTopologyListener {
00062 
00063     private class DefaultLogger implements ZeroconfLogger {
00064         public void println(String msg) {}
00065     }
00066 
00067     JmmDNS jmmdns;
00068     Set<String> listeners;
00069     Set<ServiceInfo> services;
00070     ZeroconfLogger logger;
00071     Map<String, ZeroconfDiscoveryHandler> listener_callbacks;
00072     ZeroconfDiscoveryHandler default_listener_callback;
00073 
00074     public Zeroconf() {
00075         /********************
00076          * Variables
00077          *******************/
00078         this.jmmdns = JmmDNS.Factory.getInstance();
00079         this.listeners = new HashSet<String>();
00080         this.services = new HashSet<ServiceInfo>();
00081         this.logger = new DefaultLogger();
00082         this.listener_callbacks = new HashMap<String, ZeroconfDiscoveryHandler>();
00083         this.default_listener_callback = null;
00084 
00085         /********************
00086          * Methods
00087          *******************/
00088         // be nice to get rid of this completely - and have it in the jmdns library itself.
00089         this.jmmdns.addNetworkTopologyListener(this);
00090     }
00091 
00092     public Zeroconf(ZeroconfLogger logger) {
00093         /********************
00094          * Variables
00095          *******************/
00096         this.jmmdns = JmmDNS.Factory.getInstance();
00097         this.listeners = new HashSet<String>();
00098         this.services = new HashSet<ServiceInfo>();
00099         this.logger = logger;
00100         this.listener_callbacks = new HashMap<String, ZeroconfDiscoveryHandler>();
00101         this.default_listener_callback = null;
00102 
00103         /********************
00104          * Methods
00105          *******************/
00106         // be nice to get rid of this completely - and have it in the jmdns library itself.
00107         this.jmmdns.addNetworkTopologyListener(this);
00108     }
00109 
00110     /*************************************************************************
00111      * User Interface
00112      ************************************************************************/
00113     public void setDefaultDiscoveryCallback(ZeroconfDiscoveryHandler listener_callback) {
00114         this.default_listener_callback = listener_callback;
00115     }
00116     public void addListener(String service_type, String domain) {
00117         addListener(service_type, domain, this.default_listener_callback);
00118     }
00125     public void addListener(String service_type, String domain, ZeroconfDiscoveryHandler listener_callback) {
00126         String service = service_type + "." + domain + ".";
00127         logger.println("Activating listener: " + service);
00128         listeners.add(service);
00129         if ( listener_callback != null ) {
00130             listener_callbacks.put(service, listener_callback);
00131         }
00132         // add to currently established interfaces
00133         jmmdns.addServiceListener(service, this);
00134     }
00135 
00139     public void removeListener(String service_type, String domain) {
00140         String listener_to_remove = service_type + "." + domain + ".";
00141         for ( Iterator<String> listener = listeners.iterator(); listener.hasNext(); ) {
00142             String this_listener = listener.next().toString();
00143             if ( this_listener.equals(listener_to_remove) ) {
00144                 logger.println("Deactivating listener: " + this_listener);
00145                 listener.remove();
00146                 // remove from currently established interfaces
00147                 jmmdns.removeServiceListener(listener_to_remove, this);
00148                 break;
00149             }
00150         }
00151         listener_callbacks.remove(listener_to_remove);
00152     }
00165     public void addService(String name, String type, String domain, int port, String description) {
00166         String full_service_type = type + "." + domain + ".";
00167         logger.println("Registering service: " + full_service_type);
00168         String service_key = "description"; // Max 9 chars
00169         HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
00170         properties.put(service_key,description.getBytes());
00171         ServiceInfo service_info = ServiceInfo.create(full_service_type, name, port, 0, 0, true, properties);
00172         // we need much better logic here to handle duplications.
00173         if ( services.add(service_info) ) {
00174             try {
00175                 jmmdns.registerService(service_info);
00176             } catch (IOException e) {
00177                 e.printStackTrace();
00178             }
00179         }
00180 
00181         // this is broken - it adds it, but fails to resolve it on other systems
00182         // https://sourceforge.net/tracker/?func=detail&aid=3435220&group_id=93852&atid=605791
00183         // services.add(ServiceInfo.create(service_type, service_name, service_port, 0, 0, true, text));
00184     }
00185 
00186     public void addService(String name, String type, String domain, int port, HashMap<String, byte[]> properties) {
00187         String full_service_type = type + "." + domain + ".";
00188         logger.println("Registering service: " + full_service_type);
00189         ServiceInfo service_info = ServiceInfo.create(full_service_type, name, port, 0, 0, true, properties);
00190         // we need much better logic here to handle duplications.
00191         if ( services.add(service_info) ) {
00192             try {
00193                 jmmdns.registerService(service_info);
00194             } catch (IOException e) {
00195                 e.printStackTrace();
00196             }
00197         }
00198 
00199         // this is broken - it adds it, but fails to resolve it on other systems
00200         // https://sourceforge.net/tracker/?func=detail&aid=3435220&group_id=93852&atid=605791
00201         // services.add(ServiceInfo.create(service_type, service_name, service_port, 0, 0, true, text));
00202     }
00203 
00204     public void addService(DiscoveredService service) {
00205         String full_service_type = service.type + "." + service.domain + ".";
00206         logger.println("Registering service: " + full_service_type);
00207         ServiceInfo service_info = ServiceInfo.create(full_service_type, service.name, service.port, 0, 0, true, service.properties);
00208         // we need much better logic here to handle duplications.
00209         if ( services.add(service_info) ) {
00210             try {
00211                 jmmdns.registerService(service_info);
00212             } catch (IOException e) {
00213                 e.printStackTrace();
00214             }
00215         }
00216 
00217         // this is broken - it adds it, but fails to resolve it on other systems
00218         // https://sourceforge.net/tracker/?func=detail&aid=3435220&group_id=93852&atid=605791
00219         // services.add(ServiceInfo.create(service_type, service_name, service_port, 0, 0, true, text));
00220     }
00221 
00228     public List<DiscoveredService> listDiscoveredServices() {
00229         List<ServiceInfo> service_infos = new ArrayList<ServiceInfo>();
00230         for(String service : listeners ) {
00231             service_infos.addAll(Arrays.asList(this.jmmdns.list(service)));
00232         }
00233         // At this point, we have a real problem - quite often they are duplicated
00234         // but have different addresses resolved to each, in other words, we need
00235         // to uniquely resolve them since jmdns doesn't do us that favour!
00236         // Todo: Maybe get jmdns to patch this?
00237         List<DiscoveredService> discovered_services = new ArrayList<DiscoveredService>();
00238         for(ServiceInfo service_info : service_infos ) {
00239             Boolean service_found = false;
00240             for ( DiscoveredService discovered_service : discovered_services ) {
00241                 if ( service_info.getQualifiedName().equals(discovered_service.name+"."+discovered_service.type+"."+discovered_service.domain+".") ) {
00242                     for ( InetAddress inet_address : service_info.getInetAddresses() ) {
00243                         if ( inet_address instanceof Inet4Address) {
00244                             Boolean address_found = false;
00245                             for ( String unique_address : discovered_service.ipv4_addresses ) {
00246                                 if ( inet_address.getHostAddress().equals(unique_address) ) {
00247                                     address_found = true;
00248                                     break;
00249                                 }
00250                             }
00251                             if ( !address_found ) {
00252                                 discovered_service.ipv4_addresses.add(inet_address.getHostAddress());
00253                             }
00254                         } else { // Inet6Address
00255                             Boolean address_found = false;
00256                             for ( String unique_address : discovered_service.ipv6_addresses ) {
00257                                 if ( inet_address.getHostAddress().equals(unique_address) ) {
00258                                     address_found = true;
00259                                     break;
00260                                 }
00261                             }
00262                             if ( !address_found ) {
00263                                 discovered_service.ipv6_addresses.add(inet_address.getHostAddress());
00264                             }
00265                         }
00266                     }
00267                     service_found = true;
00268                     break;
00269                 }
00270             }
00271             if ( !service_found ) {
00272                 discovered_services.add(toDiscoveredService(service_info));
00273             }
00274 
00275         }
00276         return discovered_services;
00277     }
00278 
00283     public void removeAllServices() {
00284         logger.println("Removing all services");
00285         jmmdns.unregisterAllServices();
00286         services.clear();
00287     }
00288 
00289     public void shutdown() throws IOException {
00290         removeAllServices();
00291         logger.println("Shutdown");
00292         jmmdns.close();
00293     }
00294 
00295     /*************************************************************************
00296      * Listener Callbacks - from ServiceListener and ServiceTypeListener
00297      ************************************************************************/
00298     @Override
00299     public void serviceAdded(ServiceEvent event) {
00300         final ServiceInfo service_info = event.getInfo();
00301         // might need to add a timeout as a last arg here
00302         // true tells it to keep resolving when new, new info comes in (persistent).
00303         jmmdns.getServiceInfos(service_info.getType(), service_info.getName(), true);
00304         ZeroconfDiscoveryHandler callback = listener_callbacks.get(service_info.getType());
00305         if ( callback != null ) {
00306             callback.serviceAdded(toDiscoveredService(service_info));
00307         } else {
00308             logger.println("[+] Service         : " + service_info.getQualifiedName());
00309         }
00310     }
00311 
00312     @Override
00313     public void serviceRemoved(ServiceEvent event) {
00314         final String name = event.getName();
00315         final ServiceInfo service_info = event.getInfo();
00316         ZeroconfDiscoveryHandler callback = listener_callbacks.get(service_info.getType());
00317         if ( callback != null ) {
00318             callback.serviceRemoved(toDiscoveredService(service_info));
00319         } else {
00320             logger.println("[-] Service         : " + name);
00321         }
00322     }
00323 
00324     @Override
00333     public void serviceResolved(ServiceEvent event) {
00334         final ServiceInfo service_info = event.getInfo();
00335         ZeroconfDiscoveryHandler callback = listener_callbacks.get(service_info.getType());
00336         if ( callback != null ) {
00337             callback.serviceResolved(toDiscoveredService(service_info));
00338         } else {
00339             logger.println("[=] Resolved        : " + service_info.getQualifiedName());
00340             logger.println("      Port          : " + service_info.getPort() );
00341             for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00342                 logger.println("      Address       : " + service_info.getInetAddresses()[i].getHostAddress() );
00343             }
00344         }
00345     }
00346 
00347     @Override
00348     public void serviceTypeAdded(ServiceEvent event) {
00349 //        final String aType = event.getType();
00350 //        logger.println("TYPE: " + aType);
00351     }
00352 
00353     @Override
00354     public void subTypeForServiceTypeAdded(ServiceEvent event) {
00355 //        logger.println("SUBTYPE: " + event.getType());
00356     }
00357 
00358     /******************************
00359      * Network Topology Callbacks
00360      *****************************/
00361     @Override
00362     public void inetAddressAdded(NetworkTopologyEvent event) {
00363         try {
00364             logger.println("[+] NetworkInterface: " + event.getInetAddress().getHostAddress() + " [" + NetworkInterface.getByInetAddress(event.getInetAddress()).getDisplayName() + "]");
00365         } catch (IOException e) {
00366             e.printStackTrace();
00367         }
00368         try {
00369             event.getDNS().addServiceTypeListener(this);
00370             for(String listener : listeners ) {
00371                 logger.println("      Adding service listener '" + listener + "'");
00372                 event.getDNS().addServiceListener(listener, this);
00373             }
00374             for (ServiceInfo service : services ) {
00375                 logger.println("Publishing Service on " + event.getInetAddress().getHostAddress());
00376                 logger.println("  Name   : " + service.getName() );
00377                 logger.println("  Type   : " + service.getType() );
00378                 logger.println("  Port   : " + service.getPort() );
00379                 event.getDNS().registerService(service.clone()); // if you don't clone it, it falls over badly!
00380             }
00381         } catch (IOException e) {
00382             e.printStackTrace();
00383         }
00384     }
00385 
00386     @Override
00387     public void inetAddressRemoved(NetworkTopologyEvent event) {
00388         String event_address_str = event.getInetAddress().getHostAddress();
00389         // can't get the display name like above, as the interface is no longer available.
00390         // if we really want the display name, need to store it somewhere when the network interface
00391         // is added.
00392         logger.println("[-] NetworkInterface: " + event_address_str);
00393         // Trigger service info callbacks - this is fairly brutal. It might be better to
00394         // check here if that service is no longer supplied on all interfaces, then call
00395         // serviceRemoved. If it is still supplied, provide a serviceResolved callback with
00396         // the updated addresses.
00397 
00398         event.getDNS().removeServiceTypeListener(this);
00399         for(String listener : listeners ) {
00400             logger.println("      Removing service listener '" + listener + "'");
00401             event.getDNS().removeServiceListener(listener, this);
00402         }
00403         for (ServiceInfo service : services ) {
00404             logger.println("Unpublishing Service:");
00405             logger.println("  Name   : " + service.getName() );
00406             logger.println("  Type   : " + service.getType() );
00407             logger.println("  Port   : " + service.getPort() );
00408             event.getDNS().unregisterService(service); // this may not work because we're cloning it.
00409         }
00410     }
00411 
00412     /******************************
00413      * Utility Functions
00414      *****************************/
00415     public void display(DiscoveredService discovered_service) {
00416         logger.println("Discovered Service:");
00417         logger.println("  Name   : " + discovered_service.name );
00418         logger.println("  Type   : " + discovered_service.type );
00419         logger.println("  Port   : " + discovered_service.port );
00420         for ( String address : discovered_service.ipv4_addresses ) {
00421             logger.println("  Address: " + address );
00422         }
00423         for ( String address : discovered_service.ipv6_addresses ) {
00424             logger.println("  Address: " + address );
00425         }
00426     }
00427 
00428     public String toString(DiscoveredService discovered_service) {
00429         String result = "Service Info:\n";
00430         result += "  Name   : " + discovered_service.name + "\n";
00431         result += "  Type   : " + discovered_service.type + "\n";
00432         result += "  Port   : " + discovered_service.port + "\n";
00433         for ( String address : discovered_service.ipv4_addresses ) {
00434             result += "  Address: " + address + "\n";
00435         }
00436         for ( String address : discovered_service.ipv6_addresses ) {
00437             result += "  Address: " + address + "\n";
00438         }
00439         return result;
00440     }
00441 
00442     /*************************************************************************
00443      * Private
00444      ************************************************************************/
00445 //    /******************************
00446 //   * Discovery
00447 //   *****************************/
00448 //    /**
00449 //     * If you try calling this immediately after a service added callback
00450 //     * occurred, you probably wont see anything - it needs some time to resolve.
00451 //     *
00452 //     * It will block if it needs to resolve services (and aren't in its cache yet).
00453 //     *
00454 //     * @sa listDiscoveredServices
00455 //     *
00456 //     * @return service_infos : an array of discovered ServiceInfo objects.
00457 //     */
00458 //    private List<ServiceInfo> listJmdnsDiscoveredServices() {
00459 //      List<ServiceInfo> service_infos = new ArrayList<ServiceInfo>();
00460 //      for(String service : listeners ) {
00461 //          service_infos.addAll(Arrays.asList(this.jmmdns.list(service)));
00462 //      }
00463 //        return service_infos;
00464 //    }
00465 
00466     /******************************
00467      * Utility Functions
00468      *****************************/
00469     private DiscoveredService toDiscoveredService(ServiceInfo service_info) {
00470         DiscoveredService discovered_service = new DiscoveredService();
00471         discovered_service.name = service_info.getName();
00472         String[] type_domain_str = service_info.getType().split("\\.");
00473         discovered_service.type = type_domain_str[0] + "." + type_domain_str[1];
00474         discovered_service.domain = service_info.getDomain();
00475         discovered_service.hostname = service_info.getServer();
00476         discovered_service.port = service_info.getPort();
00477         for ( InetAddress inet_address : service_info.getInetAddresses() ) {
00478             if ( inet_address instanceof Inet4Address) {
00479                 discovered_service.ipv4_addresses.add(inet_address.getHostAddress());
00480             } else { // Inet6Address
00481                 discovered_service.ipv6_addresses.add(inet_address.getHostAddress());
00482             }
00483         }
00484 
00485         Enumeration<String> propertyNames = service_info.getPropertyNames();
00486         if (propertyNames != null) {
00487             ArrayList<String> properties = Collections.list(propertyNames);
00488 
00489             for (String name : properties) {
00490                 String property = service_info.getPropertyString(name);
00491 //                if (property == null) {
00492 //                    property = service_info.getPropertyBytes(name);
00493 //                }
00494 
00495                 if (property != null) {
00496                     discovered_service.properties.put(name, property);
00497                 }
00498             }
00499         }
00500 
00501         return discovered_service;
00502     }
00503 
00504 //    private String toString(ServiceInfo service_info) {
00505 //      String result = "Service Info:\n";
00506 //      result += "  Name   : " + service_info.getName() + "\n";
00507 //      result += "  Type   : " + service_info.getType() + "\n";
00508 //      result += "  Port   : " + service_info.getPort() + "\n";
00509 //      for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00510 //          result += "  Address: " + service_info.getInetAddresses()[i].getHostAddress() + "\n";
00511 //      }
00512 //      return result;
00513 //    }
00514 //
00515 //    private void display(ServiceInfo service_info) {
00516 //      logger.println("Service Info:");
00517 //      logger.println("  Name   : " + service_info.getName() );
00518 //      logger.println("  Type   : " + service_info.getType() );
00519 //      logger.println("  Port   : " + service_info.getPort() );
00520 //      for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) {
00521 //          logger.println("  Address: " + service_info.getInetAddresses()[i].getHostAddress() );
00522 //      }
00523 //    }
00524 
00525 }


smarthome_network_zeroconf
Author(s): Mickael Gaillard , Erwan Le Huitouze
autogenerated on Sat Jun 8 2019 18:23:02