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


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