/MDCDiscovery/src/main/java/org/smpte/_2071/_2012/mdcd/naming/NamingServices.java

https://bitbucket.org/psymes/34cs-st2071 · Java · 587 lines · 433 code · 72 blank · 82 comment · 92 complexity · 028e38a4b14be2e63ccdcc7951732b74 MD5 · raw file

  1. package org.smpte._2071._2012.mdcd.naming;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.net.InetAddress;
  5. import java.net.InetSocketAddress;
  6. import java.net.URL;
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9. import java.util.Collections;
  10. import java.util.HashSet;
  11. import java.util.LinkedHashSet;
  12. import java.util.Map;
  13. import java.util.Properties;
  14. import java.util.Set;
  15. import java.util.logging.Level;
  16. import java.util.logging.Logger;
  17. import javax.naming.NamingException;
  18. import org.smpte._2071._2012.mdcd.impl.Utils;
  19. import org.smpte._2071._2012.mdcd.naming.NamingService.NAMING_TYPE;
  20. import org.smpte._2071._2012.mdcd.net.DHCPClient;
  21. import org.xbill.DNS.Name;
  22. public class NamingServices
  23. {
  24. public static String FACTORY_PROPERTIES = "NamingServices.properties";
  25. public static final String NULL_DOMAIN = "";
  26. public static final String[] LOCALLINK_DNS_DOMAINS = new String[] {"local.",
  27. "254.169.in-arpa.",
  28. "8.e.f.ip6.arpa.",
  29. "9.e.f.ip6.arpa.",
  30. "a.e.f.ip6.arpa.",
  31. "b.e.f.ip6.arpa."};
  32. private static final Logger log = Logger.getLogger(NamingServices.class.getName());
  33. private Properties props = null;
  34. private CompoundKeyMap<InetAddress, String, NamingService> namingServices = new CompoundKeyMap<InetAddress, String, NamingService>();
  35. /**
  36. * Gets the Naming Service for the local interface address and the domain.
  37. *
  38. * @param localAddress The Internet address of the local interface
  39. * @param domain The domain
  40. * @return A Naming Service that represents all of the naming services registered for the local
  41. * interface address and the domain.
  42. */
  43. public NamingService getNamingService(InetAddress localAddress, String domain)
  44. {
  45. if (domain == null || domain.length() == 0)
  46. {
  47. return getDefaultNamingService(localAddress);
  48. }
  49. return namingServices.get(localAddress, domain);
  50. }
  51. /**
  52. * Returns the default Naming Service for the specified local address. Typically the Naming Service
  53. * Assigned by the OS to the Network Interface.
  54. *
  55. * @param localAddress THe IP address of the local network interface
  56. * @return The default Naming Service for the specified local address
  57. */
  58. public NamingService getDefaultNamingService(InetAddress localAddress)
  59. {
  60. NamingService defaultNamingService = namingServices.get(localAddress, NULL_DOMAIN);
  61. if (defaultNamingService == null)
  62. {
  63. try
  64. {
  65. defaultNamingService = new org.smpte._2071._2012.mdcd.naming.jndi.NamingServiceImpl();
  66. register(localAddress, null, defaultNamingService);
  67. } catch (Exception e)
  68. {
  69. Utils.log(log, Level.WARNING, "Could not create JNDI Naming Service, trying DHCP", Level.FINE, e);
  70. try
  71. {
  72. Set<InetSocketAddress> namingServerAddresses = new LinkedHashSet<InetSocketAddress>();
  73. Set<InetAddress> addresses = DHCPClient.resolveDHCPDomainNameServers(localAddress);
  74. if (addresses != null)
  75. {
  76. for (InetAddress address : addresses)
  77. {
  78. namingServerAddresses.add(new InetSocketAddress(address, 53));
  79. }
  80. }
  81. defaultNamingService = newNamingService(null, localAddress, namingServerAddresses);
  82. register(localAddress, null, defaultNamingService);
  83. } catch (Exception e1)
  84. {
  85. Utils.log(log, Level.SEVERE, "Could not create default Naming Service!", Level.FINE, e1);
  86. }
  87. }
  88. }
  89. return defaultNamingService;
  90. }
  91. /**
  92. * Returns the complete list of domain names registered with the Naming Services.
  93. *
  94. * @return The complete list of domain names registered with the Naming Services.
  95. */
  96. public Set<String> getDomains()
  97. {
  98. Set<String> allDomains = new LinkedHashSet<String>();
  99. Set<Map.Entry<InetAddress, Set<String>>> entries = namingServices.keyMap().entrySet();
  100. for (Map.Entry<InetAddress, Set<String>> entry : entries)
  101. {
  102. Set<String> domains = entry.getValue();
  103. for (String domain : domains)
  104. {
  105. if (domain.length() != 0)
  106. {
  107. allDomains.add(domain);
  108. }
  109. }
  110. }
  111. return allDomains;
  112. }
  113. /**
  114. * Returns the list of domain names registered for the local interface address.
  115. *
  116. * @param address The Internet address of the local interface
  117. * @return The list of domains registered for the local interface address.
  118. */
  119. public Set<String> getDomains(InetAddress address)
  120. {
  121. Set<String> domains = new LinkedHashSet<String>();
  122. for (String domain : namingServices.keyMap().get(address))
  123. {
  124. if (domain.length() != 0)
  125. {
  126. domains.add(domain);
  127. }
  128. }
  129. return domains;
  130. }
  131. /**
  132. * Registers the Naming Service for the Internet Address and Domain.
  133. *
  134. * @param address The Internet Address. A null Internet Address indicates the default Naming Service for the browse domain
  135. * @param domain The browse domain. A null domain indicates the default Naming Service for the Internet Address/Interface
  136. * @param naming The Naming Service to register. Cannot be null.
  137. * @throws NamingException
  138. */
  139. public void register(InetAddress address, String domain, NamingService naming)
  140. throws NamingException
  141. {
  142. domain = (domain == null || domain.length() == 0 ? NULL_DOMAIN : domain);
  143. log.fine("Registering Naming Service for domain \"" + domain + "\"");
  144. if (naming.getNamingType() != NAMING_TYPE.MULTICAST_DNS)
  145. {
  146. for (String linkLocalDomain : LOCALLINK_DNS_DOMAINS)
  147. {
  148. if (domain.equals(linkLocalDomain))
  149. {
  150. throw new NamingException("Cannot register a Unicast Naming Service to Multicast Domain!");
  151. }
  152. }
  153. }
  154. if (naming != null)
  155. {
  156. naming.associateToDomain(domain);
  157. naming.setLocalAddress(address);
  158. NamingService existingNaming = namingServices.get(address, domain);
  159. if (existingNaming != null)
  160. {
  161. if (sameNamingDomain(existingNaming, naming))
  162. {
  163. // Eliminate duplicate addresses
  164. Set<InetSocketAddress> namingAddresses = new HashSet<InetSocketAddress>(naming.getServerAddresses());
  165. namingAddresses.removeAll(existingNaming.getServerAddresses());
  166. if (namingAddresses.size() > 0)
  167. {
  168. try
  169. {
  170. existingNaming.addServerAddresses(namingAddresses);
  171. } catch (NamingException e)
  172. {
  173. Utils.log(log, Level.WARNING, "Error registering Naming Service for an existing interface domain with additional interfaces - " + e.getMessage(), Level.FINE, e);
  174. }
  175. } else
  176. {
  177. log.fine("A Naming Service matching " + naming + " already exists and was not registered.");
  178. }
  179. } else
  180. {
  181. if (existingNaming instanceof NamingFacade)
  182. {
  183. NamingFacade facade = (NamingFacade) existingNaming;
  184. log.fine("Adding Naming Service \"" + naming + "\" to facade \"" + facade + "\"");
  185. facade.addNamingService(naming);
  186. } else
  187. {
  188. NamingService testNaming = namingServices.get(address, domain);
  189. if (existingNaming != testNaming)
  190. {
  191. log.warning("Naming Service removed for domain \"" + domain + "\" on address \"" + address + "\" is not the same as the one we received with \"get\".");
  192. }
  193. NamingFacade facade = new NamingFacade(existingNaming, naming);
  194. log.fine("Adding Naming Service \"" + naming + "\" to facade \"" + facade + "\"");
  195. namingServices.put(address, domain, facade);
  196. }
  197. }
  198. } else
  199. {
  200. log.fine("Registering new Naming Service " + naming);
  201. namingServices.put(address, domain, naming);
  202. }
  203. }
  204. }
  205. private boolean sameNamingDomain(NamingService naming1, NamingService naming2)
  206. {
  207. Set<String> domains1 = naming1.getDomains();
  208. Set<String> domains2 = naming2.getDomains();
  209. return domains1.equals(domains2) && naming1.getLocalAddress().equals(naming2.getLocalAddress());
  210. }
  211. /**
  212. * Unregisters the Naming Service(s) for the Internet Address and Domain. If null, all Naming
  213. * Services for address and domain are removed.
  214. *
  215. * @param address The Internet Address. A null Internet Address indicates the default Naming Service for the browse domain
  216. * @param domain The browse domain. A null domain indicates the default Naming Service for the Internet Address/Interface
  217. * @param naming The Naming Service to remove. If null, all Naming Services for address and domain will be unregistered.
  218. * @return The Naming Service that was unregistered
  219. */
  220. public NamingService unregister(InetAddress address, String domain, NamingService naming)
  221. {
  222. domain = (domain == null || domain.length() == 0 ? NULL_DOMAIN : domain);
  223. if (naming == null)
  224. {
  225. naming = namingServices.remove(address, domain);
  226. if (naming != null)
  227. {
  228. try
  229. {
  230. naming.close();
  231. } catch (Exception e)
  232. {
  233. Utils.log(log, Level.INFO, e.getMessage(), Level.FINE, e);
  234. }
  235. }
  236. return naming;
  237. } else
  238. {
  239. NamingService existingNaming = namingServices.get(address, domain);
  240. if (existingNaming != null && existingNaming.isAssociatedToDomain((domain == null ? NULL_DOMAIN : domain)))
  241. {
  242. if (existingNaming instanceof NamingFacade)
  243. {
  244. NamingFacade facade = (NamingFacade) existingNaming;
  245. int numberOfNamingServices;
  246. int oldNumberOfNamingServices = facade.numberOfNamingServices();
  247. if (facade.removeNamingService(naming) && oldNumberOfNamingServices != (numberOfNamingServices = facade.numberOfNamingServices()))
  248. {
  249. switch (numberOfNamingServices)
  250. {
  251. case 0 :
  252. // No Naming Services Remain, Remove Facade and all Naming Services for Domain
  253. namingServices.remove(address, domain);
  254. break;
  255. case 1 :
  256. // Only 1 Naming Service Remains, Replace Facade with the Naming Service
  257. namingServices.put(address, domain, facade.getNamingService(0));
  258. break;
  259. }
  260. return naming;
  261. }
  262. } else
  263. {
  264. naming = namingServices.remove(address, domain);
  265. if (naming != null)
  266. {
  267. try
  268. {
  269. naming.close();
  270. } catch (Exception e)
  271. {
  272. Utils.log(log, Level.INFO, e.getMessage(), Level.FINE, e);
  273. }
  274. }
  275. return naming;
  276. }
  277. }
  278. }
  279. return null;
  280. }
  281. /**
  282. * Creates and returns a Naming Service bound to the specified local Internet Address and remote Naming Service Internet Addresses.
  283. *
  284. * @param namingServiceType The Type of Naming Service to create.
  285. * @param localAddress The local Internet Address to bind too
  286. * @param namingServerAddresses An optional list of Naming Server Socket Addressed (Internet Address & Port). If empty or null, the NICs default Name Servers are used.
  287. * @return A Naming Service bound to the specified Internet Address and remote Naming Services.
  288. * @throws NamingException Thrown if the Naming Service could not be created for any reason.
  289. */
  290. /*NamingService newNamingService(NAMING_TYPE namingServiceType, InetAddress localAddress, Set<InetSocketAddress> namingServerAddresses)
  291. throws NamingException
  292. {
  293. NamingService naming = newNamingService(namingServiceType);
  294. naming.setLocalAddress(localAddress);
  295. naming.addServerAddresses(namingServerAddresses);
  296. return naming;
  297. }*/
  298. /**
  299. * Creates and returns a new Naming Service bound and associated with the specified local Internet Address, Domain and remote Naming Service Internet Addresses.
  300. * Naming Services are cached and re-used by local Internet Address and Domain, via Naming.associateDomain and registerNamingService.
  301. *
  302. * @param domain The name of the domain that the Naming Service will service.
  303. * @param localAddress The local Internet Address to bind too
  304. * @param namingServerAddresses An optional list of Naming Server Socket Addressed (Internet Address & Port). If empty or null, the NICs default Name Servers are used.
  305. * @return A Naming Service bound to the specified Internet Address, Domain and remote Naming Services.
  306. * @throws NamingException Thrown if the Naming Service could not be created for any reason.
  307. */
  308. @SuppressWarnings("resource")
  309. public NamingService newNamingService(String domain, InetAddress localAddress, Set<InetSocketAddress> namingServerAddresses)
  310. throws NamingException
  311. {
  312. domain = (domain == null || domain.length() == 0 ? NULL_DOMAIN : domain);
  313. NamingService naming = null;
  314. if (isLocalLinkDomain(domain))
  315. {
  316. naming = newNamingService(NAMING_TYPE.MULTICAST_DNS);
  317. } else
  318. {
  319. naming = newNamingService(NAMING_TYPE.UNICAST_DNS);
  320. }
  321. naming.setLocalAddress(localAddress);
  322. naming.addServerAddresses(namingServerAddresses);
  323. naming.associateToDomain(domain);
  324. return naming;
  325. }
  326. public static boolean isLocalLinkDomain(String domain)
  327. {
  328. boolean multicast = false;
  329. for (String test : LOCALLINK_DNS_DOMAINS)
  330. {
  331. multicast = domain.matches("\\.?" + test + "\\.?");
  332. if (multicast)
  333. {
  334. break;
  335. }
  336. }
  337. return multicast;
  338. }
  339. /**
  340. * Creates and returns a new, unbound, Naming Service of the type specified.
  341. *
  342. * @param multicast Multicast or Unicast.
  343. * @return
  344. * @throws NamingException
  345. */
  346. private NamingService newNamingService(NAMING_TYPE type)
  347. throws NamingException
  348. {
  349. try
  350. {
  351. String className = findClassName(type);
  352. if (className != null)
  353. {
  354. @SuppressWarnings("unchecked")
  355. Class<NamingService> clazz = (Class<NamingService>) Class.forName(className);
  356. NamingService naming = clazz.newInstance();
  357. return naming;
  358. } else
  359. {
  360. throw new NamingException(type + " name resolution is not supported.");
  361. }
  362. } catch (NamingException ne)
  363. {
  364. throw ne;
  365. } catch (Exception e)
  366. {
  367. NamingException ne = new NamingException("Error loading module for " + type + " name resolution " + (e.getMessage() != null ? " - " + e.getMessage() : "."));
  368. ne.setStackTrace(e.getStackTrace());
  369. throw ne;
  370. }
  371. }
  372. String findClassName(NAMING_TYPE type)
  373. throws IOException
  374. {
  375. String className = null;
  376. if (props == null)
  377. {
  378. URL url = NamingServices.class.getResource(FACTORY_PROPERTIES);
  379. if (url == null)
  380. {
  381. url = NamingServices.class.getResource("/" + FACTORY_PROPERTIES);
  382. if (url == null)
  383. {
  384. url = ClassLoader.getSystemResource(FACTORY_PROPERTIES);
  385. if (url == null)
  386. {
  387. url = ClassLoader.getSystemResource("/" + FACTORY_PROPERTIES);
  388. }
  389. }
  390. }
  391. if (url != null)
  392. {
  393. InputStream in = url.openStream();
  394. Properties p = new Properties();
  395. p.load(in);
  396. props = p;
  397. try
  398. {
  399. in.close();
  400. } catch (Exception e)
  401. {
  402. // ignore, no one cares if you can close the stream, your done with it!
  403. }
  404. }
  405. }
  406. if (props != null)
  407. {
  408. className = (String) props.get(type.toString());
  409. }
  410. return className;
  411. }
  412. public void unregisterDomain(InetAddress address, String domain)
  413. {
  414. domain = (domain == null || domain.length() == 0 ? NULL_DOMAIN : domain);
  415. ArrayList<NamingService> list = new ArrayList<NamingService>(namingServices.values());
  416. for (NamingService naming : list)
  417. {
  418. try
  419. {
  420. naming.unassociateToDomain(domain);
  421. } catch (NamingException e)
  422. {
  423. Utils.log(log, Level.INFO, e.getMessage(), Level.FINE, e);
  424. }
  425. }
  426. }
  427. public void unregisterAll(InetAddress address)
  428. {
  429. Map<InetAddress, Set<String>> keyMap = namingServices.keyMap();
  430. if (keyMap != null)
  431. {
  432. ArrayList<String> domains = new ArrayList<String>(keyMap.get(address));
  433. for (String domain : domains)
  434. {
  435. unregisterDomain(address, domain);
  436. }
  437. }
  438. }
  439. public Collection<NamingService> getNamingServices()
  440. {
  441. return Collections.unmodifiableCollection(namingServices.values());
  442. }
  443. public String determineParentDomain(String domainName)
  444. throws NamingException
  445. {
  446. String largestMatchingDomain = null;
  447. Set<String> domains = getDomains();
  448. for (String domain : domains)
  449. {
  450. if (largestMatchingDomain == null)
  451. {
  452. if (domainName.endsWith(domain))
  453. {
  454. largestMatchingDomain = domain;
  455. }
  456. } else
  457. {
  458. if (largestMatchingDomain.length() < domain.length())
  459. {
  460. if (domainName.endsWith(domain))
  461. {
  462. largestMatchingDomain = domain;
  463. }
  464. }
  465. }
  466. }
  467. if (largestMatchingDomain == null)
  468. {
  469. try
  470. {
  471. StringBuilder builder = new StringBuilder(domainName.length());
  472. Name name = Name.fromString(domainName);
  473. int labels = name.labels();
  474. if (labels > 1)
  475. {
  476. for (int index = 1; index < labels; index++)
  477. {
  478. String labelString = name.getLabelString(index);
  479. // Skip Service Name Type components
  480. if (labelString.charAt(0) == '_')
  481. {
  482. continue;
  483. } else
  484. {
  485. builder.append(labelString);
  486. if (index < labels - 1)
  487. {
  488. builder.append(".");
  489. }
  490. }
  491. }
  492. builder.append(".");
  493. return builder.toString();
  494. } else
  495. {
  496. return null;
  497. }
  498. } catch (Exception e)
  499. {
  500. NamingException ne = new NamingException(e.getMessage());
  501. ne.setStackTrace(e.getStackTrace());
  502. throw ne;
  503. }
  504. } else
  505. {
  506. return largestMatchingDomain;
  507. }
  508. }
  509. public boolean contains(InetAddress localAddress, String domain, Set<InetSocketAddress> serverAddresses)
  510. {
  511. NamingService naming = getNamingService(localAddress, domain);
  512. Set<InetSocketAddress> testServerAddresses = naming.getServerAddresses();
  513. return testServerAddresses.containsAll(serverAddresses);
  514. }
  515. }