/external/jmdns/src/javax/jmdns/impl/JmmDNSImpl.java

https://gitlab.com/brian0218/rk3066_r-box_android4.2.2_sdk · Java · 599 lines · 375 code · 52 blank · 172 comment · 32 complexity · f4b2fc8e0018a3879502f4eff6aab845 MD5 · raw file

  1. /**
  2. *
  3. */
  4. package javax.jmdns.impl;
  5. import java.io.IOException;
  6. import java.net.InetAddress;
  7. import java.util.ArrayList;
  8. import java.util.Arrays;
  9. import java.util.Collections;
  10. import java.util.HashMap;
  11. import java.util.HashSet;
  12. import java.util.List;
  13. import java.util.Map;
  14. import java.util.Set;
  15. import java.util.Timer;
  16. import java.util.TimerTask;
  17. import java.util.concurrent.ConcurrentHashMap;
  18. import java.util.concurrent.ConcurrentMap;
  19. import java.util.concurrent.ExecutorService;
  20. import java.util.concurrent.Executors;
  21. import java.util.concurrent.TimeUnit;
  22. import java.util.logging.Level;
  23. import java.util.logging.Logger;
  24. import javax.jmdns.JmDNS;
  25. import javax.jmdns.JmmDNS;
  26. import javax.jmdns.NetworkTopologyDiscovery;
  27. import javax.jmdns.NetworkTopologyEvent;
  28. import javax.jmdns.NetworkTopologyListener;
  29. import javax.jmdns.ServiceInfo;
  30. import javax.jmdns.ServiceListener;
  31. import javax.jmdns.ServiceTypeListener;
  32. import javax.jmdns.impl.constants.DNSConstants;
  33. /**
  34. * This class enable multihomming mDNS. It will open a mDNS per IP address of the machine.
  35. *
  36. * @author Cédrik Lime, Pierre Frisch
  37. */
  38. public class JmmDNSImpl implements JmmDNS, NetworkTopologyListener, ServiceInfoImpl.Delegate {
  39. private static Logger logger = Logger.getLogger(JmmDNSImpl.class.getName());
  40. private final Set<NetworkTopologyListener> _networkListeners;
  41. /**
  42. * Every JmDNS created.
  43. */
  44. private final ConcurrentMap<InetAddress, JmDNS> _knownMDNS;
  45. /**
  46. * This enable the service info text update.
  47. */
  48. private final ConcurrentMap<String, ServiceInfo> _services;
  49. private final ExecutorService _ListenerExecutor;
  50. private final ExecutorService _jmDNSExecutor;
  51. private final Timer _timer;
  52. /**
  53. *
  54. */
  55. public JmmDNSImpl() {
  56. super();
  57. _networkListeners = Collections.synchronizedSet(new HashSet<NetworkTopologyListener>());
  58. _knownMDNS = new ConcurrentHashMap<InetAddress, JmDNS>();
  59. _services = new ConcurrentHashMap<String, ServiceInfo>(20);
  60. _ListenerExecutor = Executors.newSingleThreadExecutor();
  61. _jmDNSExecutor = Executors.newCachedThreadPool();
  62. _timer = new Timer("Multihommed mDNS.Timer", true);
  63. (new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance())).start(_timer);
  64. }
  65. /*
  66. * (non-Javadoc)
  67. * @see java.io.Closeable#close()
  68. */
  69. @Override
  70. public void close() throws IOException {
  71. if (logger.isLoggable(Level.FINER)) {
  72. logger.finer("Cancelling JmmDNS: " + this);
  73. }
  74. _timer.cancel();
  75. _ListenerExecutor.shutdown();
  76. // We need to cancel all the DNS
  77. ExecutorService executor = Executors.newCachedThreadPool();
  78. for (final JmDNS mDNS : _knownMDNS.values()) {
  79. executor.submit(new Runnable() {
  80. /**
  81. * {@inheritDoc}
  82. */
  83. @Override
  84. public void run() {
  85. try {
  86. mDNS.close();
  87. } catch (IOException exception) {
  88. // JmDNS never throws this is only because of the closeable interface
  89. }
  90. }
  91. });
  92. }
  93. executor.shutdown();
  94. try {
  95. executor.awaitTermination(DNSConstants.CLOSE_TIMEOUT, TimeUnit.MILLISECONDS);
  96. } catch (InterruptedException exception) {
  97. logger.log(Level.WARNING, "Exception ", exception);
  98. }
  99. _knownMDNS.clear();
  100. }
  101. /*
  102. * (non-Javadoc)
  103. * @see javax.jmdns.JmmDNS#getNames()
  104. */
  105. @Override
  106. public String[] getNames() {
  107. Set<String> result = new HashSet<String>();
  108. for (JmDNS mDNS : _knownMDNS.values()) {
  109. result.add(mDNS.getName());
  110. }
  111. return result.toArray(new String[result.size()]);
  112. }
  113. /*
  114. * (non-Javadoc)
  115. * @see javax.jmdns.JmmDNS#getHostNames()
  116. */
  117. @Override
  118. public String[] getHostNames() {
  119. Set<String> result = new HashSet<String>();
  120. for (JmDNS mDNS : _knownMDNS.values()) {
  121. result.add(mDNS.getHostName());
  122. }
  123. return result.toArray(new String[result.size()]);
  124. }
  125. /*
  126. * (non-Javadoc)
  127. * @see javax.jmdns.JmmDNS#getInetAddresses()
  128. */
  129. @Override
  130. public InetAddress[] getInetAddresses() throws IOException {
  131. Set<InetAddress> result = new HashSet<InetAddress>();
  132. for (JmDNS mDNS : _knownMDNS.values()) {
  133. result.add(mDNS.getInetAddress());
  134. }
  135. return result.toArray(new InetAddress[result.size()]);
  136. }
  137. /*
  138. * (non-Javadoc)
  139. * @see javax.jmdns.JmmDNS#getInterfaces()
  140. */
  141. @Override
  142. @Deprecated
  143. public InetAddress[] getInterfaces() throws IOException {
  144. Set<InetAddress> result = new HashSet<InetAddress>();
  145. for (JmDNS mDNS : _knownMDNS.values()) {
  146. result.add(mDNS.getInterface());
  147. }
  148. return result.toArray(new InetAddress[result.size()]);
  149. }
  150. /*
  151. * (non-Javadoc)
  152. * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String)
  153. */
  154. @Override
  155. public ServiceInfo[] getServiceInfos(String type, String name) {
  156. return this.getServiceInfos(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
  157. }
  158. /*
  159. * (non-Javadoc)
  160. * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, long)
  161. */
  162. @Override
  163. public ServiceInfo[] getServiceInfos(String type, String name, long timeout) {
  164. return this.getServiceInfos(type, name, false, timeout);
  165. }
  166. /*
  167. * (non-Javadoc)
  168. * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, boolean)
  169. */
  170. @Override
  171. public ServiceInfo[] getServiceInfos(String type, String name, boolean persistent) {
  172. return this.getServiceInfos(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
  173. }
  174. /*
  175. * (non-Javadoc)
  176. * @see javax.jmdns.JmmDNS#getServiceInfos(java.lang.String, java.lang.String, boolean, long)
  177. */
  178. @Override
  179. public ServiceInfo[] getServiceInfos(final String type, final String name, final boolean persistent, final long timeout) {
  180. // We need to run this in parallel to respect the timeout.
  181. final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size()));
  182. ExecutorService executor = Executors.newCachedThreadPool();
  183. for (final JmDNS mDNS : _knownMDNS.values()) {
  184. executor.submit(new Runnable() {
  185. /**
  186. * {@inheritDoc}
  187. */
  188. @Override
  189. public void run() {
  190. result.add(mDNS.getServiceInfo(type, name, persistent, timeout));
  191. }
  192. });
  193. }
  194. executor.shutdown();
  195. try {
  196. executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
  197. } catch (InterruptedException exception) {
  198. logger.log(Level.WARNING, "Exception ", exception);
  199. }
  200. return result.toArray(new ServiceInfo[result.size()]);
  201. }
  202. /*
  203. * (non-Javadoc)
  204. * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String)
  205. */
  206. @Override
  207. public void requestServiceInfo(String type, String name) {
  208. this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
  209. }
  210. /*
  211. * (non-Javadoc)
  212. * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean)
  213. */
  214. @Override
  215. public void requestServiceInfo(String type, String name, boolean persistent) {
  216. this.requestServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
  217. }
  218. /*
  219. * (non-Javadoc)
  220. * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, long)
  221. */
  222. @Override
  223. public void requestServiceInfo(String type, String name, long timeout) {
  224. this.requestServiceInfo(type, name, false, timeout);
  225. }
  226. /*
  227. * (non-Javadoc)
  228. * @see javax.jmdns.JmmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean, long)
  229. */
  230. @Override
  231. public void requestServiceInfo(final String type, final String name, final boolean persistent, final long timeout) {
  232. // We need to run this in parallel to respect the timeout.
  233. for (final JmDNS mDNS : _knownMDNS.values()) {
  234. _jmDNSExecutor.submit(new Runnable() {
  235. /**
  236. * {@inheritDoc}
  237. */
  238. @Override
  239. public void run() {
  240. mDNS.requestServiceInfo(type, name, persistent, timeout);
  241. }
  242. });
  243. }
  244. }
  245. /*
  246. * (non-Javadoc)
  247. * @see javax.jmdns.JmmDNS#addServiceTypeListener(javax.jmdns.ServiceTypeListener)
  248. */
  249. @Override
  250. public void addServiceTypeListener(ServiceTypeListener listener) throws IOException {
  251. for (JmDNS mDNS : _knownMDNS.values()) {
  252. mDNS.addServiceTypeListener(listener);
  253. }
  254. }
  255. /*
  256. * (non-Javadoc)
  257. * @see javax.jmdns.JmmDNS#removeServiceTypeListener(javax.jmdns.ServiceTypeListener)
  258. */
  259. @Override
  260. public void removeServiceTypeListener(ServiceTypeListener listener) {
  261. for (JmDNS mDNS : _knownMDNS.values()) {
  262. mDNS.removeServiceTypeListener(listener);
  263. }
  264. }
  265. /*
  266. * (non-Javadoc)
  267. * @see javax.jmdns.JmmDNS#addServiceListener(java.lang.String, javax.jmdns.ServiceListener)
  268. */
  269. @Override
  270. public void addServiceListener(String type, ServiceListener listener) {
  271. for (JmDNS mDNS : _knownMDNS.values()) {
  272. mDNS.addServiceListener(type, listener);
  273. }
  274. }
  275. /*
  276. * (non-Javadoc)
  277. * @see javax.jmdns.JmmDNS#removeServiceListener(java.lang.String, javax.jmdns.ServiceListener)
  278. */
  279. @Override
  280. public void removeServiceListener(String type, ServiceListener listener) {
  281. for (JmDNS mDNS : _knownMDNS.values()) {
  282. mDNS.removeServiceListener(type, listener);
  283. }
  284. }
  285. /*
  286. * (non-Javadoc)
  287. * @see javax.jmdns.impl.ServiceInfoImpl.Delegate#textValueUpdated(javax.jmdns.ServiceInfo, byte[])
  288. */
  289. @Override
  290. public void textValueUpdated(ServiceInfo target, byte[] value) {
  291. synchronized (_services) {
  292. for (JmDNS mDNS : _knownMDNS.values()) {
  293. ServiceInfo info = ((JmDNSImpl) mDNS).getServices().get(target.getQualifiedName());
  294. if (info != null) {
  295. info.setText(value);
  296. } else {
  297. logger.warning("We have a mDNS that does not know about the service info being updated.");
  298. }
  299. }
  300. }
  301. }
  302. /*
  303. * (non-Javadoc)
  304. * @see javax.jmdns.JmmDNS#registerService(javax.jmdns.ServiceInfo)
  305. */
  306. @Override
  307. public void registerService(ServiceInfo info) throws IOException {
  308. // This is really complex. We need to clone the service info for each DNS but then we loose the ability to update it.
  309. synchronized (_services) {
  310. for (JmDNS mDNS : _knownMDNS.values()) {
  311. mDNS.registerService(info.clone());
  312. }
  313. ((ServiceInfoImpl) info).setDelegate(this);
  314. _services.put(info.getQualifiedName(), info);
  315. }
  316. }
  317. /*
  318. * (non-Javadoc)
  319. * @see javax.jmdns.JmmDNS#unregisterService(javax.jmdns.ServiceInfo)
  320. */
  321. @Override
  322. public void unregisterService(ServiceInfo info) {
  323. synchronized (_services) {
  324. for (JmDNS mDNS : _knownMDNS.values()) {
  325. mDNS.unregisterService(info);
  326. }
  327. ((ServiceInfoImpl) info).setDelegate(null);
  328. _services.remove(info.getQualifiedName());
  329. }
  330. }
  331. /*
  332. * (non-Javadoc)
  333. * @see javax.jmdns.JmmDNS#unregisterAllServices()
  334. */
  335. @Override
  336. public void unregisterAllServices() {
  337. synchronized (_services) {
  338. for (JmDNS mDNS : _knownMDNS.values()) {
  339. mDNS.unregisterAllServices();
  340. }
  341. _services.clear();
  342. }
  343. }
  344. /*
  345. * (non-Javadoc)
  346. * @see javax.jmdns.JmmDNS#registerServiceType(java.lang.String)
  347. */
  348. @Override
  349. public void registerServiceType(String type) {
  350. for (JmDNS mDNS : _knownMDNS.values()) {
  351. mDNS.registerServiceType(type);
  352. }
  353. }
  354. /*
  355. * (non-Javadoc)
  356. * @see javax.jmdns.JmmDNS#list(java.lang.String)
  357. */
  358. @Override
  359. public ServiceInfo[] list(String type) {
  360. return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT);
  361. }
  362. /*
  363. * (non-Javadoc)
  364. * @see javax.jmdns.JmmDNS#list(java.lang.String, long)
  365. */
  366. @Override
  367. public ServiceInfo[] list(final String type, final long timeout) {
  368. // We need to run this in parallel to respect the timeout.
  369. final Set<ServiceInfo> result = Collections.synchronizedSet(new HashSet<ServiceInfo>(_knownMDNS.size() * 5));
  370. ExecutorService executor = Executors.newCachedThreadPool();
  371. for (final JmDNS mDNS : _knownMDNS.values()) {
  372. executor.submit(new Runnable() {
  373. /**
  374. * {@inheritDoc}
  375. */
  376. @Override
  377. public void run() {
  378. result.addAll(Arrays.asList(mDNS.list(type, timeout)));
  379. }
  380. });
  381. }
  382. executor.shutdown();
  383. try {
  384. executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
  385. } catch (InterruptedException exception) {
  386. logger.log(Level.WARNING, "Exception ", exception);
  387. }
  388. return result.toArray(new ServiceInfo[result.size()]);
  389. }
  390. /*
  391. * (non-Javadoc)
  392. * @see javax.jmdns.JmmDNS#listBySubtype(java.lang.String)
  393. */
  394. @Override
  395. public Map<String, ServiceInfo[]> listBySubtype(String type) {
  396. return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT);
  397. }
  398. /*
  399. * (non-Javadoc)
  400. * @see javax.jmdns.JmmDNS#listBySubtype(java.lang.String, long)
  401. */
  402. @Override
  403. public Map<String, ServiceInfo[]> listBySubtype(final String type, final long timeout) {
  404. Map<String, List<ServiceInfo>> map = new HashMap<String, List<ServiceInfo>>(5);
  405. for (ServiceInfo info : this.list(type, timeout)) {
  406. String subtype = info.getSubtype();
  407. if (!map.containsKey(subtype)) {
  408. map.put(subtype, new ArrayList<ServiceInfo>(10));
  409. }
  410. map.get(subtype).add(info);
  411. }
  412. Map<String, ServiceInfo[]> result = new HashMap<String, ServiceInfo[]>(map.size());
  413. for (String subtype : map.keySet()) {
  414. List<ServiceInfo> infoForSubType = map.get(subtype);
  415. result.put(subtype, infoForSubType.toArray(new ServiceInfo[infoForSubType.size()]));
  416. }
  417. return result;
  418. }
  419. /*
  420. * (non-Javadoc)
  421. * @see javax.jmdns.JmmDNS#addNetworkTopologyListener(javax.jmdns.NetworkTopologyListener)
  422. */
  423. @Override
  424. public void addNetworkTopologyListener(NetworkTopologyListener listener) {
  425. _networkListeners.add(listener);
  426. }
  427. /*
  428. * (non-Javadoc)
  429. * @see javax.jmdns.JmmDNS#removeNetworkTopologyListener(javax.jmdns.NetworkTopologyListener)
  430. */
  431. @Override
  432. public void removeNetworkTopologyListener(NetworkTopologyListener listener) {
  433. _networkListeners.remove(listener);
  434. }
  435. /*
  436. * (non-Javadoc)
  437. * @see javax.jmdns.JmmDNS#networkListeners()
  438. */
  439. @Override
  440. public NetworkTopologyListener[] networkListeners() {
  441. return _networkListeners.toArray(new NetworkTopologyListener[_networkListeners.size()]);
  442. }
  443. /*
  444. * (non-Javadoc)
  445. * @see javax.jmdns.NetworkTopologyListener#inetAddressAdded(javax.jmdns.NetworkTopologyEvent)
  446. */
  447. @Override
  448. public void inetAddressAdded(NetworkTopologyEvent event) {
  449. InetAddress address = event.getInetAddress();
  450. try {
  451. synchronized (this) {
  452. if (!_knownMDNS.containsKey(address)) {
  453. _knownMDNS.put(address, JmDNS.create(address));
  454. final NetworkTopologyEvent jmdnsEvent = new NetworkTopologyEventImpl(_knownMDNS.get(address), address);
  455. for (final NetworkTopologyListener listener : this.networkListeners()) {
  456. _ListenerExecutor.submit(new Runnable() {
  457. /**
  458. * {@inheritDoc}
  459. */
  460. @Override
  461. public void run() {
  462. listener.inetAddressAdded(jmdnsEvent);
  463. }
  464. });
  465. }
  466. }
  467. }
  468. } catch (Exception e) {
  469. logger.warning("Unexpected unhandled exception: " + e);
  470. }
  471. }
  472. /*
  473. * (non-Javadoc)
  474. * @see javax.jmdns.NetworkTopologyListener#inetAddressRemoved(javax.jmdns.NetworkTopologyEvent)
  475. */
  476. @Override
  477. public void inetAddressRemoved(NetworkTopologyEvent event) {
  478. InetAddress address = event.getInetAddress();
  479. try {
  480. synchronized (this) {
  481. if (_knownMDNS.containsKey(address)) {
  482. JmDNS mDNS = _knownMDNS.remove(address);
  483. mDNS.close();
  484. final NetworkTopologyEvent jmdnsEvent = new NetworkTopologyEventImpl(mDNS, address);
  485. for (final NetworkTopologyListener listener : this.networkListeners()) {
  486. _ListenerExecutor.submit(new Runnable() {
  487. /**
  488. * {@inheritDoc}
  489. */
  490. @Override
  491. public void run() {
  492. listener.inetAddressRemoved(jmdnsEvent);
  493. }
  494. });
  495. }
  496. }
  497. }
  498. } catch (Exception e) {
  499. logger.warning("Unexpected unhandled exception: " + e);
  500. }
  501. }
  502. /**
  503. * Checks the network state.<br/>
  504. * If the network change, this class will reconfigure the list of DNS do adapt to the new configuration.
  505. */
  506. static class NetworkChecker extends TimerTask {
  507. private static Logger logger1 = Logger.getLogger(NetworkChecker.class.getName());
  508. private final NetworkTopologyListener _mmDNS;
  509. private final NetworkTopologyDiscovery _topology;
  510. private Set<InetAddress> _knownAddresses;
  511. public NetworkChecker(NetworkTopologyListener mmDNS, NetworkTopologyDiscovery topology) {
  512. super();
  513. this._mmDNS = mmDNS;
  514. this._topology = topology;
  515. _knownAddresses = Collections.synchronizedSet(new HashSet<InetAddress>());
  516. }
  517. public void start(Timer timer) {
  518. timer.schedule(this, 0, DNSConstants.NETWORK_CHECK_INTERVAL);
  519. }
  520. /**
  521. * {@inheritDoc}
  522. */
  523. @Override
  524. public void run() {
  525. try {
  526. InetAddress[] curentAddresses = _topology.getInetAddresses();
  527. Set<InetAddress> current = new HashSet<InetAddress>(curentAddresses.length);
  528. for (InetAddress address : curentAddresses) {
  529. current.add(address);
  530. if (!_knownAddresses.contains(address)) {
  531. final NetworkTopologyEvent event = new NetworkTopologyEventImpl(_mmDNS, address);
  532. _mmDNS.inetAddressAdded(event);
  533. }
  534. }
  535. for (InetAddress address : _knownAddresses) {
  536. if (!current.contains(address)) {
  537. final NetworkTopologyEvent event = new NetworkTopologyEventImpl(_mmDNS, address);
  538. _mmDNS.inetAddressRemoved(event);
  539. }
  540. }
  541. _knownAddresses = current;
  542. } catch (Exception e) {
  543. logger1.warning("Unexpected unhandled exception: " + e);
  544. }
  545. }
  546. }
  547. }