1 /*
2 * Portions Copyright 2000-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 /*
26 * @author IBM Corp.
27 *
28 * Copyright IBM Corp. 1999-2000. All rights reserved.
29 */
30
31 package javax.management.modelmbean;
32
33 import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER;
34 import static com.sun.jmx.mbeanserver.Util.cast;
35 import com.sun.jmx.mbeanserver.GetPropertyAction;
36
37 import java.io.IOException;
38 import java.io.ObjectInputStream;
39 import java.io.ObjectOutputStream;
40 import java.io.ObjectStreamField;
41
42 import java.lang.reflect.Constructor;
43
44 import java.security.AccessController;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.SortedMap;
50 import java.util.StringTokenizer;
51 import java.util.TreeMap;
52 import java.util.logging.Level;
53
54 import javax.management.Descriptor;
55 import javax.management.ImmutableDescriptor;
56 import javax.management.MBeanException;
57 import javax.management.RuntimeOperationsException;
58
59 import sun.reflect.misc.ReflectUtil;
60
61 /**
62 * This class represents the metadata set for a ModelMBean element. A
63 * descriptor is part of the ModelMBeanInfo,
64 * ModelMBeanNotificationInfo, ModelMBeanAttributeInfo,
65 * ModelMBeanConstructorInfo, and ModelMBeanParameterInfo.
66 * <P>
67 * A descriptor consists of a collection of fields. Each field is in
68 * fieldname=fieldvalue format. Field names are not case sensitive,
69 * case will be preserved on field values.
70 * <P>
71 * All field names and values are not predefined. New fields can be
72 * defined and added by any program. Some fields have been predefined
73 * for consistency of implementation and support by the
74 * ModelMBeanInfo, ModelMBeanAttributeInfo, ModelMBeanConstructorInfo,
75 * ModelMBeanNotificationInfo, ModelMBeanOperationInfo and ModelMBean
76 * classes.
77 *
78 * <p>The <b>serialVersionUID</b> of this class is <code>-6292969195866300415L</code>.
79 *
80 * @since 1.5
81 */
82 @SuppressWarnings("serial") // serialVersionUID not constant
83 public class DescriptorSupport
84 implements javax.management.Descriptor
85 {
86
87 // Serialization compatibility stuff:
88 // Two serial forms are supported in this class. The selected form depends
89 // on system property "jmx.serial.form":
90 // - "1.0" for JMX 1.0
91 // - any other value for JMX 1.1 and higher
92 //
93 // Serial version for old serial form
94 private static final long oldSerialVersionUID = 8071560848919417985L;
95 //
96 // Serial version for new serial form
97 private static final long newSerialVersionUID = -6292969195866300415L;
98 //
99 // Serializable fields in old serial form
100 private static final ObjectStreamField[] oldSerialPersistentFields =
101 {
102 new ObjectStreamField("descriptor", HashMap.class),
103 new ObjectStreamField("currClass", String.class)
104 };
105 //
106 // Serializable fields in new serial form
107 private static final ObjectStreamField[] newSerialPersistentFields =
108 {
109 new ObjectStreamField("descriptor", HashMap.class)
110 };
111 //
112 // Actual serial version and serial form
113 private static final long serialVersionUID;
114 /**
115 * @serialField descriptor HashMap The collection of fields representing this descriptor
116 */
117 private static final ObjectStreamField[] serialPersistentFields;
118 private static final String serialForm;
119 static {
120 String form = null;
121 boolean compat = false;
122 try {
123 GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
124 form = AccessController.doPrivileged(act);
125 compat = "1.0".equals(form); // form may be null
126 } catch (Exception e) {
127 // OK: No compat with 1.0
128 }
129 serialForm = form;
130 if (compat) {
131 serialPersistentFields = oldSerialPersistentFields;
132 serialVersionUID = oldSerialVersionUID;
133 } else {
134 serialPersistentFields = newSerialPersistentFields;
135 serialVersionUID = newSerialVersionUID;
136 }
137 }
138 //
139 // END Serialization compatibility stuff
140
141 /* Spec says that field names are case-insensitive, but that case
142 is preserved. This means that we need to be able to map from a
143 name that may differ in case to the actual name that is used in
144 the HashMap. Thus, descriptorMap is a TreeMap with a Comparator
145 that ignores case.
146
147 Previous versions of this class had a field called "descriptor"
148 of type HashMap where the keys were directly Strings. This is
149 hard to reconcile with the required semantics, so we fabricate
150 that field virtually during serialization and deserialization
151 but keep the real information in descriptorMap.
152 */
153 private transient SortedMap<String, Object> descriptorMap;
154
155 private static final String currClass = "DescriptorSupport";
156
157
158 /**
159 * Descriptor default constructor.
160 * Default initial descriptor size is 20. It will grow as needed.<br>
161 * Note that the created empty descriptor is not a valid descriptor
162 * (the method {@link #isValid isValid} returns <CODE>false</CODE>)
163 */
164 public DescriptorSupport() {
165 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
166 MODELMBEAN_LOGGER.logp(Level.FINEST,
167 DescriptorSupport.class.getName(),
168 "DescriptorSupport()" , "Constructor");
169 }
170 init(null);
171 }
172
173 /**
174 * Descriptor constructor. Takes as parameter the initial
175 * capacity of the Map that stores the descriptor fields.
176 * Capacity will grow as needed.<br> Note that the created empty
177 * descriptor is not a valid descriptor (the method {@link
178 * #isValid isValid} returns <CODE>false</CODE>).
179 *
180 * @param initNumFields The initial capacity of the Map that
181 * stores the descriptor fields.
182 *
183 * @exception RuntimeOperationsException for illegal value for
184 * initNumFields (<= 0)
185 * @exception MBeanException Wraps a distributed communication Exception.
186 */
187 public DescriptorSupport(int initNumFields)
188 throws MBeanException, RuntimeOperationsException {
189 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
190 MODELMBEAN_LOGGER.logp(Level.FINEST,
191 DescriptorSupport.class.getName(),
192 "Descriptor(initNumFields = " + initNumFields + ")",
193 "Constructor");
194 }
195 if (initNumFields <= 0) {
196 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
197 MODELMBEAN_LOGGER.logp(Level.FINEST,
198 DescriptorSupport.class.getName(),
199 "Descriptor(initNumFields)",
200 "Illegal arguments: initNumFields <= 0");
201 }
202 final String msg =
203 "Descriptor field limit invalid: " + initNumFields;
204 final RuntimeException iae = new IllegalArgumentException(msg);
205 throw new RuntimeOperationsException(iae, msg);
206 }
207 init(null);
208 }
209
210 /**
211 * Descriptor constructor taking a Descriptor as parameter.
212 * Creates a new descriptor initialized to the values of the
213 * descriptor passed in parameter.
214 *
215 * @param inDescr the descriptor to be used to initialize the
216 * constructed descriptor. If it is null or contains no descriptor
217 * fields, an empty Descriptor will be created.
218 */
219 public DescriptorSupport(DescriptorSupport inDescr) {
220 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
221 MODELMBEAN_LOGGER.logp(Level.FINEST,
222 DescriptorSupport.class.getName(),
223 "Descriptor(Descriptor)", "Constructor");
224 }
225 if (inDescr == null)
226 init(null);
227 else
228 init(inDescr.descriptorMap);
229 }
230
231
232 /**
233 * <p>Descriptor constructor taking an XML String.</p>
234 *
235 * <p>The format of the XML string is not defined, but an
236 * implementation must ensure that the string returned by
237 * {@link #toXMLString() toXMLString()} on an existing
238 * descriptor can be used to instantiate an equivalent
239 * descriptor using this constructor.</p>
240 *
241 * <p>In this implementation, all field values will be created
242 * as Strings. If the field values are not Strings, the
243 * programmer will have to reset or convert these fields
244 * correctly.</p>
245 *
246 * @param inStr An XML-formatted string used to populate this
247 * Descriptor. The format is not defined, but any
248 * implementation must ensure that the string returned by
249 * method {@link #toXMLString toXMLString} on an existing
250 * descriptor can be used to instantiate an equivalent
251 * descriptor when instantiated using this constructor.
252 *
253 * @exception RuntimeOperationsException If the String inStr
254 * passed in parameter is null
255 * @exception XMLParseException XML parsing problem while parsing
256 * the input String
257 * @exception MBeanException Wraps a distributed communication Exception.
258 */
259 /* At some stage we should rewrite this code to be cleverer. Using
260 a StringTokenizer as we do means, first, that we accept a lot of
261 bogus strings without noticing they are bogus, and second, that we
262 split the string being parsed at characters like > even if they
263 occur in the middle of a field value. */
264 public DescriptorSupport(String inStr)
265 throws MBeanException, RuntimeOperationsException,
266 XMLParseException {
267 /* parse an XML-formatted string and populate internal
268 * structure with it */
269 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
270 MODELMBEAN_LOGGER.logp(Level.FINEST,
271 DescriptorSupport.class.getName(),
272 "Descriptor(String = '" + inStr + "')", "Constructor");
273 }
274 if (inStr == null) {
275 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
276 MODELMBEAN_LOGGER.logp(Level.FINEST,
277 DescriptorSupport.class.getName(),
278 "Descriptor(String = null)", "Illegal arguments");
279 }
280 final String msg = "String in parameter is null";
281 final RuntimeException iae = new IllegalArgumentException(msg);
282 throw new RuntimeOperationsException(iae, msg);
283 }
284
285 final String lowerInStr = inStr.toLowerCase();
286 if (!lowerInStr.startsWith("<descriptor>")
287 || !lowerInStr.endsWith("</descriptor>")) {
288 throw new XMLParseException("No <descriptor>, </descriptor> pair");
289 }
290
291 // parse xmlstring into structures
292 init(null);
293 // create dummy descriptor: should have same size
294 // as number of fields in xmlstring
295 // loop through structures and put them in descriptor
296
297 StringTokenizer st = new StringTokenizer(inStr, "<> \t\n\r\f");
298
299 boolean inFld = false;
300 boolean inDesc = false;
301 String fieldName = null;
302 String fieldValue = null;
303
304
305 while (st.hasMoreTokens()) { // loop through tokens
306 String tok = st.nextToken();
307
308 if (tok.equalsIgnoreCase("FIELD")) {
309 inFld = true;
310 } else if (tok.equalsIgnoreCase("/FIELD")) {
311 if ((fieldName != null) && (fieldValue != null)) {
312 fieldName =
313 fieldName.substring(fieldName.indexOf('"') + 1,
314 fieldName.lastIndexOf('"'));
315 final Object fieldValueObject =
316 parseQuotedFieldValue(fieldValue);
317 setField(fieldName, fieldValueObject);
318 }
319 fieldName = null;
320 fieldValue = null;
321 inFld = false;
322 } else if (tok.equalsIgnoreCase("DESCRIPTOR")) {
323 inDesc = true;
324 } else if (tok.equalsIgnoreCase("/DESCRIPTOR")) {
325 inDesc = false;
326 fieldName = null;
327 fieldValue = null;
328 inFld = false;
329 } else if (inFld && inDesc) {
330 // want kw=value, eg, name="myname" value="myvalue"
331 int eq_separator = tok.indexOf("=");
332 if (eq_separator > 0) {
333 String kwPart = tok.substring(0,eq_separator);
334 String valPart = tok.substring(eq_separator+1);
335 if (kwPart.equalsIgnoreCase("NAME"))
336 fieldName = valPart;
337 else if (kwPart.equalsIgnoreCase("VALUE"))
338 fieldValue = valPart;
339 else { // xml parse exception
340 final String msg =
341 "Expected `name' or `value', got `" + tok + "'";
342 throw new XMLParseException(msg);
343 }
344 } else { // xml parse exception
345 final String msg =
346 "Expected `keyword=value', got `" + tok + "'";
347 throw new XMLParseException(msg);
348 }
349 }
350 } // while tokens
351 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
352 MODELMBEAN_LOGGER.logp(Level.FINEST,
353 DescriptorSupport.class.getName(),
354 "Descriptor(XMLString)", "Exit");
355 }
356 }
357
358 /**
359 * Constructor taking field names and field values. Neither array
360 * can be null.
361 *
362 * @param fieldNames String array of field names. No elements of
363 * this array can be null.
364 * @param fieldValues Object array of the corresponding field
365 * values. Elements of the array can be null. The
366 * <code>fieldValue</code> must be valid for the
367 * <code>fieldName</code> (as defined in method {@link #isValid
368 * isValid})
369 *
370 * <p>Note: array sizes of parameters should match. If both arrays
371 * are empty, then an empty descriptor is created.</p>
372 *
373 * @exception RuntimeOperationsException for illegal value for
374 * field Names or field Values. The array lengths must be equal.
375 * If the descriptor construction fails for any reason, this
376 * exception will be thrown.
377 *
378 */
379 public DescriptorSupport(String[] fieldNames, Object[] fieldValues)
380 throws RuntimeOperationsException {
381 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
382 MODELMBEAN_LOGGER.logp(Level.FINEST,
383 DescriptorSupport.class.getName(),
384 "Descriptor(fieldNames,fieldObjects)", "Constructor");
385 }
386
387 if ((fieldNames == null) || (fieldValues == null) ||
388 (fieldNames.length != fieldValues.length)) {
389 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
390 MODELMBEAN_LOGGER.logp(Level.FINEST,
391 DescriptorSupport.class.getName(),
392 "Descriptor(fieldNames,fieldObjects)",
393 "Illegal arguments");
394 }
395
396 final String msg =
397 "Null or invalid fieldNames or fieldValues";
398 final RuntimeException iae = new IllegalArgumentException(msg);
399 throw new RuntimeOperationsException(iae, msg);
400 }
401
402 /* populate internal structure with fields */
403 init(null);
404 for (int i=0; i < fieldNames.length; i++) {
405 // setField will throw an exception if a fieldName is be null.
406 // the fieldName and fieldValue will be validated in setField.
407 setField(fieldNames[i], fieldValues[i]);
408 }
409 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
410 MODELMBEAN_LOGGER.logp(Level.FINEST,
411 DescriptorSupport.class.getName(),
412 "Descriptor(fieldNames,fieldObjects)", "Exit");
413 }
414 }
415
416 /**
417 * Constructor taking fields in the <i>fieldName=fieldValue</i>
418 * format.
419 *
420 * @param fields String array with each element containing a
421 * field name and value. If this array is null or empty, then the
422 * default constructor will be executed. Null strings or empty
423 * strings will be ignored.
424 *
425 * <p>All field values should be Strings. If the field values are
426 * not Strings, the programmer will have to reset or convert these
427 * fields correctly.
428 *
429 * <p>Note: Each string should be of the form
430 * <i>fieldName=fieldValue</i>. The field name
431 * ends at the first {@code =} character; for example if the String
432 * is {@code a=b=c} then the field name is {@code a} and its value
433 * is {@code b=c}.
434 *
435 * @exception RuntimeOperationsException for illegal value for
436 * field Names or field Values. The field must contain an
437 * "=". "=fieldValue", "fieldName", and "fieldValue" are illegal.
438 * FieldName cannot be null. "fieldName=" will cause the value to
439 * be null. If the descriptor construction fails for any reason,
440 * this exception will be thrown.
441 *
442 */
443 public DescriptorSupport(String... fields)
444 {
445 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
446 MODELMBEAN_LOGGER.logp(Level.FINEST,
447 DescriptorSupport.class.getName(),
448 "Descriptor(String... fields)", "Constructor");
449 }
450 init(null);
451 if (( fields == null ) || ( fields.length == 0))
452 return;
453
454 init(null);
455
456 for (int i=0; i < fields.length; i++) {
457 if ((fields[i] == null) || (fields[i].equals(""))) {
458 continue;
459 }
460 int eq_separator = fields[i].indexOf("=");
461 if (eq_separator < 0) {
462 // illegal if no = or is first character
463 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
464 MODELMBEAN_LOGGER.logp(Level.FINEST,
465 DescriptorSupport.class.getName(),
466 "Descriptor(String... fields)",
467 "Illegal arguments: field does not have " +
468 "'=' as a name and value separator");
469 }
470 final String msg = "Field in invalid format: no equals sign";
471 final RuntimeException iae = new IllegalArgumentException(msg);
472 throw new RuntimeOperationsException(iae, msg);
473 }
474
475 String fieldName = fields[i].substring(0,eq_separator);
476 String fieldValue = null;
477 if (eq_separator < fields[i].length()) {
478 // = is not in last character
479 fieldValue = fields[i].substring(eq_separator+1);
480 }
481
482 if (fieldName.equals("")) {
483 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
484 MODELMBEAN_LOGGER.logp(Level.FINEST,
485 DescriptorSupport.class.getName(),
486 "Descriptor(String... fields)",
487 "Illegal arguments: fieldName is empty");
488 }
489
490 final String msg = "Field in invalid format: no fieldName";
491 final RuntimeException iae = new IllegalArgumentException(msg);
492 throw new RuntimeOperationsException(iae, msg);
493 }
494
495 setField(fieldName,fieldValue);
496 }
497 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
498 MODELMBEAN_LOGGER.logp(Level.FINEST,
499 DescriptorSupport.class.getName(),
500 "Descriptor(String... fields)", "Exit");
501 }
502 }
503
504 private void init(Map<String, ?> initMap) {
505 descriptorMap =
506 new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
507 if (initMap != null)
508 descriptorMap.putAll(initMap);
509 }
510
511 // Implementation of the Descriptor interface
512
513
514 public synchronized Object getFieldValue(String fieldName)
515 throws RuntimeOperationsException {
516
517 if ((fieldName == null) || (fieldName.equals(""))) {
518 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
519 MODELMBEAN_LOGGER.logp(Level.FINEST,
520 DescriptorSupport.class.getName(),
521 "getFieldValue(String fieldName)",
522 "Illegal arguments: null field name");
523 }
524 final String msg = "Fieldname requested is null";
525 final RuntimeException iae = new IllegalArgumentException(msg);
526 throw new RuntimeOperationsException(iae, msg);
527 }
528 Object retValue = descriptorMap.get(fieldName);
529 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
530 MODELMBEAN_LOGGER.logp(Level.FINEST,
531 DescriptorSupport.class.getName(),
532 "getFieldValue(String fieldName = " + fieldName + ")",
533 "Returns '" + retValue + "'");
534 }
535 return(retValue);
536 }
537
538 public synchronized void setField(String fieldName, Object fieldValue)
539 throws RuntimeOperationsException {
540
541 // field name cannot be null or empty
542 if ((fieldName == null) || (fieldName.equals(""))) {
543 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
544 MODELMBEAN_LOGGER.logp(Level.FINEST,
545 DescriptorSupport.class.getName(),
546 "setField(fieldName,fieldValue)",
547 "Illegal arguments: null or empty field name");
548 }
549
550 final String msg = "Field name to be set is null or empty";
551 final RuntimeException iae = new IllegalArgumentException(msg);
552 throw new RuntimeOperationsException(iae, msg);
553 }
554
555 if (!validateField(fieldName, fieldValue)) {
556 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
557 MODELMBEAN_LOGGER.logp(Level.FINEST,
558 DescriptorSupport.class.getName(),
559 "setField(fieldName,fieldValue)",
560 "Illegal arguments");
561 }
562
563 final String msg =
564 "Field value invalid: " + fieldName + "=" + fieldValue;
565 final RuntimeException iae = new IllegalArgumentException(msg);
566 throw new RuntimeOperationsException(iae, msg);
567 }
568
569 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
570 MODELMBEAN_LOGGER.logp(Level.FINEST,
571 DescriptorSupport.class.getName(),
572 "setField(fieldName,fieldValue)", "Entry: setting '"
573 + fieldName + "' to '" + fieldValue + "'");
574 }
575
576 // Since we do not remove any existing entry with this name,
577 // the field will preserve whatever case it had, ignoring
578 // any difference there might be in fieldName.
579 descriptorMap.put(fieldName, fieldValue);
580 }
581
582 public synchronized String[] getFields() {
583 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
584 MODELMBEAN_LOGGER.logp(Level.FINEST,
585 DescriptorSupport.class.getName(),
586 "getFields()", "Entry");
587 }
588 int numberOfEntries = descriptorMap.size();
589
590 String[] responseFields = new String[numberOfEntries];
591 Set returnedSet = descriptorMap.entrySet();
592
593 int i = 0;
594
595 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
596 MODELMBEAN_LOGGER.logp(Level.FINEST,
597 DescriptorSupport.class.getName(),
598 "getFields()", "Returning " + numberOfEntries + " fields");
599 }
600 for (Iterator iter = returnedSet.iterator(); iter.hasNext(); i++) {
601 Map.Entry currElement = (Map.Entry) iter.next();
602
603 if (currElement == null) {
604 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
605 MODELMBEAN_LOGGER.logp(Level.FINEST,
606 DescriptorSupport.class.getName(),
607 "getFields()", "Element is null");
608 }
609 } else {
610 Object currValue = currElement.getValue();
611 if (currValue == null) {
612 responseFields[i] = currElement.getKey() + "=";
613 } else {
614 if (currValue instanceof java.lang.String) {
615 responseFields[i] =
616 currElement.getKey() + "=" + currValue.toString();
617 } else {
618 responseFields[i] =
619 currElement.getKey() + "=(" +
620 currValue.toString() + ")";
621 }
622 }
623 }
624 }
625
626 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
627 MODELMBEAN_LOGGER.logp(Level.FINEST,
628 DescriptorSupport.class.getName(),
629 "getFields()", "Exit");
630 }
631
632 return responseFields;
633 }
634
635 public synchronized String[] getFieldNames() {
636 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
637 MODELMBEAN_LOGGER.logp(Level.FINEST,
638 DescriptorSupport.class.getName(),
639 "getFieldNames()", "Entry");
640 }
641 int numberOfEntries = descriptorMap.size();
642
643 String[] responseFields = new String[numberOfEntries];
644 Set returnedSet = descriptorMap.entrySet();
645
646 int i = 0;
647
648 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
649 MODELMBEAN_LOGGER.logp(Level.FINEST,
650 DescriptorSupport.class.getName(),
651 "getFieldNames()",
652 "Returning " + numberOfEntries + " fields");
653 }
654
655 for (Iterator iter = returnedSet.iterator(); iter.hasNext(); i++) {
656 Map.Entry currElement = (Map.Entry) iter.next();
657
658 if (( currElement == null ) || (currElement.getKey() == null)) {
659 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
660 MODELMBEAN_LOGGER.logp(Level.FINEST,
661 DescriptorSupport.class.getName(),
662 "getFieldNames()", "Field is null");
663 }
664 } else {
665 responseFields[i] = currElement.getKey().toString();
666 }
667 }
668
669 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
670 MODELMBEAN_LOGGER.logp(Level.FINEST,
671 DescriptorSupport.class.getName(),
672 "getFieldNames()", "Exit");
673 }
674
675 return responseFields;
676 }
677
678
679 public synchronized Object[] getFieldValues(String... fieldNames) {
680 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
681 MODELMBEAN_LOGGER.logp(Level.FINEST,
682 DescriptorSupport.class.getName(),
683 "getFieldValues(String... fieldNames)", "Entry");
684 }
685 // if fieldNames == null return all values
686 // if fieldNames is String[0] return no values
687
688 final int numberOfEntries =
689 (fieldNames == null) ? descriptorMap.size() : fieldNames.length;
690 final Object[] responseFields = new Object[numberOfEntries];
691
692 int i = 0;
693
694 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
695 MODELMBEAN_LOGGER.logp(Level.FINEST,
696 DescriptorSupport.class.getName(),
697 "getFieldValues(String... fieldNames)",
698 "Returning " + numberOfEntries + " fields");
699 }
700
701 if (fieldNames == null) {
702 for (Iterator iter = descriptorMap.values().iterator();
703 iter.hasNext(); i++)
704 responseFields[i] = iter.next();
705 } else {
706 for (i=0; i < fieldNames.length; i++) {
707 if ((fieldNames[i] == null) || (fieldNames[i].equals(""))) {
708 responseFields[i] = null;
709 } else {
710 responseFields[i] = getFieldValue(fieldNames[i]);
711 }
712 }
713 }
714
715 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
716 MODELMBEAN_LOGGER.logp(Level.FINEST,
717 DescriptorSupport.class.getName(),
718 "getFieldValues(String... fieldNames)", "Exit");
719 }
720
721 return responseFields;
722 }
723
724 public synchronized void setFields(String[] fieldNames,
725 Object[] fieldValues)
726 throws RuntimeOperationsException {
727
728 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
729 MODELMBEAN_LOGGER.logp(Level.FINEST,
730 DescriptorSupport.class.getName(),
731 "setFields(fieldNames,fieldValues)", "Entry");
732 }
733
734 if ((fieldNames == null) || (fieldValues == null) ||
735 (fieldNames.length != fieldValues.length)) {
736 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
737 MODELMBEAN_LOGGER.logp(Level.FINEST,
738 DescriptorSupport.class.getName(),
739 "setFields(fieldNames,fieldValues)",
740 "Illegal arguments");
741 }
742
743 final String msg = "fieldNames and fieldValues are null or invalid";
744 final RuntimeException iae = new IllegalArgumentException(msg);
745 throw new RuntimeOperationsException(iae, msg);
746 }
747
748 for (int i=0; i < fieldNames.length; i++) {
749 if (( fieldNames[i] == null) || (fieldNames[i].equals(""))) {
750 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
751 MODELMBEAN_LOGGER.logp(Level.FINEST,
752 DescriptorSupport.class.getName(),
753 "setFields(fieldNames,fieldValues)",
754 "Null field name encountered at element " + i);
755 }
756 final String msg = "fieldNames is null or invalid";
757 final RuntimeException iae = new IllegalArgumentException(msg);
758 throw new RuntimeOperationsException(iae, msg);
759 }
760 setField(fieldNames[i], fieldValues[i]);
761 }
762 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
763 MODELMBEAN_LOGGER.logp(Level.FINEST,
764 DescriptorSupport.class.getName(),
765 "setFields(fieldNames,fieldValues)", "Exit");
766 }
767 }
768
769 /**
770 * Returns a new Descriptor which is a duplicate of the Descriptor.
771 *
772 * @exception RuntimeOperationsException for illegal value for
773 * field Names or field Values. If the descriptor construction
774 * fails for any reason, this exception will be thrown.
775 */
776
777 public synchronized Object clone() throws RuntimeOperationsException {
778 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
779 MODELMBEAN_LOGGER.logp(Level.FINEST,
780 DescriptorSupport.class.getName(),
781 "clone()", "Entry");
782 }
783 return(new DescriptorSupport(this));
784 }
785
786 public synchronized void removeField(String fieldName) {
787 if ((fieldName == null) || (fieldName.equals(""))) {
788 return;
789 }
790
791 descriptorMap.remove(fieldName);
792 }
793
794 /**
795 * Compares this descriptor to the given object. The objects are equal if
796 * the given object is also a Descriptor, and if the two Descriptors have
797 * the same field names (possibly differing in case) and the same
798 * associated values. The respective values for a field in the two
799 * Descriptors are equal if the following conditions hold:</p>
800 *
801 * <ul>
802 * <li>If one value is null then the other must be too.</li>
803 * <li>If one value is a primitive array then the other must be a primitive
804 * array of the same type with the same elements.</li>
805 * <li>If one value is an object array then the other must be too and
806 * {@link java.util.Arrays#deepEquals(Object[],Object[]) Arrays.deepEquals}
807 * must return true.</li>
808 * <li>Otherwise {@link Object#equals(Object)} must return true.</li>
809 * </ul>
810 *
811 * @param o the object to compare with.
812 *
813 * @return {@code true} if the objects are the same; {@code false}
814 * otherwise.
815 *
816 */
817 // XXXX TODO: This is not very efficient!
818 // Note: this Javadoc is copied from javax.management.Descriptor
819 // due to 6369229.
820 public synchronized boolean equals(Object o) {
821 if (o == this)
822 return true;
823
824 return new ImmutableDescriptor(descriptorMap).equals(o);
825 }
826
827 /**
828 * <p>Returns the hash code value for this descriptor. The hash
829 * code is computed as the sum of the hash codes for each field in
830 * the descriptor. The hash code of a field with name {@code n}
831 * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}.
832 * Here {@code h} is the hash code of {@code v}, computed as
833 * follows:</p>
834 *
835 * <ul>
836 * <li>If {@code v} is null then {@code h} is 0.</li>
837 * <li>If {@code v} is a primitive array then {@code h} is computed using
838 * the appropriate overloading of {@code java.util.Arrays.hashCode}.</li>
839 * <li>If {@code v} is an object array then {@code h} is computed using
840 * {@link java.util.Arrays#deepHashCode(Object[]) Arrays.deepHashCode}.</li>
841 * <li>Otherwise {@code h} is {@code v.hashCode()}.</li>
842 * </ul>
843 *
844 * @return A hash code value for this object.
845 *
846 */
847 // XXXX TODO: This is not very efficient!
848 // Note: this Javadoc is copied from javax.management.Descriptor
849 // due to 6369229.
850 public synchronized int hashCode() {
851 return new ImmutableDescriptor(descriptorMap).hashCode();
852 }
853
854 /**
855 * Returns true if all of the fields have legal values given their
856 * names.
857 * <P>
858 * This implementation does not support interoperating with a directory
859 * or lookup service. Thus, conforming to the specification, no checking is
860 * done on the <i>"export"</i> field.
861 * <P>
862 * Otherwise this implementation returns false if:
863 * <P>
864 * <UL>
865 * <LI> name and descriptorType fieldNames are not defined, or
866 * null, or empty, or not String
867 * <LI> class, role, getMethod, setMethod fieldNames, if defined,
868 * are null or not String
869 * <LI> persistPeriod, currencyTimeLimit, lastUpdatedTimeStamp,
870 * lastReturnedTimeStamp if defined, are null, or not a Numeric
871 * String or not a Numeric Value >= -1
872 * <LI> log fieldName, if defined, is null, or not a Boolean or
873 * not a String with value "t", "f", "true", "false". These String
874 * values must not be case sensitive.
875 * <LI> visibility fieldName, if defined, is null, or not a
876 * Numeric String or a not Numeric Value >= 1 and <= 4
877 * <LI> severity fieldName, if defined, is null, or not a Numeric
878 * String or not a Numeric Value >= 0 and <= 6<br>
879 * <LI> persistPolicy fieldName, if defined, is null, or not one of
880 * the following strings:<br>
881 * "OnUpdate", "OnTimer", "NoMoreOftenThan", "OnUnregister", "Always",
882 * "Never". These String values must not be case sensitive.<br>
883 * </UL>
884 *
885 * @exception RuntimeOperationsException If the validity checking
886 * fails for any reason, this exception will be thrown.
887 */
888
889 public synchronized boolean isValid() throws RuntimeOperationsException {
890 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
891 MODELMBEAN_LOGGER.logp(Level.FINEST,
892 DescriptorSupport.class.getName(),
893 "isValid()", "Entry");
894 }
895 // verify that the descriptor is valid, by iterating over each field...
896
897 Set returnedSet = descriptorMap.entrySet();
898
899 if (returnedSet == null) { // null descriptor, not valid
900 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
901 MODELMBEAN_LOGGER.logp(Level.FINEST,
902 DescriptorSupport.class.getName(),
903 "isValid()", "Returns false (null set)");
904 }
905 return false;
906 }
907 // must have a name and descriptor type field
908 String thisName = (String)(this.getFieldValue("name"));
909 String thisDescType = (String)(getFieldValue("descriptorType"));
910
911 if ((thisName == null) || (thisDescType == null) ||
912 (thisName.equals("")) || (thisDescType.equals(""))) {
913 return false;
914 }
915
916 // According to the descriptor type we validate the fields contained
917
918 for (Iterator iter = returnedSet.iterator(); iter.hasNext();) {
919 Map.Entry currElement = (Map.Entry) iter.next();
920
921 if (currElement != null) {
922 if (currElement.getValue() != null) {
923 // validate the field valued...
924 if (validateField((currElement.getKey()).toString(),
925 (currElement.getValue()).toString())) {
926 continue;
927 } else {
928 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
929 MODELMBEAN_LOGGER.logp(Level.FINEST,
930 DescriptorSupport.class.getName(),
931 "isValid()",
932 "Field " + currElement.getKey() + "=" +
933 currElement.getValue() + " is not valid");
934 }
935 return false;
936 }
937 }
938 }
939 }
940
941 // fell through, all fields OK
942 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
943 MODELMBEAN_LOGGER.logp(Level.FINEST,
944 DescriptorSupport.class.getName(),
945 "isValid()", "Returns true");
946 }
947 return true;
948 }
949
950
951 // worker routine for isValid()
952 // name is not null
953 // descriptorType is not null
954 // getMethod and setMethod are not null
955 // persistPeriod is numeric
956 // currencyTimeLimit is numeric
957 // lastUpdatedTimeStamp is numeric
958 // visibility is 1-4
959 // severity is 0-6
960 // log is T or F
961 // role is not null
962 // class is not null
963 // lastReturnedTimeStamp is numeric
964
965
966 private boolean validateField(String fldName, Object fldValue) {
967 if ((fldName == null) || (fldName.equals("")))
968 return false;
969 String SfldValue = "";
970 boolean isAString = false;
971 if ((fldValue != null) && (fldValue instanceof java.lang.String)) {
972 SfldValue = (String) fldValue;
973 isAString = true;
974 }
975
976 boolean nameOrDescriptorType =
977 (fldName.equalsIgnoreCase("Name") ||
978 fldName.equalsIgnoreCase("DescriptorType"));
979 if (nameOrDescriptorType ||
980 fldName.equalsIgnoreCase("SetMethod") ||
981 fldName.equalsIgnoreCase("GetMethod") ||
982 fldName.equalsIgnoreCase("Role") ||
983 fldName.equalsIgnoreCase("Class")) {
984 if (fldValue == null || !isAString)
985 return false;
986 if (nameOrDescriptorType && SfldValue.equals(""))
987 return false;
988 return true;
989 } else if (fldName.equalsIgnoreCase("visibility")) {
990 long v;
991 if ((fldValue != null) && (isAString)) {
992 v = toNumeric(SfldValue);
993 } else if (fldValue instanceof java.lang.Integer) {
994 v = ((Integer)fldValue).intValue();
995 } else return false;
996
997 if (v >= 1 && v <= 4)
998 return true;
999 else
1000 return false;
1001 } else if (fldName.equalsIgnoreCase("severity")) {
1002
1003 long v;
1004 if ((fldValue != null) && (isAString)) {
1005 v = toNumeric(SfldValue);
1006 } else if (fldValue instanceof java.lang.Integer) {
1007 v = ((Integer)fldValue).intValue();
1008 } else return false;
1009
1010 return (v >= 0 && v <= 6);
1011 } else if (fldName.equalsIgnoreCase("PersistPolicy")) {
1012 return (((fldValue != null) && (isAString)) &&
1013 ( SfldValue.equalsIgnoreCase("OnUpdate") ||
1014 SfldValue.equalsIgnoreCase("OnTimer") ||
1015 SfldValue.equalsIgnoreCase("NoMoreOftenThan") ||
1016 SfldValue.equalsIgnoreCase("Always") ||
1017 SfldValue.equalsIgnoreCase("Never") ||
1018 SfldValue.equalsIgnoreCase("OnUnregister")));
1019 } else if (fldName.equalsIgnoreCase("PersistPeriod") ||
1020 fldName.equalsIgnoreCase("CurrencyTimeLimit") ||
1021 fldName.equalsIgnoreCase("LastUpdatedTimeStamp") ||
1022 fldName.equalsIgnoreCase("LastReturnedTimeStamp")) {
1023
1024 long v;
1025 if ((fldValue != null) && (isAString)) {
1026 v = toNumeric(SfldValue);
1027 } else if (fldValue instanceof java.lang.Number) {
1028 v = ((Number)fldValue).longValue();
1029 } else return false;
1030
1031 return (v >= -1);
1032 } else if (fldName.equalsIgnoreCase("log")) {
1033 return ((fldValue instanceof java.lang.Boolean) ||
1034 (isAString &&
1035 (SfldValue.equalsIgnoreCase("T") ||
1036 SfldValue.equalsIgnoreCase("true") ||
1037 SfldValue.equalsIgnoreCase("F") ||
1038 SfldValue.equalsIgnoreCase("false") )));
1039 }
1040
1041 // default to true, it is a field we aren't validating (user etc.)
1042 return true;
1043 }
1044
1045
1046
1047 /**
1048 * <p>Returns an XML String representing the descriptor.</p>
1049 *
1050 * <p>The format is not defined, but an implementation must
1051 * ensure that the string returned by this method can be
1052 * used to build an equivalent descriptor when instantiated
1053 * using the constructor {@link #DescriptorSupport(String)
1054 * DescriptorSupport(String inStr)}.</p>
1055 *
1056 * <p>Fields which are not String objects will have toString()
1057 * called on them to create the value. The value will be
1058 * enclosed in parentheses. It is not guaranteed that you can
1059 * reconstruct these objects unless they have been
1060 * specifically set up to support toString() in a meaningful
1061 * format and have a matching constructor that accepts a
1062 * String in the same format.</p>
1063 *
1064 * <p>If the descriptor is empty the following String is
1065 * returned: <Descriptor></Descriptor></p>
1066 *
1067 * @return the XML string.
1068 *
1069 * @exception RuntimeOperationsException for illegal value for
1070 * field Names or field Values. If the XML formatted string
1071 * construction fails for any reason, this exception will be
1072 * thrown.
1073 */
1074 public synchronized String toXMLString() {
1075 final StringBuilder buf = new StringBuilder("<Descriptor>");
1076 Set returnedSet = descriptorMap.entrySet();
1077 for (Iterator iter = returnedSet.iterator(); iter.hasNext(); ) {
1078 final Map.Entry currElement = (Map.Entry) iter.next();
1079 final String name = currElement.getKey().toString();
1080 Object value = currElement.getValue();
1081 String valueString = null;
1082 /* Set valueString to non-null if and only if this is a string that
1083 cannot be confused with the encoding of an object. If it
1084 could be so confused (surrounded by parentheses) then we
1085 call makeFieldValue as for any non-String object and end
1086 up with an encoding like "(java.lang.String/(thing))". */
1087 if (value instanceof String) {
1088 final String svalue = (String) value;
1089 if (!svalue.startsWith("(") || !svalue.endsWith(")"))
1090 valueString = quote(svalue);
1091 }
1092 if (valueString == null)
1093 valueString = makeFieldValue(value);
1094 buf.append("<field name=\"").append(name).append("\" value=\"")
1095 .append(valueString).append("\"></field>");
1096 }
1097 buf.append("</Descriptor>");
1098 return buf.toString();
1099 }
1100
1101 private static final String[] entities = {
1102 "  ",
1103 "\""",
1104 "<<",
1105 ">>",
1106 "&&",
1107 "\r ",
1108 "\t	",
1109 "\n ",
1110 "\f",
1111 };
1112 private static final Map<String,Character> entityToCharMap =
1113 new HashMap<String,Character>();
1114 private static final String[] charToEntityMap;
1115
1116 static {
1117 char maxChar = 0;
1118 for (int i = 0; i < entities.length; i++) {
1119 final char c = entities[i].charAt(0);
1120 if (c > maxChar)
1121 maxChar = c;
1122 }
1123 charToEntityMap = new String[maxChar + 1];
1124 for (int i = 0; i < entities.length; i++) {
1125 final char c = entities[i].charAt(0);
1126 final String entity = entities[i].substring(1);
1127 charToEntityMap[c] = entity;
1128 entityToCharMap.put(entity, c);
1129 }
1130 }
1131
1132 private static boolean isMagic(char c) {
1133 return (c < charToEntityMap.length && charToEntityMap[c] != null);
1134 }
1135
1136 /*
1137 * Quote the string so that it will be acceptable to the (String)
1138 * constructor. Since the parsing code in that constructor is fairly
1139 * stupid, we're obliged to quote apparently innocuous characters like
1140 * space, <, and >. In a future version, we should rewrite the parser
1141 * and only quote " plus either \ or & (depending on the quote syntax).
1142 */
1143 private static String quote(String s) {
1144 boolean found = false;
1145 for (int i = 0; i < s.length(); i++) {
1146 if (isMagic(s.charAt(i))) {
1147 found = true;
1148 break;
1149 }
1150 }
1151 if (!found)
1152 return s;
1153 final StringBuilder buf = new StringBuilder();
1154 for (int i = 0; i < s.length(); i++) {
1155 char c = s.charAt(i);
1156 if (isMagic(c))
1157 buf.append(charToEntityMap[c]);
1158 else
1159 buf.append(c);
1160 }
1161 return buf.toString();
1162 }
1163
1164 private static String unquote(String s) throws XMLParseException {
1165 if (!s.startsWith("\"") || !s.endsWith("\""))
1166 throw new XMLParseException("Value must be quoted: <" + s + ">");
1167 final StringBuilder buf = new StringBuilder();
1168 final int len = s.length() - 1;
1169 for (int i = 1; i < len; i++) {
1170 final char c = s.charAt(i);
1171 final int semi;
1172 final Character quoted;
1173 if (c == '&'
1174 && (semi = s.indexOf(';', i + 1)) >= 0
1175 && ((quoted = entityToCharMap.get(s.substring(i, semi+1)))
1176 != null)) {
1177 buf.append(quoted);
1178 i = semi;
1179 } else
1180 buf.append(c);
1181 }
1182 return buf.toString();
1183 }
1184
1185 /**
1186 * Make the string that will go inside "..." for a value that is not
1187 * a plain String.
1188 * @throws RuntimeOperationsException if the value cannot be encoded.
1189 */
1190 private static String makeFieldValue(Object value) {
1191 if (value == null)
1192 return "(null)";
1193
1194 Class<?> valueClass = value.getClass();
1195 try {
1196 valueClass.getConstructor(String.class);
1197 } catch (NoSuchMethodException e) {
1198 final String msg =
1199 "Class " + valueClass + " does not have a public " +
1200 "constructor with a single string arg";
1201 final RuntimeException iae = new IllegalArgumentException(msg);
1202 throw new RuntimeOperationsException(iae,
1203 "Cannot make XML descriptor");
1204 } catch (SecurityException e) {
1205 // OK: we'll pretend the constructor is there
1206 // too bad if it's not: we'll find out when we try to
1207 // reconstruct the DescriptorSupport
1208 }
1209
1210 final String quotedValueString = quote(value.toString());
1211
1212 return "(" + valueClass.getName() + "/" + quotedValueString + ")";
1213 }
1214
1215 /*
1216 * Parse a field value from the XML produced by toXMLString().
1217 * Given a descriptor XML containing <field name="nnn" value="vvv">,
1218 * the argument to this method will be "vvv" (a string including the
1219 * containing quote characters). If vvv begins and ends with parentheses,
1220 * then it may contain:
1221 * - the characters "null", in which case the result is null;
1222 * - a value of the form "some.class.name/xxx", in which case the
1223 * result is equivalent to `new some.class.name("xxx")';
1224 * - some other string, in which case the result is that string,
1225 * without the parentheses.
1226 */
1227 private static Object parseQuotedFieldValue(String s)
1228 throws XMLParseException {
1229 s = unquote(s);
1230 if (s.equalsIgnoreCase("(null)"))
1231 return null;
1232 if (!s.startsWith("(") || !s.endsWith(")"))
1233 return s;
1234 final int slash = s.indexOf('/');
1235 if (slash < 0) {
1236 // compatibility: old code didn't include class name
1237 return s.substring(1, s.length() - 1);
1238 }
1239 final String className = s.substring(1, slash);
1240 final Constructor<?> constr;
1241 try {
1242 final ClassLoader contextClassLoader =
1243 Thread.currentThread().getContextClassLoader();
1244 if (contextClassLoader == null) {
1245 ReflectUtil.checkPackageAccess(className);
1246 }
1247 final Class<?> c =
1248 Class.forName(className, false, contextClassLoader);
1249 constr = c.getConstructor(new Class[] {String.class});
1250 } catch (Exception e) {
1251 throw new XMLParseException(e,
1252 "Cannot parse value: <" + s + ">");
1253 }
1254 final String arg = s.substring(slash + 1, s.length() - 1);
1255 try {
1256 return constr.newInstance(new Object[] {arg});
1257 } catch (Exception e) {
1258 final String msg =
1259 "Cannot construct instance of " + className +
1260 " with arg: <" + s + ">";
1261 throw new XMLParseException(e, msg);
1262 }
1263 }
1264
1265 /**
1266 * Returns <pv>a human readable string representing the
1267 * descriptor</pv>. The string will be in the format of
1268 * "fieldName=fieldValue,fieldName2=fieldValue2,..."<br>
1269 *
1270 * If there are no fields in the descriptor, then an empty String
1271 * is returned.<br>
1272 *
1273 * If a fieldValue is an object then the toString() method is
1274 * called on it and its returned value is used as the value for
1275 * the field enclosed in parenthesis.
1276 *
1277 * @exception RuntimeOperationsException for illegal value for
1278 * field Names or field Values. If the descriptor string fails
1279 * for any reason, this exception will be thrown.
1280 */
1281 public synchronized String toString() {
1282 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1283 MODELMBEAN_LOGGER.logp(Level.FINEST,
1284 DescriptorSupport.class.getName(),
1285 "toString()", "Entry");
1286 }
1287
1288 String respStr = "";
1289 String[] fields = getFields();
1290
1291 if ((fields == null) || (fields.length == 0)) {
1292 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1293 MODELMBEAN_LOGGER.logp(Level.FINEST,
1294 DescriptorSupport.class.getName(),
1295 "toString()", "Empty Descriptor");
1296 }
1297 return respStr;
1298 }
1299
1300 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1301 MODELMBEAN_LOGGER.logp(Level.FINEST,
1302 DescriptorSupport.class.getName(),
1303 "toString()", "Printing " + fields.length + " fields");
1304 }
1305
1306 for (int i=0; i < fields.length; i++) {
1307 if (i == (fields.length - 1)) {
1308 respStr = respStr.concat(fields[i]);
1309 } else {
1310 respStr = respStr.concat(fields[i] + ", ");
1311 }
1312 }
1313
1314 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1315 MODELMBEAN_LOGGER.logp(Level.FINEST,
1316 DescriptorSupport.class.getName(),
1317 "toString()", "Exit returning " + respStr);
1318 }
1319
1320 return respStr;
1321 }
1322
1323 // utility to convert to int, returns -2 if bogus.
1324
1325 private long toNumeric(String inStr) {
1326 try {
1327 return java.lang.Long.parseLong(inStr);
1328 } catch (Exception e) {
1329 return -2;
1330 }
1331 }
1332
1333
1334 /**
1335 * Deserializes a {@link DescriptorSupport} from an {@link
1336 * ObjectInputStream}.
1337 */
1338 private void readObject(ObjectInputStream in)
1339 throws IOException, ClassNotFoundException {
1340 ObjectInputStream.GetField fields = in.readFields();
1341 Map<String, Object> descriptor = cast(fields.get("descriptor", null));
1342 init(null);
1343 if (descriptor != null) {
1344 descriptorMap.putAll(descriptor);
1345 }
1346 }
1347
1348
1349 /**
1350 * Serializes a {@link DescriptorSupport} to an {@link ObjectOutputStream}.
1351 */
1352 /* If you set jmx.serial.form to "1.2.0" or "1.2.1", then we are
1353 bug-compatible with those versions. Specifically, field names
1354 are forced to lower-case before being written. This
1355 contradicts the spec, which, though it does not mention
1356 serialization explicitly, does say that the case of field names
1357 is preserved. But in 1.2.0 and 1.2.1, this requirement was not
1358 met. Instead, field names in the descriptor map were forced to
1359 lower case. Those versions expect this to have happened to a
1360 descriptor they deserialize and e.g. getFieldValue will not
1361 find a field whose name is spelt with a different case.
1362 */
1363 private void writeObject(ObjectOutputStream out) throws IOException {
1364 ObjectOutputStream.PutField fields = out.putFields();
1365 boolean compat = "1.0".equals(serialForm);
1366 if (compat)
1367 fields.put("currClass", currClass);
1368
1369 /* Purge the field "targetObject" from the DescriptorSupport before
1370 * serializing since the referenced object is typically not
1371 * serializable. We do this here rather than purging the "descriptor"
1372 * variable below because that HashMap doesn't do case-insensitivity.
1373 * See CR 6332962.
1374 */
1375 SortedMap<String, Object> startMap = descriptorMap;
1376 if (startMap.containsKey("targetObject")) {
1377 startMap = new TreeMap<String, Object>(descriptorMap);
1378 startMap.remove("targetObject");
1379 }
1380
1381 final HashMap<String, Object> descriptor;
1382 if (compat || "1.2.0".equals(serialForm) ||
1383 "1.2.1".equals(serialForm)) {
1384 descriptor = new HashMap<String, Object>();
1385 for (Map.Entry<String, Object> entry : startMap.entrySet())
1386 descriptor.put(entry.getKey().toLowerCase(), entry.getValue());
1387 } else
1388 descriptor = new HashMap<String, Object>(startMap);
1389
1390 fields.put("descriptor", descriptor);
1391 out.writeFields();
1392 }
1393
1394 }