1. /*
  2. * @(#)ObjectName.java 1.67 04/04/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.management;
  8. // java import
  9. import java.io.InvalidObjectException;
  10. import java.io.IOException;
  11. import java.io.ObjectInputStream;
  12. import java.io.ObjectOutputStream;
  13. import java.io.ObjectStreamField;
  14. import java.io.Serializable;
  15. import java.security.AccessController;
  16. import java.security.PrivilegedAction;
  17. import java.util.Arrays;
  18. import java.util.Enumeration;
  19. import java.util.HashMap;
  20. import java.util.Hashtable;
  21. import com.sun.jmx.mbeanserver.GetPropertyAction;
  22. /**
  23. * <p>Represents the object name of an MBean, or a pattern that can
  24. * match the names of several MBeans. Instances of this class are
  25. * immutable.</p>
  26. *
  27. * <p>An instance of this class can
  28. * be used to represent:
  29. * <ul>
  30. * <li> An object name
  31. * <li> An object name pattern, within the context of a query
  32. * </ul></p>
  33. *
  34. * <p>An object name consists of two parts, the domain and the key
  35. * properties.</p>
  36. *
  37. * <p>The <em>domain</em> is a string of characters not including
  38. * the character colon (<code>:</code>).</p>
  39. *
  40. * <p>If the domain includes at least one occurrence of the wildcard
  41. * characters asterisk (<code>*</code>) or question mark
  42. * (<code>?</code>), then the object name is a pattern. The asterisk
  43. * matches any sequence of zero or more characters, while the question
  44. * mark matches any single character.</p>
  45. *
  46. * <p>If the domain is empty, it will be replaced in certain contexts
  47. * by the <em>default domain</em> of the MBean server in which the
  48. * ObjectName is used.</p>
  49. *
  50. * <p>The <em>key properties</em> are an unordered set of keys and
  51. * associated values.</p>
  52. *
  53. * <p>Each <em>key</em> is a nonempty string of characters which may
  54. * not contain any of the characters comma (<code>,</code>), equals
  55. * (<code>=</code>), colon, asterisk, or question mark. The same key
  56. * may not occur twice in a given ObjectName.</p>
  57. *
  58. * <p>Each <em>value</em> associated with a key is a string of
  59. * characters that is either unquoted or quoted.</p>
  60. *
  61. * <p>An <em>unquoted value</em> is a possibly empty string of
  62. * characters which may not contain any of the characters comma,
  63. * equals, colon, quote, asterisk, or question mark.</p>
  64. *
  65. * <p>A <em>quoted value</em> consists of a quote (<code>"</code>),
  66. * followed by a possibly empty string of characters, followed by
  67. * another quote. Within the string of characters, the backslash
  68. * (<code>\</code>) has a special meaning. It must be followed by
  69. * one of the following characters:</p>
  70. *
  71. * <ul>
  72. * <li>Another backslash. The second backslash has no special
  73. * meaning and the two characters represent a single backslash.
  74. *
  75. * <li>The character 'n'. The two characters represent a newline
  76. * ('\n' in Java).
  77. *
  78. * <li>A quote. The two characters represent a quote, and that quote
  79. * is not considered to terminate the quoted value. An ending closing
  80. * quote must be present for the quoted value to be valid.
  81. *
  82. * <li>A question mark (?) or star (*). The two characters represent
  83. * a question mark or star respectively.
  84. * </ul>
  85. *
  86. * <p>A quote, question mark, or star may not appear inside a quoted
  87. * value except immediately after an odd number of consecutive
  88. * backslashes.</p>
  89. *
  90. * <p>The quotes surrounding a quoted value, and any backslashes
  91. * within that value, are considered to be part of the value.</p>
  92. *
  93. * <p>An ObjectName may be a <em>property pattern</em>. In this case
  94. * it may have zero or more keys and associated values. It matches a
  95. * nonpattern ObjectName whose domain matches and that contains the
  96. * same keys and associated values, as well as possibly other keys and
  97. * values.</p>
  98. *
  99. * <p>An ObjectName is a pattern if its domain contains a wildcard or
  100. * if the ObjectName is a property pattern.</p>
  101. *
  102. * <p>If an ObjectName is not a pattern, it must contain at least one
  103. * key with its associated value.</p>
  104. *
  105. * <p>An ObjectName can be written as a String with the following
  106. * elements in order:</p>
  107. *
  108. * <ul>
  109. * <li>The domain.
  110. * <li>A colon (<code>:</code>).
  111. * <li>A key property list as defined below.
  112. * </ul>
  113. *
  114. * <p>A key property list written as a String is a comma-separated
  115. * list of elements. Each element is either an asterisk or a key
  116. * property. A key property consists of a key, an equals
  117. * (<code>=</code>), and the associated value.</p>
  118. *
  119. * <p>At most one element of a key property list may be an asterisk.
  120. * If the key property list contains an asterisk element, the
  121. * ObjectName is a property pattern.</p>
  122. *
  123. * <p>Spaces have no special significance in a String representing an
  124. * ObjectName. For example, the String:
  125. * <pre>
  126. * domain: key1 = value1 , key2 = value2
  127. * </pre>
  128. * represents an ObjectName with two keys. The name of each key
  129. * contains six characters, of which the first and last are spaces.
  130. * The value associated with the key <code>" key1 "</code>
  131. * also begins and ends with a space.</p>
  132. *
  133. * <p>In addition to the restrictions on characters spelt out above,
  134. * no part of an ObjectName may contain a newline character
  135. * (<code>'\n'</code>), whether the domain, a key, or a value, whether
  136. * quoted or unquoted. The newline character can be represented in a
  137. * quoted value with the sequence <code>\n</code>.
  138. *
  139. * <p>The rules on special characters and quoting apply regardless of
  140. * which constructor is used to make an ObjectName.</p>
  141. *
  142. * <p>To avoid collisions between MBeans supplied by different
  143. * vendors, a useful convention is to begin the domain name with the
  144. * reverse DNS name of the organization that specifies the MBeans,
  145. * followed by a period and a string whose interpretation is
  146. * determined by that organization. For example, MBeans specified by
  147. * Sun Microsystems Inc., DNS name <code>sun.com</code>, would have
  148. * domains such as <code>com.sun.MyDomain</code>. This is essentially
  149. * the same convention as for Java-language package names.</p>
  150. *
  151. * @since 1.5
  152. */
  153. public class ObjectName implements QueryExp, Serializable {
  154. /**
  155. * A structure recording property structure and
  156. * proposing minimal services
  157. */
  158. private final static class Property {
  159. int _key_index;
  160. int _key_length;
  161. int _value_length;
  162. /**
  163. * Constructor.
  164. */
  165. Property(int key_index, int key_length, int value_length) {
  166. _key_index = key_index;
  167. _key_length = key_length;
  168. _value_length = value_length;
  169. }
  170. /**
  171. * Assigns the key index of property
  172. */
  173. void setKeyIndex(int key_index) {
  174. _key_index = key_index;
  175. }
  176. /**
  177. * Returns a key string for receiver key
  178. */
  179. String getKeyString(String name) {
  180. return name.substring(_key_index, _key_index + _key_length);
  181. }
  182. /**
  183. * Returns a value string for receiver key
  184. */
  185. String getValueString(String name) {
  186. int in_begin = _key_index + _key_length + 1;
  187. int out_end = in_begin + _value_length;
  188. return name.substring(in_begin, out_end);
  189. }
  190. }
  191. // Inner classes <========================================
  192. // Private fields ---------------------------------------->
  193. // Serialization compatibility stuff -------------------->
  194. // Two serial forms are supported in this class. The selected form depends
  195. // on system property "jmx.serial.form":
  196. // - "1.0" for JMX 1.0
  197. // - any other value for JMX 1.1 and higher
  198. //
  199. // Serial version for old serial form
  200. private static final long oldSerialVersionUID = -5467795090068647408L;
  201. //
  202. // Serial version for new serial form
  203. private static final long newSerialVersionUID = 1081892073854801359L;
  204. //
  205. // Serializable fields in old serial form
  206. private static final ObjectStreamField[] oldSerialPersistentFields =
  207. {
  208. new ObjectStreamField("domain", String.class),
  209. new ObjectStreamField("propertyList", Hashtable.class),
  210. new ObjectStreamField("propertyListString", String.class),
  211. new ObjectStreamField("canonicalName", String.class),
  212. new ObjectStreamField("pattern", Boolean.TYPE),
  213. new ObjectStreamField("propertyPattern", Boolean.TYPE)
  214. };
  215. //
  216. // Serializable fields in new serial form
  217. private static final ObjectStreamField[] newSerialPersistentFields = { };
  218. //
  219. // Actual serial version and serial form
  220. private static final long serialVersionUID;
  221. private static final ObjectStreamField[] serialPersistentFields;
  222. private static boolean compat = false;
  223. static {
  224. try {
  225. PrivilegedAction act = new GetPropertyAction("jmx.serial.form");
  226. String form = (String) AccessController.doPrivileged(act);
  227. compat = (form != null && form.equals("1.0"));
  228. } catch (Exception e) {
  229. // OK: exception means no compat with 1.0, too bad
  230. }
  231. if (compat) {
  232. serialPersistentFields = oldSerialPersistentFields;
  233. serialVersionUID = oldSerialVersionUID;
  234. } else {
  235. serialPersistentFields = newSerialPersistentFields;
  236. serialVersionUID = newSerialVersionUID;
  237. }
  238. }
  239. //
  240. // Serialization compatibility stuff <==============================
  241. // Class private fields ----------------------------------->
  242. /**
  243. * a shared empty array for empty property lists
  244. */
  245. static final private Property[] _Empty_property_array = new Property[0];
  246. /**
  247. * a shared empty hashtable for empty property lists
  248. */
  249. static final private Hashtable _EmptyPropertyList = new Hashtable(1);
  250. // Class private fields <==============================
  251. // Instance private fields ----------------------------------->
  252. /**
  253. * a String containing the canonical name
  254. */
  255. private transient String _canonicalName;
  256. /**
  257. * An array of properties in the same seq order as time creation
  258. */
  259. private transient Property[] _kp_array;
  260. /**
  261. * An array of properties in the same seq order as canonical order
  262. */
  263. private transient Property[] _ca_array;
  264. /**
  265. * The length of the domain part of built objectname
  266. */
  267. private transient int _domain_length = 0;
  268. /**
  269. * The propertyList of built object name. Initialized lazily.
  270. * Table that contains all the pairs (key,value) for this ObjectName.
  271. */
  272. private transient Hashtable _propertyList;
  273. /**
  274. * boolean that declares if this ObjectName domain part is a pattern
  275. */
  276. private transient boolean _domain_pattern = false;
  277. /**
  278. * boolean that declares if this ObjectName contains a pattern on the
  279. * key property list
  280. */
  281. private transient boolean _property_pattern = false;
  282. // Instance private fields <=======================================
  283. // Private fields <========================================
  284. // Private methods ---------------------------------------->
  285. // Category : Instance construction ------------------------->
  286. /**
  287. * Initializes this {@link ObjectName} from the given string
  288. * representation.
  289. *
  290. * @param name A string representation of the {@link ObjectName}
  291. *
  292. * @exception MalformedObjectNameException The string passed as a
  293. * parameter does not have the right format.
  294. * @exception NullPointerException The <code>name</code> parameter
  295. * is null.
  296. */
  297. private void construct(String name)
  298. throws MalformedObjectNameException, NullPointerException {
  299. // The name cannot be null
  300. if (name == null)
  301. throw new NullPointerException("name cannot be null");
  302. // Test if the name is empty
  303. if (name.length() == 0) {
  304. // this is equivalent to the whole word query object name.
  305. _canonicalName = "*:*";
  306. _kp_array = _Empty_property_array;
  307. _ca_array = _Empty_property_array;
  308. _domain_length = 1;
  309. _propertyList = null;
  310. _domain_pattern = true;
  311. _property_pattern = true;
  312. return;
  313. }
  314. // initialize parsing of the string
  315. char[] name_chars = name.toCharArray();
  316. int len = name_chars.length;
  317. char[] canonical_chars = new char[len]; // canonical form will be same
  318. // length at most
  319. int cname_index = 0;
  320. int index = 0;
  321. char c, c1;
  322. // parses domain part
  323. domain_parsing:
  324. while (index < len) {
  325. switch (c = name_chars[index]) {
  326. case ':' :
  327. _domain_length = index++;
  328. break domain_parsing;
  329. case '=' :
  330. int i = ++index;
  331. while ((i < len) && (name_chars[i++] != ':'))
  332. if (i == len)
  333. throw new MalformedObjectNameException(
  334. "Domain part must be specified");
  335. break;
  336. case '\n' :
  337. throw new MalformedObjectNameException(
  338. "Invalid character '\\n' in domain name");
  339. case '*' :
  340. case '?' :
  341. _domain_pattern = true;
  342. default :
  343. index++;
  344. }
  345. }
  346. // check for non-empty properties
  347. if (index == len)
  348. throw new MalformedObjectNameException(
  349. "Key properties cannot be empty");
  350. // we have got the domain part, begins building of _canonicalName
  351. System.arraycopy(name_chars, 0, canonical_chars, 0, _domain_length);
  352. canonical_chars[_domain_length] = ':';
  353. cname_index = _domain_length + 1;
  354. // parses property list
  355. Property prop;
  356. HashMap keys_map = new HashMap();
  357. String[] keys;
  358. String key_name;
  359. boolean quoted_value;
  360. int property_index = 0;
  361. int in_index;
  362. int key_index, key_length, value_index, value_length;
  363. keys = new String[10];
  364. _kp_array = new Property[10];
  365. _property_pattern = false;
  366. while (index < len) {
  367. c = name_chars[index];
  368. // case of pattern properties
  369. if (c == '*') {
  370. if (_property_pattern)
  371. throw new MalformedObjectNameException(
  372. "Cannot have several '*' characters in pattern " +
  373. "properties");
  374. else {
  375. _property_pattern = true;
  376. if ((++index < len ) && (name_chars[index] != ','))
  377. throw new MalformedObjectNameException(
  378. "Invalid character found after '*': end of " +
  379. "name or ',' expected");
  380. else if (index == len) {
  381. if (property_index == 0) {
  382. // empty properties case
  383. _kp_array = _Empty_property_array;
  384. _ca_array = _Empty_property_array;
  385. _propertyList = _EmptyPropertyList;
  386. }
  387. break;
  388. }
  389. else {
  390. // correct pattern spec in props, continue
  391. index++;
  392. continue;
  393. }
  394. }
  395. }
  396. // standard property case, key part
  397. in_index = index;
  398. key_index = in_index;
  399. while ((in_index < len) && ((c1 = name_chars[in_index++]) != '='))
  400. switch (c1) {
  401. // '=' considered to introduce value part
  402. case '*' :
  403. case '?' :
  404. case ',' :
  405. case ':' :
  406. case '\n' :
  407. final String ichar = ((c1=='\n')?"\\n":""+c1);
  408. throw new MalformedObjectNameException(
  409. "Invalid character '" + ichar +
  410. "' in key part of property");
  411. default: ;
  412. }
  413. if (in_index == len)
  414. throw new MalformedObjectNameException(
  415. "Unterminated key property part");
  416. if (in_index == index)
  417. throw new MalformedObjectNameException("Invalid key (empty)");
  418. value_index = in_index; // in_index pointing after '=' char
  419. key_length = value_index - key_index - 1; // found end of key
  420. // standard property case, value part
  421. if (name_chars[in_index] == '\"') {
  422. quoted_value = true;
  423. // the case of quoted value part
  424. quoted_value_parsing:
  425. while ((++in_index < len) &&
  426. ((c1 = name_chars[in_index]) != '\"')) {
  427. // the case of an escaped character
  428. if (c1 == '\\') {
  429. if (++in_index == len)
  430. throw new MalformedObjectNameException(
  431. "Unterminated quoted value");
  432. switch (c1 = name_chars[in_index]) {
  433. case '\\' :
  434. case '\"' :
  435. case '?' :
  436. case '*' :
  437. case 'n' :
  438. break; // valid character
  439. default :
  440. throw new MalformedObjectNameException(
  441. "Invalid escape sequence '\\" +
  442. c1 + "' in quoted value");
  443. }
  444. } else if (c1 == '\n') {
  445. throw new MalformedObjectNameException(
  446. "Newline in quoted value");
  447. } else {
  448. switch (c1) {
  449. case '?' :
  450. case '*' :
  451. throw new MalformedObjectNameException(
  452. "Invalid unescaped reserved character '" +
  453. c1 + "' in quoted value");
  454. default:
  455. break;
  456. }
  457. }
  458. }
  459. if (in_index == len)
  460. throw new MalformedObjectNameException(
  461. "Unterminated quoted value");
  462. else value_length = ++in_index - value_index;
  463. }
  464. else {
  465. // the case of standard value part
  466. quoted_value = false;
  467. while ((in_index < len) && ((c1 = name_chars[in_index]) != ','))
  468. switch (c1) {
  469. // ',' considered to be the value separator
  470. case '*' :
  471. case '?' :
  472. case '=' :
  473. case ':' :
  474. case '"' :
  475. case '\n' :
  476. final String ichar = ((c1=='\n')?"\\n":""+c1);
  477. throw new MalformedObjectNameException(
  478. "Invalid character '" + c1 +
  479. "' in value part of property");
  480. default : in_index++;
  481. }
  482. value_length = in_index - value_index;
  483. }
  484. // Parsed property, checks the end of name
  485. if (in_index == len - 1) {
  486. if (quoted_value)
  487. throw new MalformedObjectNameException(
  488. "Invalid ending character `" +
  489. name_chars[in_index] + "'");
  490. else throw new MalformedObjectNameException(
  491. "Invalid ending comma");
  492. }
  493. else in_index++;
  494. // we got the key and value part, prepare a property for this
  495. prop = new Property(key_index, key_length, value_length);
  496. key_name = name.substring(key_index, key_index + key_length);
  497. if (property_index == keys.length) {
  498. String[] tmp_string_array = new String[property_index + 10];
  499. System.arraycopy(keys, 0, tmp_string_array, 0, property_index);
  500. keys = tmp_string_array;
  501. }
  502. keys[property_index] = key_name;
  503. addProperty(prop, property_index, keys_map, key_name);
  504. property_index++;
  505. index = in_index;
  506. }
  507. // computes and set canonical name
  508. setCanonicalName(name_chars, canonical_chars, keys,
  509. keys_map, cname_index, property_index);
  510. }
  511. /**
  512. * Construct an ObjectName from a domain and a Hashtable.
  513. *
  514. * @param domain Domain of the ObjectName.
  515. * @param props Hashtable containing couples <i>key</i> -> <i>value</i>.
  516. *
  517. * @exception MalformedObjectNameException The <code>domain</code>
  518. * contains an illegal character, or one of the keys or values in
  519. * <code>table</code> contains an illegal character, or one of the
  520. * values in <code>table</code> does not follow the rules for quoting.
  521. * @exception NullPointerException One of the parameters is null.
  522. */
  523. private void construct(String domain, Hashtable props)
  524. throws MalformedObjectNameException, NullPointerException {
  525. // The domain cannot be null
  526. if (domain == null)
  527. throw new NullPointerException("domain cannot be null");
  528. // The key property list cannot be null
  529. if (props == null)
  530. throw new NullPointerException("key property list cannot be null");
  531. // The key property list cannot be empty
  532. if (props.isEmpty())
  533. throw new MalformedObjectNameException(
  534. "key property list cannot be empty");
  535. // checks domain validity
  536. if (!isDomain(domain))
  537. throw new MalformedObjectNameException("Invalid domain: " + domain);
  538. // init canonicalname
  539. final StringBuffer sb = new StringBuffer();
  540. sb.append(domain).append(':');
  541. _domain_length = domain.length();
  542. // allocates the property array
  543. int nb_props = props.size();
  544. _kp_array = new Property[nb_props];
  545. String[] keys = new String[nb_props];
  546. final Enumeration e = props.keys();
  547. final HashMap keys_map = new HashMap();
  548. Property prop;
  549. int key_index;
  550. for (int i = 0; e.hasMoreElements(); i++ ) {
  551. if (i > 0) sb.append(",");
  552. String key = "";
  553. try {
  554. key = (String)e.nextElement();
  555. } catch (Exception x) {
  556. throw new MalformedObjectNameException("Invalid key `" +
  557. key + "'");
  558. }
  559. String value = "";
  560. try {
  561. value = (String)props.get(key);
  562. } catch (Exception x) {
  563. throw new MalformedObjectNameException("Invalid value `" +
  564. value + "'");
  565. }
  566. key_index = sb.length();
  567. checkKey(key);
  568. sb.append(key);
  569. keys[i] = key;
  570. sb.append("=");
  571. checkValue(value);
  572. sb.append(value);
  573. prop = new Property(key_index, key.length(), value.length());
  574. addProperty(prop, i, keys_map, key);
  575. }
  576. // initialise canonical name and data structure
  577. int len = sb.length();
  578. char[] initial_chars = new char[len];
  579. sb.getChars(0, len, initial_chars, 0);
  580. char[] canonical_chars = new char[len];
  581. System.arraycopy(initial_chars, 0, canonical_chars, 0,
  582. _domain_length + 1);
  583. setCanonicalName(initial_chars, canonical_chars, keys, keys_map,
  584. _domain_length + 1, _kp_array.length);
  585. }
  586. // Category : Instance construction <==============================
  587. // Category : Internal utilities ------------------------------>
  588. /**
  589. * Add passed property to the list at the given index
  590. * for the passed key name
  591. */
  592. private void addProperty(Property prop, int index,
  593. HashMap keys_map, String key_name)
  594. throws MalformedObjectNameException {
  595. if (keys_map.containsKey(key_name)) throw new
  596. MalformedObjectNameException("key `" +
  597. key_name +"' already defined");
  598. // if no more space for property arrays, have to increase it
  599. if (index == _kp_array.length) {
  600. Property[] tmp_prop_array = new Property[index + 10];
  601. System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index);
  602. _kp_array = tmp_prop_array;
  603. }
  604. _kp_array[index] = prop;
  605. keys_map.put(key_name, prop);
  606. }
  607. /**
  608. * Sets the canonical name of receiver from input 'specified_chars'
  609. * array, by filling 'canonical_chars' array with found 'nb-props'
  610. * properties starting at position 'prop_index'.
  611. */
  612. private void setCanonicalName(char[] specified_chars,
  613. char[] canonical_chars,
  614. String[] keys, HashMap keys_map,
  615. int prop_index, int nb_props) {
  616. // Sort the list of found properties
  617. if (_kp_array != _Empty_property_array) {
  618. String[] tmp_keys = new String[nb_props];
  619. Property[] tmp_props = new Property[nb_props];
  620. System.arraycopy(keys, 0, tmp_keys, 0, nb_props);
  621. Arrays.sort(tmp_keys);
  622. keys = tmp_keys;
  623. System.arraycopy(_kp_array, 0, tmp_props, 0 , nb_props);
  624. _kp_array = tmp_props;
  625. _ca_array = new Property[nb_props];
  626. // now assigns _ca_array to the sorted list of keys
  627. // (there cannot be two identical keys in an objectname.
  628. for (int i = 0; i < nb_props; i++)
  629. _ca_array[i] = (Property) keys_map.get(keys[i]);
  630. // now we build the canonical name and set begin indexes of
  631. // properties to reflect canonical form
  632. int last_index = nb_props - 1;
  633. int prop_len;
  634. Property prop;
  635. for (int i = 0; i <= last_index; i++) {
  636. prop = _ca_array[i];
  637. // length of prop including '=' char
  638. prop_len = prop._key_length + prop._value_length + 1;
  639. System.arraycopy(specified_chars, prop._key_index,
  640. canonical_chars, prop_index, prop_len);
  641. prop.setKeyIndex(prop_index);
  642. prop_index += prop_len;
  643. if (i != last_index) {
  644. canonical_chars[prop_index] = ',';
  645. prop_index++;
  646. }
  647. }
  648. }
  649. // terminate canonicalname with '*' in case of pattern
  650. if (_property_pattern) {
  651. if (_kp_array != _Empty_property_array)
  652. canonical_chars[prop_index++] = ',';
  653. canonical_chars[prop_index++] = '*';
  654. }
  655. // we now build the canonicalname string
  656. _canonicalName = (new String(canonical_chars, 0, prop_index)).intern();
  657. }
  658. /**
  659. * Parse a key.
  660. * <pre>final int endKey=parseKey(s,startKey);</pre>
  661. * <p>key starts at startKey (included), and ends at endKey (excluded).
  662. * If (startKey == endKey), then the key is empty.
  663. *
  664. * @param s The char array of the original string.
  665. * @param startKey index at which to begin parsing.
  666. * @return The index following the last character of the key.
  667. **/
  668. private final static int parseKey(final char[] s, final int startKey)
  669. throws MalformedObjectNameException {
  670. int next = startKey;
  671. int endKey = startKey;
  672. final int len = s.length;
  673. while (next < len) {
  674. final char k = s[next++];
  675. switch (k) {
  676. case '*':
  677. case '?':
  678. case ',':
  679. case ':':
  680. case '\n':
  681. final String ichar = ((k=='\n')?"\\n":""+k);
  682. throw new
  683. MalformedObjectNameException("Invalid character in key: `"
  684. + ichar + "'");
  685. case '=':
  686. // we got the key.
  687. endKey = next-1;
  688. break;
  689. default:
  690. if (next < len) continue;
  691. else endKey=next;
  692. }
  693. break;
  694. }
  695. return endKey;
  696. }
  697. /**
  698. * Parse a value.
  699. * <pre>final int endVal=parseValue(s,startVal);</pre>
  700. * <p>value starts at startVal (included), and ends at endVal (excluded).
  701. * If (startVal == endVal), then the key is empty.
  702. *
  703. * @param s The char array of the original string.
  704. * @param startValue index at which to begin parsing.
  705. * @return The index following the last character of the value.
  706. **/
  707. private final static int parseValue(final char[] s, final int startValue)
  708. throws MalformedObjectNameException {
  709. int next = startValue;
  710. int endValue = startValue;
  711. final int len = s.length;
  712. final char q=s[startValue];
  713. if (q == '"') {
  714. // quoted value
  715. if (++next == len) throw new
  716. MalformedObjectNameException("Invalid quote");
  717. while (next < len) {
  718. char last = s[next];
  719. if (last == '\\') {
  720. if (++next == len) throw new
  721. MalformedObjectNameException(
  722. "Invalid unterminated quoted character sequence");
  723. last = s[next];
  724. switch (last) {
  725. case '\\' :
  726. case '?' :
  727. case '*' :
  728. case 'n' :
  729. break;
  730. case '\"' :
  731. // We have an escaped quote. If this escaped
  732. // quote is the last character, it does not
  733. // qualify as a valid termination quote.
  734. //
  735. if (next+1 == len) throw new
  736. MalformedObjectNameException(
  737. "Missing termination quote");
  738. break;
  739. default:
  740. throw new
  741. MalformedObjectNameException(
  742. "Invalid quoted character sequence '\\" +
  743. last + "'");
  744. }
  745. } else if (last == '\n') {
  746. throw new MalformedObjectNameException(
  747. "Newline in quoted value");
  748. } else if (last == '\"') {
  749. next++;
  750. break;
  751. } else {
  752. switch (last) {
  753. case '?' :
  754. case '*' :
  755. throw new MalformedObjectNameException(
  756. "Invalid unescaped reserved character '" +
  757. last + "' in quoted value");
  758. default:
  759. break;
  760. }
  761. }
  762. next++;
  763. // Check that last character is a termination quote.
  764. // We have already handled the case were the last
  765. // character is an escaped quote earlier.
  766. //
  767. if ((next >= len) && (last != '\"')) throw new
  768. MalformedObjectNameException("Missing termination quote");
  769. }
  770. endValue = next;
  771. if (next < len) {
  772. if (s[next++] != ',') throw new
  773. MalformedObjectNameException("Invalid quote");
  774. }
  775. }
  776. else {
  777. // Non quoted value.
  778. while (next < len) {
  779. final char v=s[next++];
  780. switch(v) {
  781. case '*':
  782. case '?':
  783. case '=':
  784. case ':':
  785. case '\n' :
  786. final String ichar = ((v=='\n')?"\\n":""+v);
  787. throw new
  788. MalformedObjectNameException("Invalid character `" +
  789. ichar + "' in value");
  790. case ',':
  791. endValue = next-1;
  792. break;
  793. default:
  794. if (next < len) continue;
  795. else endValue=next;
  796. }
  797. break;
  798. }
  799. }
  800. return endValue;
  801. }
  802. /**
  803. * Check if the value given in parameter in the first constructor is a
  804. * valid value
  805. */
  806. private String checkValue(String val)
  807. throws MalformedObjectNameException {
  808. if (val == null) throw new
  809. MalformedObjectNameException("Invalid value (null)");
  810. final int len = val.length();
  811. if (len == 0) throw new
  812. MalformedObjectNameException("Invalid value (empty)");
  813. final char[] s = val.toCharArray();
  814. final int endValue = parseValue(s,0);
  815. if (endValue < len) throw new
  816. MalformedObjectNameException("Invalid character in value: `" +
  817. s[endValue] + "'");
  818. return val;
  819. }
  820. /**
  821. * Check if the key given in parameter in the first constructor is a
  822. * valid key.
  823. */
  824. private String checkKey(String key)
  825. throws MalformedObjectNameException {
  826. if (key == null) throw new
  827. MalformedObjectNameException("Invalid key (null)");
  828. final int len = key.length();
  829. if (len == 0) throw new
  830. MalformedObjectNameException("Invalid key (empty)");
  831. final char[] k=key.toCharArray();
  832. final int endKey = parseKey(k,0);
  833. if (endKey < len) throw new
  834. MalformedObjectNameException("Invalid character in value: `" +
  835. k[endKey] + "'");
  836. return key;
  837. }
  838. /*
  839. * Tests whether string s is matched by pattern p.
  840. * Supports "?", "*" each of which may be escaped with "\";
  841. * Not yet supported: internationalization; "\" inside brackets.<P>
  842. * Wildcard matching routine by Karl Heuer. Public Domain.<P>
  843. */
  844. private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
  845. char c;
  846. final int slen = s.length;
  847. final int plen = p.length;
  848. while (pi < plen) { // While still string
  849. c = p[pi++];
  850. if (c == '?') {
  851. if (++si > slen) return false;
  852. } else if (c == '*') { // Wildcard
  853. if (pi >= plen) return true;
  854. do {
  855. if (wildmatch(s,p,si,pi)) return true;
  856. } while (++si < slen);
  857. return false;
  858. } else {
  859. if (si >= slen || c != s[si++]) return false;
  860. }
  861. }
  862. return (si == slen);
  863. }
  864. // Category : Internal utilities <==============================
  865. // Category : Internal accessors ------------------------------>
  866. /**
  867. * Check if domain is a valid domain
  868. */
  869. private boolean isDomain(String domain) {
  870. if (domain == null) return true;
  871. final char[] d=domain.toCharArray();
  872. final int len = d.length;
  873. int next = 0;
  874. while (next < len) {
  875. final char c = d[next++];
  876. switch (c) {
  877. case ':' :
  878. case '\n' :
  879. return false;
  880. case '*' :
  881. case '?' :
  882. _domain_pattern = true;
  883. default:
  884. continue;
  885. }
  886. }
  887. return true;
  888. }
  889. // Category : Internal accessors <==============================
  890. // Category : Serialization ----------------------------------->
  891. /**
  892. * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}.
  893. * @serialData <ul>
  894. * <li>In the current serial form (value of property
  895. * <code>jmx.serial.form</code> differs from
  896. * <code>1.0</code>): the string
  897. * "<domain>:<properties><wild>",
  898. * where: <ul>
  899. * <li><domain> represents the domain part
  900. * of the {@link ObjectName}</li>
  901. * <li><properties> represents the list of
  902. * properties, as returned by
  903. * {@link #getKeyPropertyListString}
  904. * <li><wild> is empty if not
  905. * <code>isPropertyPattern</code>, or
  906. * is the character "<code>*</code>" if
  907. * <code>isPropertyPattern</code>
  908. * and <properties> is empty, or
  909. * is "<code>,*</code>" if
  910. * <code>isPropertyPattern</code> and
  911. * <properties> is not empty.
  912. * </li>
  913. * </ul>
  914. * The intent is that this string could be supplied
  915. * to the {@link #ObjectName(String)} constructor to
  916. * produce an equivalent {@link ObjectName}.
  917. * </li>
  918. * <li>In the old serial form (value of property
  919. * <code>jmx.serial.form</code> is
  920. * <code>1.0</code>): <domain> <propertyList>
  921. * <propertyListString> <canonicalName>
  922. * <pattern> <propertyPattern>,
  923. * where: <ul>
  924. * <li><domain> represents the domain part
  925. * of the {@link ObjectName}</li>
  926. * <li><propertyList> is the
  927. * {@link Hashtable} that contains all the
  928. * pairs (key,value) for this
  929. * {@link ObjectName}</li>
  930. * <li><propertyListString> is the
  931. * {@link String} representation of the
  932. * list of properties in any order (not
  933. * mandatorily a canonical representation)
  934. * </li>
  935. * <li><canonicalName> is the
  936. * {@link String} containing this
  937. * {@link ObjectName}'s canonical name</li>
  938. * <li><pattern> is a boolean which is
  939. * <code>true</code> if this
  940. * {@link ObjectName} contains a pattern</li>
  941. * <li><propertyPattern> is a boolean which
  942. * is <code>true</code> if this
  943. * {@link ObjectName} contains a pattern in
  944. * the list of properties</li>
  945. * </ul>
  946. * </li>
  947. * </ul>
  948. */
  949. private void readObject(ObjectInputStream in)
  950. throws IOException, ClassNotFoundException {
  951. if (compat) {
  952. // Read an object serialized in the old serial form
  953. //
  954. in.defaultReadObject();
  955. } else {
  956. // Read an object serialized in the new serial form
  957. //
  958. in.defaultReadObject();
  959. String s = (String)in.readObject();
  960. try {
  961. construct(s);
  962. } catch (NullPointerException e) {
  963. throw new InvalidObjectException(e.toString());
  964. } catch (MalformedObjectNameException e) {
  965. throw new InvalidObjectException(e.toString());
  966. }
  967. }
  968. }
  969. /**
  970. * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
  971. * @serialData <ul>
  972. * <li>In the current serial form (value of property
  973. * <code>jmx.serial.form</code> differs from
  974. * <code>1.0</code>): the string
  975. * "<domain>:<properties><wild>",
  976. * where: <ul>
  977. * <li><domain> represents the domain part
  978. * of the {@link ObjectName}</li>
  979. * <li><properties> represents the list of
  980. * properties, as returned by
  981. * {@link #getKeyPropertyListString}
  982. * <li><wild> is empty if not
  983. * <code>isPropertyPattern</code>, or
  984. * is the character "<code>*</code>" if
  985. * this <code>isPropertyPattern</code>
  986. * and <properties> is empty, or
  987. * is "<code>,*</code>" if
  988. * <code>isPropertyPattern</code> and
  989. * <properties> is not empty.
  990. * </li>
  991. * </ul>
  992. * The intent is that this string could be supplied
  993. * to the {@link #ObjectName(String)} constructor to
  994. * produce an equivalent {@link ObjectName}.
  995. * </li>
  996. * <li>In the old serial form (value of property
  997. * <code>jmx.serial.form</code> is
  998. * <code>1.0</code>): <domain> <propertyList>
  999. * <propertyListString> <canonicalName>
  1000. * <pattern> <propertyPattern>,
  1001. * where: <ul>
  1002. * <li><domain> represents the domain part
  1003. * of the {@link ObjectName}</li>
  1004. * <li><propertyList> is the
  1005. * {@link Hashtable} that contains all the
  1006. * pairs (key,value) for this
  1007. * {@link ObjectName}</li>
  1008. * <li><propertyListString> is the
  1009. * {@link String} representation of the
  1010. * list of properties in any order (not
  1011. * mandatorily a canonical representation)
  1012. * </li>
  1013. * <li><canonicalName> is the
  1014. * {@link String} containing this
  1015. * {@link ObjectName}'s canonical name</li>
  1016. * <li><pattern> is a boolean which is
  1017. * <code>true</code> if this
  1018. * {@link ObjectName} contains a pattern</li>
  1019. * <li><propertyPattern> is a boolean which
  1020. * is <code>true</code> if this
  1021. * {@link ObjectName} contains a pattern in
  1022. * the list of properties</li>
  1023. * </ul>
  1024. * </li>
  1025. * </ul>
  1026. */
  1027. private void writeObject(ObjectOutputStream out)
  1028. throws IOException {
  1029. if (compat)
  1030. {
  1031. // Serializes this instance in the old serial form
  1032. //
  1033. ObjectOutputStream.PutField fields = out.putFields();
  1034. fields.put("domain", _canonicalName.substring(0, _domain_length));
  1035. fields.put("propertyList", getKeyPropertyList());
  1036. fields.put("propertyListString", getKeyPropertyListString());
  1037. fields.put("canonicalName", _canonicalName);
  1038. fields.put("pattern", (_domain_pattern || _property_pattern));
  1039. fields.put("propertyPattern", _property_pattern);
  1040. out.writeFields();
  1041. }
  1042. else
  1043. {
  1044. // Serializes this instance in the new serial form
  1045. //
  1046. out.defaultWriteObject();
  1047. out.writeObject(getSerializedNameString());
  1048. }
  1049. }
  1050. // Category : Serialization <===================================
  1051. // Private methods <========================================
  1052. // Public methods ---------------------------------------->
  1053. // Category : ObjectName Construction ------------------------------>
  1054. /**
  1055. * <p>Return an instance of ObjectName that can be used anywhere
  1056. * an object obtained with {@link #ObjectName(String) new
  1057. * ObjectName(name)} can be used. The returned object may be of
  1058. * a subclass of ObjectName. Calling this method twice with the
  1059. * same parameters may return the same object or two equal but
  1060. * not identical objects.</p>
  1061. *
  1062. * @param name A string representation of the object name.
  1063. *
  1064. * @return an ObjectName corresponding to the given String.
  1065. *
  1066. * @exception MalformedObjectNameException The string passed as a
  1067. * parameter does not have the right format.
  1068. * @exception NullPointerException The <code>name</code> parameter
  1069. * is null.
  1070. *
  1071. * @since.unbundled JMX 1.2
  1072. */
  1073. public static ObjectName getInstance(String name)
  1074. throws MalformedObjectNameException, NullPointerException {
  1075. return new ObjectName(name);
  1076. }
  1077. /**
  1078. * <p>Return an instance of ObjectName that can be used anywhere
  1079. * an object obtained with {@link #ObjectName(String, String,
  1080. * String) new ObjectName(domain, key, value)} can be used. The
  1081. * returned object may be of a subclass of ObjectName. Calling
  1082. * this method twice with the same parameters may return the same
  1083. * object or two equal but not identical objects.</p>
  1084. *
  1085. * @param domain The domain part of the object name.
  1086. * @param key The attribute in the key property of the object name.
  1087. * @param value The value in the key property of the object name.
  1088. *
  1089. * @return an ObjectName corresponding to the given domain,
  1090. * key, and value.
  1091. *
  1092. * @exception MalformedObjectNameException The
  1093. * <code>domain</code>, <code>key</code>, or <code>value</code>
  1094. * contains an illegal character, or <code>value</code> does not
  1095. * follow the rules for quoting.
  1096. * @exception NullPointerException One of the parameters is null.
  1097. *
  1098. * @since.unbundled JMX 1.2
  1099. */
  1100. public static ObjectName getInstance(String domain, String key,
  1101. String value)
  1102. throws MalformedObjectNameException, NullPointerException {
  1103. return new ObjectName(domain, key, value);
  1104. }
  1105. /**
  1106. * <p>Return an instance of ObjectName that can be used anywhere
  1107. * an object obtained with {@link #ObjectName(String, Hashtable)
  1108. * new ObjectName(domain, table)} can be used. The returned
  1109. * object may be of a subclass of ObjectName. Calling this method
  1110. * twice with the same parameters may return the same object or
  1111. * two equal but not identical objects.</p>
  1112. *
  1113. * @param domain The domain part of the object name.
  1114. * @param table A hash table containing one or more key
  1115. * properties. The key of each entry in the table is the key of a
  1116. * key property in the object name. The associated value in the
  1117. * table is the associated value in the object name.
  1118. *
  1119. * @return an ObjectName corresponding to the given domain and
  1120. * key mappings.
  1121. *
  1122. * @exception MalformedObjectNameException The <code>domain</code>
  1123. * contains an illegal character, or one of the keys or values in
  1124. * <code>table</code> contains an illegal character, or one of the
  1125. * values in <code>table</code> does not follow the rules for
  1126. * quoting.
  1127. * @exception NullPointerException One of the parameters is null.
  1128. *
  1129. * @since.unbundled JMX 1.2
  1130. */
  1131. public static ObjectName getInstance(String domain, Hashtable table)
  1132. throws MalformedObjectNameException, NullPointerException {
  1133. return new ObjectName(domain, table);
  1134. }
  1135. /**
  1136. * <p>Return an instance of ObjectName that can be used anywhere
  1137. * the given object can be used. The returned object may be of a
  1138. * subclass of ObjectName. If <code>name</code> is of a subclass
  1139. * of ObjectName, it is not guaranteed that the returned object
  1140. * will be of the same class.</p>
  1141. *
  1142. * <p>The returned value may or may not be identical to
  1143. * <code>name</code>. Calling this method twice with the same
  1144. * parameters may return the same object or two equal but not
  1145. * identical objects.</p>
  1146. *
  1147. * <p>Since ObjectName is immutable, it is not usually useful to
  1148. * make a copy of an ObjectName. The principal use of this method
  1149. * is to guard against a malicious caller who might pass an
  1150. * instance of a subclass with surprising behavior to sensitive
  1151. * code. Such code can call this method to obtain an ObjectName
  1152. * that is known not to have surprising behavior.</p>
  1153. *
  1154. * @param name an instance of the ObjectName class or of a subclass
  1155. *
  1156. * @return an instance of ObjectName or a subclass that is known to
  1157. * have the same semantics. If <code>name</code> respects the
  1158. * semantics of ObjectName, then the returned object is equal
  1159. * (though not necessarily identical) to <code>name</code>.
  1160. *
  1161. * @exception NullPointerException The <code>name</code> is null.
  1162. *
  1163. * @since.unbundled JMX 1.2
  1164. */
  1165. public static ObjectName getInstance(ObjectName name)
  1166. throws NullPointerException {
  1167. if (name.getClass().equals(ObjectName.class))
  1168. return name;
  1169. try {
  1170. return new ObjectName(name.getSerializedNameString());
  1171. } catch (MalformedObjectNameException e) {
  1172. throw new IllegalArgumentException("Unexpected: " + e);
  1173. // can't happen
  1174. }
  1175. }
  1176. /**
  1177. * Construct an object name from the given string.
  1178. *
  1179. * @param name A string representation of the object name.
  1180. *
  1181. * @exception MalformedObjectNameException The string passed as a
  1182. * parameter does not have the right format.
  1183. * @exception NullPointerException The <code>name</code> parameter
  1184. * is null.
  1185. */
  1186. public ObjectName(String name)
  1187. throws MalformedObjectNameException, NullPointerException {
  1188. construct(name);
  1189. }
  1190. /**
  1191. * Construct an object name with exactly one key property.
  1192. *
  1193. * @param domain The domain part of the object name.
  1194. * @param key The attribute in the key property of the object name.
  1195. * @param value The value in the key property of the object name.
  1196. *
  1197. * @exception MalformedObjectNameException The
  1198. * <code>domain</code>, <code>key</code>, or <code>value</code>
  1199. * contains an illegal character, or <code>value</code> does not
  1200. * follow the rules for quoting.
  1201. * @exception NullPointerException One of the parameters is null.
  1202. */
  1203. public ObjectName(String domain, String key, String value)
  1204. throws MalformedObjectNameException, NullPointerException {
  1205. // If key or value are null a NullPointerException
  1206. // will be thrown by the put method in Hashtable.
  1207. //
  1208. Hashtable table = new Hashtable(1);
  1209. table.put(key, value);
  1210. construct(domain, table);
  1211. }
  1212. /**
  1213. * Construct an object name with several key properties from a Hashtable.
  1214. *
  1215. * @param domain The domain part of the object name.
  1216. * @param table A hash table containing one or more key
  1217. * properties. The key of each entry in the table is the key of a
  1218. * key property in the object name. The associated value in the
  1219. * table is the associated value in the object name.
  1220. *
  1221. * @exception MalformedObjectNameException The <code>domain</code>
  1222. * contains an illegal character, or one of the keys or values in
  1223. * <code>table</code> contains an illegal character, or one of the
  1224. * values in <code>table</code> does not follow the rules for
  1225. * quoting.
  1226. * @exception NullPointerException One of the parameters is null.
  1227. */
  1228. public ObjectName(String domain, Hashtable table)
  1229. throws MalformedObjectNameException, NullPointerException {
  1230. construct(domain, table);
  1231. }
  1232. // Category : ObjectName Construction <==============================
  1233. // Category : Getter methods ------------------------------>
  1234. /**
  1235. * Checks whether the object name is a pattern. An object name is
  1236. * a pattern if its domain contains a wildcard or if the object
  1237. * name is a property pattern.
  1238. *
  1239. * @return True if the name is a pattern, otherwise false.
  1240. */
  1241. public boolean isPattern() {
  1242. return (_domain_pattern || _property_pattern);
  1243. }
  1244. /**
  1245. * Checks whether the object name is a pattern on the domain part.
  1246. *
  1247. * @return True if the name is a domain pattern, otherwise false.
  1248. *
  1249. * @since.unbundled JMX 1.2
  1250. */
  1251. public boolean isDomainPattern() {
  1252. return _domain_pattern;
  1253. }
  1254. /**
  1255. * Checks whether the object name is a pattern on the key properties.
  1256. *
  1257. * @return True if the name is a pattern, otherwise false.
  1258. */
  1259. public boolean isPropertyPattern() {
  1260. return _property_pattern;
  1261. }
  1262. /**
  1263. * <p>Returns the canonical form of the name; that is, a string
  1264. * representation where the properties are sorted in lexical
  1265. * order.</p>
  1266. *
  1267. * <p>More precisely, the canonical form of the name is a String
  1268. * consisting of the <em>domain part</em>, a colon
  1269. * (<code>:</code>), the <em>canonical key property list</em>, and
  1270. * a <em>pattern indication</em>.</p>
  1271. *
  1272. * <p>The <em>canonical key property list</em> is the same string
  1273. * as described for {@link #getCanonicalKeyPropertyListString()}.</p>
  1274. *
  1275. * <p>The <em>pattern indication</em> is:
  1276. * <ul>
  1277. * <li>empty for an ObjectName
  1278. * that is not a property pattern;
  1279. * <li>an asterisk for an ObjectName
  1280. * that is a property pattern with no keys; or
  1281. * <li>a comma and an
  1282. * asterisk (<code>,*</code>) for an ObjectName that is a property
  1283. * pattern with at least one key.
  1284. * </ul></p>
  1285. *
  1286. * @return The canonical form of the name.
  1287. */
  1288. public String getCanonicalName() {
  1289. return _canonicalName;
  1290. }
  1291. /**
  1292. * Returns the domain part.
  1293. *
  1294. * @return the domain.
  1295. */
  1296. public String getDomain() {
  1297. return _canonicalName.substring(0, _domain_length);
  1298. }
  1299. /**
  1300. * Obtains the value associated with a key in a key property.
  1301. *
  1302. * @param property The property whose value is to be obtained.
  1303. *
  1304. * @return The value of the property, or null if there is no such
  1305. * property in this ObjectName.
  1306. *
  1307. * @exception NullPointerException If <code>property</code> is null.
  1308. */
  1309. public String getKeyProperty(String property) throws NullPointerException {
  1310. return (String) _getKeyPropertyList().get(property);
  1311. }
  1312. /**
  1313. * <p>Returns the key properties as a Hashtable. The returned
  1314. * value is a Hashtable in which each key is a key in the
  1315. * ObjectName's key property list and each value is the associated
  1316. * value.</p>
  1317. *
  1318. * <p>The returned value must not be modidied.</p>
  1319. *
  1320. * @return The table of key properties.
  1321. */
  1322. private final Hashtable _getKeyPropertyList() {
  1323. synchronized (this) {
  1324. if (_propertyList == null) {
  1325. // build (lazy eval) the property list from the canonical
  1326. // properties array
  1327. _propertyList = new Hashtable();
  1328. int len = _ca_array.length;
  1329. Property prop;
  1330. for (int i = len - 1; i >= 0; i--) {
  1331. prop = _ca_array[i];
  1332. _propertyList.put(prop.getKeyString(_canonicalName),
  1333. prop.getValueString(_canonicalName));
  1334. }
  1335. }
  1336. }
  1337. return _propertyList;
  1338. }
  1339. /**
  1340. * <p>Returns the key properties as a Hashtable. The returned
  1341. * value is a Hashtable in which each key is a key in the
  1342. * ObjectName's key property list and each value is the associated
  1343. * value.</p>
  1344. *
  1345. * <p>The returned value may be unmodifiable. If it is
  1346. * modifiable, changing it has no effect on this ObjectName.</p>
  1347. *
  1348. * @return The table of key properties.
  1349. */
  1350. public Hashtable getKeyPropertyList() {
  1351. return (Hashtable)_getKeyPropertyList().clone();
  1352. }
  1353. /**
  1354. * <p>Returns a string representation of the list of key
  1355. * properties specified at creation time. If this ObjectName was
  1356. * constructed with the constructor {@link #ObjectName(String)},
  1357. * the key properties in the returned String will be in the same
  1358. * order as in the argument to the constructor.</p>
  1359. *
  1360. * @return The key property list string. This string is
  1361. * independent of whether the ObjectName is a pattern.
  1362. */
  1363. public String getKeyPropertyListString() {
  1364. // BEWARE : we rebuild the propertyliststring at each call !!
  1365. if (_kp_array.length == 0) return "";
  1366. // the size of the string is the canonical one minus domain
  1367. // part and pattern part
  1368. final int total_size = _canonicalName.length() - _domain_length - 1
  1369. - (_property_pattern?2:0);
  1370. final char[] dest_chars = new char[total_size];
  1371. final char[] value = _canonicalName.toCharArray();
  1372. writeKeyPropertyListString(value,dest_chars,0);
  1373. return new String(dest_chars);
  1374. }
  1375. /**
  1376. * <p>Returns the serialized string of the ObjectName.
  1377. * properties specified at creation time. If this ObjectName was
  1378. * constructed with the constructor {@link #ObjectName(String)},
  1379. * the key properties in the returned String will be in the same
  1380. * order as in the argument to the constructor.</p>
  1381. *
  1382. * @return The key property list string. This string is
  1383. * independent of whether the ObjectName is a pattern.
  1384. */
  1385. private String getSerializedNameString() {
  1386. // the size of the string is the canonical one
  1387. final int total_size = _canonicalName.length();
  1388. final char[] dest_chars = new char[total_size];
  1389. final char[] value = _canonicalName.toCharArray();
  1390. final int offset = _domain_length+1;
  1391. // copy "domain:" into dest_chars
  1392. //
  1393. System.arraycopy(value, 0, dest_chars, 0, offset);
  1394. // Add property list string
  1395. final int end = writeKeyPropertyListString(value,dest_chars,offset);
  1396. // Add ",*" if necessary
  1397. if (_property_pattern) {
  1398. if (end == offset) {
  1399. // Property list string is empty.
  1400. dest_chars[end] = '*';
  1401. } else {
  1402. // Property list string is not empty.
  1403. dest_chars[end] = ',';
  1404. dest_chars[end+1] = '*';
  1405. }
  1406. }
  1407. return new String(dest_chars);
  1408. }
  1409. /**
  1410. * <p>Write a string representation of the list of key
  1411. * properties specified at creation time in the given array, starting
  1412. * at the specified offset. If this ObjectName was
  1413. * constructed with the constructor {@link #ObjectName(String)},
  1414. * the key properties in the returned String will be in the same
  1415. * order as in the argument to the constructor.</p>
  1416. *
  1417. * @return offset + #of chars written
  1418. */
  1419. private int writeKeyPropertyListString(char[] canonicalChars,
  1420. char[] data, int offset) {
  1421. if (_kp_array.length == 0) return offset;
  1422. final char[] dest_chars = data;
  1423. final char[] value = _canonicalName.toCharArray();
  1424. int index = offset;
  1425. final int len = _kp_array.length;
  1426. final int last = len - 1;
  1427. for (int i = 0; i < len; i++) {
  1428. final Property prop = _kp_array[i];
  1429. final int prop_len = prop._key_length + prop._value_length + 1;
  1430. System.arraycopy(value, prop._key_index, dest_chars, index,
  1431. prop_len);
  1432. index += prop_len;
  1433. if (i < last ) dest_chars[index++] = ',';
  1434. }
  1435. return index;
  1436. }
  1437. /**
  1438. * Returns a string representation of the list of key properties,
  1439. * in which the key properties are sorted in lexical order. This
  1440. * is used in lexicographic comparisons performed in order to
  1441. * select MBeans based on their key property list. Lexical order
  1442. * is the order implied by {@link String#compareTo(String)
  1443. * String.compareTo(String)}.
  1444. *
  1445. * @return The canonical key property list string. This string is
  1446. * independent of whether the ObjectName is a pattern.
  1447. */
  1448. public String getCanonicalKeyPropertyListString() {
  1449. if (_ca_array.length == 0) return "";
  1450. int len = _canonicalName.length();
  1451. if (_property_pattern) len -= 2;
  1452. return _canonicalName.substring(_domain_length +1, len);
  1453. }
  1454. // Category : Getter methods <===================================
  1455. // Category : Utilities ---------------------------------------->
  1456. /**
  1457. * <p>Returns a string representation of the object name. The
  1458. * format of this string is not specified, but users can expect
  1459. * that two ObjectNames return the same string if and only if they
  1460. * are equal.</p>
  1461. *
  1462. * @return a string representation of this object name.
  1463. */
  1464. public String toString() {
  1465. return getSerializedNameString();
  1466. }
  1467. /**
  1468. * Compares the current object name with another object name. Two
  1469. * ObjectName instances are equal if and only if their canonical
  1470. * forms are equal. The canonical form is the string described
  1471. * for {@link #getCanonicalName()}.
  1472. *
  1473. * @param object The object name that the current object name is to be
  1474. * compared with.
  1475. *
  1476. * @return True if <code>object</code> is an ObjectName whose
  1477. * canonical form is equal to that of this ObjectName.
  1478. */
  1479. public boolean equals(Object object) {
  1480. // same object case
  1481. if (this == object) return true;
  1482. // object is not an object name case
  1483. if (!(object instanceof ObjectName)) return false;
  1484. // equality when canonical names are the same
  1485. // (because usage of intern())
  1486. ObjectName on = (ObjectName) object;
  1487. String on_string = on._canonicalName;
  1488. if (_canonicalName == on_string) return true;
  1489. // Because we are sharing canonical form between object names,
  1490. // we have finished the comparison at this stage ==> unequal
  1491. return false;
  1492. }
  1493. /**
  1494. * Returns a hash code for this object name.
  1495. *
  1496. */
  1497. public int hashCode() {
  1498. return _canonicalName.hashCode();
  1499. }
  1500. /**
  1501. * <p>Returns a quoted form of the given String, suitable for
  1502. * inclusion in an ObjectName. The returned value can be used as
  1503. * the value associated with a key in an ObjectName. The String
  1504. * <code>s</code> may contain any character. Appropriate quoting
  1505. * ensures that the returned value is legal in an ObjectName.</p>
  1506. *
  1507. * <p>The returned value consists of a quote ('"'), a sequence of
  1508. * characters corresponding to the characters of <code>s</code>,
  1509. * and another quote. Characters in <code>s</code> appear
  1510. * unchanged within the returned value except:</p>
  1511. *
  1512. * <ul>
  1513. * <li>A quote ('"')is replaced by a backslash (\) followed by a quote.
  1514. * <li>A star ('*') is replaced by a backslash (\) followed by a star.
  1515. * <li>A question mark ('?') is replaced by a backslash (\) followed by
  1516. * a question mark.
  1517. * <li>A backslash ('\') is replaced by two backslashes.
  1518. * <li>A newline character (the character '\n' in Java) is replaced
  1519. * by a backslash followed by the character '\n'.
  1520. * </ul>
  1521. *
  1522. * @param s the String to be quoted.
  1523. *
  1524. * @return the quoted String.
  1525. *
  1526. * @exception NullPointerException if <code>s</code> is null.
  1527. *
  1528. * @since.unbundled JMX 1.2
  1529. */
  1530. public static String quote(String s)
  1531. throws NullPointerException {
  1532. final StringBuffer buf = new StringBuffer("\"");
  1533. final int len = s.length();
  1534. for (int i = 0; i < len; i++) {
  1535. char c = s.charAt(i);
  1536. switch (c) {
  1537. case '\n':
  1538. c = 'n';
  1539. // fall in...
  1540. case '\\':
  1541. case '\"':
  1542. case '*':
  1543. case '?':
  1544. buf.append('\\');
  1545. break;
  1546. }
  1547. buf.append(c);
  1548. }
  1549. buf.append('"');
  1550. return buf.toString();
  1551. }
  1552. /**
  1553. * <p>Returns an unquoted form of the given String. If
  1554. * <code>q</code> is a String returned by {@link #quote quote(s)},
  1555. * then <code>unquote(q).equals(s)</code>. If there is no String
  1556. * <code>s</code> for which <code>quote(s).equals(q)</code>, then
  1557. * unquote(q) throws an IllegalArgumentException.</p>
  1558. *
  1559. * <p>These rules imply that there is a one-to-one mapping between
  1560. * quoted and unquoted forms.</p>
  1561. *
  1562. * @param q the String to be unquoted.
  1563. *
  1564. * @return the unquoted String.
  1565. *
  1566. * @exception IllegalArgumentException if <code>q</code> could not
  1567. * have been returned by the {@link #quote} method, for instance
  1568. * if it does not begin and end with a quote (").
  1569. *
  1570. * @exception NullPointerException if <code>q</code> is null.
  1571. *
  1572. * @since.unbundled JMX 1.2
  1573. */
  1574. public static String unquote(String q)
  1575. throws IllegalArgumentException, NullPointerException {
  1576. final StringBuffer buf = new StringBuffer();
  1577. final int len = q.length();
  1578. if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"')
  1579. throw new IllegalArgumentException("Argument not quoted");
  1580. for (int i = 1; i < len - 1; i++) {
  1581. char c = q.charAt(i);
  1582. if (c == '\\') {
  1583. if (i == len - 2)
  1584. throw new IllegalArgumentException("Trailing backslash");
  1585. c = q.charAt(++i);
  1586. switch (c) {
  1587. case 'n':
  1588. c = '\n';
  1589. break;
  1590. case '\\':
  1591. case '\"':
  1592. case '*':
  1593. case '?':
  1594. break;
  1595. default:
  1596. throw new IllegalArgumentException(
  1597. "Bad character '" + c + "' after backslash");
  1598. }
  1599. }
  1600. else {
  1601. switch (c) {
  1602. case '*' :
  1603. case '?' :
  1604. case '\"':
  1605. case '\n':
  1606. throw new IllegalArgumentException(
  1607. "Invalid unescaped character '" + c +
  1608. "' in the string to unquote");
  1609. default : ;
  1610. }
  1611. }
  1612. buf.append(c);
  1613. }
  1614. return buf.toString();
  1615. }
  1616. // Category : Utilities <===================================
  1617. // Category : QueryExp Interface ---------------------------------------->
  1618. /**
  1619. * <p>Test whether this ObjectName, which may be a pattern,
  1620. * matches another ObjectName. If <code>name</code> is a pattern,
  1621. * the result is false. If this ObjectName is a pattern, the
  1622. * result is true if and only if <code>name</code> matches the
  1623. * pattern. If neither this ObjectName nor <code>name</code> is
  1624. * a pattern, the result is true if and only if the two
  1625. * ObjectNames are equal as described for the {@link
  1626. * #equals(Object)} method.</p>
  1627. *
  1628. * @param name The name of the MBean to compare to.
  1629. *
  1630. * @return True if <code>name</code> matches this ObjectName.
  1631. *
  1632. * @exception NullPointerException if <code>name</code> is null.
  1633. *
  1634. * @since.unbundled JMX 1.2
  1635. */
  1636. public boolean apply(ObjectName name) throws NullPointerException {
  1637. if (name == null) throw new NullPointerException();
  1638. if (name._domain_pattern || name._property_pattern)
  1639. return false;
  1640. // No pattern
  1641. if (!_domain_pattern && !_property_pattern)
  1642. return _canonicalName.equals(name._canonicalName);
  1643. return matchDomains(name) && matchKeys(name);
  1644. }
  1645. private final boolean matchDomains(ObjectName name) {
  1646. if (_domain_pattern) {
  1647. // wildmatch domains
  1648. final char[] dom_pattern = getDomain().toCharArray();
  1649. final char[] dom_string = name.getDomain().toCharArray();
  1650. return wildmatch(dom_string,dom_pattern,0,0);
  1651. }
  1652. return getDomain().equals(name.getDomain());
  1653. }
  1654. private final boolean matchKeys(ObjectName name) {
  1655. if (_property_pattern) {
  1656. // Every property inside pattern should exist in name
  1657. final Hashtable nameProps = name._getKeyPropertyList();
  1658. final Property[] props=_ca_array;
  1659. final String cn=_canonicalName;
  1660. for (int i= props.length -1; i >= 0 ; i--) {
  1661. // find value in given object name for key at current
  1662. // index in receiver
  1663. final Property p = props[i];
  1664. final String k = p.getKeyString(cn);
  1665. final String v = (String)nameProps.get(k);
  1666. // did we find a value for this key ?
  1667. if (v == null) return false;
  1668. // if this property is ok (same key, same value),
  1669. // go to next
  1670. if (v.equals(p.getValueString(cn))) continue;
  1671. return false;
  1672. }
  1673. return true;
  1674. }
  1675. final String p1 = name.getCanonicalKeyPropertyListString();
  1676. final String p2 = getCanonicalKeyPropertyListString();
  1677. return (p1.equals(p2));
  1678. }
  1679. /* Method inherited from QueryExp, no implementation needed here
  1680. because ObjectName is not relative to an MBeanServer and does
  1681. not contain a subquery.
  1682. */
  1683. public void setMBeanServer(MBeanServer mbs) { }
  1684. // Category : QueryExp Interface <=========================
  1685. // Public methods <========================================
  1686. }