001 /* ObjectName.java -- Represent the name of a bean, or a pattern for a name. 002 Copyright (C) 2006, 2007 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 package javax.management; 039 040 import java.io.Serializable; 041 042 import java.util.Collections; 043 import java.util.Hashtable; 044 import java.util.Iterator; 045 import java.util.Map; 046 import java.util.TreeMap; 047 048 import java.io.IOException; 049 import java.io.InvalidObjectException; 050 import java.io.ObjectInputStream; 051 import java.io.ObjectOutputStream; 052 053 /** 054 * <p> 055 * An {@link ObjectName} instance represents the name of a management 056 * bean, or a pattern which may match the name of one or more 057 * management beans. Patterns are distinguished from names by the 058 * presence of the '?' and '*' characters (which match a single 059 * character and a series of zero or more characters, respectively). 060 * </p> 061 * <p> 062 * Each name begins with a domain element, which is terminated by 063 * a ':' character. The domain may be empty. If so, it will be 064 * replaced by the default domain of the bean server in certain 065 * contexts. The domain is a pattern, if it contains either '?' 066 * or '*'. To avoid collisions, it is usual to use reverse 067 * DNS names for the domain, as in Java package and property names. 068 * </p> 069 * <p> 070 * Following the ':' character is a series of properties. The list 071 * is separated by commas, and largely consists of unordered key-value 072 * pairs, separated by an equals sign ('='). At most one element may 073 * be an asterisk ('*'), which turns the {@link ObjectName} instance 074 * into a <emph>property list pattern</emph>. In this situation, the pattern 075 * matches a name if the name contains at least those key-value pairs 076 * given and has the same domain. 077 * </p> 078 * <p> 079 * A <emph>key</emph> is a string of characters which doesn't include 080 * any of those used as delimiters or in patterns (':', '=', ',', '?' 081 * and '*'). Keys must be unique. 082 * </p> 083 * <p> 084 * A value may be <emph>quoted</emph> or <emph>unquoted</emph>. Unquoted 085 * values obey the same rules as given for keys above. Quoted values are 086 * surrounded by quotation marks ("), and use a backslash ('\') character 087 * to include quotes ('\"'), backslashes ('\\'), newlines ('\n'), and 088 * the pattern characters ('\?' and '\*'). The quotes and backslashes 089 * (after expansion) are considered part of the value. 090 * </p> 091 * <p> 092 * Both quoted and unquoted values may contain the wildcard characters 093 * '?' and '*'. A name with at least one value containing a wildcard 094 * character is known as a <emph>property value pattern</emph>. A 095 * name is generally a <emph>property pattern</emph> if it is either 096 * a <emph>property list pattern</emph> or <emph>property value pattern</emph>. 097 * </p> 098 * <p> 099 * 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 }