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