Save This Page
Home » openjdk-7 » javax » management » modelmbean » [javadoc | source]
    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 (&lt;= 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: &lt;Descriptor&gt;&lt;/Descriptor&gt;</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           " &#32;",
 1103           "\"&quot;",
 1104           "<&lt;",
 1105           ">&gt;",
 1106           "&&amp;",
 1107           "\r&#13;",
 1108           "\t&#9;",
 1109           "\n&#10;",
 1110           "\f&#12;",
 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   }

Save This Page
Home » openjdk-7 » javax » management » modelmbean » [javadoc | source]