Frames | No Frames |
1: /* ObjectName.java -- Represent the name of a bean, or a pattern for a name. 2: Copyright (C) 2006, 2007 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.management; 39: 40: import java.io.Serializable; 41: 42: import java.util.Collections; 43: import java.util.Hashtable; 44: import java.util.Iterator; 45: import java.util.Map; 46: import java.util.TreeMap; 47: 48: import java.io.IOException; 49: import java.io.InvalidObjectException; 50: import java.io.ObjectInputStream; 51: import java.io.ObjectOutputStream; 52: 53: /** 54: * <p> 55: * An {@link ObjectName} instance represents the name of a management 56: * bean, or a pattern which may match the name of one or more 57: * management beans. Patterns are distinguished from names by the 58: * presence of the '?' and '*' characters (which match a single 59: * character and a series of zero or more characters, respectively). 60: * </p> 61: * <p> 62: * Each name begins with a domain element, which is terminated by 63: * a ':' character. The domain may be empty. If so, it will be 64: * replaced by the default domain of the bean server in certain 65: * contexts. The domain is a pattern, if it contains either '?' 66: * or '*'. To avoid collisions, it is usual to use reverse 67: * DNS names for the domain, as in Java package and property names. 68: * </p> 69: * <p> 70: * Following the ':' character is a series of properties. The list 71: * is separated by commas, and largely consists of unordered key-value 72: * pairs, separated by an equals sign ('='). At most one element may 73: * be an asterisk ('*'), which turns the {@link ObjectName} instance 74: * into a <emph>property list pattern</emph>. In this situation, the pattern 75: * matches a name if the name contains at least those key-value pairs 76: * given and has the same domain. 77: * </p> 78: * <p> 79: * A <emph>key</emph> is a string of characters which doesn't include 80: * any of those used as delimiters or in patterns (':', '=', ',', '?' 81: * and '*'). Keys must be unique. 82: * </p> 83: * <p> 84: * A value may be <emph>quoted</emph> or <emph>unquoted</emph>. Unquoted 85: * values obey the same rules as given for keys above. Quoted values are 86: * surrounded by quotation marks ("), and use a backslash ('\') character 87: * to include quotes ('\"'), backslashes ('\\'), newlines ('\n'), and 88: * the pattern characters ('\?' and '\*'). The quotes and backslashes 89: * (after expansion) are considered part of the value. 90: * </p> 91: * <p> 92: * Both quoted and unquoted values may contain the wildcard characters 93: * '?' and '*'. A name with at least one value containing a wildcard 94: * character is known as a <emph>property value pattern</emph>. A 95: * name is generally a <emph>property pattern</emph> if it is either 96: * a <emph>property list pattern</emph> or <emph>property value pattern</emph>. 97: * </p> 98: * <p> 99: * Spaces are maintained within the different parts of the name. Thus, 100: * '<code>domain: key1 = value1 </code>' has a key ' key1 ' with value 101: * ' value1 '. Newlines are disallowed, except where escaped in quoted 102: * values. 103: * </p> 104: * 105: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 106: * @since 1.5 107: */ 108: public class ObjectName 109: implements Serializable, QueryExp 110: { 111: 112: private static final long serialVersionUID = 1081892073854801359L; 113: 114: /** 115: * The wildcard {@link ObjectName} {@code "*:*"} 116: * 117: * @since 1.6 118: */ 119: public static final ObjectName WILDCARD; 120: 121: /** 122: * The domain of the name. 123: */ 124: private transient String domain; 125: 126: /** 127: * The properties, as key-value pairs. 128: */ 129: private transient TreeMap<String,String> properties; 130: 131: /** 132: * The properties as a string (stored for ordering). 133: */ 134: private transient String propertyListString; 135: 136: /** 137: * True if this object name is a property list pattern. 138: */ 139: private transient boolean propertyListPattern; 140: 141: /** 142: * True if this object name is a property value pattern. 143: */ 144: private transient boolean propertyValuePattern; 145: 146: /** 147: * The management server associated with this object name. 148: */ 149: private transient MBeanServer server; 150: 151: /** 152: * Static initializer to set up the wildcard. 153: */ 154: static 155: { 156: try 157: { 158: WILDCARD = new ObjectName(""); 159: } 160: catch (MalformedObjectNameException e) 161: { 162: throw (InternalError) (new InternalError("A problem occurred " + 163: "initializing the ObjectName " + 164: "wildcard.").initCause(e)); 165: } 166: } 167: 168: /** 169: * Constructs an {@link ObjectName} instance from the given string, 170: * which should be of the form 171: * <domain>:<properties><wild>. <domain> 172: * represents the domain section of the name. <properties> 173: * represents the key-value pairs, as returned by {@link 174: * #getKeyPropertyListString()}. <wild> is the optional 175: * asterisk present in the property list. If the string doesn't 176: * represent a property pattern, it will be empty. If it does, 177: * it will be either ',*' or '*', depending on whether other 178: * properties are present or not, respectively. 179: * 180: * @param name the string to use to construct this instance. 181: * @throws MalformedObjectNameException if the string is of the 182: * wrong format. 183: * @throws NullPointerException if <code>name</code> is 184: * <code>null</code>. 185: */ 186: public ObjectName(String name) 187: throws MalformedObjectNameException 188: { 189: if (name.length() == 0) 190: name = "*:*"; 191: parse(name); 192: } 193: 194: /** 195: * Parse the name in the same form as the constructor. Used by 196: * readObject(). 197: */ 198: private void parse(String name) 199: throws MalformedObjectNameException 200: { 201: int domainSep = name.indexOf(':'); 202: if (domainSep == -1) 203: throw new MalformedObjectNameException("No domain separator was found."); 204: domain = name.substring(0, domainSep); 205: String rest = name.substring(domainSep + 1); 206: properties = new TreeMap<String,String>(); 207: String[] pairs = rest.split(","); 208: if (pairs.length == 0 && !isPattern()) 209: throw new MalformedObjectNameException("A name that is not a " + 210: "pattern must contain at " + 211: "least one key-value pair."); 212: propertyListString = ""; 213: for (int a = 0; a < pairs.length; ++a) 214: { 215: if (pairs[a].equals("*")) 216: { 217: if (propertyListPattern) 218: throw new MalformedObjectNameException("Multiple wildcards " + 219: "in properties."); 220: propertyListPattern = true; 221: continue; 222: } 223: int sep = pairs[a].indexOf('='); 224: if (sep == -1) 225: throw new MalformedObjectNameException("A key must be " + 226: "followed by a value."); 227: String key = pairs[a].substring(0, sep); 228: if (properties.containsKey(key)) 229: throw new MalformedObjectNameException("The same key occurs " + 230: "more than once."); 231: String value = pairs[a].substring(sep+1); 232: properties.put(key, value); 233: propertyListString += key + "=" + value + ","; 234: } 235: if (propertyListString.length() > 0) 236: propertyListString = 237: propertyListString.substring(0, propertyListString.length() - 1); 238: checkComponents(); 239: } 240: 241: /** 242: * Constructs an {@link ObjectName} instance using the given 243: * domain and the one specified property. 244: * 245: * @param domain the domain part of the object name. 246: * @param key the key of the property. 247: * @param value the value of the property. 248: * @throws MalformedObjectNameException the domain, key or value 249: * contains an illegal 250: * character or the value 251: * does not follow the quoting 252: * specifications. 253: * @throws NullPointerException if one of the parameters is 254: * <code>null</code>. 255: */ 256: public ObjectName(String domain, String key, String value) 257: throws MalformedObjectNameException 258: { 259: this.domain = domain; 260: properties = new TreeMap<String,String>(); 261: properties.put(key, value); 262: checkComponents(); 263: } 264: 265: /** 266: * Constructs an {@link ObjectName} instance using the given 267: * domain and properties. 268: * 269: * @param domain the domain part of the object name. 270: * @param properties the key-value property pairs. 271: * @throws MalformedObjectNameException the domain, a key or a value 272: * contains an illegal 273: * character or a value 274: * does not follow the quoting 275: * specifications. 276: * @throws NullPointerException if one of the parameters is 277: * <code>null</code>. 278: */ 279: public ObjectName(String domain, Hashtable<String,String> properties) 280: throws MalformedObjectNameException 281: { 282: this.domain = domain; 283: this.properties = new TreeMap<String,String>(); 284: this.properties.putAll(properties); 285: checkComponents(); 286: } 287: 288: /** 289: * Checks the legality of the domain and the properties. 290: * 291: * @throws MalformedObjectNameException the domain, a key or a value 292: * contains an illegal 293: * character or a value 294: * does not follow the quoting 295: * specifications. 296: */ 297: private void checkComponents() 298: throws MalformedObjectNameException 299: { 300: if (domain.indexOf(':') != -1) 301: throw new MalformedObjectNameException("The domain includes a ':' " + 302: "character."); 303: if (domain.indexOf('\n') != -1) 304: throw new MalformedObjectNameException("The domain includes a newline " + 305: "character."); 306: char[] keychars = new char[] { '\n', ':', ',', '*', '?', '=' }; 307: char[] valchars = new char[] { '\n', ':', ',', '=' }; 308: Iterator i = properties.entrySet().iterator(); 309: while (i.hasNext()) 310: { 311: Map.Entry entry = (Map.Entry) i.next(); 312: String key = (String) entry.getKey(); 313: for (int a = 0; a < keychars.length; ++a) 314: if (key.indexOf(keychars[a]) != -1) 315: throw new MalformedObjectNameException("A key contains a '" + 316: keychars[a] + "' " + 317: "character."); 318: String value = (String) entry.getValue(); 319: int quote = value.indexOf('"'); 320: if (quote == 0) 321: { 322: try 323: { 324: unquote(value); 325: } 326: catch (IllegalArgumentException e) 327: { 328: throw (MalformedObjectNameException) 329: new MalformedObjectNameException("The quoted value is " + 330: "invalid.").initCause(e); 331: } 332: } 333: else if (quote != -1) 334: throw new MalformedObjectNameException("A value contains " + 335: "a '\"' character."); 336: else 337: { 338: for (int a = 0; a < valchars.length; ++a) 339: if (value.indexOf(valchars[a]) != -1) 340: throw new MalformedObjectNameException("A value contains " + 341: "a '" + valchars[a] + "' " + 342: "character."); 343: 344: } 345: if (value.indexOf('*') != -1 || value.indexOf('?') != -1) 346: propertyValuePattern = true; 347: } 348: } 349: 350: /** 351: * <p> 352: * Attempts to find a match between this name and the one supplied. 353: * The following criteria are used: 354: * </p> 355: * <ul> 356: * <li>If the supplied name is a pattern, <code>false</code> is 357: * returned.</li> 358: * <li>If this name is a pattern, this method returns <code>true</code> 359: * if the supplied name matches the pattern.</li> 360: * <li>If this name is not a pattern, the result of 361: * <code>equals(name)</code> is returned. 362: * </ul> 363: * 364: * @param name the name to find a match with. 365: * @return true if the name either matches this pattern or is 366: * equivalent to this name under the criteria of 367: * {@link #equals(java.lang.Object)} 368: * @throws NullPointerException if <code>name</code> is <code>null</code>. 369: */ 370: public boolean apply(ObjectName name) 371: { 372: if (name.isPattern()) 373: return false; 374: 375: if (!isPattern()) 376: return equals(name); 377: 378: if (isDomainPattern()) 379: { 380: if (!domainMatches(domain, 0, name.getDomain(), 0)) 381: return false; 382: } 383: else 384: { 385: if (!domain.equals(name.getDomain())) 386: return false; 387: } 388: 389: if (isPropertyPattern()) 390: { 391: Hashtable oProps = name.getKeyPropertyList(); 392: Iterator i = properties.entrySet().iterator(); 393: while (i.hasNext()) 394: { 395: Map.Entry entry = (Map.Entry) i.next(); 396: String key = (String) entry.getKey(); 397: if (!(oProps.containsKey(key))) 398: return false; 399: String val = (String) entry.getValue(); 400: if (!(val.equals(oProps.get(key)))) 401: return false; 402: } 403: } 404: else 405: { 406: if (!getCanonicalKeyPropertyListString().equals 407: (name.getCanonicalKeyPropertyListString())) 408: return false; 409: } 410: return true; 411: } 412: 413: /** 414: * Returns true if the domain matches the pattern. 415: * 416: * @param pattern the pattern to match against. 417: * @param patternindex the index into the pattern to start matching. 418: * @param domain the domain to match. 419: * @param domainindex the index into the domain to start matching. 420: * @return true if the domain matches the pattern. 421: */ 422: private static boolean domainMatches(String pattern, int patternindex, 423: String domain, int domainindex) 424: { 425: while (patternindex < pattern.length()) 426: { 427: char c = pattern.charAt(patternindex++); 428: 429: if (c == '*') 430: { 431: for (int i = domain.length(); i >= domainindex; i--) 432: { 433: if (domainMatches(pattern, patternindex, domain, i)) 434: return true; 435: } 436: return false; 437: } 438: 439: if (domainindex >= domain.length()) 440: return false; 441: 442: if (c != '?' && c != domain.charAt(domainindex)) 443: return false; 444: 445: domainindex++; 446: } 447: return true; 448: } 449: 450: /** 451: * Compares the specified object with this one. The two 452: * are judged to be equivalent if the given object is an 453: * instance of {@link ObjectName} and has an equal canonical 454: * form (as returned by {@link #getCanonicalName()}). 455: * 456: * @param obj the object to compare with this. 457: * @return true if the object is also an {@link ObjectName} 458: * with an equivalent canonical form. 459: */ 460: public boolean equals(Object obj) 461: { 462: if (obj instanceof ObjectName) 463: { 464: ObjectName o = (ObjectName) obj; 465: return getCanonicalName().equals(o.getCanonicalName()); 466: } 467: return false; 468: } 469: 470: /** 471: * Returns the property list in canonical form. The keys 472: * are ordered using the lexicographic ordering used by 473: * {@link java.lang.String#compareTo(java.lang.Object)}. 474: * 475: * @return the property list, with the keys in lexicographic 476: * order. 477: */ 478: public String getCanonicalKeyPropertyListString() 479: { 480: StringBuilder builder = new StringBuilder(); 481: Iterator i = properties.entrySet().iterator(); 482: while (i.hasNext()) 483: { 484: Map.Entry entry = (Map.Entry) i.next(); 485: builder.append(entry.getKey() + "=" + entry.getValue()); 486: if (i.hasNext()) 487: builder.append(","); 488: } 489: return builder.toString(); 490: } 491: 492: /** 493: * <p> 494: * Returns the name as a string in canonical form. More precisely, 495: * this returns a string of the format 496: * <domain>:<properties><wild>. <properties> 497: * is the same value as returned by 498: * {@link #getCanonicalKeyPropertyListString()}. <wild> 499: * is: 500: * </p> 501: * <ul> 502: * <li>an empty string, if the object name is not a property pattern.</li> 503: * <li>'*' if <properties> is empty.</li> 504: * <li>',*' if there is at least one key-value pair.</li> 505: * </ul> 506: * 507: * @return the canonical string form of the object name, as specified 508: * above. 509: */ 510: public String getCanonicalName() 511: { 512: return domain + ":" + 513: getCanonicalKeyPropertyListString() + 514: (isPropertyPattern() ? (properties.isEmpty() ? "*" : ",*") : ""); 515: } 516: 517: /** 518: * Returns the domain part of the object name. 519: * 520: * @return the domain. 521: */ 522: public String getDomain() 523: { 524: return domain; 525: } 526: 527: /** 528: * Returns an {@link ObjectName} instance that is substitutable for the 529: * one given. The instance returned may be a subclass of {@link ObjectName}, 530: * but is not guaranteed to be of the same type as the given name, if that 531: * should also turn out to be a subclass. The returned instance may or may 532: * not be equivalent to the one given. The purpose of this method is to provide 533: * an instance of {@link ObjectName} with a well-defined semantics, such as may 534: * be used in cases where the given name is not trustworthy. 535: * 536: * @param name the {@link ObjectName} to provide a substitute for. 537: * @return a substitute for the given name, which may or may not be a subclass 538: * of {@link ObjectName}. In either case, the returned object is 539: * guaranteed to have the semantics defined here. 540: * @throws NullPointerException if <code>name</code> is <code>null</code>. 541: */ 542: public static ObjectName getInstance(ObjectName name) 543: { 544: try 545: { 546: return new ObjectName(name.getCanonicalName()); 547: } 548: catch (MalformedObjectNameException e) 549: { 550: throw (InternalError) 551: (new InternalError("The canonical name of " + 552: "the given name is invalid.").initCause(e)); 553: } 554: } 555: 556: /** 557: * Returns an {@link ObjectName} instance for the specified name, represented 558: * as a {@link java.lang.String}. The instance returned may be a subclass of 559: * {@link ObjectName} and may or may not be equivalent to earlier instances 560: * returned by this method for the same string. 561: * 562: * @param name the {@link ObjectName} to provide an instance of. 563: * @return a instance for the given name, which may or may not be a subclass 564: * of {@link ObjectName}. 565: * @throws MalformedObjectNameException the domain, a key or a value 566: * contains an illegal 567: * character or a value 568: * does not follow the quoting 569: * specifications. 570: * @throws NullPointerException if <code>name</code> is <code>null</code>. 571: */ 572: public static ObjectName getInstance(String name) 573: throws MalformedObjectNameException 574: { 575: return new ObjectName(name); 576: } 577: 578: /** 579: * Returns an {@link ObjectName} instance for the specified name, represented 580: * as a series of {@link java.lang.String} objects for the domain and a single 581: * property, as a key-value pair. The instance returned may be a subclass of 582: * {@link ObjectName} and may or may not be equivalent to earlier instances 583: * returned by this method for the same parameters. 584: * 585: * @param domain the domain part of the object name. 586: * @param key the key of the property. 587: * @param value the value of the property. 588: * @return a instance for the given name, which may or may not be a subclass 589: * of {@link ObjectName}. 590: * @throws MalformedObjectNameException the domain, a key or a value 591: * contains an illegal 592: * character or a value 593: * does not follow the quoting 594: * specifications. 595: * @throws NullPointerException if <code>name</code> is <code>null</code>. 596: */ 597: public static ObjectName getInstance(String domain, String key, String value) 598: throws MalformedObjectNameException 599: { 600: return new ObjectName(domain, key, value); 601: } 602: 603: /** 604: * Returns an {@link ObjectName} instance for the specified name, represented 605: * as a domain {@link java.lang.String} and a table of properties. The 606: * instance returned may be a subclass of {@link ObjectName} and may or may 607: * not be equivalent to earlier instances returned by this method for the 608: * same string. 609: * 610: * @param domain the domain part of the object name. 611: * @param properties the key-value property pairs. 612: * @return a instance for the given name, which may or may not be a subclass 613: * of {@link ObjectName}. 614: * @throws MalformedObjectNameException the domain, a key or a value 615: * contains an illegal 616: * character or a value 617: * does not follow the quoting 618: * specifications. 619: * @throws NullPointerException if <code>name</code> is <code>null</code>. 620: */ 621: public static ObjectName getInstance(String domain, 622: Hashtable<String,String> properties) 623: throws MalformedObjectNameException 624: { 625: return new ObjectName(domain, properties); 626: } 627: 628: /** 629: * Returns the property value corresponding to the given key. 630: * 631: * @param key the key of the property to be obtained. 632: * @return the value of the specified property. 633: * @throws NullPointerException if <code>key</code> is <code>null</code>. 634: */ 635: public String getKeyProperty(String key) 636: { 637: if (key == null) 638: throw new NullPointerException("Null key given in request for a value."); 639: return (String) properties.get(key); 640: } 641: 642: /** 643: * Returns the properties in a {@link java.util.Hashtable}. The table 644: * contains each of the properties as keys mapped to their value. The 645: * returned table is not unmodifiable, but changes made to it will not 646: * be reflected in the object name. 647: * 648: * @return a {@link java.util.Hashtable}, containing each of the object 649: * name's properties. 650: */ 651: public Hashtable<String,String> getKeyPropertyList() 652: { 653: return new Hashtable<String,String>(properties); 654: } 655: 656: /** 657: * Returns a {@link java.lang.String} representation of the property 658: * list. If the object name was created using {@link 659: * ObjectName(String)}, then this string will contain the properties 660: * in the same order they were given in at creation. 661: * 662: * @return the property list. 663: */ 664: public String getKeyPropertyListString() 665: { 666: if (propertyListString != null) 667: return propertyListString; 668: return getCanonicalKeyPropertyListString(); 669: } 670: 671: /** 672: * Returns a hash code for this object name. This is calculated as the 673: * summation of the hash codes of the domain and the properties. 674: * 675: * @return a hash code for this object name. 676: */ 677: public int hashCode() 678: { 679: return domain.hashCode() + properties.hashCode(); 680: } 681: 682: /** 683: * Returns true if the domain of this object name is a pattern. 684: * This is the case if it contains one or more wildcard characters 685: * ('*' or '?'). 686: * 687: * @return true if the domain is a pattern. 688: */ 689: public boolean isDomainPattern() 690: { 691: return domain.contains("?") || domain.contains("*"); 692: } 693: 694: /** 695: * Returns true if this is an object name pattern. An object 696: * name pattern has a domain containing a wildcard character 697: * ('*' or '?') and/or a '*' in the list of properties. 698: * This method will return true if either {@link #isDomainPattern()} 699: * or {@link #isPropertyPattern()} does. 700: * 701: * @return true if this is an object name pattern. 702: */ 703: public boolean isPattern() 704: { 705: return isDomainPattern() || isPropertyPattern(); 706: } 707: 708: /** 709: * Returns true if this object name is a property list 710: * pattern, a property value pattern or both. 711: * 712: * @return true if the properties of this name contain a pattern. 713: * @see #isPropertyListPattern 714: * @see #isPropertyValuePattern 715: */ 716: public boolean isPropertyPattern() 717: { 718: return propertyListPattern || propertyValuePattern; 719: } 720: 721: /** 722: * Returns true if this object name is a property list pattern. This is 723: * the case if the list of properties contains an '*'. 724: * 725: * @return true if this is a property list pattern. 726: * @since 1.6 727: */ 728: public boolean isPropertyListPattern() 729: { 730: return propertyListPattern; 731: } 732: 733: /** 734: * Returns true if this object name is a property value pattern. This is 735: * the case if one of the values contains a wildcard character, 736: * '?' or '*'. 737: * 738: * @return true if this is a property value pattern. 739: * @since 1.6 740: */ 741: public boolean isPropertyValuePattern() 742: { 743: return propertyValuePattern; 744: } 745: 746: /** 747: * Returns true if the value of the given key is a pattern. This is 748: * the case if the value contains a wildcard character, '?' or '*'. 749: * 750: * @param key the key whose value should be checked. 751: * @return true if the value of the given key is a pattern. 752: * @since 1.6 753: * @throws NullPointerException if {@code key} is {@code null}. 754: * @throws IllegalArgumentException if {@code key} is not a valid 755: * property. 756: */ 757: public boolean isPropertyValuePattern(String key) 758: { 759: String value = getKeyProperty(key); 760: if (value == null) 761: throw new IllegalArgumentException(key + " is not a valid property."); 762: return value.indexOf('?') != -1 || value.indexOf('*') != -1; 763: } 764: 765: /** 766: * <p> 767: * Returns a quoted version of the supplied string. The string may 768: * contain any character. The resulting quoted version is guaranteed 769: * to be usable as the value of a property, so this method provides 770: * a good way of ensuring that a value is legal. 771: * </p> 772: * <p> 773: * The string is transformed as follows: 774: * </p> 775: * <ul> 776: * <li>The string is prefixed with an opening quote character, '"'. 777: * <li>For each character, s: 778: * <ul> 779: * <li>If s is a quote ('"'), it is replaced by a backslash 780: * followed by a quote.</li> 781: * <li>If s is a star ('*'), it is replaced by a backslash followed 782: * by a star.</li> 783: * <li>If s is a question mark ('?'), it is replaced by a backslash 784: * followed by a question mark.</li> 785: * <li>If s is a backslash ('\'), it is replaced by two backslashes.</li> 786: * <li>If s is a newline character, it is replaced by a backslash followed by 787: * a '\n'.</li> 788: * <li>Otherwise, s is used verbatim. 789: * </ul></li> 790: * <li>The string is terminated with a closing quote character, '"'.</li> 791: * </ul> 792: * 793: * @param string the string to quote. 794: * @return a quoted version of the supplied string. 795: * @throws NullPointerException if <code>string</code> is <code>null</code>. 796: */ 797: public static String quote(String string) 798: { 799: StringBuilder builder = new StringBuilder(); 800: builder.append('"'); 801: for (int a = 0; a < string.length(); ++a) 802: { 803: char s = string.charAt(a); 804: switch (s) 805: { 806: case '"': 807: builder.append("\\\""); 808: break; 809: case '*': 810: builder.append("\\*"); 811: break; 812: case '?': 813: builder.append("\\?"); 814: break; 815: case '\\': 816: builder.append("\\\\"); 817: break; 818: case '\n': 819: builder.append("\\\n"); 820: break; 821: default: 822: builder.append(s); 823: } 824: } 825: builder.append('"'); 826: return builder.toString(); 827: } 828: 829: /** 830: * Changes the {@link MBeanServer} on which this query is performed. 831: * 832: * @param server the new server to use. 833: */ 834: public void setMBeanServer(MBeanServer server) 835: { 836: this.server = server; 837: } 838: 839: /** 840: * Returns a textual representation of the object name. 841: * 842: * <p>The format is unspecified beyond that equivalent object 843: * names will return the same string from this method, but note 844: * that Tomcat depends on the string returned by this method 845: * being a valid textual representation of the object name and 846: * will fail to start if it is not. 847: * 848: * @return a textual representation of the object name. 849: */ 850: public String toString() 851: { 852: return getCanonicalName(); 853: } 854: 855: 856: /** 857: * Serialize this {@link ObjectName}. The serialized 858: * form is the same as the string parsed by the constructor. 859: * 860: * @param out the output stream to write to. 861: * @throws IOException if an I/O error occurs. 862: */ 863: private void writeObject(ObjectOutputStream out) 864: throws IOException 865: { 866: out.defaultWriteObject(); 867: StringBuffer buffer = new StringBuffer(getDomain()); 868: buffer.append(':'); 869: String properties = getKeyPropertyListString(); 870: buffer.append(properties); 871: if (isPropertyPattern()) 872: { 873: if (properties.length() == 0) 874: buffer.append("*"); 875: else 876: buffer.append(",*"); 877: } 878: out.writeObject(buffer.toString()); 879: } 880: 881: /** 882: * Reads the serialized form, which is that used 883: * by the constructor. 884: * 885: * @param in the input stream to read from. 886: * @throws IOException if an I/O error occurs. 887: */ 888: private void readObject(ObjectInputStream in) 889: throws IOException, ClassNotFoundException 890: { 891: in.defaultReadObject(); 892: String objectName = (String)in.readObject(); 893: try 894: { 895: parse(objectName); 896: } 897: catch (MalformedObjectNameException x) 898: { 899: throw new InvalidObjectException(x.toString()); 900: } 901: } 902: 903: 904: /** 905: * Unquotes the supplied string. The quotation marks are removed as 906: * are the backslashes preceding the escaped characters ('"', '?', 907: * '*', '\n', '\\'). A one-to-one mapping exists between quoted and 908: * unquoted values. As a result, a string <code>s</code> should be 909: * equal to <code>unquote(quote(s))</code>. 910: * 911: * @param q the quoted string to unquote. 912: * @return the unquoted string. 913: * @throws NullPointerException if <code>q</code> is <code>null</code>. 914: * @throws IllegalArgumentException if the string is not a valid 915: * quoted string i.e. it is not 916: * surrounded by quotation marks 917: * and/or characters are not properly 918: * escaped. 919: */ 920: public static String unquote(String q) 921: { 922: if (q.charAt(0) != '"') 923: throw new IllegalArgumentException("The string does " + 924: "not start with a quote."); 925: if (q.charAt(q.length() - 1) != '"') 926: throw new IllegalArgumentException("The string does " + 927: "not end with a quote."); 928: StringBuilder builder = new StringBuilder(); 929: for (int a = 1; a < (q.length() - 1); ++a) 930: { 931: char n = q.charAt(a); 932: if (n == '\\') 933: { 934: n = q.charAt(++a); 935: if (n != '"' && n != '?' && n != '*' && 936: n != 'n' && n != '\\') 937: throw new IllegalArgumentException("Illegal escaped character: " 938: + n); 939: } 940: else if (n == '"' || n == '\n') 941: throw new IllegalArgumentException("Illegal character: " + n); 942: builder.append(n); 943: } 944: 945: return builder.toString(); 946: } 947: 948: }