PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/jre-1.6.0/src/javax/management/remote/JMXServiceURL.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 680 lines | 322 code | 65 blank | 293 comment | 85 complexity | 0a686007c3f220e1ac496bbcc796fb51 MD5 | raw file
  1. /*
  2. * %W% %E%
  3. *
  4. * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
  5. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.management.remote;
  8. import java.io.IOException;
  9. import java.io.Serializable;
  10. import java.net.InetAddress;
  11. import java.net.MalformedURLException;
  12. import java.net.UnknownHostException;
  13. import java.util.BitSet;
  14. import java.util.StringTokenizer;
  15. import com.sun.jmx.remote.util.ClassLogger;
  16. import com.sun.jmx.remote.util.EnvHelp;
  17. /**
  18. * <p>The address of a JMX API connector server. Instances of this class
  19. * are immutable.</p>
  20. *
  21. * <p>The address is an <em>Abstract Service URL</em> for SLP, as
  22. * defined in RFC 2609 and amended by RFC 3111. It must look like
  23. * this:</p>
  24. *
  25. * <blockquote>
  26. *
  27. * <code>service:jmx:<em>protocol</em>:<em>sap</em></code>
  28. *
  29. * </blockquote>
  30. *
  31. * <p>Here, <code><em>protocol</em></code> is the transport
  32. * protocol to be used to connect to the connector server. It is
  33. * a string of one or more ASCII characters, each of which is a
  34. * letter, a digit, or one of the characters <code>+</code> or
  35. * <code>-</code>. The first character must be a letter.
  36. * Uppercase letters are converted into lowercase ones.</p>
  37. *
  38. * <p><code><em>sap</em></code> is the address at which the connector
  39. * server is found. This address uses a subset of the syntax defined
  40. * by RFC 2609 for IP-based protocols. It is a subset because the
  41. * <code>user@host</code> syntax is not supported.</p>
  42. *
  43. * <p>The other syntaxes defined by RFC 2609 are not currently
  44. * supported by this class.</p>
  45. *
  46. * <p>The supported syntax is:</p>
  47. *
  48. * <blockquote>
  49. *
  50. * <code>//<em>[host[</em>:<em>port]][url-path]</em></code>
  51. *
  52. * </blockquote>
  53. *
  54. * <p>Square brackets <code>[]</code> indicate optional parts of
  55. * the address. Not all protocols will recognize all optional
  56. * parts.</p>
  57. *
  58. * <p>The <code><em>host</em></code> is a host name, an IPv4 numeric
  59. * host address, or an IPv6 numeric address enclosed in square
  60. * brackets.</p>
  61. *
  62. * <p>The <code><em>port</em></code> is a decimal port number. 0
  63. * means a default or anonymous port, depending on the protocol.</p>
  64. *
  65. * <p>The <code><em>host</em></code> and <code><em>port</em></code>
  66. * can be omitted. The <code><em>port</em></code> cannot be supplied
  67. * without a <code><em>host</em></code>.</p>
  68. *
  69. * <p>The <code><em>url-path</em></code>, if any, begins with a slash
  70. * (<code>/</code>) or a semicolon (<code>;</code>) and continues to
  71. * the end of the address. It can contain attributes using the
  72. * semicolon syntax specified in RFC 2609. Those attributes are not
  73. * parsed by this class and incorrect attribute syntax is not
  74. * detected.</p>
  75. *
  76. * <p>Although it is legal according to RFC 2609 to have a
  77. * <code><em>url-path</em></code> that begins with a semicolon, not
  78. * all implementations of SLP allow it, so it is recommended to avoid
  79. * that syntax.</p>
  80. *
  81. * <p>Case is not significant in the initial
  82. * <code>service:jmx:<em>protocol</em></code> string or in the host
  83. * part of the address. Depending on the protocol, case can be
  84. * significant in the <code><em>url-path</em></code>.</p>
  85. *
  86. * @see <a
  87. * href="ftp://ftp.rfc-editor.org/in-notes/rfc2609.txt">RFC 2609,
  88. * "Service Templates and <code>Service:</code> Schemes"</a>
  89. * @see <a
  90. * href="ftp://ftp.rfc-editor.org/in-notes/rfc3111.txt">RFC 3111,
  91. * "Service Location Protocol Modifications for IPv6"</a>
  92. *
  93. * @since 1.5
  94. * @since.unbundled 1.0
  95. */
  96. public class JMXServiceURL implements Serializable {
  97. private static final long serialVersionUID = 8173364409860779292L;
  98. /**
  99. * <p>Constructs a <code>JMXServiceURL</code> by parsing a Service URL
  100. * string.</p>
  101. *
  102. * @param serviceURL the URL string to be parsed.
  103. *
  104. * @exception NullPointerException if <code>serviceURL</code> is
  105. * null.
  106. *
  107. * @exception MalformedURLException if <code>serviceURL</code>
  108. * does not conform to the syntax for an Abstract Service URL or
  109. * if it is not a valid name for a JMX Remote API service. A
  110. * <code>JMXServiceURL</code> must begin with the string
  111. * <code>"service:jmx:"</code> (case-insensitive). It must not
  112. * contain any characters that are not printable ASCII characters.
  113. */
  114. public JMXServiceURL(String serviceURL) throws MalformedURLException {
  115. final int serviceURLLength = serviceURL.length();
  116. /* Check that there are no non-ASCII characters in the URL,
  117. following RFC 2609. */
  118. for (int i = 0; i < serviceURLLength; i++) {
  119. char c = serviceURL.charAt(i);
  120. if (c < 32 || c >= 127) {
  121. throw new MalformedURLException("Service URL contains " +
  122. "non-ASCII character 0x" +
  123. Integer.toHexString(c));
  124. }
  125. }
  126. // Parse the required prefix
  127. final String requiredPrefix = "service:jmx:";
  128. final int requiredPrefixLength = requiredPrefix.length();
  129. if (!serviceURL.regionMatches(true, // ignore case
  130. 0, // serviceURL offset
  131. requiredPrefix,
  132. 0, // requiredPrefix offset
  133. requiredPrefixLength)) {
  134. throw new MalformedURLException("Service URL must start with " +
  135. requiredPrefix);
  136. }
  137. int[] ptr = new int[1];
  138. // Parse the protocol name
  139. final int protoStart = requiredPrefixLength;
  140. final int protoEnd = indexOf(serviceURL, ':', protoStart);
  141. this.protocol =
  142. serviceURL.substring(protoStart, protoEnd).toLowerCase();
  143. if (!serviceURL.regionMatches(protoEnd, "://", 0, 3)) {
  144. throw new MalformedURLException("Missing \"://\" after " +
  145. "protocol name");
  146. }
  147. // Parse the host name
  148. final int hostStart = protoEnd + 3;
  149. final int hostEnd;
  150. if (hostStart < serviceURLLength
  151. && serviceURL.charAt(hostStart) == '[') {
  152. hostEnd = serviceURL.indexOf(']', hostStart) + 1;
  153. if (hostEnd == 0)
  154. throw new MalformedURLException("Bad host name: [ without ]");
  155. this.host = serviceURL.substring(hostStart + 1, hostEnd - 1);
  156. if (!isNumericIPv6Address(this.host)) {
  157. throw new MalformedURLException("Address inside [...] must " +
  158. "be numeric IPv6 address");
  159. }
  160. } else {
  161. hostEnd =
  162. indexOfFirstNotInSet(serviceURL, hostNameBitSet, hostStart);
  163. this.host = serviceURL.substring(hostStart, hostEnd);
  164. }
  165. // Parse the port number
  166. final int portEnd;
  167. if (hostEnd < serviceURLLength && serviceURL.charAt(hostEnd) == ':') {
  168. if (this.host.length() == 0) {
  169. throw new MalformedURLException("Cannot give port number " +
  170. "without host name");
  171. }
  172. final int portStart = hostEnd + 1;
  173. portEnd =
  174. indexOfFirstNotInSet(serviceURL, numericBitSet, portStart);
  175. final String portString = serviceURL.substring(portStart, portEnd);
  176. try {
  177. this.port = Integer.parseInt(portString);
  178. } catch (NumberFormatException e) {
  179. throw new MalformedURLException("Bad port number: \"" +
  180. portString + "\": " + e);
  181. }
  182. } else {
  183. portEnd = hostEnd;
  184. this.port = 0;
  185. }
  186. // Parse the URL path
  187. final int urlPathStart = portEnd;
  188. if (urlPathStart < serviceURLLength)
  189. this.urlPath = serviceURL.substring(urlPathStart);
  190. else
  191. this.urlPath = "";
  192. validate();
  193. }
  194. /**
  195. * <p>Constructs a <code>JMXServiceURL</code> with the given protocol,
  196. * host, and port. This constructor is equivalent to
  197. * {@link #JMXServiceURL(String, String, int, String)
  198. * JMXServiceURL(protocol, host, port, null)}.</p>
  199. *
  200. * @param protocol the protocol part of the URL. If null, defaults
  201. * to <code>jmxmp</code>.
  202. *
  203. * @param host the host part of the URL. If null, defaults to the
  204. * local host name, as determined by
  205. * <code>InetAddress.getLocalHost().getHostName()</code>. If it
  206. * is a numeric IPv6 address, it can optionally be enclosed in
  207. * square brackets <code>[]</code>.
  208. *
  209. * @param port the port part of the URL.
  210. *
  211. * @exception MalformedURLException if one of the parts is
  212. * syntactically incorrect, or if <code>host</code> is null and it
  213. * is not possible to find the local host name, or if
  214. * <code>port</code> is negative.
  215. */
  216. public JMXServiceURL(String protocol, String host, int port)
  217. throws MalformedURLException {
  218. this(protocol, host, port, null);
  219. }
  220. /**
  221. * <p>Constructs a <code>JMXServiceURL</code> with the given parts.
  222. *
  223. * @param protocol the protocol part of the URL. If null, defaults
  224. * to <code>jmxmp</code>.
  225. *
  226. * @param host the host part of the URL. If null, defaults to the
  227. * local host name, as determined by
  228. * <code>InetAddress.getLocalHost().getHostName()</code>. If it
  229. * is a numeric IPv6 address, it can optionally be enclosed in
  230. * square brackets <code>[]</code>.
  231. *
  232. * @param port the port part of the URL.
  233. *
  234. * @param urlPath the URL path part of the URL. If null, defaults to
  235. * the empty string.
  236. *
  237. * @exception MalformedURLException if one of the parts is
  238. * syntactically incorrect, or if <code>host</code> is null and it
  239. * is not possible to find the local host name, or if
  240. * <code>port</code> is negative.
  241. */
  242. public JMXServiceURL(String protocol, String host, int port,
  243. String urlPath)
  244. throws MalformedURLException {
  245. if (protocol == null)
  246. protocol = "jmxmp";
  247. if (host == null) {
  248. InetAddress local;
  249. try {
  250. local = InetAddress.getLocalHost();
  251. } catch (UnknownHostException e) {
  252. throw new MalformedURLException("Local host name unknown: " +
  253. e);
  254. }
  255. host = local.getHostName();
  256. /* We might have a hostname that violates DNS naming
  257. rules, for example that contains an `_'. While we
  258. could be strict and throw an exception, this is rather
  259. user-hostile. Instead we use its numerical IP address.
  260. We can only reasonably do this for the host==null case.
  261. If we're given an explicit host name that is illegal we
  262. have to reject it. (Bug 5057532.) */
  263. try {
  264. validateHost(host);
  265. } catch (MalformedURLException e) {
  266. if (logger.fineOn()) {
  267. logger.fine("JMXServiceURL",
  268. "Replacing illegal local host name " +
  269. host + " with numeric IP address " +
  270. "(see RFC 1034)", e);
  271. }
  272. host = local.getHostAddress();
  273. /* Use the numeric address, which could be either IPv4
  274. or IPv6. validateHost will accept either. */
  275. }
  276. }
  277. if (host.startsWith("[")) {
  278. if (!host.endsWith("]")) {
  279. throw new MalformedURLException("Host starts with [ but " +
  280. "does not end with ]");
  281. }
  282. host = host.substring(1, host.length() - 1);
  283. if (!isNumericIPv6Address(host)) {
  284. throw new MalformedURLException("Address inside [...] must " +
  285. "be numeric IPv6 address");
  286. }
  287. if (host.startsWith("["))
  288. throw new MalformedURLException("More than one [[...]]");
  289. }
  290. this.protocol = protocol.toLowerCase();
  291. this.host = host;
  292. this.port = port;
  293. if (urlPath == null)
  294. urlPath = "";
  295. this.urlPath = urlPath;
  296. validate();
  297. }
  298. private void validate() throws MalformedURLException {
  299. // Check protocol
  300. final int protoEnd = indexOfFirstNotInSet(protocol, protocolBitSet, 0);
  301. if (protoEnd == 0 || protoEnd < protocol.length()
  302. || !alphaBitSet.get(protocol.charAt(0))) {
  303. throw new MalformedURLException("Missing or invalid protocol " +
  304. "name: \"" + protocol + "\"");
  305. }
  306. // Check host
  307. validateHost();
  308. // Check port
  309. if (port < 0)
  310. throw new MalformedURLException("Bad port: " + port);
  311. // Check URL path
  312. if (urlPath.length() > 0) {
  313. if (!urlPath.startsWith("/") && !urlPath.startsWith(";"))
  314. throw new MalformedURLException("Bad URL path: " + urlPath);
  315. }
  316. }
  317. private void validateHost() throws MalformedURLException {
  318. if (host.length() == 0) {
  319. if (port != 0) {
  320. throw new MalformedURLException("Cannot give port number " +
  321. "without host name");
  322. }
  323. return;
  324. }
  325. validateHost(host);
  326. }
  327. private static void validateHost(String h)
  328. throws MalformedURLException {
  329. if (isNumericIPv6Address(h)) {
  330. /* We assume J2SE >= 1.4 here. Otherwise you can't
  331. use the address anyway. We can't call
  332. InetAddress.getByName without checking for a
  333. numeric IPv6 address, because we mustn't try to do
  334. a DNS lookup in case the address is not actually
  335. numeric. */
  336. try {
  337. InetAddress.getByName(h);
  338. } catch (Exception e) {
  339. /* We should really catch UnknownHostException
  340. here, but a bug in JDK 1.4 causes it to throw
  341. ArrayIndexOutOfBoundsException, e.g. if the
  342. string is ":". */
  343. MalformedURLException bad =
  344. new MalformedURLException("Bad IPv6 address: " + h);
  345. EnvHelp.initCause(bad, e);
  346. throw bad;
  347. }
  348. } else {
  349. /* Tiny state machine to check valid host name. This
  350. checks the hostname grammar from RFC 1034 (DNS),
  351. page 11. A hostname is a dot-separated list of one
  352. or more labels, where each label consists of
  353. letters, numbers, or hyphens. A label cannot begin
  354. or end with a hyphen. Empty hostnames are not
  355. allowed. Note that numeric IPv4 addresses are a
  356. special case of this grammar.
  357. The state is entirely captured by the last
  358. character seen, with a virtual `.' preceding the
  359. name. We represent any alphanumeric character by
  360. `a'.
  361. We need a special hack to check, as required by the
  362. RFC 2609 (SLP) grammar, that the last component of
  363. the hostname begins with a letter. Respecting the
  364. intent of the RFC, we only do this if there is more
  365. than one component. If your local hostname begins
  366. with a digit, we don't reject it. */
  367. final int hostLen = h.length();
  368. char lastc = '.';
  369. boolean sawDot = false;
  370. char componentStart = 0;
  371. loop:
  372. for (int i = 0; i < hostLen; i++) {
  373. char c = h.charAt(i);
  374. boolean isAlphaNumeric = alphaNumericBitSet.get(c);
  375. if (lastc == '.')
  376. componentStart = c;
  377. if (isAlphaNumeric)
  378. lastc = 'a';
  379. else if (c == '-') {
  380. if (lastc == '.')
  381. break; // will throw exception
  382. lastc = '-';
  383. } else if (c == '.') {
  384. sawDot = true;
  385. if (lastc != 'a')
  386. break; // will throw exception
  387. lastc = '.';
  388. } else {
  389. lastc = '.'; // will throw exception
  390. break;
  391. }
  392. }
  393. try {
  394. if (lastc != 'a')
  395. throw randomException;
  396. if (sawDot && !alphaBitSet.get(componentStart)) {
  397. /* Must be a numeric IPv4 address. In addition to
  398. the explicitly-thrown exceptions, we can get
  399. NoSuchElementException from the calls to
  400. tok.nextToken and NumberFormatException from
  401. the call to Integer.parseInt. Using exceptions
  402. for control flow this way is a bit evil but it
  403. does simplify things enormously. */
  404. StringTokenizer tok = new StringTokenizer(h, ".", true);
  405. for (int i = 0; i < 4; i++) {
  406. String ns = tok.nextToken();
  407. int n = Integer.parseInt(ns);
  408. if (n < 0 || n > 255)
  409. throw randomException;
  410. if (i < 3 && !tok.nextToken().equals("."))
  411. throw randomException;
  412. }
  413. if (tok.hasMoreTokens())
  414. throw randomException;
  415. }
  416. } catch (Exception e) {
  417. throw new MalformedURLException("Bad host: \"" + h + "\"");
  418. }
  419. }
  420. }
  421. private static final Exception randomException = new Exception();
  422. /**
  423. * <p>The protocol part of the Service URL.
  424. *
  425. * @return the protocol part of the Service URL. This is never null.
  426. */
  427. public String getProtocol() {
  428. return protocol;
  429. }
  430. /**
  431. * <p>The host part of the Service URL. If the Service URL was
  432. * constructed with the constructor that takes a URL string
  433. * parameter, the result is the substring specifying the host in
  434. * that URL. If the Service URL was constructed with a
  435. * constructor that takes a separate host parameter, the result is
  436. * the string that was specified. If that string was null, the
  437. * result is
  438. * <code>InetAddress.getLocalHost().getHostName()</code>.</p>
  439. *
  440. * <p>In either case, if the host was specified using the
  441. * <code>[...]</code> syntax for numeric IPv6 addresses, the
  442. * square brackets are not included in the return value here.</p>
  443. *
  444. * @return the host part of the Service URL. This is never null.
  445. */
  446. public String getHost() {
  447. return host;
  448. }
  449. /**
  450. * <p>The port of the Service URL. If no port was
  451. * specified, the returned value is 0.</p>
  452. *
  453. * @return the port of the Service URL, or 0 if none.
  454. */
  455. public int getPort() {
  456. return port;
  457. }
  458. /**
  459. * <p>The URL Path part of the Service URL. This is an empty
  460. * string, or a string beginning with a slash (<code>/</code>), or
  461. * a string beginning with a semicolon (<code>;</code>).
  462. *
  463. * @return the URL Path part of the Service URL. This is never
  464. * null.
  465. */
  466. public String getURLPath() {
  467. return urlPath;
  468. }
  469. /**
  470. * <p>The string representation of this Service URL. If the value
  471. * returned by this method is supplied to the
  472. * <code>JMXServiceURL</code> constructor, the resultant object is
  473. * equal to this one.</p>
  474. *
  475. * <p>The <code><em>host</em></code> part of the returned string
  476. * is the value returned by {@link #getHost()}. If that value
  477. * specifies a numeric IPv6 address, it is surrounded by square
  478. * brackets <code>[]</code>.</p>
  479. *
  480. * <p>The <code><em>port</em></code> part of the returned string
  481. * is the value returned by {@link #getPort()} in its shortest
  482. * decimal form. If the value is zero, it is omitted.</p>
  483. *
  484. * @return the string representation of this Service URL.
  485. */
  486. public String toString() {
  487. /* We don't bother synchronizing the access to toString. At worst,
  488. n threads will independently compute and store the same value. */
  489. if (toString != null)
  490. return toString;
  491. StringBuffer buf = new StringBuffer("service:jmx:");
  492. buf.append(getProtocol()).append("://");
  493. final String getHost = getHost();
  494. if (isNumericIPv6Address(getHost))
  495. buf.append('[').append(getHost).append(']');
  496. else
  497. buf.append(getHost);
  498. final int getPort = getPort();
  499. if (getPort != 0)
  500. buf.append(':').append(getPort);
  501. buf.append(getURLPath());
  502. toString = buf.toString();
  503. return toString;
  504. }
  505. /**
  506. * <p>Indicates whether some other object is equal to this one.
  507. * This method returns true if and only if <code>obj</code> is an
  508. * instance of <code>JMXServiceURL</code> whose {@link
  509. * #getProtocol()}, {@link #getHost()}, {@link #getPort()}, and
  510. * {@link #getURLPath()} methods return the same values as for
  511. * this object. The values for {@link #getProtocol()} and {@link
  512. * #getHost()} can differ in case without affecting equality.
  513. *
  514. * @param obj the reference object with which to compare.
  515. *
  516. * @return <code>true</code> if this object is the same as the
  517. * <code>obj</code> argument; <code>false</code> otherwise.
  518. */
  519. public boolean equals(Object obj) {
  520. if (!(obj instanceof JMXServiceURL))
  521. return false;
  522. JMXServiceURL u = (JMXServiceURL) obj;
  523. return
  524. (u.getProtocol().equalsIgnoreCase(getProtocol()) &&
  525. u.getHost().equalsIgnoreCase(getHost()) &&
  526. u.getPort() == getPort() &&
  527. u.getURLPath().equals(getURLPath()));
  528. }
  529. public int hashCode() {
  530. return toString().hashCode();
  531. }
  532. /* True if this string, assumed to be a valid argument to
  533. * InetAddress.getByName, is a numeric IPv6 address.
  534. */
  535. private static boolean isNumericIPv6Address(String s) {
  536. // address contains colon if and only if it's a numeric IPv6 address
  537. return (s.indexOf(':') >= 0);
  538. }
  539. // like String.indexOf but returns string length not -1 if not present
  540. private static int indexOf(String s, char c, int fromIndex) {
  541. int index = s.indexOf(c, fromIndex);
  542. if (index < 0)
  543. return s.length();
  544. else
  545. return index;
  546. }
  547. private static int indexOfFirstNotInSet(String s, BitSet set,
  548. int fromIndex) {
  549. final int slen = s.length();
  550. int i = fromIndex;
  551. while (true) {
  552. if (i >= slen)
  553. break;
  554. char c = s.charAt(i);
  555. if (c >= 128)
  556. break; // not ASCII
  557. if (!set.get(c))
  558. break;
  559. i++;
  560. }
  561. return i;
  562. }
  563. private final static BitSet alphaBitSet = new BitSet(128);
  564. private final static BitSet numericBitSet = new BitSet(128);
  565. private final static BitSet alphaNumericBitSet = new BitSet(128);
  566. private final static BitSet protocolBitSet = new BitSet(128);
  567. private final static BitSet hostNameBitSet = new BitSet(128);
  568. static {
  569. /* J2SE 1.4 adds lots of handy methods to BitSet that would
  570. allow us to simplify here, e.g. by not writing loops, but
  571. we want to work on J2SE 1.3 too. */
  572. for (char c = '0'; c <= '9'; c++)
  573. numericBitSet.set(c);
  574. for (char c = 'A'; c <= 'Z'; c++)
  575. alphaBitSet.set(c);
  576. for (char c = 'a'; c <= 'z'; c++)
  577. alphaBitSet.set(c);
  578. alphaNumericBitSet.or(alphaBitSet);
  579. alphaNumericBitSet.or(numericBitSet);
  580. protocolBitSet.or(alphaNumericBitSet);
  581. protocolBitSet.set('+');
  582. protocolBitSet.set('-');
  583. hostNameBitSet.or(alphaNumericBitSet);
  584. hostNameBitSet.set('-');
  585. hostNameBitSet.set('.');
  586. }
  587. private static void addCharsToBitSet(BitSet set, String chars) {
  588. for (int i = 0; i < chars.length(); i++)
  589. set.set(chars.charAt(i));
  590. }
  591. /**
  592. * The value returned by {@link #getProtocol()}.
  593. */
  594. private final String protocol;
  595. /**
  596. * The value returned by {@link #getHost()}.
  597. */
  598. private final String host;
  599. /**
  600. * The value returned by {@link #getPort()}.
  601. */
  602. private final int port;
  603. /**
  604. * The value returned by {@link #getURLPath()}.
  605. */
  606. private final String urlPath;
  607. /**
  608. * Cached result of {@link #toString()}.
  609. */
  610. private transient String toString;
  611. private static final ClassLogger logger =
  612. new ClassLogger("javax.management.remote.misc", "JMXServiceURL");
  613. }