0001 /*
0002 * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 /*
0026 * @(#)author IBM Corp.
0027 *
0028 * Copyright IBM Corp. 1999-2000. All rights reserved.
0029 */
0030
0031 package javax.management.modelmbean;
0032
0033 import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER;
0034 import static com.sun.jmx.mbeanserver.Util.cast;
0035 import com.sun.jmx.mbeanserver.GetPropertyAction;
0036
0037 import java.io.IOException;
0038 import java.io.ObjectInputStream;
0039 import java.io.ObjectOutputStream;
0040 import java.io.ObjectStreamField;
0041
0042 import java.lang.reflect.Constructor;
0043
0044 import java.security.AccessController;
0045 import java.util.HashMap;
0046 import java.util.Iterator;
0047 import java.util.Map;
0048 import java.util.Set;
0049 import java.util.SortedMap;
0050 import java.util.StringTokenizer;
0051 import java.util.TreeMap;
0052 import java.util.logging.Level;
0053
0054 import javax.management.Descriptor;
0055 import javax.management.ImmutableDescriptor;
0056 import javax.management.MBeanException;
0057 import javax.management.RuntimeOperationsException;
0058
0059 import sun.reflect.misc.ReflectUtil;
0060
0061 /**
0062 * This class represents the metadata set for a ModelMBean element. A
0063 * descriptor is part of the ModelMBeanInfo,
0064 * ModelMBeanNotificationInfo, ModelMBeanAttributeInfo,
0065 * ModelMBeanConstructorInfo, and ModelMBeanParameterInfo.
0066 * <P>
0067 * A descriptor consists of a collection of fields. Each field is in
0068 * fieldname=fieldvalue format. Field names are not case sensitive,
0069 * case will be preserved on field values.
0070 * <P>
0071 * All field names and values are not predefined. New fields can be
0072 * defined and added by any program. Some fields have been predefined
0073 * for consistency of implementation and support by the
0074 * ModelMBeanInfo, ModelMBeanAttributeInfo, ModelMBeanConstructorInfo,
0075 * ModelMBeanNotificationInfo, ModelMBeanOperationInfo and ModelMBean
0076 * classes.
0077 *
0078 * <p>The <b>serialVersionUID</b> of this class is <code>-6292969195866300415L</code>.
0079 *
0080 * @since 1.5
0081 */
0082 @SuppressWarnings("serial")
0083 // serialVersionUID not constant
0084 public class DescriptorSupport implements javax.management.Descriptor {
0085
0086 // Serialization compatibility stuff:
0087 // Two serial forms are supported in this class. The selected form depends
0088 // on system property "jmx.serial.form":
0089 // - "1.0" for JMX 1.0
0090 // - any other value for JMX 1.1 and higher
0091 //
0092 // Serial version for old serial form
0093 private static final long oldSerialVersionUID = 8071560848919417985L;
0094 //
0095 // Serial version for new serial form
0096 private static final long newSerialVersionUID = -6292969195866300415L;
0097 //
0098 // Serializable fields in old serial form
0099 private static final ObjectStreamField[] oldSerialPersistentFields = {
0100 new ObjectStreamField("descriptor", HashMap.class),
0101 new ObjectStreamField("currClass", String.class) };
0102 //
0103 // Serializable fields in new serial form
0104 private static final ObjectStreamField[] newSerialPersistentFields = { new ObjectStreamField(
0105 "descriptor", HashMap.class) };
0106 //
0107 // Actual serial version and serial form
0108 private static final long serialVersionUID;
0109 /**
0110 * @serialField descriptor HashMap The collection of fields representing this descriptor
0111 */
0112 private static final ObjectStreamField[] serialPersistentFields;
0113 private static final String serialForm;
0114 static {
0115 String form = null;
0116 boolean compat = false;
0117 try {
0118 GetPropertyAction act = new GetPropertyAction(
0119 "jmx.serial.form");
0120 form = AccessController.doPrivileged(act);
0121 compat = "1.0".equals(form); // form may be null
0122 } catch (Exception e) {
0123 // OK: No compat with 1.0
0124 }
0125 serialForm = form;
0126 if (compat) {
0127 serialPersistentFields = oldSerialPersistentFields;
0128 serialVersionUID = oldSerialVersionUID;
0129 } else {
0130 serialPersistentFields = newSerialPersistentFields;
0131 serialVersionUID = newSerialVersionUID;
0132 }
0133 }
0134 //
0135 // END Serialization compatibility stuff
0136
0137 /* Spec says that field names are case-insensitive, but that case
0138 is preserved. This means that we need to be able to map from a
0139 name that may differ in case to the actual name that is used in
0140 the HashMap. Thus, descriptorMap is a TreeMap with a Comparator
0141 that ignores case.
0142
0143 Previous versions of this class had a field called "descriptor"
0144 of type HashMap where the keys were directly Strings. This is
0145 hard to reconcile with the required semantics, so we fabricate
0146 that field virtually during serialization and deserialization
0147 but keep the real information in descriptorMap.
0148 */
0149 private transient SortedMap<String, Object> descriptorMap;
0150
0151 private static final String currClass = "DescriptorSupport";
0152
0153 /**
0154 * Descriptor default constructor.
0155 * Default initial descriptor size is 20. It will grow as needed.<br>
0156 * Note that the created empty descriptor is not a valid descriptor
0157 * (the method {@link #isValid isValid} returns <CODE>false</CODE>)
0158 */
0159 public DescriptorSupport() {
0160 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0161 MODELMBEAN_LOGGER.logp(Level.FINEST,
0162 DescriptorSupport.class.getName(),
0163 "DescriptorSupport()", "Constructor");
0164 }
0165 init(null);
0166 }
0167
0168 /**
0169 * Descriptor constructor. Takes as parameter the initial
0170 * capacity of the Map that stores the descriptor fields.
0171 * Capacity will grow as needed.<br> Note that the created empty
0172 * descriptor is not a valid descriptor (the method {@link
0173 * #isValid isValid} returns <CODE>false</CODE>).
0174 *
0175 * @param initNumFields The initial capacity of the Map that
0176 * stores the descriptor fields.
0177 *
0178 * @exception RuntimeOperationsException for illegal value for
0179 * initNumFields (<= 0)
0180 * @exception MBeanException Wraps a distributed communication Exception.
0181 */
0182 public DescriptorSupport(int initNumFields) throws MBeanException,
0183 RuntimeOperationsException {
0184 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0185 MODELMBEAN_LOGGER
0186 .logp(Level.FINEST, DescriptorSupport.class
0187 .getName(), "Descriptor(initNumFields = "
0188 + initNumFields + ")", "Constructor");
0189 }
0190 if (initNumFields <= 0) {
0191 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0192 MODELMBEAN_LOGGER.logp(Level.FINEST,
0193 DescriptorSupport.class.getName(),
0194 "Descriptor(initNumFields)",
0195 "Illegal arguments: initNumFields <= 0");
0196 }
0197 final String msg = "Descriptor field limit invalid: "
0198 + initNumFields;
0199 final RuntimeException iae = new IllegalArgumentException(
0200 msg);
0201 throw new RuntimeOperationsException(iae, msg);
0202 }
0203 init(null);
0204 }
0205
0206 /**
0207 * Descriptor constructor taking a Descriptor as parameter.
0208 * Creates a new descriptor initialized to the values of the
0209 * descriptor passed in parameter.
0210 *
0211 * @param inDescr the descriptor to be used to initialize the
0212 * constructed descriptor. If it is null or contains no descriptor
0213 * fields, an empty Descriptor will be created.
0214 */
0215 public DescriptorSupport(DescriptorSupport inDescr) {
0216 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0217 MODELMBEAN_LOGGER.logp(Level.FINEST,
0218 DescriptorSupport.class.getName(),
0219 "Descriptor(Descriptor)", "Constructor");
0220 }
0221 if (inDescr == null)
0222 init(null);
0223 else
0224 init(inDescr.descriptorMap);
0225 }
0226
0227 /**
0228 * <p>Descriptor constructor taking an XML String.</p>
0229 *
0230 * <p>The format of the XML string is not defined, but an
0231 * implementation must ensure that the string returned by
0232 * {@link #toXMLString() toXMLString()} on an existing
0233 * descriptor can be used to instantiate an equivalent
0234 * descriptor using this constructor.</p>
0235 *
0236 * <p>In this implementation, all field values will be created
0237 * as Strings. If the field values are not Strings, the
0238 * programmer will have to reset or convert these fields
0239 * correctly.</p>
0240 *
0241 * @param inStr An XML-formatted string used to populate this
0242 * Descriptor. The format is not defined, but any
0243 * implementation must ensure that the string returned by
0244 * method {@link #toXMLString toXMLString} on an existing
0245 * descriptor can be used to instantiate an equivalent
0246 * descriptor when instantiated using this constructor.
0247 *
0248 * @exception RuntimeOperationsException If the String inStr
0249 * passed in parameter is null
0250 * @exception XMLParseException XML parsing problem while parsing
0251 * the input String
0252 * @exception MBeanException Wraps a distributed communication Exception.
0253 */
0254 /* At some stage we should rewrite this code to be cleverer. Using
0255 a StringTokenizer as we do means, first, that we accept a lot of
0256 bogus strings without noticing they are bogus, and second, that we
0257 split the string being parsed at characters like > even if they
0258 occur in the middle of a field value. */
0259 public DescriptorSupport(String inStr) throws MBeanException,
0260 RuntimeOperationsException, XMLParseException {
0261 /* parse an XML-formatted string and populate internal
0262 * structure with it */
0263 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0264 MODELMBEAN_LOGGER.logp(Level.FINEST,
0265 DescriptorSupport.class.getName(),
0266 "Descriptor(String = '" + inStr + "')",
0267 "Constructor");
0268 }
0269 if (inStr == null) {
0270 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0271 MODELMBEAN_LOGGER.logp(Level.FINEST,
0272 DescriptorSupport.class.getName(),
0273 "Descriptor(String = null)",
0274 "Illegal arguments");
0275 }
0276 final String msg = "String in parameter is null";
0277 final RuntimeException iae = new IllegalArgumentException(
0278 msg);
0279 throw new RuntimeOperationsException(iae, msg);
0280 }
0281
0282 final String lowerInStr = inStr.toLowerCase();
0283 if (!lowerInStr.startsWith("<descriptor>")
0284 || !lowerInStr.endsWith("</descriptor>")) {
0285 throw new XMLParseException(
0286 "No <descriptor>, </descriptor> pair");
0287 }
0288
0289 // parse xmlstring into structures
0290 init(null);
0291 // create dummy descriptor: should have same size
0292 // as number of fields in xmlstring
0293 // loop through structures and put them in descriptor
0294
0295 StringTokenizer st = new StringTokenizer(inStr, "<> \t\n\r\f");
0296
0297 boolean inFld = false;
0298 boolean inDesc = false;
0299 String fieldName = null;
0300 String fieldValue = null;
0301
0302 while (st.hasMoreTokens()) { // loop through tokens
0303 String tok = st.nextToken();
0304
0305 if (tok.equalsIgnoreCase("FIELD")) {
0306 inFld = true;
0307 } else if (tok.equalsIgnoreCase("/FIELD")) {
0308 if ((fieldName != null) && (fieldValue != null)) {
0309 fieldName = fieldName.substring(fieldName
0310 .indexOf('"') + 1, fieldName
0311 .lastIndexOf('"'));
0312 final Object fieldValueObject = parseQuotedFieldValue(fieldValue);
0313 setField(fieldName, fieldValueObject);
0314 }
0315 fieldName = null;
0316 fieldValue = null;
0317 inFld = false;
0318 } else if (tok.equalsIgnoreCase("DESCRIPTOR")) {
0319 inDesc = true;
0320 } else if (tok.equalsIgnoreCase("/DESCRIPTOR")) {
0321 inDesc = false;
0322 fieldName = null;
0323 fieldValue = null;
0324 inFld = false;
0325 } else if (inFld && inDesc) {
0326 // want kw=value, eg, name="myname" value="myvalue"
0327 int eq_separator = tok.indexOf("=");
0328 if (eq_separator > 0) {
0329 String kwPart = tok.substring(0, eq_separator);
0330 String valPart = tok.substring(eq_separator + 1);
0331 if (kwPart.equalsIgnoreCase("NAME"))
0332 fieldName = valPart;
0333 else if (kwPart.equalsIgnoreCase("VALUE"))
0334 fieldValue = valPart;
0335 else { // xml parse exception
0336 final String msg = "Expected `name' or `value', got `"
0337 + tok + "'";
0338 throw new XMLParseException(msg);
0339 }
0340 } else { // xml parse exception
0341 final String msg = "Expected `keyword=value', got `"
0342 + tok + "'";
0343 throw new XMLParseException(msg);
0344 }
0345 }
0346 } // while tokens
0347 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0348 MODELMBEAN_LOGGER.logp(Level.FINEST,
0349 DescriptorSupport.class.getName(),
0350 "Descriptor(XMLString)", "Exit");
0351 }
0352 }
0353
0354 /**
0355 * Constructor taking field names and field values. Neither array
0356 * can be null.
0357 *
0358 * @param fieldNames String array of field names. No elements of
0359 * this array can be null.
0360 * @param fieldValues Object array of the corresponding field
0361 * values. Elements of the array can be null. The
0362 * <code>fieldValue</code> must be valid for the
0363 * <code>fieldName</code> (as defined in method {@link #isValid
0364 * isValid})
0365 *
0366 * <p>Note: array sizes of parameters should match. If both arrays
0367 * are empty, then an empty descriptor is created.</p>
0368 *
0369 * @exception RuntimeOperationsException for illegal value for
0370 * field Names or field Values. The array lengths must be equal.
0371 * If the descriptor construction fails for any reason, this
0372 * exception will be thrown.
0373 *
0374 */
0375 public DescriptorSupport(String[] fieldNames, Object[] fieldValues)
0376 throws RuntimeOperationsException {
0377 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0378 MODELMBEAN_LOGGER.logp(Level.FINEST,
0379 DescriptorSupport.class.getName(),
0380 "Descriptor(fieldNames,fieldObjects)",
0381 "Constructor");
0382 }
0383
0384 if ((fieldNames == null) || (fieldValues == null)
0385 || (fieldNames.length != fieldValues.length)) {
0386 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0387 MODELMBEAN_LOGGER.logp(Level.FINEST,
0388 DescriptorSupport.class.getName(),
0389 "Descriptor(fieldNames,fieldObjects)",
0390 "Illegal arguments");
0391 }
0392
0393 final String msg = "Null or invalid fieldNames or fieldValues";
0394 final RuntimeException iae = new IllegalArgumentException(
0395 msg);
0396 throw new RuntimeOperationsException(iae, msg);
0397 }
0398
0399 /* populate internal structure with fields */
0400 init(null);
0401 for (int i = 0; i < fieldNames.length; i++) {
0402 // setField will throw an exception if a fieldName is be null.
0403 // the fieldName and fieldValue will be validated in setField.
0404 setField(fieldNames[i], fieldValues[i]);
0405 }
0406 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0407 MODELMBEAN_LOGGER.logp(Level.FINEST,
0408 DescriptorSupport.class.getName(),
0409 "Descriptor(fieldNames,fieldObjects)", "Exit");
0410 }
0411 }
0412
0413 /**
0414 * Constructor taking fields in the <i>fieldName=fieldValue</i>
0415 * format.
0416 *
0417 * @param fields String array with each element containing a
0418 * field name and value. If this array is null or empty, then the
0419 * default constructor will be executed. Null strings or empty
0420 * strings will be ignored.
0421 *
0422 * <p>All field values should be Strings. If the field values are
0423 * not Strings, the programmer will have to reset or convert these
0424 * fields correctly.
0425 *
0426 * <p>Note: Each string should be of the form
0427 * <i>fieldName=fieldValue</i>. The field name
0428 * ends at the first {@code =} character; for example if the String
0429 * is {@code a=b=c} then the field name is {@code a} and its value
0430 * is {@code b=c}.
0431 *
0432 * @exception RuntimeOperationsException for illegal value for
0433 * field Names or field Values. The field must contain an
0434 * "=". "=fieldValue", "fieldName", and "fieldValue" are illegal.
0435 * FieldName cannot be null. "fieldName=" will cause the value to
0436 * be null. If the descriptor construction fails for any reason,
0437 * this exception will be thrown.
0438 *
0439 */
0440 public DescriptorSupport(String... fields) {
0441 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0442 MODELMBEAN_LOGGER.logp(Level.FINEST,
0443 DescriptorSupport.class.getName(),
0444 "Descriptor(String... fields)", "Constructor");
0445 }
0446 init(null);
0447 if ((fields == null) || (fields.length == 0))
0448 return;
0449
0450 init(null);
0451
0452 for (int i = 0; i < fields.length; i++) {
0453 if ((fields[i] == null) || (fields[i].equals(""))) {
0454 continue;
0455 }
0456 int eq_separator = fields[i].indexOf("=");
0457 if (eq_separator < 0) {
0458 // illegal if no = or is first character
0459 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0460 MODELMBEAN_LOGGER
0461 .logp(
0462 Level.FINEST,
0463 DescriptorSupport.class.getName(),
0464 "Descriptor(String... fields)",
0465 "Illegal arguments: field does not have "
0466 + "'=' as a name and value separator");
0467 }
0468 final String msg = "Field in invalid format: no equals sign";
0469 final RuntimeException iae = new IllegalArgumentException(
0470 msg);
0471 throw new RuntimeOperationsException(iae, msg);
0472 }
0473
0474 String fieldName = fields[i].substring(0, eq_separator);
0475 String fieldValue = null;
0476 if (eq_separator < fields[i].length()) {
0477 // = is not in last character
0478 fieldValue = fields[i].substring(eq_separator + 1);
0479 }
0480
0481 if (fieldName.equals("")) {
0482 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0483 MODELMBEAN_LOGGER.logp(Level.FINEST,
0484 DescriptorSupport.class.getName(),
0485 "Descriptor(String... fields)",
0486 "Illegal arguments: fieldName is empty");
0487 }
0488
0489 final String msg = "Field in invalid format: no fieldName";
0490 final RuntimeException iae = new IllegalArgumentException(
0491 msg);
0492 throw new RuntimeOperationsException(iae, msg);
0493 }
0494
0495 setField(fieldName, fieldValue);
0496 }
0497 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0498 MODELMBEAN_LOGGER.logp(Level.FINEST,
0499 DescriptorSupport.class.getName(),
0500 "Descriptor(String... fields)", "Exit");
0501 }
0502 }
0503
0504 private void init(Map<String, ?> initMap) {
0505 descriptorMap = new TreeMap<String, Object>(
0506 String.CASE_INSENSITIVE_ORDER);
0507 if (initMap != null)
0508 descriptorMap.putAll(initMap);
0509 }
0510
0511 // Implementation of the Descriptor interface
0512
0513 public synchronized Object getFieldValue(String fieldName)
0514 throws RuntimeOperationsException {
0515
0516 if ((fieldName == null) || (fieldName.equals(""))) {
0517 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0518 MODELMBEAN_LOGGER.logp(Level.FINEST,
0519 DescriptorSupport.class.getName(),
0520 "getFieldValue(String fieldName)",
0521 "Illegal arguments: null field name");
0522 }
0523 final String msg = "Fieldname requested is null";
0524 final RuntimeException iae = new IllegalArgumentException(
0525 msg);
0526 throw new RuntimeOperationsException(iae, msg);
0527 }
0528 Object retValue = descriptorMap.get(fieldName);
0529 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0530 MODELMBEAN_LOGGER.logp(Level.FINEST,
0531 DescriptorSupport.class.getName(),
0532 "getFieldValue(String fieldName = " + fieldName
0533 + ")", "Returns '" + retValue + "'");
0534 }
0535 return (retValue);
0536 }
0537
0538 public synchronized void setField(String fieldName,
0539 Object fieldValue) throws RuntimeOperationsException {
0540
0541 // field name cannot be null or empty
0542 if ((fieldName == null) || (fieldName.equals(""))) {
0543 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0544 MODELMBEAN_LOGGER.logp(Level.FINEST,
0545 DescriptorSupport.class.getName(),
0546 "setField(fieldName,fieldValue)",
0547 "Illegal arguments: null or empty field name");
0548 }
0549
0550 final String msg = "Field name to be set is null or empty";
0551 final RuntimeException iae = new IllegalArgumentException(
0552 msg);
0553 throw new RuntimeOperationsException(iae, msg);
0554 }
0555
0556 if (!validateField(fieldName, fieldValue)) {
0557 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0558 MODELMBEAN_LOGGER.logp(Level.FINEST,
0559 DescriptorSupport.class.getName(),
0560 "setField(fieldName,fieldValue)",
0561 "Illegal arguments");
0562 }
0563
0564 final String msg = "Field value invalid: " + fieldName
0565 + "=" + fieldValue;
0566 final RuntimeException iae = new IllegalArgumentException(
0567 msg);
0568 throw new RuntimeOperationsException(iae, msg);
0569 }
0570
0571 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0572 MODELMBEAN_LOGGER.logp(Level.FINEST,
0573 DescriptorSupport.class.getName(),
0574 "setField(fieldName,fieldValue)",
0575 "Entry: setting '" + fieldName + "' to '"
0576 + fieldValue + "'");
0577 }
0578
0579 // Since we do not remove any existing entry with this name,
0580 // the field will preserve whatever case it had, ignoring
0581 // any difference there might be in fieldName.
0582 descriptorMap.put(fieldName, fieldValue);
0583 }
0584
0585 public synchronized String[] getFields() {
0586 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0587 MODELMBEAN_LOGGER.logp(Level.FINEST,
0588 DescriptorSupport.class.getName(), "getFields()",
0589 "Entry");
0590 }
0591 int numberOfEntries = descriptorMap.size();
0592
0593 String[] responseFields = new String[numberOfEntries];
0594 Set returnedSet = descriptorMap.entrySet();
0595
0596 int i = 0;
0597 Object currValue = null;
0598 Map.Entry currElement = null;
0599
0600 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0601 MODELMBEAN_LOGGER.logp(Level.FINEST,
0602 DescriptorSupport.class.getName(), "getFields()",
0603 "Returning " + numberOfEntries + " fields");
0604 }
0605 for (Iterator iter = returnedSet.iterator(); iter.hasNext(); i++) {
0606 currElement = (Map.Entry) iter.next();
0607
0608 if (currElement == null) {
0609 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0610 MODELMBEAN_LOGGER.logp(Level.FINEST,
0611 DescriptorSupport.class.getName(),
0612 "getFields()", "Element is null");
0613 }
0614 } else {
0615 currValue = currElement.getValue();
0616 if (currValue == null) {
0617 responseFields[i] = currElement.getKey() + "=";
0618 } else {
0619 if (currValue instanceof java.lang.String) {
0620 responseFields[i] = currElement.getKey() + "="
0621 + currValue.toString();
0622 } else {
0623 responseFields[i] = currElement.getKey() + "=("
0624 + currValue.toString() + ")";
0625 }
0626 }
0627 }
0628 }
0629
0630 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0631 MODELMBEAN_LOGGER.logp(Level.FINEST,
0632 DescriptorSupport.class.getName(), "getFields()",
0633 "Exit");
0634 }
0635
0636 return responseFields;
0637 }
0638
0639 public synchronized String[] getFieldNames() {
0640 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0641 MODELMBEAN_LOGGER.logp(Level.FINEST,
0642 DescriptorSupport.class.getName(),
0643 "getFieldNames()", "Entry");
0644 }
0645 int numberOfEntries = descriptorMap.size();
0646
0647 String[] responseFields = new String[numberOfEntries];
0648 Set returnedSet = descriptorMap.entrySet();
0649
0650 int i = 0;
0651
0652 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0653 MODELMBEAN_LOGGER.logp(Level.FINEST,
0654 DescriptorSupport.class.getName(),
0655 "getFieldNames()", "Returning " + numberOfEntries
0656 + " fields");
0657 }
0658
0659 for (Iterator iter = returnedSet.iterator(); iter.hasNext(); i++) {
0660 Map.Entry currElement = (Map.Entry) iter.next();
0661
0662 if ((currElement == null) || (currElement.getKey() == null)) {
0663 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0664 MODELMBEAN_LOGGER.logp(Level.FINEST,
0665 DescriptorSupport.class.getName(),
0666 "getFieldNames()", "Field is null");
0667 }
0668 } else {
0669 responseFields[i] = currElement.getKey().toString();
0670 }
0671 }
0672
0673 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0674 MODELMBEAN_LOGGER.logp(Level.FINEST,
0675 DescriptorSupport.class.getName(),
0676 "getFieldNames()", "Exit");
0677 }
0678
0679 return responseFields;
0680 }
0681
0682 public synchronized Object[] getFieldValues(String... fieldNames) {
0683 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0684 MODELMBEAN_LOGGER.logp(Level.FINEST,
0685 DescriptorSupport.class.getName(),
0686 "getFieldValues(String... fieldNames)", "Entry");
0687 }
0688 // if fieldNames == null return all values
0689 // if fieldNames is String[0] return no values
0690
0691 final int numberOfEntries = (fieldNames == null) ? descriptorMap
0692 .size()
0693 : fieldNames.length;
0694 final Object[] responseFields = new Object[numberOfEntries];
0695
0696 int i = 0;
0697
0698 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0699 MODELMBEAN_LOGGER.logp(Level.FINEST,
0700 DescriptorSupport.class.getName(),
0701 "getFieldValues(String... fieldNames)",
0702 "Returning " + numberOfEntries + " fields");
0703 }
0704
0705 if (fieldNames == null) {
0706 for (Iterator iter = descriptorMap.values().iterator(); iter
0707 .hasNext(); i++)
0708 responseFields[i] = iter.next();
0709 } else {
0710 for (i = 0; i < fieldNames.length; i++) {
0711 if ((fieldNames[i] == null)
0712 || (fieldNames[i].equals(""))) {
0713 responseFields[i] = null;
0714 } else {
0715 responseFields[i] = getFieldValue(fieldNames[i]);
0716 }
0717 }
0718 }
0719
0720 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0721 MODELMBEAN_LOGGER.logp(Level.FINEST,
0722 DescriptorSupport.class.getName(),
0723 "getFieldValues(String... fieldNames)", "Exit");
0724 }
0725
0726 return responseFields;
0727 }
0728
0729 public synchronized void setFields(String[] fieldNames,
0730 Object[] fieldValues) throws RuntimeOperationsException {
0731
0732 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0733 MODELMBEAN_LOGGER.logp(Level.FINEST,
0734 DescriptorSupport.class.getName(),
0735 "setFields(fieldNames,fieldValues)", "Entry");
0736 }
0737
0738 if ((fieldNames == null) || (fieldValues == null)
0739 || (fieldNames.length != fieldValues.length)) {
0740 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0741 MODELMBEAN_LOGGER.logp(Level.FINEST,
0742 DescriptorSupport.class.getName(),
0743 "setFields(fieldNames,fieldValues)",
0744 "Illegal arguments");
0745 }
0746
0747 final String msg = "fieldNames and fieldValues are null or invalid";
0748 final RuntimeException iae = new IllegalArgumentException(
0749 msg);
0750 throw new RuntimeOperationsException(iae, msg);
0751 }
0752
0753 for (int i = 0; i < fieldNames.length; i++) {
0754 if ((fieldNames[i] == null) || (fieldNames[i].equals(""))) {
0755 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0756 MODELMBEAN_LOGGER.logp(Level.FINEST,
0757 DescriptorSupport.class.getName(),
0758 "setFields(fieldNames,fieldValues)",
0759 "Null field name encountered at element "
0760 + i);
0761 }
0762 final String msg = "fieldNames is null or invalid";
0763 final RuntimeException iae = new IllegalArgumentException(
0764 msg);
0765 throw new RuntimeOperationsException(iae, msg);
0766 }
0767 setField(fieldNames[i], fieldValues[i]);
0768 }
0769 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0770 MODELMBEAN_LOGGER.logp(Level.FINEST,
0771 DescriptorSupport.class.getName(),
0772 "setFields(fieldNames,fieldValues)", "Exit");
0773 }
0774 }
0775
0776 /**
0777 * Returns a new Descriptor which is a duplicate of the Descriptor.
0778 *
0779 * @exception RuntimeOperationsException for illegal value for
0780 * field Names or field Values. If the descriptor construction
0781 * fails for any reason, this exception will be thrown.
0782 */
0783
0784 public synchronized Object clone()
0785 throws RuntimeOperationsException {
0786 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0787 MODELMBEAN_LOGGER.logp(Level.FINEST,
0788 DescriptorSupport.class.getName(), "clone()",
0789 "Entry");
0790 }
0791 return (new DescriptorSupport(this ));
0792 }
0793
0794 public synchronized void removeField(String fieldName) {
0795 if ((fieldName == null) || (fieldName.equals(""))) {
0796 return;
0797 }
0798
0799 descriptorMap.remove(fieldName);
0800 }
0801
0802 /**
0803 * Compares this descriptor to the given object. The objects are equal if
0804 * the given object is also a Descriptor, and if the two Descriptors have
0805 * the same field names (possibly differing in case) and the same
0806 * associated values. The respective values for a field in the two
0807 * Descriptors are equal if the following conditions hold:</p>
0808 *
0809 * <ul>
0810 * <li>If one value is null then the other must be too.</li>
0811 * <li>If one value is a primitive array then the other must be a primitive
0812 * array of the same type with the same elements.</li>
0813 * <li>If one value is an object array then the other must be too and
0814 * {@link java.util.Arrays#deepEquals(Object[],Object[]) Arrays.deepEquals}
0815 * must return true.</li>
0816 * <li>Otherwise {@link Object#equals(Object)} must return true.</li>
0817 * </ul>
0818 *
0819 * @param o the object to compare with.
0820 *
0821 * @return {@code true} if the objects are the same; {@code false}
0822 * otherwise.
0823 *
0824 */
0825 // XXXX TODO: This is not very efficient!
0826 // Note: this Javadoc is copied from javax.management.Descriptor
0827 // due to 6369229.
0828 public synchronized boolean equals(Object o) {
0829 if (o == this )
0830 return true;
0831
0832 return new ImmutableDescriptor(descriptorMap).equals(o);
0833 }
0834
0835 /**
0836 * <p>Returns the hash code value for this descriptor. The hash
0837 * code is computed as the sum of the hash codes for each field in
0838 * the descriptor. The hash code of a field with name {@code n}
0839 * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}.
0840 * Here {@code h} is the hash code of {@code v}, computed as
0841 * follows:</p>
0842 *
0843 * <ul>
0844 * <li>If {@code v} is null then {@code h} is 0.</li>
0845 * <li>If {@code v} is a primitive array then {@code h} is computed using
0846 * the appropriate overloading of {@code java.util.Arrays.hashCode}.</li>
0847 * <li>If {@code v} is an object array then {@code h} is computed using
0848 * {@link java.util.Arrays#deepHashCode(Object[]) Arrays.deepHashCode}.</li>
0849 * <li>Otherwise {@code h} is {@code v.hashCode()}.</li>
0850 * </ul>
0851 *
0852 * @return A hash code value for this object.
0853 *
0854 */
0855 // XXXX TODO: This is not very efficient!
0856 // Note: this Javadoc is copied from javax.management.Descriptor
0857 // due to 6369229.
0858 public synchronized int hashCode() {
0859 return new ImmutableDescriptor(descriptorMap).hashCode();
0860 }
0861
0862 /**
0863 * Returns true if all of the fields have legal values given their
0864 * names.
0865 * <P>
0866 * This implementation does not support interoperating with a directory
0867 * or lookup service. Thus, conforming to the specification, no checking is
0868 * done on the <i>"export"</i> field.
0869 * <P>
0870 * Otherwise this implementation returns false if:
0871 * <P>
0872 * <UL>
0873 * <LI> name and descriptorType fieldNames are not defined, or
0874 * null, or empty, or not String
0875 * <LI> class, role, getMethod, setMethod fieldNames, if defined,
0876 * are null or not String
0877 * <LI> persistPeriod, currencyTimeLimit, lastUpdatedTimeStamp,
0878 * lastReturnedTimeStamp if defined, are null, or not a Numeric
0879 * String or not a Numeric Value >= -1
0880 * <LI> log fieldName, if defined, is null, or not a Boolean or
0881 * not a String with value "t", "f", "true", "false". These String
0882 * values must not be case sensitive.
0883 * <LI> visibility fieldName, if defined, is null, or not a
0884 * Numeric String or a not Numeric Value >= 1 and <= 4
0885 * <LI> severity fieldName, if defined, is null, or not a Numeric
0886 * String or not a Numeric Value >= 0 and <= 6<br>
0887 * <LI> persistPolicy fieldName, if defined, is null, or not one of
0888 * the following strings:<br>
0889 * "OnUpdate", "OnTimer", "NoMoreOftenThan", "OnUnregister", "Always",
0890 * "Never". These String values must not be case sensitive.<br>
0891 * </UL>
0892 *
0893 * @exception RuntimeOperationsException If the validity checking
0894 * fails for any reason, this exception will be thrown.
0895 */
0896
0897 public synchronized boolean isValid()
0898 throws RuntimeOperationsException {
0899 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0900 MODELMBEAN_LOGGER.logp(Level.FINEST,
0901 DescriptorSupport.class.getName(), "isValid()",
0902 "Entry");
0903 }
0904 // verify that the descriptor is valid, by iterating over each field...
0905
0906 Set returnedSet = descriptorMap.entrySet();
0907
0908 if (returnedSet == null) { // null descriptor, not valid
0909 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0910 MODELMBEAN_LOGGER.logp(Level.FINEST,
0911 DescriptorSupport.class.getName(), "isValid()",
0912 "Returns false (null set)");
0913 }
0914 return false;
0915 }
0916 // must have a name and descriptor type field
0917 String this Name = (String) (this .getFieldValue("name"));
0918 String this DescType = (String) (getFieldValue("descriptorType"));
0919
0920 if ((this Name == null) || (this DescType == null)
0921 || (this Name.equals("")) || (this DescType.equals(""))) {
0922 return false;
0923 }
0924
0925 // According to the descriptor type we validate the fields contained
0926
0927 for (Iterator iter = returnedSet.iterator(); iter.hasNext();) {
0928 Map.Entry currElement = (Map.Entry) iter.next();
0929
0930 if (currElement != null) {
0931 if (currElement.getValue() != null) {
0932 // validate the field valued...
0933 if (validateField(
0934 (currElement.getKey()).toString(),
0935 (currElement.getValue()).toString())) {
0936 continue;
0937 } else {
0938 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0939 MODELMBEAN_LOGGER.logp(Level.FINEST,
0940 DescriptorSupport.class.getName(),
0941 "isValid()", "Field "
0942 + currElement.getKey()
0943 + "="
0944 + currElement.getValue()
0945 + " is not valid");
0946 }
0947 return false;
0948 }
0949 }
0950 }
0951 }
0952
0953 // fell through, all fields OK
0954 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
0955 MODELMBEAN_LOGGER.logp(Level.FINEST,
0956 DescriptorSupport.class.getName(), "isValid()",
0957 "Returns true");
0958 }
0959 return true;
0960 }
0961
0962 // worker routine for isValid()
0963 // name is not null
0964 // descriptorType is not null
0965 // getMethod and setMethod are not null
0966 // persistPeriod is numeric
0967 // currencyTimeLimit is numeric
0968 // lastUpdatedTimeStamp is numeric
0969 // visibility is 1-4
0970 // severity is 0-6
0971 // log is T or F
0972 // role is not null
0973 // class is not null
0974 // lastReturnedTimeStamp is numeric
0975
0976 private boolean validateField(String fldName, Object fldValue) {
0977 if ((fldName == null) || (fldName.equals("")))
0978 return false;
0979 String SfldValue = "";
0980 boolean isAString = false;
0981 if ((fldValue != null)
0982 && (fldValue instanceof java.lang.String)) {
0983 SfldValue = (String) fldValue;
0984 isAString = true;
0985 }
0986
0987 boolean nameOrDescriptorType = (fldName
0988 .equalsIgnoreCase("Name") || fldName
0989 .equalsIgnoreCase("DescriptorType"));
0990 if (nameOrDescriptorType
0991 || fldName.equalsIgnoreCase("SetMethod")
0992 || fldName.equalsIgnoreCase("GetMethod")
0993 || fldName.equalsIgnoreCase("Role")
0994 || fldName.equalsIgnoreCase("Class")) {
0995 if (fldValue == null || !isAString)
0996 return false;
0997 if (nameOrDescriptorType && SfldValue.equals(""))
0998 return false;
0999 return true;
1000 } else if (fldName.equalsIgnoreCase("visibility")) {
1001 long v;
1002 if ((fldValue != null) && (isAString)) {
1003 v = toNumeric(SfldValue);
1004 } else if (fldValue instanceof java.lang.Integer) {
1005 v = ((Integer) fldValue).intValue();
1006 } else
1007 return false;
1008
1009 if (v >= 1 && v <= 4)
1010 return true;
1011 else
1012 return false;
1013 } else if (fldName.equalsIgnoreCase("severity")) {
1014
1015 long v;
1016 if ((fldValue != null) && (isAString)) {
1017 v = toNumeric(SfldValue);
1018 } else if (fldValue instanceof java.lang.Integer) {
1019 v = ((Integer) fldValue).intValue();
1020 } else
1021 return false;
1022
1023 return (v >= 0 && v <= 6);
1024 } else if (fldName.equalsIgnoreCase("PersistPolicy")) {
1025 return (((fldValue != null) && (isAString)) && (SfldValue
1026 .equalsIgnoreCase("OnUpdate")
1027 || SfldValue.equalsIgnoreCase("OnTimer")
1028 || SfldValue.equalsIgnoreCase("NoMoreOftenThan")
1029 || SfldValue.equalsIgnoreCase("Always")
1030 || SfldValue.equalsIgnoreCase("Never") || SfldValue
1031 .equalsIgnoreCase("OnUnregister")));
1032 } else if (fldName.equalsIgnoreCase("PersistPeriod")
1033 || fldName.equalsIgnoreCase("CurrencyTimeLimit")
1034 || fldName.equalsIgnoreCase("LastUpdatedTimeStamp")
1035 || fldName.equalsIgnoreCase("LastReturnedTimeStamp")) {
1036
1037 long v;
1038 if ((fldValue != null) && (isAString)) {
1039 v = toNumeric(SfldValue);
1040 } else if (fldValue instanceof java.lang.Number) {
1041 v = ((Number) fldValue).longValue();
1042 } else
1043 return false;
1044
1045 return (v >= -1);
1046 } else if (fldName.equalsIgnoreCase("log")) {
1047 return ((fldValue instanceof java.lang.Boolean) || (isAString && (SfldValue
1048 .equalsIgnoreCase("T")
1049 || SfldValue.equalsIgnoreCase("true")
1050 || SfldValue.equalsIgnoreCase("F") || SfldValue
1051 .equalsIgnoreCase("false"))));
1052 }
1053
1054 // default to true, it is a field we aren't validating (user etc.)
1055 return true;
1056 }
1057
1058 /**
1059 * <p>Returns an XML String representing the descriptor.</p>
1060 *
1061 * <p>The format is not defined, but an implementation must
1062 * ensure that the string returned by this method can be
1063 * used to build an equivalent descriptor when instantiated
1064 * using the constructor {@link #DescriptorSupport(String)
1065 * DescriptorSupport(String inStr)}.</p>
1066 *
1067 * <p>Fields which are not String objects will have toString()
1068 * called on them to create the value. The value will be
1069 * enclosed in parentheses. It is not guaranteed that you can
1070 * reconstruct these objects unless they have been
1071 * specifically set up to support toString() in a meaningful
1072 * format and have a matching constructor that accepts a
1073 * String in the same format.</p>
1074 *
1075 * <p>If the descriptor is empty the following String is
1076 * returned: <Descriptor></Descriptor></p>
1077 *
1078 * @return the XML string.
1079 *
1080 * @exception RuntimeOperationsException for illegal value for
1081 * field Names or field Values. If the XML formatted string
1082 * construction fails for any reason, this exception will be
1083 * thrown.
1084 */
1085 public synchronized String toXMLString() {
1086 final StringBuilder buf = new StringBuilder("<Descriptor>");
1087 Set returnedSet = descriptorMap.entrySet();
1088 for (Iterator iter = returnedSet.iterator(); iter.hasNext();) {
1089 final Map.Entry currElement = (Map.Entry) iter.next();
1090 final String name = currElement.getKey().toString();
1091 Object value = currElement.getValue();
1092 String valueString = null;
1093 /* Set valueString to non-null if and only if this is a string that
1094 cannot be confused with the encoding of an object. If it
1095 could be so confused (surrounded by parentheses) then we
1096 call makeFieldValue as for any non-String object and end
1097 up with an encoding like "(java.lang.String/(thing))". */
1098 if (value instanceof String) {
1099 final String svalue = (String) value;
1100 if (!svalue.startsWith("(") || !svalue.endsWith(")"))
1101 valueString = quote(svalue);
1102 }
1103 if (valueString == null)
1104 valueString = makeFieldValue(value);
1105 buf.append("<field name=\"").append(name).append(
1106 "\" value=\"").append(valueString).append(
1107 "\"></field>");
1108 }
1109 buf.append("</Descriptor>");
1110 return buf.toString();
1111 }
1112
1113 private static final String[] entities = { "  ", "\""",
1114 "<<", ">>", "&&", "\r ", "\t	", "\n ",
1115 "\f", };
1116 private static final Map<String, Character> entityToCharMap = new HashMap<String, Character>();
1117 private static final String[] charToEntityMap;
1118
1119 static {
1120 char maxChar = 0;
1121 for (int i = 0; i < entities.length; i++) {
1122 final char c = entities[i].charAt(0);
1123 if (c > maxChar)
1124 maxChar = c;
1125 }
1126 charToEntityMap = new String[maxChar + 1];
1127 for (int i = 0; i < entities.length; i++) {
1128 final char c = entities[i].charAt(0);
1129 final String entity = entities[i].substring(1);
1130 charToEntityMap[c] = entity;
1131 entityToCharMap.put(entity, new Character(c));
1132 }
1133 }
1134
1135 private static boolean isMagic(char c) {
1136 return (c < charToEntityMap.length && charToEntityMap[c] != null);
1137 }
1138
1139 /*
1140 * Quote the string so that it will be acceptable to the (String)
1141 * constructor. Since the parsing code in that constructor is fairly
1142 * stupid, we're obliged to quote apparently innocuous characters like
1143 * space, <, and >. In a future version, we should rewrite the parser
1144 * and only quote " plus either \ or & (depending on the quote syntax).
1145 */
1146 private static String quote(String s) {
1147 boolean found = false;
1148 for (int i = 0; i < s.length(); i++) {
1149 if (isMagic(s.charAt(i))) {
1150 found = true;
1151 break;
1152 }
1153 }
1154 if (!found)
1155 return s;
1156 final StringBuilder buf = new StringBuilder();
1157 for (int i = 0; i < s.length(); i++) {
1158 char c = s.charAt(i);
1159 if (isMagic(c))
1160 buf.append(charToEntityMap[c]);
1161 else
1162 buf.append(c);
1163 }
1164 return buf.toString();
1165 }
1166
1167 private static String unquote(String s) throws XMLParseException {
1168 if (!s.startsWith("\"") || !s.endsWith("\""))
1169 throw new XMLParseException("Value must be quoted: <" + s
1170 + ">");
1171 final StringBuilder buf = new StringBuilder();
1172 final int len = s.length() - 1;
1173 for (int i = 1; i < len; i++) {
1174 final char c = s.charAt(i);
1175 final int semi;
1176 final Character quoted;
1177 if (c == '&'
1178 && (semi = s.indexOf(';', i + 1)) >= 0
1179 && ((quoted = entityToCharMap.get(s.substring(i,
1180 semi + 1))) != null)) {
1181 buf.append(quoted);
1182 i = semi;
1183 } else
1184 buf.append(c);
1185 }
1186 return buf.toString();
1187 }
1188
1189 /**
1190 * Make the string that will go inside "..." for a value that is not
1191 * a plain String.
1192 * @throws RuntimeOperationsException if the value cannot be encoded.
1193 */
1194 private static String makeFieldValue(Object value) {
1195 if (value == null)
1196 return "(null)";
1197
1198 Class<?> valueClass = value.getClass();
1199 try {
1200 valueClass.getConstructor(String.class);
1201 } catch (NoSuchMethodException e) {
1202 final String msg = "Class " + valueClass
1203 + " does not have a public "
1204 + "constructor with a single string arg";
1205 final RuntimeException iae = new IllegalArgumentException(
1206 msg);
1207 throw new RuntimeOperationsException(iae,
1208 "Cannot make XML descriptor");
1209 } catch (SecurityException e) {
1210 // OK: we'll pretend the constructor is there
1211 // too bad if it's not: we'll find out when we try to
1212 // reconstruct the DescriptorSupport
1213 }
1214
1215 final String quotedValueString = quote(value.toString());
1216
1217 return "(" + valueClass.getName() + "/" + quotedValueString
1218 + ")";
1219 }
1220
1221 /*
1222 * Parse a field value from the XML produced by toXMLString().
1223 * Given a descriptor XML containing <field name="nnn" value="vvv">,
1224 * the argument to this method will be "vvv" (a string including the
1225 * containing quote characters). If vvv begins and ends with parentheses,
1226 * then it may contain:
1227 * - the characters "null", in which case the result is null;
1228 * - a value of the form "some.class.name/xxx", in which case the
1229 * result is equivalent to `new some.class.name("xxx")';
1230 * - some other string, in which case the result is that string,
1231 * without the parentheses.
1232 */
1233 private static Object parseQuotedFieldValue(String s)
1234 throws XMLParseException {
1235 s = unquote(s);
1236 if (s.equalsIgnoreCase("(null)"))
1237 return null;
1238 if (!s.startsWith("(") || !s.endsWith(")"))
1239 return s;
1240 final int slash = s.indexOf('/');
1241 if (slash < 0) {
1242 // compatibility: old code didn't include class name
1243 return s.substring(1, s.length() - 1);
1244 }
1245 final String className = s.substring(1, slash);
1246 final Constructor<?> constr;
1247 try {
1248 final ClassLoader contextClassLoader = Thread
1249 .currentThread().getContextClassLoader();
1250 if (contextClassLoader == null) {
1251 ReflectUtil.checkPackageAccess(className);
1252 }
1253 final Class<?> c = Class.forName(className, false,
1254 contextClassLoader);
1255 constr = c.getConstructor(new Class[] { String.class });
1256 } catch (Exception e) {
1257 throw new XMLParseException(e, "Cannot parse value: <" + s
1258 + ">");
1259 }
1260 final String arg = s.substring(slash + 1, s.length() - 1);
1261 try {
1262 return constr.newInstance(new Object[] { arg });
1263 } catch (Exception e) {
1264 final String msg = "Cannot construct instance of "
1265 + className + " with arg: <" + s + ">";
1266 throw new XMLParseException(e, msg);
1267 }
1268 }
1269
1270 /**
1271 * Returns <pv>a human readable string representing the
1272 * descriptor</pv>. The string will be in the format of
1273 * "fieldName=fieldValue,fieldName2=fieldValue2,..."<br>
1274 *
1275 * If there are no fields in the descriptor, then an empty String
1276 * is returned.<br>
1277 *
1278 * If a fieldValue is an object then the toString() method is
1279 * called on it and its returned value is used as the value for
1280 * the field enclosed in parenthesis.
1281 *
1282 * @exception RuntimeOperationsException for illegal value for
1283 * field Names or field Values. If the descriptor string fails
1284 * for any reason, this exception will be thrown.
1285 */
1286 public synchronized String toString() {
1287 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1288 MODELMBEAN_LOGGER.logp(Level.FINEST,
1289 DescriptorSupport.class.getName(), "toString()",
1290 "Entry");
1291 }
1292
1293 String respStr = "";
1294 String[] fields = getFields();
1295
1296 if ((fields == null) || (fields.length == 0)) {
1297 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1298 MODELMBEAN_LOGGER.logp(Level.FINEST,
1299 DescriptorSupport.class.getName(),
1300 "toString()", "Empty Descriptor");
1301 }
1302 return respStr;
1303 }
1304
1305 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1306 MODELMBEAN_LOGGER.logp(Level.FINEST,
1307 DescriptorSupport.class.getName(), "toString()",
1308 "Printing " + fields.length + " fields");
1309 }
1310
1311 for (int i = 0; i < fields.length; i++) {
1312 if (i == (fields.length - 1)) {
1313 respStr = respStr.concat(fields[i]);
1314 } else {
1315 respStr = respStr.concat(fields[i] + ", ");
1316 }
1317 }
1318
1319 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) {
1320 MODELMBEAN_LOGGER.logp(Level.FINEST,
1321 DescriptorSupport.class.getName(), "toString()",
1322 "Exit returning " + respStr);
1323 }
1324
1325 return respStr;
1326 }
1327
1328 // utility to convert to int, returns -2 if bogus.
1329
1330 private long toNumeric(String inStr) {
1331 long result = -2;
1332 try {
1333 result = java.lang.Long.parseLong(inStr);
1334 } catch (Exception e) {
1335 return -2;
1336 }
1337 return result;
1338 }
1339
1340 /**
1341 * Deserializes a {@link DescriptorSupport} from an {@link
1342 * ObjectInputStream}.
1343 */
1344 private void readObject(ObjectInputStream in) throws IOException,
1345 ClassNotFoundException {
1346 ObjectInputStream.GetField fields = in.readFields();
1347 Map<String, Object> descriptor = cast(fields.get("descriptor",
1348 null));
1349 init(null);
1350 if (descriptor != null) {
1351 descriptorMap.putAll(descriptor);
1352 }
1353 }
1354
1355 /**
1356 * Serializes a {@link DescriptorSupport} to an {@link ObjectOutputStream}.
1357 */
1358 /* If you set jmx.serial.form to "1.2.0" or "1.2.1", then we are
1359 bug-compatible with those versions. Specifically, field names
1360 are forced to lower-case before being written. This
1361 contradicts the spec, which, though it does not mention
1362 serialization explicitly, does say that the case of field names
1363 is preserved. But in 1.2.0 and 1.2.1, this requirement was not
1364 met. Instead, field names in the descriptor map were forced to
1365 lower case. Those versions expect this to have happened to a
1366 descriptor they deserialize and e.g. getFieldValue will not
1367 find a field whose name is spelt with a different case.
1368 */
1369 private void writeObject(ObjectOutputStream out) throws IOException {
1370 ObjectOutputStream.PutField fields = out.putFields();
1371 boolean compat = "1.0".equals(serialForm);
1372 if (compat)
1373 fields.put("currClass", currClass);
1374
1375 /* Purge the field "targetObject" from the DescriptorSupport before
1376 * serializing since the referenced object is typically not
1377 * serializable. We do this here rather than purging the "descriptor"
1378 * variable below because that HashMap doesn't do case-insensitivity.
1379 * See CR 6332962.
1380 */
1381 SortedMap<String, Object> startMap = descriptorMap;
1382 if (startMap.containsKey("targetObject")) {
1383 startMap = new TreeMap<String, Object>(descriptorMap);
1384 startMap.remove("targetObject");
1385 }
1386
1387 final HashMap<String, Object> descriptor;
1388 if (compat || "1.2.0".equals(serialForm)
1389 || "1.2.1".equals(serialForm)) {
1390 descriptor = new HashMap<String, Object>();
1391 for (Map.Entry<String, Object> entry : startMap.entrySet())
1392 descriptor.put(entry.getKey().toLowerCase(), entry
1393 .getValue());
1394 } else
1395 descriptor = new HashMap<String, Object>(startMap);
1396
1397 fields.put("descriptor", descriptor);
1398 out.writeFields();
1399 }
1400
1401 }
|