$search
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 * (non-Javadoc) 00085 * @see java.io.Closeable#close() 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 // We need to cancel all the DNS 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 // JmDNS never throws this is only because of the closeable interface 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 * (non-Javadoc) 00124 * @see javax.jmdns.JmmDNS#getNames() 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 * (non-Javadoc) 00137 * @see javax.jmdns.JmmDNS#getHostNames() 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 * (non-Javadoc) 00150 * @see javax.jmdns.JmmDNS#getInetAddresses() 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 * (non-Javadoc) 00163 * @see javax.jmdns.JmmDNS#getInterfaces() 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 * (non-Javadoc) 00177 * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String) 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 * (non-Javadoc) 00186 * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, long) 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 * (non-Javadoc) 00195 * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, boolean) 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 * (non-Javadoc) 00204 * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, boolean, long) 00205 */ 00206 @Override 00207 public ServiceInfo[] getServiceInfos(final String type, final String name, final boolean persistent, final long timeout) { 00208 // We need to run this in parallel to respect the timeout. 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 * (non-Javadoc) 00233 * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String) 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 * (non-Javadoc) 00242 * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean) 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 * (non-Javadoc) 00251 * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, long) 00252 */ 00253 @Override 00254 public void requestServiceInfo(String type, String name, long timeout) { 00255 this.requestServiceInfo(type, name, false, timeout); 00256 } 00257 00258 /* 00259 * (non-Javadoc) 00260 * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean, long) 00261 */ 00262 @Override 00263 public void requestServiceInfo(final String type, final String name, final boolean persistent, final long timeout) { 00264 // We need to run this in parallel to respect the timeout. 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 * (non-Javadoc) 00280 * @see javax.jmdns.JmmDNS#addServiceTypeListener(javax.jmdns.ServiceTypeListener) 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 * (non-Javadoc) 00291 * @see javax.jmdns.JmmDNS#removeServiceTypeListener(javax.jmdns.ServiceTypeListener) 00292 */ 00293 @Override 00294 public void removeServiceTypeListener(ServiceTypeListener listener) { 00295 for (JmDNS mDNS : _knownMDNS.values()) { 00296 mDNS.removeServiceTypeListener(listener); 00297 } 00298 } 00299 00300 /* 00301 * (non-Javadoc) 00302 * @see javax.jmdns.JmmDNS#addServiceListener(java.lang.String, javax.jmdns.ServiceListener) 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 * (non-Javadoc) 00322 * @see javax.jmdns.JmmDNS#removeServiceListener(java.lang.String, javax.jmdns.ServiceListener) 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 * (non-Javadoc) 00333 * @see javax.jmdns.impl.ServiceInfoImpl.Delegate#textValueUpdated(javax.jmdns.ServiceInfo, byte[]) 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 * (non-Javadoc) 00351 * @see javax.jmdns.JmmDNS#registerService(javax.jmdns.ServiceInfo) 00352 */ 00353 @Override 00354 public void registerService(ServiceInfo info) throws IOException { 00355 // This is really complex. We need to clone the service info for each DNS but then we loose the ability to update it. 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 * (non-Javadoc) 00367 * @see javax.jmdns.JmmDNS#unregisterService(javax.jmdns.ServiceInfo) 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 * (non-Javadoc) 00382 * @see javax.jmdns.JmmDNS#unregisterAllServices() 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 * (non-Javadoc) 00396 * @see javax.jmdns.JmmDNS#registerServiceType(java.lang.String) 00397 */ 00398 @Override 00399 public void registerServiceType(String type) { 00400 for (JmDNS mDNS : _knownMDNS.values()) { 00401 mDNS.registerServiceType(type); 00402 } 00403 } 00404 00405 /* 00406 * (non-Javadoc) 00407 * @see javax.jmdns.JmmDNS#list(java.lang.String) 00408 */ 00409 @Override 00410 public ServiceInfo[] list(String type) { 00411 return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT); 00412 } 00413 00414 /* 00415 * (non-Javadoc) 00416 * @see javax.jmdns.JmmDNS#list(java.lang.String, long) 00417 */ 00418 @Override 00419 public ServiceInfo[] list(final String type, final long timeout) { 00420 // We need to run this in parallel to respect the timeout. 00421 00422 // The .equals for ServiceInfo only compares qualified names, no good for resolved 00423 // service info comparison here. 00424 //final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size() * 5)); 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 // try { 00431 // System.out.printf("jmmdns.list(): %s\n",mDNS.getInetAddress().getHostAddress()); 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 // System.out.println(" jmmdns.list(): found duplicate service info"); 00440 duplicate = true; 00441 } 00442 } 00443 if ( !duplicate ) { 00444 // System.out.printf(" Service: %s\n", service_info.getQualifiedName()); 00445 // for ( int i = 0; i < service_info.getInetAddresses().length; ++i ) { 00446 // System.out.printf(" Address: %s\n", service_info.getInetAddresses()[i].getHostAddress() ); 00447 // } 00448 single_mdns_list.add(service_info); 00449 } 00450 } 00451 // } catch (IOException exception) {} 00452 final List<ServiceInfo> service_list = new ArrayList<ServiceInfo>(single_mdns_list); 00453 executor.submit(new Runnable() { 00457 @Override 00458 public void run() { 00459 //result.addAll(Arrays.asList(mDNS.list(type, timeout))); 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 * (non-Javadoc) 00475 * @see javax.jmdns.JmmDNS#listBySubtype(java.lang.String) 00476 */ 00477 @Override 00478 public Map<String, ServiceInfo[]> listBySubtype(String type) { 00479 return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT); 00480 } 00481 00482 /* 00483 * (non-Javadoc) 00484 * @see javax.jmdns.JmmDNS#listBySubtype(java.lang.String, long) 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 * (non-Javadoc) 00508 * @see javax.jmdns.JmmDNS#addNetworkTopologyListener(javax.jmdns.NetworkTopologyListener) 00509 */ 00510 @Override 00511 public void addNetworkTopologyListener(NetworkTopologyListener listener) { 00512 _networkListeners.add(listener); 00513 } 00514 00515 /* 00516 * (non-Javadoc) 00517 * @see javax.jmdns.JmmDNS#removeNetworkTopologyListener(javax.jmdns.NetworkTopologyListener) 00518 */ 00519 @Override 00520 public void removeNetworkTopologyListener(NetworkTopologyListener listener) { 00521 _networkListeners.remove(listener); 00522 } 00523 00524 /* 00525 * (non-Javadoc) 00526 * @see javax.jmdns.JmmDNS#networkListeners() 00527 */ 00528 @Override 00529 public NetworkTopologyListener[] networkListeners() { 00530 return _networkListeners.toArray(new NetworkTopologyListener[_networkListeners.size()]); 00531 } 00532 00533 /* 00534 * (non-Javadoc) 00535 * @see javax.jmdns.NetworkTopologyListener#inetAddressAdded(javax.jmdns.NetworkTopologyEvent) 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 * (non-Javadoc) 00565 * @see javax.jmdns.NetworkTopologyListener#inetAddressRemoved(javax.jmdns.NetworkTopologyEvent) 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 }