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

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

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