$search
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 }