blob: 8721bb62d18a9fdac85397a1cf6a49fec357f53c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.management;
27
28import com.sun.jmx.mbeanserver.GetPropertyAction;
29import java.io.IOException;
30import java.io.InvalidObjectException;
31import java.io.ObjectInputStream;
32import java.io.ObjectOutputStream;
33import java.io.ObjectStreamField;
34import java.security.AccessController;
35import java.util.Arrays;
36import java.util.Collections;
37import java.util.HashMap;
38import java.util.Hashtable;
39import java.util.Map;
40import javax.management.MBeanServer;
41import javax.management.MalformedObjectNameException;
42import javax.management.QueryExp;
43
44/**
45 * <p>Represents the object name of an MBean, or a pattern that can
46 * match the names of several MBeans. Instances of this class are
47 * immutable.</p>
48 *
49 * <p>An instance of this class can be used to represent:</p>
50 * <ul>
51 * <li>An object name</li>
52 * <li>An object name pattern, within the context of a query</li>
53 * </ul>
54 *
55 * <p>An object name consists of two parts, the domain and the key
56 * properties.</p>
57 *
58 * <p>The <em>domain</em> is a string of characters not including
59 * the character colon (<code>:</code>). It is recommended that the domain
60 * should not contain the string "{@code //}", which is reserved for future use.
61 *
62 * <p>If the domain includes at least one occurrence of the wildcard
63 * characters asterisk (<code>*</code>) or question mark
64 * (<code>?</code>), then the object name is a pattern. The asterisk
65 * matches any sequence of zero or more characters, while the question
66 * mark matches any single character.</p>
67 *
68 * <p>If the domain is empty, it will be replaced in certain contexts
69 * by the <em>default domain</em> of the MBean server in which the
70 * ObjectName is used.</p>
71 *
72 * <p>The <em>key properties</em> are an unordered set of keys and
73 * associated values.</p>
74 *
75 * <p>Each <em>key</em> is a nonempty string of characters which may
76 * not contain any of the characters comma (<code>,</code>), equals
77 * (<code>=</code>), colon, asterisk, or question mark. The same key
78 * may not occur twice in a given ObjectName.</p>
79 *
80 * <p>Each <em>value</em> associated with a key is a string of
81 * characters that is either unquoted or quoted.</p>
82 *
83 * <p>An <em>unquoted value</em> is a possibly empty string of
84 * characters which may not contain any of the characters comma,
85 * equals, colon, or quote.</p>
86 *
87 * <p>If the <em>unquoted value</em> contains at least one occurrence
88 * of the wildcard characters asterisk or question mark, then the object
89 * name is a <em>property value pattern</em>. The asterisk matches any
90 * sequence of zero or more characters, while the question mark matches
91 * any single character.</p>
92 *
93 * <p>A <em>quoted value</em> consists of a quote (<code>"</code>),
94 * followed by a possibly empty string of characters, followed by
95 * another quote. Within the string of characters, the backslash
96 * (<code>\</code>) has a special meaning. It must be followed by
97 * one of the following characters:</p>
98 *
99 * <ul>
100 * <li>Another backslash. The second backslash has no special
101 * meaning and the two characters represent a single backslash.</li>
102 *
103 * <li>The character 'n'. The two characters represent a newline
104 * ('\n' in Java).</li>
105 *
106 * <li>A quote. The two characters represent a quote, and that quote
107 * is not considered to terminate the quoted value. An ending closing
108 * quote must be present for the quoted value to be valid.</li>
109 *
110 * <li>A question mark (?) or asterisk (*). The two characters represent
111 * a question mark or asterisk respectively.</li>
112 * </ul>
113 *
114 * <p>A quote may not appear inside a quoted value except immediately
115 * after an odd number of consecutive backslashes.</p>
116 *
117 * <p>The quotes surrounding a quoted value, and any backslashes
118 * within that value, are considered to be part of the value.</p>
119 *
120 * <p>If the <em>quoted value</em> contains at least one occurrence of
121 * the characters asterisk or question mark and they are not preceded
122 * by a backslash, then they are considered as wildcard characters and
123 * the object name is a <em>property value pattern</em>. The asterisk
124 * matches any sequence of zero or more characters, while the question
125 * mark matches any single character.</p>
126 *
127 * <p>An ObjectName may be a <em>property list pattern</em>. In this
128 * case it may have zero or more keys and associated values. It matches
129 * a nonpattern ObjectName whose domain matches and that contains the
130 * same keys and associated values, as well as possibly other keys and
131 * values.</p>
132 *
133 * <p>An ObjectName is a <em>property value pattern</em> when at least
134 * one of its <em>quoted</em> or <em>unquoted</em> key property values
135 * contains the wildcard characters asterisk or question mark as described
136 * above. In this case it has one or more keys and associated values, with
137 * at least one of the values containing wildcard characters. It matches a
138 * nonpattern ObjectName whose domain matches and that contains the same
139 * keys whose values match; if the property value pattern is also a
140 * property list pattern then the nonpattern ObjectName can contain
141 * other keys and values.</p>
142 *
143 * <p>An ObjectName is a <em>property pattern</em> if it is either a
144 * <em>property list pattern</em> or a <em>property value pattern</em>
145 * or both.</p>
146 *
147 * <p>An ObjectName is a pattern if its domain contains a wildcard or
148 * if the ObjectName is a property pattern.</p>
149 *
150 * <p>If an ObjectName is not a pattern, it must contain at least one
151 * key with its associated value.</p>
152 *
153 * <p>Examples of ObjectName patterns are:</p>
154 *
155 * <ul>
156 * <li>{@code *:type=Foo,name=Bar} to match names in any domain whose
157 * exact set of keys is {@code type=Foo,name=Bar}.</li>
158 * <li>{@code d:type=Foo,name=Bar,*} to match names in the domain
159 * {@code d} that have the keys {@code type=Foo,name=Bar} plus
160 * zero or more other keys.</li>
161 * <li>{@code *:type=Foo,name=Bar,*} to match names in any domain
162 * that has the keys {@code type=Foo,name=Bar} plus zero or
163 * more other keys.</li>
164 * <li>{@code d:type=F?o,name=Bar} will match e.g.
165 * {@code d:type=Foo,name=Bar} and {@code d:type=Fro,name=Bar}.</li>
166 * <li>{@code d:type=F*o,name=Bar} will match e.g.
167 * {@code d:type=Fo,name=Bar} and {@code d:type=Frodo,name=Bar}.</li>
168 * <li>{@code d:type=Foo,name="B*"} will match e.g.
169 * {@code d:type=Foo,name="Bling"}. Wildcards are recognized even
170 * inside quotes, and like other special characters can be escaped
171 * with {@code \}.</li>
172 * </ul>
173 *
174 * <p>An ObjectName can be written as a String with the following
175 * elements in order:</p>
176 *
177 * <ul>
178 * <li>The domain.
179 * <li>A colon (<code>:</code>).
180 * <li>A key property list as defined below.
181 * </ul>
182 *
183 * <p>A key property list written as a String is a comma-separated
184 * list of elements. Each element is either an asterisk or a key
185 * property. A key property consists of a key, an equals
186 * (<code>=</code>), and the associated value.</p>
187 *
188 * <p>At most one element of a key property list may be an asterisk.
189 * If the key property list contains an asterisk element, the
190 * ObjectName is a property list pattern.</p>
191 *
192 * <p>Spaces have no special significance in a String representing an
193 * ObjectName. For example, the String:
194 * <pre>
195 * domain: key1 = value1 , key2 = value2
196 * </pre>
197 * represents an ObjectName with two keys. The name of each key
198 * contains six characters, of which the first and last are spaces.
199 * The value associated with the key <code>"&nbsp;key1&nbsp;"</code>
200 * also begins and ends with a space.</p>
201 *
202 * <p>In addition to the restrictions on characters spelt out above,
203 * no part of an ObjectName may contain a newline character
204 * (<code>'\n'</code>), whether the domain, a key, or a value, whether
205 * quoted or unquoted. The newline character can be represented in a
206 * quoted value with the sequence <code>\n</code>.
207 *
208 * <p>The rules on special characters and quoting apply regardless of
209 * which constructor is used to make an ObjectName.</p>
210 *
211 * <p>To avoid collisions between MBeans supplied by different
212 * vendors, a useful convention is to begin the domain name with the
213 * reverse DNS name of the organization that specifies the MBeans,
214 * followed by a period and a string whose interpretation is
215 * determined by that organization. For example, MBeans specified by
216 * Sun Microsystems Inc., DNS name <code>sun.com</code>, would have
217 * domains such as <code>com.sun.MyDomain</code>. This is essentially
218 * the same convention as for Java-language package names.</p>
219 *
220 * <p>The <b>serialVersionUID</b> of this class is <code>1081892073854801359L</code>.
221 *
222 * @since 1.5
223 */
224@SuppressWarnings("serial") // don't complain serialVersionUID not constant
225public class ObjectName implements Comparable<ObjectName>, QueryExp {
226
227 /**
228 * A structure recording property structure and
229 * proposing minimal services
230 */
231 private static class Property {
232
233 int _key_index;
234 int _key_length;
235 int _value_length;
236
237 /**
238 * Constructor.
239 */
240 Property(int key_index, int key_length, int value_length) {
241 _key_index = key_index;
242 _key_length = key_length;
243 _value_length = value_length;
244 }
245
246 /**
247 * Assigns the key index of property
248 */
249 void setKeyIndex(int key_index) {
250 _key_index = key_index;
251 }
252
253 /**
254 * Returns a key string for receiver key
255 */
256 String getKeyString(String name) {
257 return name.substring(_key_index, _key_index + _key_length);
258 }
259
260 /**
261 * Returns a value string for receiver key
262 */
263 String getValueString(String name) {
264 int in_begin = _key_index + _key_length + 1;
265 int out_end = in_begin + _value_length;
266 return name.substring(in_begin, out_end);
267 }
268 }
269
270 /**
271 * Marker class for value pattern property.
272 */
273 private static class PatternProperty extends Property {
274 /**
275 * Constructor.
276 */
277 PatternProperty(int key_index, int key_length, int value_length) {
278 super(key_index, key_length, value_length);
279 }
280 }
281
282 // Inner classes <========================================
283
284
285
286 // Private fields ---------------------------------------->
287
288
289 // Serialization compatibility stuff -------------------->
290
291 // Two serial forms are supported in this class. The selected form depends
292 // on system property "jmx.serial.form":
293 // - "1.0" for JMX 1.0
294 // - any other value for JMX 1.1 and higher
295 //
296 // Serial version for old serial form
297 private static final long oldSerialVersionUID = -5467795090068647408L;
298 //
299 // Serial version for new serial form
300 private static final long newSerialVersionUID = 1081892073854801359L;
301 //
302 // Serializable fields in old serial form
303 private static final ObjectStreamField[] oldSerialPersistentFields =
304 {
305 new ObjectStreamField("domain", String.class),
306 new ObjectStreamField("propertyList", Hashtable.class),
307 new ObjectStreamField("propertyListString", String.class),
308 new ObjectStreamField("canonicalName", String.class),
309 new ObjectStreamField("pattern", Boolean.TYPE),
310 new ObjectStreamField("propertyPattern", Boolean.TYPE)
311 };
312 //
313 // Serializable fields in new serial form
314 private static final ObjectStreamField[] newSerialPersistentFields = { };
315 //
316 // Actual serial version and serial form
317 private static final long serialVersionUID;
318 private static final ObjectStreamField[] serialPersistentFields;
319 private static boolean compat = false;
320 static {
321 try {
322 GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
323 String form = AccessController.doPrivileged(act);
324 compat = (form != null && form.equals("1.0"));
325 } catch (Exception e) {
326 // OK: exception means no compat with 1.0, too bad
327 }
328 if (compat) {
329 serialPersistentFields = oldSerialPersistentFields;
330 serialVersionUID = oldSerialVersionUID;
331 } else {
332 serialPersistentFields = newSerialPersistentFields;
333 serialVersionUID = newSerialVersionUID;
334 }
335 }
336
337 //
338 // Serialization compatibility stuff <==============================
339
340 // Class private fields ----------------------------------->
341
342 /**
343 * a shared empty array for empty property lists
344 */
345 static final private Property[] _Empty_property_array = new Property[0];
346
347
348 // Class private fields <==============================
349
350 // Instance private fields ----------------------------------->
351
352 /**
353 * a String containing the canonical name
354 */
355 private transient String _canonicalName;
356
357
358 /**
359 * An array of properties in the same seq order as time creation
360 */
361 private transient Property[] _kp_array;
362
363 /**
364 * An array of properties in the same seq order as canonical order
365 */
366 private transient Property[] _ca_array;
367
368
369 /**
370 * The length of the domain part of built objectname
371 */
372 private transient int _domain_length = 0;
373
374
375 /**
376 * The propertyList of built object name. Initialized lazily.
377 * Table that contains all the pairs (key,value) for this ObjectName.
378 */
379 private transient Map<String,String> _propertyList;
380
381 /**
382 * boolean that declares if this ObjectName domain part is a pattern
383 */
384 private transient boolean _domain_pattern = false;
385
386 /**
387 * boolean that declares if this ObjectName contains a pattern on the
388 * key property list
389 */
390 private transient boolean _property_list_pattern = false;
391
392 /**
393 * boolean that declares if this ObjectName contains a pattern on the
394 * value of at least one key property
395 */
396 private transient boolean _property_value_pattern = false;
397
398 // Instance private fields <=======================================
399
400 // Private fields <========================================
401
402
403 // Private methods ---------------------------------------->
404
405 // Category : Instance construction ------------------------->
406
407 /**
408 * Initializes this {@link ObjectName} from the given string
409 * representation.
410 *
411 * @param name A string representation of the {@link ObjectName}
412 *
413 * @exception MalformedObjectNameException The string passed as a
414 * parameter does not have the right format.
415 * @exception NullPointerException The <code>name</code> parameter
416 * is null.
417 */
418 private void construct(String name)
419 throws MalformedObjectNameException, NullPointerException {
420
421 // The name cannot be null
422 if (name == null)
423 throw new NullPointerException("name cannot be null");
424
425 // Test if the name is empty
426 if (name.length() == 0) {
427 // this is equivalent to the whole word query object name.
428 _canonicalName = "*:*";
429 _kp_array = _Empty_property_array;
430 _ca_array = _Empty_property_array;
431 _domain_length = 1;
432 _propertyList = null;
433 _domain_pattern = true;
434 _property_list_pattern = true;
435 _property_value_pattern = false;
436 return;
437 }
438
439 // initialize parsing of the string
440 char[] name_chars = name.toCharArray();
441 int len = name_chars.length;
442 char[] canonical_chars = new char[len]; // canonical form will be same
443 // length at most
444 int cname_index = 0;
445 int index = 0;
446 char c, c1;
447
448 // parses domain part
449 domain_parsing:
450 while (index < len) {
451 switch (c = name_chars[index]) {
452 case ':' :
453 _domain_length = index++;
454 break domain_parsing;
455 case '=' :
456 // ":" omission check.
457 //
458 // Although "=" is a valid character in the domain part
459 // it is true that it is rarely used in the real world.
460 // So check straight away if the ":" has been omitted
461 // from the ObjectName. This allows us to provide a more
462 // accurate exception message.
463 int i = ++index;
464 while ((i < len) && (name_chars[i++] != ':'))
465 if (i == len)
466 throw new MalformedObjectNameException(
467 "Domain part must be specified");
468 break;
469 case '\n' :
470 throw new MalformedObjectNameException(
471 "Invalid character '\\n' in domain name");
472 case '*' :
473 case '?' :
474 _domain_pattern = true;
475 index++;
476 break;
477 default :
478 index++;
479 break;
480 }
481 }
482
483 // check for non-empty properties
484 if (index == len)
485 throw new MalformedObjectNameException(
486 "Key properties cannot be empty");
487
488 // we have got the domain part, begins building of _canonicalName
489 System.arraycopy(name_chars, 0, canonical_chars, 0, _domain_length);
490 canonical_chars[_domain_length] = ':';
491 cname_index = _domain_length + 1;
492
493 // parses property list
494 Property prop;
495 Map<String,Property> keys_map = new HashMap<String,Property>();
496 String[] keys;
497 String key_name;
498 boolean quoted_value;
499 int property_index = 0;
500 int in_index;
501 int key_index, key_length, value_index, value_length;
502
503 keys = new String[10];
504 _kp_array = new Property[10];
505 _property_list_pattern = false;
506 _property_value_pattern = false;
507
508 while (index < len) {
509 c = name_chars[index];
510
511 // case of pattern properties
512 if (c == '*') {
513 if (_property_list_pattern)
514 throw new MalformedObjectNameException(
515 "Cannot have several '*' characters in pattern " +
516 "property list");
517 else {
518 _property_list_pattern = true;
519 if ((++index < len ) && (name_chars[index] != ','))
520 throw new MalformedObjectNameException(
521 "Invalid character found after '*': end of " +
522 "name or ',' expected");
523 else if (index == len) {
524 if (property_index == 0) {
525 // empty properties case
526 _kp_array = _Empty_property_array;
527 _ca_array = _Empty_property_array;
528 _propertyList = Collections.emptyMap();
529 }
530 break;
531 } else {
532 // correct pattern spec in props, continue
533 index++;
534 continue;
535 }
536 }
537 }
538
539 // standard property case, key part
540 in_index = index;
541 key_index = in_index;
542 if (name_chars[in_index] == '=')
543 throw new MalformedObjectNameException("Invalid key (empty)");
544 while ((in_index < len) && ((c1 = name_chars[in_index++]) != '='))
545 switch (c1) {
546 // '=' considered to introduce value part
547 case '*' :
548 case '?' :
549 case ',' :
550 case ':' :
551 case '\n' :
552 final String ichar = ((c1=='\n')?"\\n":""+c1);
553 throw new MalformedObjectNameException(
554 "Invalid character '" + ichar +
555 "' in key part of property");
556 }
557 if (name_chars[in_index - 1] != '=')
558 throw new MalformedObjectNameException(
559 "Unterminated key property part");
560 value_index = in_index; // in_index pointing after '=' char
561 key_length = value_index - key_index - 1; // found end of key
562
563 // standard property case, value part
564 boolean value_pattern = false;
565 if (in_index < len && name_chars[in_index] == '\"') {
566 quoted_value = true;
567 // the case of quoted value part
568 quoted_value_parsing:
569 while ((++in_index < len) &&
570 ((c1 = name_chars[in_index]) != '\"')) {
571 // the case of an escaped character
572 if (c1 == '\\') {
573 if (++in_index == len)
574 throw new MalformedObjectNameException(
575 "Unterminated quoted value");
576 switch (c1 = name_chars[in_index]) {
577 case '\\' :
578 case '\"' :
579 case '?' :
580 case '*' :
581 case 'n' :
582 break; // valid character
583 default :
584 throw new MalformedObjectNameException(
585 "Invalid escape sequence '\\" +
586 c1 + "' in quoted value");
587 }
588 } else if (c1 == '\n') {
589 throw new MalformedObjectNameException(
590 "Newline in quoted value");
591 } else {
592 switch (c1) {
593 case '?' :
594 case '*' :
595 value_pattern = true;
596 break;
597 }
598 }
599 }
600 if (in_index == len)
601 throw new MalformedObjectNameException(
602 "Unterminated quoted value");
603 else value_length = ++in_index - value_index;
604 } else {
605 // the case of standard value part
606 quoted_value = false;
607 while ((in_index < len) && ((c1 = name_chars[in_index]) != ','))
608 switch (c1) {
609 // ',' considered to be the value separator
610 case '*' :
611 case '?' :
612 value_pattern = true;
613 in_index++;
614 break;
615 case '=' :
616 case ':' :
617 case '"' :
618 case '\n' :
619 final String ichar = ((c1=='\n')?"\\n":""+c1);
620 throw new MalformedObjectNameException(
621 "Invalid character '" + c1 +
622 "' in value part of property");
623 default :
624 in_index++;
625 break;
626 }
627 value_length = in_index - value_index;
628 }
629
630 // Parsed property, checks the end of name
631 if (in_index == len - 1) {
632 if (quoted_value)
633 throw new MalformedObjectNameException(
634 "Invalid ending character `" +
635 name_chars[in_index] + "'");
636 else throw new MalformedObjectNameException(
637 "Invalid ending comma");
638 } else in_index++;
639
640 // we got the key and value part, prepare a property for this
641 if (!value_pattern) {
642 prop = new Property(key_index, key_length, value_length);
643 } else {
644 _property_value_pattern = true;
645 prop = new PatternProperty(key_index, key_length, value_length);
646 }
647 key_name = name.substring(key_index, key_index + key_length);
648
649 if (property_index == keys.length) {
650 String[] tmp_string_array = new String[property_index + 10];
651 System.arraycopy(keys, 0, tmp_string_array, 0, property_index);
652 keys = tmp_string_array;
653 }
654 keys[property_index] = key_name;
655
656 addProperty(prop, property_index, keys_map, key_name);
657 property_index++;
658 index = in_index;
659 }
660
661 // computes and set canonical name
662 setCanonicalName(name_chars, canonical_chars, keys,
663 keys_map, cname_index, property_index);
664 }
665
666 /**
667 * Construct an ObjectName from a domain and a Hashtable.
668 *
669 * @param domain Domain of the ObjectName.
670 * @param props Map containing couples <i>key</i> -> <i>value</i>.
671 *
672 * @exception MalformedObjectNameException The <code>domain</code>
673 * contains an illegal character, or one of the keys or values in
674 * <code>table</code> contains an illegal character, or one of the
675 * values in <code>table</code> does not follow the rules for quoting.
676 * @exception NullPointerException One of the parameters is null.
677 */
678 private void construct(String domain, Map<String,String> props)
679 throws MalformedObjectNameException, NullPointerException {
680
681 // The domain cannot be null
682 if (domain == null)
683 throw new NullPointerException("domain cannot be null");
684
685 // The key property list cannot be null
686 if (props == null)
687 throw new NullPointerException("key property list cannot be null");
688
689 // The key property list cannot be empty
690 if (props.isEmpty())
691 throw new MalformedObjectNameException(
692 "key property list cannot be empty");
693
694 // checks domain validity
695 if (!isDomain(domain))
696 throw new MalformedObjectNameException("Invalid domain: " + domain);
697
698 // init canonicalname
699 final StringBuilder sb = new StringBuilder();
700 sb.append(domain).append(':');
701 _domain_length = domain.length();
702
703 // allocates the property array
704 int nb_props = props.size();
705 _kp_array = new Property[nb_props];
706
707 String[] keys = new String[nb_props];
708 final Map<String,Property> keys_map = new HashMap<String,Property>();
709 Property prop;
710 int key_index;
711 int i = 0;
712 for (Map.Entry<String,String> entry : props.entrySet()) {
713 if (sb.length() > 0)
714 sb.append(",");
715 String key = entry.getKey();
716 String value;
717 try {
718 value = entry.getValue();
719 } catch (ClassCastException e) {
720 throw new MalformedObjectNameException(e.getMessage());
721 }
722 key_index = sb.length();
723 checkKey(key);
724 sb.append(key);
725 keys[i] = key;
726 sb.append("=");
727 boolean value_pattern = checkValue(value);
728 sb.append(value);
729 if (!value_pattern) {
730 prop = new Property(key_index,
731 key.length(),
732 value.length());
733 } else {
734 _property_value_pattern = true;
735 prop = new PatternProperty(key_index,
736 key.length(),
737 value.length());
738 }
739 addProperty(prop, i, keys_map, key);
740 i++;
741 }
742
743 // initialize canonical name and data structure
744 int len = sb.length();
745 char[] initial_chars = new char[len];
746 sb.getChars(0, len, initial_chars, 0);
747 char[] canonical_chars = new char[len];
748 System.arraycopy(initial_chars, 0, canonical_chars, 0,
749 _domain_length + 1);
750 setCanonicalName(initial_chars, canonical_chars, keys, keys_map,
751 _domain_length + 1, _kp_array.length);
752 }
753 // Category : Instance construction <==============================
754
755 // Category : Internal utilities ------------------------------>
756
757 /**
758 * Add passed property to the list at the given index
759 * for the passed key name
760 */
761 private void addProperty(Property prop, int index,
762 Map<String,Property> keys_map, String key_name)
763 throws MalformedObjectNameException {
764
765 if (keys_map.containsKey(key_name)) throw new
766 MalformedObjectNameException("key `" +
767 key_name +"' already defined");
768
769 // if no more space for property arrays, have to increase it
770 if (index == _kp_array.length) {
771 Property[] tmp_prop_array = new Property[index + 10];
772 System.arraycopy(_kp_array, 0, tmp_prop_array, 0, index);
773 _kp_array = tmp_prop_array;
774 }
775 _kp_array[index] = prop;
776 keys_map.put(key_name, prop);
777 }
778
779 /**
780 * Sets the canonical name of receiver from input 'specified_chars'
781 * array, by filling 'canonical_chars' array with found 'nb-props'
782 * properties starting at position 'prop_index'.
783 */
784 private void setCanonicalName(char[] specified_chars,
785 char[] canonical_chars,
786 String[] keys, Map<String,Property> keys_map,
787 int prop_index, int nb_props) {
788
789 // Sort the list of found properties
790 if (_kp_array != _Empty_property_array) {
791 String[] tmp_keys = new String[nb_props];
792 Property[] tmp_props = new Property[nb_props];
793
794 System.arraycopy(keys, 0, tmp_keys, 0, nb_props);
795 Arrays.sort(tmp_keys);
796 keys = tmp_keys;
797 System.arraycopy(_kp_array, 0, tmp_props, 0 , nb_props);
798 _kp_array = tmp_props;
799 _ca_array = new Property[nb_props];
800
801 // now assigns _ca_array to the sorted list of keys
802 // (there cannot be two identical keys in an objectname.
803 for (int i = 0; i < nb_props; i++)
804 _ca_array[i] = keys_map.get(keys[i]);
805
806 // now we build the canonical name and set begin indexes of
807 // properties to reflect canonical form
808 int last_index = nb_props - 1;
809 int prop_len;
810 Property prop;
811 for (int i = 0; i <= last_index; i++) {
812 prop = _ca_array[i];
813 // length of prop including '=' char
814 prop_len = prop._key_length + prop._value_length + 1;
815 System.arraycopy(specified_chars, prop._key_index,
816 canonical_chars, prop_index, prop_len);
817 prop.setKeyIndex(prop_index);
818 prop_index += prop_len;
819 if (i != last_index) {
820 canonical_chars[prop_index] = ',';
821 prop_index++;
822 }
823 }
824 }
825
826 // terminate canonicalname with '*' in case of pattern
827 if (_property_list_pattern) {
828 if (_kp_array != _Empty_property_array)
829 canonical_chars[prop_index++] = ',';
830 canonical_chars[prop_index++] = '*';
831 }
832
833 // we now build the canonicalname string
834 _canonicalName = (new String(canonical_chars, 0, prop_index)).intern();
835 }
836
837 /**
838 * Parse a key.
839 * <pre>final int endKey=parseKey(s,startKey);</pre>
840 * <p>key starts at startKey (included), and ends at endKey (excluded).
841 * If (startKey == endKey), then the key is empty.
842 *
843 * @param s The char array of the original string.
844 * @param startKey index at which to begin parsing.
845 * @return The index following the last character of the key.
846 **/
847 private static int parseKey(final char[] s, final int startKey)
848 throws MalformedObjectNameException {
849 int next = startKey;
850 int endKey = startKey;
851 final int len = s.length;
852 while (next < len) {
853 final char k = s[next++];
854 switch (k) {
855 case '*':
856 case '?':
857 case ',':
858 case ':':
859 case '\n':
860 final String ichar = ((k=='\n')?"\\n":""+k);
861 throw new
862 MalformedObjectNameException("Invalid character in key: `"
863 + ichar + "'");
864 case '=':
865 // we got the key.
866 endKey = next-1;
867 break;
868 default:
869 if (next < len) continue;
870 else endKey=next;
871 }
872 break;
873 }
874 return endKey;
875 }
876
877 /**
878 * Parse a value.
879 * <pre>final int endVal=parseValue(s,startVal);</pre>
880 * <p>value starts at startVal (included), and ends at endVal (excluded).
881 * If (startVal == endVal), then the key is empty.
882 *
883 * @param s The char array of the original string.
884 * @param startValue index at which to begin parsing.
885 * @return The first element of the int array indicates the index
886 * following the last character of the value. The second
887 * element of the int array indicates that the value is
888 * a pattern when its value equals 1.
889 **/
890 private static int[] parseValue(final char[] s, final int startValue)
891 throws MalformedObjectNameException {
892
893 boolean value_pattern = false;
894
895 int next = startValue;
896 int endValue = startValue;
897
898 final int len = s.length;
899 final char q=s[startValue];
900
901 if (q == '"') {
902 // quoted value
903 if (++next == len) throw new
904 MalformedObjectNameException("Invalid quote");
905 while (next < len) {
906 char last = s[next];
907 if (last == '\\') {
908 if (++next == len) throw new
909 MalformedObjectNameException(
910 "Invalid unterminated quoted character sequence");
911 last = s[next];
912 switch (last) {
913 case '\\' :
914 case '?' :
915 case '*' :
916 case 'n' :
917 break;
918 case '\"' :
919 // We have an escaped quote. If this escaped
920 // quote is the last character, it does not
921 // qualify as a valid termination quote.
922 //
923 if (next+1 == len) throw new
924 MalformedObjectNameException(
925 "Missing termination quote");
926 break;
927 default:
928 throw new
929 MalformedObjectNameException(
930 "Invalid quoted character sequence '\\" +
931 last + "'");
932 }
933 } else if (last == '\n') {
934 throw new MalformedObjectNameException(
935 "Newline in quoted value");
936 } else if (last == '\"') {
937 next++;
938 break;
939 } else {
940 switch (last) {
941 case '?' :
942 case '*' :
943 value_pattern = true;
944 break;
945 }
946 }
947 next++;
948
949 // Check that last character is a termination quote.
950 // We have already handled the case were the last
951 // character is an escaped quote earlier.
952 //
953 if ((next >= len) && (last != '\"')) throw new
954 MalformedObjectNameException("Missing termination quote");
955 }
956 endValue = next;
957 if (next < len) {
958 if (s[next++] != ',') throw new
959 MalformedObjectNameException("Invalid quote");
960 }
961 } else {
962 // Non quoted value.
963 while (next < len) {
964 final char v=s[next++];
965 switch(v) {
966 case '*':
967 case '?':
968 value_pattern = true;
969 if (next < len) continue;
970 else endValue=next;
971 break;
972 case '=':
973 case ':':
974 case '\n' :
975 final String ichar = ((v=='\n')?"\\n":""+v);
976 throw new
977 MalformedObjectNameException("Invalid character `" +
978 ichar + "' in value");
979 case ',':
980 endValue = next-1;
981 break;
982 default:
983 if (next < len) continue;
984 else endValue=next;
985 }
986 break;
987 }
988 }
989 return new int[] { endValue, value_pattern ? 1 : 0 };
990 }
991
992 /**
993 * Check if the supplied value is a valid value.
994 *
995 * @return true if the value is a pattern, otherwise false.
996 */
997 private static boolean checkValue(String val)
998 throws MalformedObjectNameException {
999
1000 if (val == null) throw new
1001 NullPointerException("Invalid value (null)");
1002
1003 final int len = val.length();
1004 if (len == 0)
1005 return false;
1006
1007 final char[] s = val.toCharArray();
1008 final int[] result = parseValue(s,0);
1009 final int endValue = result[0];
1010 final boolean value_pattern = result[1] == 1;
1011 if (endValue < len) throw new
1012 MalformedObjectNameException("Invalid character in value: `" +
1013 s[endValue] + "'");
1014 return value_pattern;
1015 }
1016
1017 /**
1018 * Check if the supplied key is a valid key.
1019 */
1020 private static void checkKey(String key)
1021 throws MalformedObjectNameException, NullPointerException {
1022
1023 if (key == null) throw new
1024 NullPointerException("Invalid key (null)");
1025
1026 final int len = key.length();
1027 if (len == 0) throw new
1028 MalformedObjectNameException("Invalid key (empty)");
1029 final char[] k=key.toCharArray();
1030 final int endKey = parseKey(k,0);
1031 if (endKey < len) throw new
1032 MalformedObjectNameException("Invalid character in value: `" +
1033 k[endKey] + "'");
1034 }
1035
1036 /*
1037 * Tests whether string s is matched by pattern p.
1038 * Supports "?", "*" each of which may be escaped with "\";
1039 * Not yet supported: internationalization; "\" inside brackets.<P>
1040 * Wildcard matching routine by Karl Heuer. Public Domain.<P>
1041 */
1042 private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
1043 char c;
1044 final int slen = s.length;
1045 final int plen = p.length;
1046
1047 while (pi < plen) { // While still string
1048 c = p[pi++];
1049 if (c == '?') {
1050 if (++si > slen) return false;
1051 } else if (c == '*') { // Wildcard
1052 if (pi >= plen) return true;
1053 do {
1054 if (wildmatch(s,p,si,pi)) return true;
1055 } while (++si < slen);
1056 return false;
1057 } else {
1058 if (si >= slen || c != s[si++]) return false;
1059 }
1060 }
1061 return (si == slen);
1062 }
1063
1064 // Category : Internal utilities <==============================
1065
1066 // Category : Internal accessors ------------------------------>
1067
1068 /**
1069 * Check if domain is a valid domain. Set _domain_pattern if appropriate.
1070 */
1071 private boolean isDomain(String domain) {
1072 if (domain == null) return true;
1073 final char[] d=domain.toCharArray();
1074 final int len = d.length;
1075 int next = 0;
1076 while (next < len) {
1077 final char c = d[next++];
1078 switch (c) {
1079 case ':' :
1080 case '\n' :
1081 return false;
1082 case '*' :
1083 case '?' :
1084 _domain_pattern = true;
1085 break;
1086 }
1087 }
1088 return true;
1089 }
1090
1091 // Category : Internal accessors <==============================
1092
1093 // Category : Serialization ----------------------------------->
1094
1095 /**
1096 * Deserializes an {@link ObjectName} from an {@link ObjectInputStream}.
1097 * @serialData <ul>
1098 * <li>In the current serial form (value of property
1099 * <code>jmx.serial.form</code> differs from
1100 * <code>1.0</code>): the string
1101 * &quot;&lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;&quot;,
1102 * where: <ul>
1103 * <li>&lt;domain&gt; represents the domain part
1104 * of the {@link ObjectName}</li>
1105 * <li>&lt;properties&gt; represents the list of
1106 * properties, as returned by
1107 * {@link #getKeyPropertyListString}
1108 * <li>&lt;wild&gt; is empty if not
1109 * <code>isPropertyPattern</code>, or
1110 * is the character "<code>*</code>" if
1111 * <code>isPropertyPattern</code>
1112 * and &lt;properties&gt; is empty, or
1113 * is "<code>,*</code>" if
1114 * <code>isPropertyPattern</code> and
1115 * &lt;properties&gt; is not empty.
1116 * </li>
1117 * </ul>
1118 * The intent is that this string could be supplied
1119 * to the {@link #ObjectName(String)} constructor to
1120 * produce an equivalent {@link ObjectName}.
1121 * </li>
1122 * <li>In the old serial form (value of property
1123 * <code>jmx.serial.form</code> is
1124 * <code>1.0</code>): &lt;domain&gt; &lt;propertyList&gt;
1125 * &lt;propertyListString&gt; &lt;canonicalName&gt;
1126 * &lt;pattern&gt; &lt;propertyPattern&gt;,
1127 * where: <ul>
1128 * <li>&lt;domain&gt; represents the domain part
1129 * of the {@link ObjectName}</li>
1130 * <li>&lt;propertyList&gt; is the
1131 * {@link Hashtable} that contains all the
1132 * pairs (key,value) for this
1133 * {@link ObjectName}</li>
1134 * <li>&lt;propertyListString&gt; is the
1135 * {@link String} representation of the
1136 * list of properties in any order (not
1137 * mandatorily a canonical representation)
1138 * </li>
1139 * <li>&lt;canonicalName&gt; is the
1140 * {@link String} containing this
1141 * {@link ObjectName}'s canonical name</li>
1142 * <li>&lt;pattern&gt; is a boolean which is
1143 * <code>true</code> if this
1144 * {@link ObjectName} contains a pattern</li>
1145 * <li>&lt;propertyPattern&gt; is a boolean which
1146 * is <code>true</code> if this
1147 * {@link ObjectName} contains a pattern in
1148 * the list of properties</li>
1149 * </ul>
1150 * </li>
1151 * </ul>
1152 */
1153 private void readObject(ObjectInputStream in)
1154 throws IOException, ClassNotFoundException {
1155
1156 String cn;
1157 if (compat) {
1158 // Read an object serialized in the old serial form
1159 //
1160 //in.defaultReadObject();
1161 final ObjectInputStream.GetField fields = in.readFields();
1162 cn = (String)fields.get("domain", "default")+
1163 ":"+
1164 (String)fields.get("propertyListString", "");
1165 } else {
1166 // Read an object serialized in the new serial form
1167 //
1168 in.defaultReadObject();
1169 cn = (String)in.readObject();
1170 }
1171
1172 try {
1173 construct(cn);
1174 } catch (NullPointerException e) {
1175 throw new InvalidObjectException(e.toString());
1176 } catch (MalformedObjectNameException e) {
1177 throw new InvalidObjectException(e.toString());
1178 }
1179 }
1180
1181
1182 /**
1183 * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
1184 * @serialData <ul>
1185 * <li>In the current serial form (value of property
1186 * <code>jmx.serial.form</code> differs from
1187 * <code>1.0</code>): the string
1188 * &quot;&lt;domain&gt;:&lt;properties&gt;&lt;wild&gt;&quot;,
1189 * where: <ul>
1190 * <li>&lt;domain&gt; represents the domain part
1191 * of the {@link ObjectName}</li>
1192 * <li>&lt;properties&gt; represents the list of
1193 * properties, as returned by
1194 * {@link #getKeyPropertyListString}
1195 * <li>&lt;wild&gt; is empty if not
1196 * <code>isPropertyPattern</code>, or
1197 * is the character "<code>*</code>" if
1198 * this <code>isPropertyPattern</code>
1199 * and &lt;properties&gt; is empty, or
1200 * is "<code>,*</code>" if
1201 * <code>isPropertyPattern</code> and
1202 * &lt;properties&gt; is not empty.
1203 * </li>
1204 * </ul>
1205 * The intent is that this string could be supplied
1206 * to the {@link #ObjectName(String)} constructor to
1207 * produce an equivalent {@link ObjectName}.
1208 * </li>
1209 * <li>In the old serial form (value of property
1210 * <code>jmx.serial.form</code> is
1211 * <code>1.0</code>): &lt;domain&gt; &lt;propertyList&gt;
1212 * &lt;propertyListString&gt; &lt;canonicalName&gt;
1213 * &lt;pattern&gt; &lt;propertyPattern&gt;,
1214 * where: <ul>
1215 * <li>&lt;domain&gt; represents the domain part
1216 * of the {@link ObjectName}</li>
1217 * <li>&lt;propertyList&gt; is the
1218 * {@link Hashtable} that contains all the
1219 * pairs (key,value) for this
1220 * {@link ObjectName}</li>
1221 * <li>&lt;propertyListString&gt; is the
1222 * {@link String} representation of the
1223 * list of properties in any order (not
1224 * mandatorily a canonical representation)
1225 * </li>
1226 * <li>&lt;canonicalName&gt; is the
1227 * {@link String} containing this
1228 * {@link ObjectName}'s canonical name</li>
1229 * <li>&lt;pattern&gt; is a boolean which is
1230 * <code>true</code> if this
1231 * {@link ObjectName} contains a pattern</li>
1232 * <li>&lt;propertyPattern&gt; is a boolean which
1233 * is <code>true</code> if this
1234 * {@link ObjectName} contains a pattern in
1235 * the list of properties</li>
1236 * </ul>
1237 * </li>
1238 * </ul>
1239 */
1240 private void writeObject(ObjectOutputStream out)
1241 throws IOException {
1242
1243 if (compat)
1244 {
1245 // Serializes this instance in the old serial form
1246 // Read CR 6441274 before making any changes to this code
1247 ObjectOutputStream.PutField fields = out.putFields();
1248 fields.put("domain", _canonicalName.substring(0, _domain_length));
1249 fields.put("propertyList", getKeyPropertyList());
1250 fields.put("propertyListString", getKeyPropertyListString());
1251 fields.put("canonicalName", _canonicalName);
1252 fields.put("pattern", (_domain_pattern || _property_list_pattern));
1253 fields.put("propertyPattern", _property_list_pattern);
1254 out.writeFields();
1255 }
1256 else
1257 {
1258 // Serializes this instance in the new serial form
1259 //
1260 out.defaultWriteObject();
1261 out.writeObject(getSerializedNameString());
1262 }
1263 }
1264
1265 // Category : Serialization <===================================
1266
1267 // Private methods <========================================
1268
1269 // Public methods ---------------------------------------->
1270
1271 // Category : ObjectName Construction ------------------------------>
1272
1273 /**
1274 * <p>Return an instance of ObjectName that can be used anywhere
1275 * an object obtained with {@link #ObjectName(String) new
1276 * ObjectName(name)} can be used. The returned object may be of
1277 * a subclass of ObjectName. Calling this method twice with the
1278 * same parameters may return the same object or two equal but
1279 * not identical objects.</p>
1280 *
1281 * @param name A string representation of the object name.
1282 *
1283 * @return an ObjectName corresponding to the given String.
1284 *
1285 * @exception MalformedObjectNameException The string passed as a
1286 * parameter does not have the right format.
1287 * @exception NullPointerException The <code>name</code> parameter
1288 * is null.
1289 *
1290 */
1291 public static ObjectName getInstance(String name)
1292 throws MalformedObjectNameException, NullPointerException {
1293 return new ObjectName(name);
1294 }
1295
1296 /**
1297 * <p>Return an instance of ObjectName that can be used anywhere
1298 * an object obtained with {@link #ObjectName(String, String,
1299 * String) new ObjectName(domain, key, value)} can be used. The
1300 * returned object may be of a subclass of ObjectName. Calling
1301 * this method twice with the same parameters may return the same
1302 * object or two equal but not identical objects.</p>
1303 *
1304 * @param domain The domain part of the object name.
1305 * @param key The attribute in the key property of the object name.
1306 * @param value The value in the key property of the object name.
1307 *
1308 * @return an ObjectName corresponding to the given domain,
1309 * key, and value.
1310 *
1311 * @exception MalformedObjectNameException The
1312 * <code>domain</code>, <code>key</code>, or <code>value</code>
1313 * contains an illegal character, or <code>value</code> does not
1314 * follow the rules for quoting.
1315 * @exception NullPointerException One of the parameters is null.
1316 *
1317 */
1318 public static ObjectName getInstance(String domain, String key,
1319 String value)
1320 throws MalformedObjectNameException, NullPointerException {
1321 return new ObjectName(domain, key, value);
1322 }
1323
1324 /**
1325 * <p>Return an instance of ObjectName that can be used anywhere
1326 * an object obtained with {@link #ObjectName(String, Hashtable)
1327 * new ObjectName(domain, table)} can be used. The returned
1328 * object may be of a subclass of ObjectName. Calling this method
1329 * twice with the same parameters may return the same object or
1330 * two equal but not identical objects.</p>
1331 *
1332 * @param domain The domain part of the object name.
1333 * @param table A hash table containing one or more key
1334 * properties. The key of each entry in the table is the key of a
1335 * key property in the object name. The associated value in the
1336 * table is the associated value in the object name.
1337 *
1338 * @return an ObjectName corresponding to the given domain and
1339 * key mappings.
1340 *
1341 * @exception MalformedObjectNameException The <code>domain</code>
1342 * contains an illegal character, or one of the keys or values in
1343 * <code>table</code> contains an illegal character, or one of the
1344 * values in <code>table</code> does not follow the rules for
1345 * quoting.
1346 * @exception NullPointerException One of the parameters is null.
1347 *
1348 */
1349 public static ObjectName getInstance(String domain,
1350 Hashtable<String,String> table)
1351 throws MalformedObjectNameException, NullPointerException {
1352 return new ObjectName(domain, table);
1353 }
1354
1355 /**
1356 * <p>Return an instance of ObjectName that can be used anywhere
1357 * the given object can be used. The returned object may be of a
1358 * subclass of ObjectName. If <code>name</code> is of a subclass
1359 * of ObjectName, it is not guaranteed that the returned object
1360 * will be of the same class.</p>
1361 *
1362 * <p>The returned value may or may not be identical to
1363 * <code>name</code>. Calling this method twice with the same
1364 * parameters may return the same object or two equal but not
1365 * identical objects.</p>
1366 *
1367 * <p>Since ObjectName is immutable, it is not usually useful to
1368 * make a copy of an ObjectName. The principal use of this method
1369 * is to guard against a malicious caller who might pass an
1370 * instance of a subclass with surprising behavior to sensitive
1371 * code. Such code can call this method to obtain an ObjectName
1372 * that is known not to have surprising behavior.</p>
1373 *
1374 * @param name an instance of the ObjectName class or of a subclass
1375 *
1376 * @return an instance of ObjectName or a subclass that is known to
1377 * have the same semantics. If <code>name</code> respects the
1378 * semantics of ObjectName, then the returned object is equal
1379 * (though not necessarily identical) to <code>name</code>.
1380 *
1381 * @exception NullPointerException The <code>name</code> is null.
1382 *
1383 */
1384 public static ObjectName getInstance(ObjectName name)
1385 throws NullPointerException {
1386 if (name.getClass().equals(ObjectName.class))
1387 return name;
1388 try {
1389 return new ObjectName(name.getSerializedNameString());
1390 } catch (MalformedObjectNameException e) {
1391 throw new IllegalArgumentException("Unexpected: " + e);
1392 // can't happen
1393 }
1394 }
1395
1396 /**
1397 * Construct an object name from the given string.
1398 *
1399 * @param name A string representation of the object name.
1400 *
1401 * @exception MalformedObjectNameException The string passed as a
1402 * parameter does not have the right format.
1403 * @exception NullPointerException The <code>name</code> parameter
1404 * is null.
1405 */
1406 public ObjectName(String name)
1407 throws MalformedObjectNameException, NullPointerException {
1408 construct(name);
1409 }
1410
1411 /**
1412 * Construct an object name with exactly one key property.
1413 *
1414 * @param domain The domain part of the object name.
1415 * @param key The attribute in the key property of the object name.
1416 * @param value The value in the key property of the object name.
1417 *
1418 * @exception MalformedObjectNameException The
1419 * <code>domain</code>, <code>key</code>, or <code>value</code>
1420 * contains an illegal character, or <code>value</code> does not
1421 * follow the rules for quoting.
1422 * @exception NullPointerException One of the parameters is null.
1423 */
1424 public ObjectName(String domain, String key, String value)
1425 throws MalformedObjectNameException, NullPointerException {
1426 // If key or value are null a NullPointerException
1427 // will be thrown by the put method in Hashtable.
1428 //
1429 Map<String,String> table = Collections.singletonMap(key, value);
1430 construct(domain, table);
1431 }
1432
1433 /**
1434 * Construct an object name with several key properties from a Hashtable.
1435 *
1436 * @param domain The domain part of the object name.
1437 * @param table A hash table containing one or more key
1438 * properties. The key of each entry in the table is the key of a
1439 * key property in the object name. The associated value in the
1440 * table is the associated value in the object name.
1441 *
1442 * @exception MalformedObjectNameException The <code>domain</code>
1443 * contains an illegal character, or one of the keys or values in
1444 * <code>table</code> contains an illegal character, or one of the
1445 * values in <code>table</code> does not follow the rules for
1446 * quoting.
1447 * @exception NullPointerException One of the parameters is null.
1448 */
1449 public ObjectName(String domain, Hashtable<String,String> table)
1450 throws MalformedObjectNameException, NullPointerException {
1451 construct(domain, table);
1452 /* The exception for when a key or value in the table is not a
1453 String is now ClassCastException rather than
1454 MalformedObjectNameException. This was not previously
1455 specified. */
1456 }
1457
1458 // Category : ObjectName Construction <==============================
1459
1460
1461 // Category : Getter methods ------------------------------>
1462
1463 /**
1464 * Checks whether the object name is a pattern.
1465 * <p>
1466 * An object name is a pattern if its domain contains a
1467 * wildcard or if the object name is a property pattern.
1468 *
1469 * @return True if the name is a pattern, otherwise false.
1470 */
1471 public boolean isPattern() {
1472 return (_domain_pattern ||
1473 _property_list_pattern ||
1474 _property_value_pattern);
1475 }
1476
1477 /**
1478 * Checks whether the object name is a pattern on the domain part.
1479 *
1480 * @return True if the name is a domain pattern, otherwise false.
1481 *
1482 */
1483 public boolean isDomainPattern() {
1484 return _domain_pattern;
1485 }
1486
1487 /**
1488 * Checks whether the object name is a pattern on the key properties.
1489 * <p>
1490 * An object name is a pattern on the key properties if it is a
1491 * pattern on the key property list (e.g. "d:k=v,*") or on the
1492 * property values (e.g. "d:k=*") or on both (e.g. "d:k=*,*").
1493 *
1494 * @return True if the name is a property pattern, otherwise false.
1495 */
1496 public boolean isPropertyPattern() {
1497 return _property_list_pattern || _property_value_pattern;
1498 }
1499
1500 /**
1501 * Checks whether the object name is a pattern on the key property list.
1502 * <p>
1503 * For example, "d:k=v,*" and "d:k=*,*" are key property list patterns
1504 * whereas "d:k=*" is not.
1505 *
1506 * @return True if the name is a property list pattern, otherwise false.
1507 *
1508 * @since 1.6
1509 */
1510 public boolean isPropertyListPattern() {
1511 return _property_list_pattern;
1512 }
1513
1514 /**
1515 * Checks whether the object name is a pattern on the value part
1516 * of at least one of the key properties.
1517 * <p>
1518 * For example, "d:k=*" and "d:k=*,*" are property value patterns
1519 * whereas "d:k=v,*" is not.
1520 *
1521 * @return True if the name is a property value pattern, otherwise false.
1522 *
1523 * @since 1.6
1524 */
1525 public boolean isPropertyValuePattern() {
1526 return _property_value_pattern;
1527 }
1528
1529 /**
1530 * Checks whether the value associated with a key in a key
1531 * property is a pattern.
1532 *
1533 * @param property The property whose value is to be checked.
1534 *
1535 * @return True if the value associated with the given key property
1536 * is a pattern, otherwise false.
1537 *
1538 * @exception NullPointerException If <code>property</code> is null.
1539 * @exception IllegalArgumentException If <code>property</code> is not
1540 * a valid key property for this ObjectName.
1541 *
1542 * @since 1.6
1543 */
1544 public boolean isPropertyValuePattern(String property)
1545 throws NullPointerException, IllegalArgumentException {
1546 if (property == null)
1547 throw new NullPointerException("key property can't be null");
1548 for (int i = 0; i < _ca_array.length; i++) {
1549 Property prop = _ca_array[i];
1550 String key = prop.getKeyString(_canonicalName);
1551 if (key.equals(property))
1552 return (prop instanceof PatternProperty);
1553 }
1554 throw new IllegalArgumentException("key property not found");
1555 }
1556
1557 /**
1558 * <p>Returns the canonical form of the name; that is, a string
1559 * representation where the properties are sorted in lexical
1560 * order.</p>
1561 *
1562 * <p>More precisely, the canonical form of the name is a String
1563 * consisting of the <em>domain part</em>, a colon
1564 * (<code>:</code>), the <em>canonical key property list</em>, and
1565 * a <em>pattern indication</em>.</p>
1566 *
1567 * <p>The <em>canonical key property list</em> is the same string
1568 * as described for {@link #getCanonicalKeyPropertyListString()}.</p>
1569 *
1570 * <p>The <em>pattern indication</em> is:
1571 * <ul>
1572 * <li>empty for an ObjectName
1573 * that is not a property list pattern;
1574 * <li>an asterisk for an ObjectName
1575 * that is a property list pattern with no keys; or
1576 * <li>a comma and an
1577 * asterisk (<code>,*</code>) for an ObjectName that is a property
1578 * list pattern with at least one key.
1579 * </ul></p>
1580 *
1581 * @return The canonical form of the name.
1582 */
1583 public String getCanonicalName() {
1584 return _canonicalName;
1585 }
1586
1587 /**
1588 * Returns the domain part.
1589 *
1590 * @return The domain.
1591 */
1592 public String getDomain() {
1593 return _canonicalName.substring(0, _domain_length);
1594 }
1595
1596 /**
1597 * Obtains the value associated with a key in a key property.
1598 *
1599 * @param property The property whose value is to be obtained.
1600 *
1601 * @return The value of the property, or null if there is no such
1602 * property in this ObjectName.
1603 *
1604 * @exception NullPointerException If <code>property</code> is null.
1605 */
1606 public String getKeyProperty(String property) throws NullPointerException {
1607 return _getKeyPropertyList().get(property);
1608 }
1609
1610 /**
1611 * <p>Returns the key properties as a Map. The returned
1612 * value is a Map in which each key is a key in the
1613 * ObjectName's key property list and each value is the associated
1614 * value.</p>
1615 *
1616 * <p>The returned value must not be modified.</p>
1617 *
1618 * @return The table of key properties.
1619 */
1620 private Map<String,String> _getKeyPropertyList() {
1621 synchronized (this) {
1622 if (_propertyList == null) {
1623 // build (lazy eval) the property list from the canonical
1624 // properties array
1625 _propertyList = new HashMap<String,String>();
1626 int len = _ca_array.length;
1627 Property prop;
1628 for (int i = len - 1; i >= 0; i--) {
1629 prop = _ca_array[i];
1630 _propertyList.put(prop.getKeyString(_canonicalName),
1631 prop.getValueString(_canonicalName));
1632 }
1633 }
1634 }
1635 return _propertyList;
1636 }
1637
1638 /**
1639 * <p>Returns the key properties as a Hashtable. The returned
1640 * value is a Hashtable in which each key is a key in the
1641 * ObjectName's key property list and each value is the associated
1642 * value.</p>
1643 *
1644 * <p>The returned value may be unmodifiable. If it is
1645 * modifiable, changing it has no effect on this ObjectName.</p>
1646 *
1647 * @return The table of key properties.
1648 */
1649 // CR 6441274 depends on the modification property defined above
1650 public Hashtable<String,String> getKeyPropertyList() {
1651 return new Hashtable<String,String>(_getKeyPropertyList());
1652 }
1653
1654 /**
1655 * <p>Returns a string representation of the list of key
1656 * properties specified at creation time. If this ObjectName was
1657 * constructed with the constructor {@link #ObjectName(String)},
1658 * the key properties in the returned String will be in the same
1659 * order as in the argument to the constructor.</p>
1660 *
1661 * @return The key property list string. This string is
1662 * independent of whether the ObjectName is a pattern.
1663 */
1664 public String getKeyPropertyListString() {
1665 // BEWARE : we rebuild the propertyliststring at each call !!
1666 if (_kp_array.length == 0) return "";
1667
1668 // the size of the string is the canonical one minus domain
1669 // part and pattern part
1670 final int total_size = _canonicalName.length() - _domain_length - 1
1671 - (_property_list_pattern?2:0);
1672
1673 final char[] dest_chars = new char[total_size];
1674 final char[] value = _canonicalName.toCharArray();
1675 writeKeyPropertyListString(value,dest_chars,0);
1676 return new String(dest_chars);
1677 }
1678
1679 /**
1680 * <p>Returns the serialized string of the ObjectName.
1681 * properties specified at creation time. If this ObjectName was
1682 * constructed with the constructor {@link #ObjectName(String)},
1683 * the key properties in the returned String will be in the same
1684 * order as in the argument to the constructor.</p>
1685 *
1686 * @return The key property list string. This string is
1687 * independent of whether the ObjectName is a pattern.
1688 */
1689 private String getSerializedNameString() {
1690
1691 // the size of the string is the canonical one
1692 final int total_size = _canonicalName.length();
1693 final char[] dest_chars = new char[total_size];
1694 final char[] value = _canonicalName.toCharArray();
1695 final int offset = _domain_length+1;
1696
1697 // copy "domain:" into dest_chars
1698 //
1699 System.arraycopy(value, 0, dest_chars, 0, offset);
1700
1701 // Add property list string
1702 final int end = writeKeyPropertyListString(value,dest_chars,offset);
1703
1704 // Add ",*" if necessary
1705 if (_property_list_pattern) {
1706 if (end == offset) {
1707 // Property list string is empty.
1708 dest_chars[end] = '*';
1709 } else {
1710 // Property list string is not empty.
1711 dest_chars[end] = ',';
1712 dest_chars[end+1] = '*';
1713 }
1714 }
1715
1716 return new String(dest_chars);
1717 }
1718
1719 /**
1720 * <p>Write a string representation of the list of key
1721 * properties specified at creation time in the given array, starting
1722 * at the specified offset. If this ObjectName was
1723 * constructed with the constructor {@link #ObjectName(String)},
1724 * the key properties in the returned String will be in the same
1725 * order as in the argument to the constructor.</p>
1726 *
1727 * @return offset + #of chars written
1728 */
1729 private int writeKeyPropertyListString(char[] canonicalChars,
1730 char[] data, int offset) {
1731 if (_kp_array.length == 0) return offset;
1732
1733 final char[] dest_chars = data;
1734 final char[] value = _canonicalName.toCharArray();
1735
1736 int index = offset;
1737 final int len = _kp_array.length;
1738 final int last = len - 1;
1739 for (int i = 0; i < len; i++) {
1740 final Property prop = _kp_array[i];
1741 final int prop_len = prop._key_length + prop._value_length + 1;
1742 System.arraycopy(value, prop._key_index, dest_chars, index,
1743 prop_len);
1744 index += prop_len;
1745 if (i < last ) dest_chars[index++] = ',';
1746 }
1747 return index;
1748 }
1749
1750
1751
1752 /**
1753 * Returns a string representation of the list of key properties,
1754 * in which the key properties are sorted in lexical order. This
1755 * is used in lexicographic comparisons performed in order to
1756 * select MBeans based on their key property list. Lexical order
1757 * is the order implied by {@link String#compareTo(String)
1758 * String.compareTo(String)}.
1759 *
1760 * @return The canonical key property list string. This string is
1761 * independent of whether the ObjectName is a pattern.
1762 */
1763 public String getCanonicalKeyPropertyListString() {
1764 if (_ca_array.length == 0) return "";
1765
1766 int len = _canonicalName.length();
1767 if (_property_list_pattern) len -= 2;
1768 return _canonicalName.substring(_domain_length +1, len);
1769 }
1770 // Category : Getter methods <===================================
1771
1772 // Category : Utilities ---------------------------------------->
1773
1774 /**
1775 * <p>Returns a string representation of the object name. The
1776 * format of this string is not specified, but users can expect
1777 * that two ObjectNames return the same string if and only if they
1778 * are equal.</p>
1779 *
1780 * @return a string representation of this object name.
1781 */
1782 public String toString() {
1783 return getSerializedNameString();
1784 }
1785
1786 /**
1787 * Compares the current object name with another object name. Two
1788 * ObjectName instances are equal if and only if their canonical
1789 * forms are equal. The canonical form is the string described
1790 * for {@link #getCanonicalName()}.
1791 *
1792 * @param object The object name that the current object name is to be
1793 * compared with.
1794 *
1795 * @return True if <code>object</code> is an ObjectName whose
1796 * canonical form is equal to that of this ObjectName.
1797 */
1798 public boolean equals(Object object) {
1799
1800 // same object case
1801 if (this == object) return true;
1802
1803 // object is not an object name case
1804 if (!(object instanceof ObjectName)) return false;
1805
1806 // equality when canonical names are the same
1807 // (because usage of intern())
1808 ObjectName on = (ObjectName) object;
1809 String on_string = on._canonicalName;
1810 if (_canonicalName == on_string) return true;
1811
1812 // Because we are sharing canonical form between object names,
1813 // we have finished the comparison at this stage ==> unequal
1814 return false;
1815 }
1816
1817 /**
1818 * Returns a hash code for this object name.
1819 *
1820 */
1821 public int hashCode() {
1822 return _canonicalName.hashCode();
1823 }
1824
1825 /**
1826 * <p>Returns a quoted form of the given String, suitable for
1827 * inclusion in an ObjectName. The returned value can be used as
1828 * the value associated with a key in an ObjectName. The String
1829 * <code>s</code> may contain any character. Appropriate quoting
1830 * ensures that the returned value is legal in an ObjectName.</p>
1831 *
1832 * <p>The returned value consists of a quote ('"'), a sequence of
1833 * characters corresponding to the characters of <code>s</code>,
1834 * and another quote. Characters in <code>s</code> appear
1835 * unchanged within the returned value except:</p>
1836 *
1837 * <ul>
1838 * <li>A quote ('"') is replaced by a backslash (\) followed by a quote.</li>
1839 * <li>An asterisk ('*') is replaced by a backslash (\) followed by an
1840 * asterisk.</li>
1841 * <li>A question mark ('?') is replaced by a backslash (\) followed by
1842 * a question mark.</li>
1843 * <li>A backslash ('\') is replaced by two backslashes.</li>
1844 * <li>A newline character (the character '\n' in Java) is replaced
1845 * by a backslash followed by the character '\n'.</li>
1846 * </ul>
1847 *
1848 * @param s the String to be quoted.
1849 *
1850 * @return the quoted String.
1851 *
1852 * @exception NullPointerException if <code>s</code> is null.
1853 *
1854 */
1855 public static String quote(String s)
1856 throws NullPointerException {
1857 final StringBuilder buf = new StringBuilder("\"");
1858 final int len = s.length();
1859 for (int i = 0; i < len; i++) {
1860 char c = s.charAt(i);
1861 switch (c) {
1862 case '\n':
1863 c = 'n';
1864 buf.append('\\');
1865 break;
1866 case '\\':
1867 case '\"':
1868 case '*':
1869 case '?':
1870 buf.append('\\');
1871 break;
1872 }
1873 buf.append(c);
1874 }
1875 buf.append('"');
1876 return buf.toString();
1877 }
1878
1879 /**
1880 * <p>Returns an unquoted form of the given String. If
1881 * <code>q</code> is a String returned by {@link #quote quote(s)},
1882 * then <code>unquote(q).equals(s)</code>. If there is no String
1883 * <code>s</code> for which <code>quote(s).equals(q)</code>, then
1884 * unquote(q) throws an IllegalArgumentException.</p>
1885 *
1886 * <p>These rules imply that there is a one-to-one mapping between
1887 * quoted and unquoted forms.</p>
1888 *
1889 * @param q the String to be unquoted.
1890 *
1891 * @return the unquoted String.
1892 *
1893 * @exception IllegalArgumentException if <code>q</code> could not
1894 * have been returned by the {@link #quote} method, for instance
1895 * if it does not begin and end with a quote (").
1896 *
1897 * @exception NullPointerException if <code>q</code> is null.
1898 *
1899 */
1900 public static String unquote(String q)
1901 throws IllegalArgumentException, NullPointerException {
1902 final StringBuilder buf = new StringBuilder();
1903 final int len = q.length();
1904 if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"')
1905 throw new IllegalArgumentException("Argument not quoted");
1906 for (int i = 1; i < len - 1; i++) {
1907 char c = q.charAt(i);
1908 if (c == '\\') {
1909 if (i == len - 2)
1910 throw new IllegalArgumentException("Trailing backslash");
1911 c = q.charAt(++i);
1912 switch (c) {
1913 case 'n':
1914 c = '\n';
1915 break;
1916 case '\\':
1917 case '\"':
1918 case '*':
1919 case '?':
1920 break;
1921 default:
1922 throw new IllegalArgumentException(
1923 "Bad character '" + c + "' after backslash");
1924 }
1925 } else {
1926 switch (c) {
1927 case '*' :
1928 case '?' :
1929 case '\"':
1930 case '\n':
1931 throw new IllegalArgumentException(
1932 "Invalid unescaped character '" + c +
1933 "' in the string to unquote");
1934 }
1935 }
1936 buf.append(c);
1937 }
1938 return buf.toString();
1939 }
1940
1941 /**
1942 * Defines the wildcard "*:*" ObjectName.
1943 *
1944 * @since 1.6
1945 */
1946 public static final ObjectName WILDCARD;
1947 static {
1948 try {
1949 WILDCARD = new ObjectName("*:*");
1950 } catch (MalformedObjectNameException e) {
1951 throw new Error("Can't initialize wildcard name", e);
1952 }
1953 }
1954
1955 // Category : Utilities <===================================
1956
1957 // Category : QueryExp Interface ---------------------------------------->
1958
1959 /**
1960 * <p>Test whether this ObjectName, which may be a pattern,
1961 * matches another ObjectName. If <code>name</code> is a pattern,
1962 * the result is false. If this ObjectName is a pattern, the
1963 * result is true if and only if <code>name</code> matches the
1964 * pattern. If neither this ObjectName nor <code>name</code> is
1965 * a pattern, the result is true if and only if the two
1966 * ObjectNames are equal as described for the {@link
1967 * #equals(Object)} method.</p>
1968 *
1969 * @param name The name of the MBean to compare to.
1970 *
1971 * @return True if <code>name</code> matches this ObjectName.
1972 *
1973 * @exception NullPointerException if <code>name</code> is null.
1974 *
1975 */
1976 public boolean apply(ObjectName name) throws NullPointerException {
1977
1978 if (name == null) throw new NullPointerException();
1979
1980 if (name._domain_pattern ||
1981 name._property_list_pattern ||
1982 name._property_value_pattern)
1983 return false;
1984
1985 // No pattern
1986 if (!_domain_pattern &&
1987 !_property_list_pattern &&
1988 !_property_value_pattern)
1989 return _canonicalName.equals(name._canonicalName);
1990
1991 return matchDomains(name) && matchKeys(name);
1992 }
1993
1994 private final boolean matchDomains(ObjectName name) {
1995 if (_domain_pattern) {
1996 // wildmatch domains
1997 final char[] dom_pattern = getDomain().toCharArray();
1998 final char[] dom_string = name.getDomain().toCharArray();
1999 return wildmatch(dom_string,dom_pattern,0,0);
2000 }
2001 return getDomain().equals(name.getDomain());
2002 }
2003
2004 private final boolean matchKeys(ObjectName name) {
2005 // If key property value pattern but not key property list
2006 // pattern, then the number of key properties must be equal
2007 //
2008 if (_property_value_pattern &&
2009 !_property_list_pattern &&
2010 (name._ca_array.length != _ca_array.length))
2011 return false;
2012
2013 // If key property value pattern or key property list pattern,
2014 // then every property inside pattern should exist in name
2015 //
2016 if (_property_value_pattern || _property_list_pattern) {
2017 final Map<String,String> nameProps = name._getKeyPropertyList();
2018 final Property[] props = _ca_array;
2019 final String cn = _canonicalName;
2020 for (int i = props.length - 1; i >= 0 ; i--) {
2021 // Find value in given object name for key at current
2022 // index in receiver
2023 //
2024 final Property p = props[i];
2025 final String k = p.getKeyString(cn);
2026 final String v = nameProps.get(k);
2027 // Did we find a value for this key ?
2028 //
2029 if (v == null) return false;
2030 // If this property is ok (same key, same value), go to next
2031 //
2032 if (_property_value_pattern && (p instanceof PatternProperty)) {
2033 // wildmatch key property values
2034 final char[] val_pattern =
2035 p.getValueString(cn).toCharArray();
2036 final char[] val_string = v.toCharArray();
2037 if (wildmatch(val_string,val_pattern,0,0))
2038 continue;
2039 else
2040 return false;
2041 }
2042 if (v.equals(p.getValueString(cn))) continue;
2043 return false;
2044 }
2045 return true;
2046 }
2047
2048 // If no pattern, then canonical names must be equal
2049 //
2050 final String p1 = name.getCanonicalKeyPropertyListString();
2051 final String p2 = getCanonicalKeyPropertyListString();
2052 return (p1.equals(p2));
2053 }
2054
2055 /* Method inherited from QueryExp, no implementation needed here
2056 because ObjectName is not relative to an MBeanServer and does
2057 not contain a subquery.
2058 */
2059 public void setMBeanServer(MBeanServer mbs) { }
2060
2061 // Category : QueryExp Interface <=========================
2062
2063 // Category : Comparable Interface ---------------------------------------->
2064
2065 /**
2066 * <p>Compares two ObjectName instances. The ordering relation between
2067 * ObjectNames is not completely specified but is intended to be such
2068 * that a sorted list of ObjectNames will appear in an order that is
2069 * convenient for a person to read.</p>
2070 *
2071 * <p>In particular, if the two ObjectName instances have different
2072 * domains then their order is the lexicographical order of the domains.
2073 * The ordering of the key property list remains unspecified.</p>
2074 *
2075 * <p>For example, the ObjectName instances below:</p>
2076 * <ul>
2077 * <li>Shapes:type=Square,name=3</li>
2078 * <li>Colors:type=Red,name=2</li>
2079 * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2080 * <li>Colors:type=Red,name=1</li>
2081 * <li>Shapes:type=Square,name=1</li>
2082 * <li>Colors:type=Blue,name=1</li>
2083 * <li>Shapes:type=Square,name=2</li>
2084 * <li>JMImplementation:type=MBeanServerDelegate</li>
2085 * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2086 * </ul>
2087 * <p>could be ordered as follows:</p>
2088 * <ul>
2089 * <li>Colors:type=Blue,name=1</li>
2090 * <li>Colors:type=Red,name=1</li>
2091 * <li>Colors:type=Red,name=2</li>
2092 * <li>JMImplementation:type=MBeanServerDelegate</li>
2093 * <li>Shapes:type=Square,name=1</li>
2094 * <li>Shapes:type=Square,name=2</li>
2095 * <li>Shapes:type=Square,name=3</li>
2096 * <li>Shapes:type=Triangle,side=scalene,name=1</li>
2097 * <li>Shapes:type=Triangle,side=isosceles,name=2</li>
2098 * </ul>
2099 *
2100 * @param name the ObjectName to be compared.
2101 *
2102 * @return a negative integer, zero, or a positive integer as this
2103 * ObjectName is less than, equal to, or greater than the
2104 * specified ObjectName.
2105 *
2106 * @since 1.6
2107 */
2108 public int compareTo(ObjectName name) {
2109 // (1) Compare domains
2110 //
2111 int domainValue = this.getDomain().compareTo(name.getDomain());
2112 if (domainValue != 0)
2113 return domainValue;
2114
2115 // (2) Compare "type=" keys
2116 //
2117 // Within a given domain, all names with missing or empty "type="
2118 // come before all names with non-empty type.
2119 //
2120 // When both types are missing or empty, canonical-name ordering
2121 // applies which is a total order.
2122 //
2123 String thisTypeKey = this.getKeyProperty("type");
2124 String anotherTypeKey = name.getKeyProperty("type");
2125 if (thisTypeKey == null)
2126 thisTypeKey = "";
2127 if (anotherTypeKey == null)
2128 anotherTypeKey = "";
2129 int typeKeyValue = thisTypeKey.compareTo(anotherTypeKey);
2130 if (typeKeyValue != 0)
2131 return typeKeyValue;
2132
2133 // (3) Compare canonical names
2134 //
2135 return this.getCanonicalName().compareTo(name.getCanonicalName());
2136 }
2137
2138 // Category : Comparable Interface <=========================
2139
2140 // Public methods <========================================
2141
2142}