docs: final Serializable getUserProperty(String name, boolean forMatching)
                      throws IOException, ClassNotFoundException {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(this, tc, "getUserProperty", new Object[] { name, Boolean.valueOf(forMatching) });
  
          Serializable item = null;
  
          /* First see if Maelstrom's transportVersion property is wanted & set */
          /* in its own field. The name is never null as outer methods check it. */
          if (name.equals(MfpConstants.PRP_TRANSVER) && isTransportVersionSet()) {
              item = (Serializable) getTransportVersion();
          }
  
          /* Otherwise, try getting the property from the JMS-valid Property Map */
          else {
              if (mayHaveJmsUserProperties()) {
                  item = (Serializable) getJmsUserPropertyMap().get(name);
              }
          }
  
          /* If the item was there, we don't need to do anything else has it is */
          /* already a suitable Object for returning. */
          if (item != null) {
          }
  
          /* Try getting the property from the other Property Map */
          else {
              if (mayHaveOtherUserProperties()) {
                  item = (Serializable) getOtherUserPropertyMap().get(name);
              }
  
              /* If the item is null (i.e. no such property) just return it. */
              /* If forMatching is true, we can return whatever we have as is. */
              /* If is is not a byte array it must already be a proper value - this */
              /* can't be the case at the moment, but, once WAS 6 is no longer */
              /* supported we can put the JMS-supported-type properties with dodgy */
              /* names into the OtherUserPropertyMap without flattening them. d392521 */
              if ((item == null) || forMatching || !(item instanceof byte[])) {
              }
  
              /* Otherwise, it is a byte array of some sort & needs some work done. */
              else {
                  item = restoreMapObject((byte[]) item);
              }
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(this, tc, "getUserProperty", PasswordUtils.replaceValueIfKeyIsPassword(name, item));
          return item;
      }
  
      /**
       * Return the value of the given SI_ Message Property.
       * <p>
       * This method supports only those SI_ properties which are not header
       * fields accessible by SIMessage/SIBusSdoMessage get methods.
       * <p>
       * The method has package level visibility as it is called by JsSdoMessageImpl.
       * 
       * @param name The name of the Property to be returned.
       * @param forMatching Indicates what the property will be used for (i.e. matching or getting)
       *            If forMatching:
       *            a) the message header properties are supported
       *            b) no Exceptions can be thrown
       *            c) some properties may use different algorithms
       * 
       * @return Serializable A reference to the Message Property.
       *         Null is returned if there is no such item.
       * 
       */
      final Serializable getSIProperty(String name, boolean forMatching) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(this, tc, "getSIProperty", name);
  
          Serializable value = null;
          int count;
  
          /*------------------------------------------------------------------------*/
          /* First check for the SI_Report properties */
          /*------------------------------------------------------------------------*/
          if (name.regionMatches(SI_PREFIX_LENGTH, SI_REPORT, 0, SI_REPORT_LENGTH)) {
  
              count = name.length() - SI_REPORT_OFFSET;
  
              if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportExpiration, SI_REPORT_OFFSET, count)) {
                  value = getReportExpiry();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportException, SI_REPORT_OFFSET, count)) {
                  value = getReportException();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportCOA, SI_REPORT_OFFSET, count)) {
                  value = getReportCOA();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportCOD, SI_REPORT_OFFSET, count)) {
                  value = getReportCOD();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportPAN, SI_REPORT_OFFSET, count)) {
                  value = getReportPAN();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportNAN, SI_REPORT_OFFSET, count)) {
                  value = getReportNAN();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportPassMsgID, SI_REPORT_OFFSET, count)) {
                  value = getReportPassMsgId();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportPassCorrelID, SI_REPORT_OFFSET, count)) {
                  value = getReportPassCorrelId();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportDiscardMsg, SI_REPORT_OFFSET, count)) {
                  value = getReportDiscardMsg();
              }
              else if (name.regionMatches(SI_REPORT_OFFSET, SIProperties.SI_ReportFeedback, SI_REPORT_OFFSET, count)) {
                  value = getReportFeedback();
              }
              else {
                  if (!forMatching) {
                      throw new IllegalArgumentException(name);
                  }
              }
          }
  
          /*------------------------------------------------------------------------*/
          /* Then try the SI_Exception properties */
          /*------------------------------------------------------------------------*/
          else if (name.regionMatches(SI_PREFIX_LENGTH, SI_EXCEPTION, 0, SI_EXCEPTION_LENGTH)) {
  
              count = name.length() - SI_EXCEPTION_OFFSET;
  
              if (name.regionMatches(SI_EXCEPTION_OFFSET, SIProperties.SI_ExceptionReason, SI_EXCEPTION_OFFSET, count)) {
                  value = getExceptionReason();
              }
              else if (name.regionMatches(SI_EXCEPTION_OFFSET, SIProperties.SI_ExceptionInserts, SI_EXCEPTION_OFFSET, count)) {
                  value = getExceptionInserts();
              }
              else if (name.regionMatches(SI_EXCEPTION_OFFSET, SIProperties.SI_ExceptionTimestamp, SI_EXCEPTION_OFFSET, count)) {
                  value = getExceptionTimestamp();
              }
              else if (name.regionMatches(SI_EXCEPTION_OFFSET, SIProperties.SI_ExceptionProblemDestination, SI_EXCEPTION_OFFSET, count)) {
                  value = getExceptionProblemDestination();
              }
              else if (name.regionMatches(SI_EXCEPTION_OFFSET, SIProperties.SI_ExceptionProblemSubscription, SI_EXCEPTION_OFFSET, count)) {
                  value = getExceptionProblemSubscription();
              }
              else {
                  if (!forMatching) {
                      throw new IllegalArgumentException(name);
                  }
              }
  
          }
  
          /*------------------------------------------------------------------------*/
          /* Only if forMatching is true, now try all the message header properties */
          /*------------------------------------------------------------------------*/
          else if (forMatching) {
  
              count = name.length() - SI_PREFIX_LENGTH;
  
              if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_NextDestination, SI_PREFIX_LENGTH, count)) {
                  List<SIDestinationAddress> frp = getForwardRoutingPath();
                  if ((frp != null) && (frp.size() > 0)) {
                      value = frp.get(0).getDestinationName();
                  }
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_SystemMessageID, SI_PREFIX_LENGTH, count)) {
                  value = getSystemMessageId();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_Reliability, SI_PREFIX_LENGTH, count)) {
                  value = getReliability().toString();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_Priority, SI_PREFIX_LENGTH, count)) {
                  value = getPriority();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_TimeToLive, SI_PREFIX_LENGTH, count)) {
                  value = getTimeToLive();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_Discriminator, SI_PREFIX_LENGTH, count)) {
                  value = getDiscriminator();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_ReplyReliability, SI_PREFIX_LENGTH, count)) {
                  Reliability r = getReplyReliability();
                  value = (r == null) ? null : r.toString();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_ReplyPriority, SI_PREFIX_LENGTH, count)) {
                  value = getReplyPriority();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_ReplyTimeToLive, SI_PREFIX_LENGTH, count)) {
                  value = getReplyTimeToLive();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_ReplyDiscriminator, SI_PREFIX_LENGTH, count)) {
                  value = getReplyDiscriminator();
              }
              // For matching, SI_RedeliveredCount is based on deliveryCount rather than the real message field
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_RedeliveredCount, SI_PREFIX_LENGTH, count)) {
                  value = deliveryCount;
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_MessageID, SI_PREFIX_LENGTH, count)) {
                  value = getApiMessageId();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_CorrelationID, SI_PREFIX_LENGTH, count)) {
                  value = getCorrelationId();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_UserID, SI_PREFIX_LENGTH, count)) {
                  value = getUserid();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_Format, SI_PREFIX_LENGTH, count)) {
                  value = getFormat();
              }
              else if (name.regionMatches(SI_PREFIX_LENGTH, SIProperties.SI_DeliveryDelay, SI_PREFIX_LENGTH, count)) {
                  value = getDeliveryDelay();
              }
  
          }
  
          /*------------------------------------------------------------------------*/
          /* If not forMatching & neither Report or Exception it must be an error */
          /*------------------------------------------------------------------------*/
          else {
              throw new IllegalArgumentException(name);
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(this, tc, "getSIProperty", value);
          return value;
      }
  
      /**
       * Return the value of the given JMS Message Property.
       * <p>
       * For this method, JMS Message Properties include those which are strictly
       * JMS Header fields, as they are treated as properties for Message
       * Selection and for SIMessage.getMessageProperty().
       * All JMS properties are supported by this method - if a caller wants to
       * veto access of some properties then the caller must provide that code.
       * <p>
       * Some JMS properties map to header fields, some are extrapolated from
       * header fields, and some are stored directly in the system property map.
       * <p>
       * The method has package level visibility as it is called by JsJmsMessageImpl
       * and JsSdoMessageImpl.
       * 
       * @param name The name of the Property to be returned.
       * @param forMatching Indicates what the property will be used for (i.e. matching or getting)
       *            If forMatching:
       *            a) the byte array properties do not need
       *            to be copied as they will not be updated.
       *            b) some properties may use different algorithms
       * @return Serializable A reference to the Message Property.
       *         Null is returned if there is no such item.
       */
      final Serializable getJMSSystemProperty(String name, boolean forMatching) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(this, tc, "getJMSSystemProperty", name);
  
          Serializable value = null;
          int count;
  
          /*------------------------------------------------------------------------*/
          /* If it is a JMSX property.... */
          /*------------------------------------------------------------------------*/
          if (name.charAt(JMS_LENGTH) == JMSX_EXTRA_PREFIX) {
              count = name.length() - JMSX_LENGTH;
  
              if (name.regionMatches(JMSX_LENGTH, SIProperties.JMSXDeliveryCount, JMSX_LENGTH, count)) {
                  // For matching, JMSXDeliveryCount is based on deliveryCount rather than the real message field
                  if (forMatching) {
                      value = deliveryCount + 1;
                  }
                  else {
                      value = Integer.valueOf(getJmsxDeliveryCount());
                  }
              }
              else if (name.regionMatches(JMSX_LENGTH, SIProperties.JMSXAppID, JMSX_LENGTH, count)) {
                  value = getJmsxAppId();
              }
              else if (name.regionMatches(JMSX_LENGTH, SIProperties.JMSXUserID, JMSX_LENGTH, count)) {
                  value = getUserid();
              }
  
              /* The other supported JMSX properties just live in the system map */
              else {
                  if (mayHaveMappedJmsSystemProperties()) {
                      value = (Serializable) getJmsSystemPropertyMap().get(name);
                  }
              }
          }
  
          /*------------------------------------------------------------------------*/
          /* If it is a JMS_IBM_ property.... */
          /*------------------------------------------------------------------------*/
          else if (name.startsWith(JMS_IBM_EXTRA_PREFIX, JMS_LENGTH)) {
  
              /* First check for the Report ones */
              if (name.regionMatches(JMS_IBM_LENGTH, REPORT, 0, REPORT_LENGTH)) {
  
                  count = name.length() - REPORT_OFFSET;
  
                  if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_Expiration, REPORT_OFFSET, count)) {
                      value = getReportExpiry();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_Exception, REPORT_OFFSET, count)) {
                      value = getReportException();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_COA, REPORT_OFFSET, count)) {
                      value = getReportCOA();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_COD, REPORT_OFFSET, count)) {
                      value = getReportCOD();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_PAN, REPORT_OFFSET, count)) {
                      value = getReportPAN();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_NAN, REPORT_OFFSET, count)) {
                      value = getReportNAN();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_Pass_Msg_ID, REPORT_OFFSET, count)) {
                      value = getReportPassMsgId();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_Pass_Correl_ID, REPORT_OFFSET, count)) {
                      value = getReportPassCorrelId();
                  }
                  else if (name.regionMatches(REPORT_OFFSET, SIProperties.JMS_IBM_Report_Discard_Msg, REPORT_OFFSET, count)) {
                      value = getReportDiscardMsg();
                  }
              }
  
              else {
                  /* Then try the other smoke-and-mirrors ones */
                  count = name.length() - JMS_IBM_LENGTH;
  
                  if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_ExceptionReason, JMS_IBM_LENGTH, count)) {
                      value = getExceptionReason();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_ExceptionTimestamp, JMS_IBM_LENGTH, count)) {
                      value = getExceptionTimestamp();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_ExceptionProblemDestination, JMS_IBM_LENGTH, count)) {
                      value = getExceptionProblemDestination();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_ExceptionProblemSubscription, JMS_IBM_LENGTH, count)) {
                      value = getExceptionProblemSubscription();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_ExceptionMessage, JMS_IBM_LENGTH, count)) {
                      value = getExceptionMessage();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_Feedback, JMS_IBM_LENGTH, count)) {
                      value = getReportFeedback();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_System_MessageID, JMS_IBM_LENGTH, count)) {
                      value = getSystemMessageId();
                  }
                  else if (name.regionMatches(JMS_IBM_LENGTH, SIProperties.JMS_IBM_ArmCorrelator, JMS_IBM_LENGTH, count)) {
                      value = getARMCorrelator();
                  }
  
                  /* The other supported JMS_IBM_ properties just live in the system map */
                  else {
                      if (mayHaveMappedJmsSystemProperties()) {
                          value = (Serializable) getJmsSystemPropertyMap().get(name);
                      }
                  }
              }
  
          }
  
          /*------------------------------------------------------------------------------------*/
          /* If neither JMSX nor JMS_IBM_ then it must be a JMS header, or JMS_TOG_ARM_Correlator or not exist */
          /*------------------------------------------------------------------------------------*/
          else {
              count = name.length() - JMS_LENGTH;
  
              if (name.regionMatches(JMS_LENGTH, SIProperties.JMSDestination, JMS_LENGTH, count)) {
                  value = getJmsDestination();
                  if ((value != null) && (!forMatching)) {
                      byte[] copy = new byte[((byte[]) value).length];
                      System.arraycopy(value, 0, copy, 0, ((byte[]) value).length);
                      value = copy;
                  }
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSDeliveryMode, JMS_LENGTH, count)) {
                  value = getJmsDeliveryMode().toString();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSMessageID, JMS_LENGTH, count)) {
                  value = getApiMessageId();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSTimestamp, JMS_LENGTH, count)) {
                  value = getTimestamp();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSExpiration, JMS_LENGTH, count)) {
                  value = getJmsExpiration();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSRedelivered, JMS_LENGTH, count)) {
                  // For matching, JMSRedelivered is based on deliveryCount rather than the real message field
                  if (forMatching) {
                      if (deliveryCount > 0) {
                          value = Boolean.TRUE;
                      }
                      else {
                          value = Boolean.FALSE;
                      }
                  }
                  // Otherwise we just call the normal getJmsRedelivered() method which uses the real message field
                  else {
                      value = getJmsRedelivered();
                  }
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSPriority, JMS_LENGTH, count)) {
                  value = getPriority();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSReplyTo, JMS_LENGTH, count)) {
                  value = getJmsReplyTo();
                  if ((value != null) && (!forMatching)) {
                      byte[] copy = new byte[((byte[]) value).length];
                      System.arraycopy(value, 0, copy, 0, ((byte[]) value).length);
                      value = copy;
                  }
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSCorrelationID, JMS_LENGTH, count)) {
                  value = getCorrelationId();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSType, JMS_LENGTH, count)) {
                  value = getJmsType();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMS_TOG_ARM_Correlator, JMS_LENGTH, count)) {
                  value = getARMCorrelator();
              }
              else if (name.regionMatches(JMS_LENGTH, SIProperties.JMSDeliveryTime, JMS_LENGTH, count)) {
                  value = getJmsDeliveryTime();
              }
  
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(this, tc, "getJMSSystemProperty", value);
          return value;
      }
  
      /**
       * getMQMDProperty
       * Return the requested JMS_IBM_MQMD_ property, which may be in the table of explicitly
       * set properties, or may be in an underlying MQMD in the message itself.
       * <p>
       * The method has package level visibility as it is used by JsJmsMessageImpl
       * and JsSdoMessageImpl.
       * 
       * @param name The name of the property
       * 
       * @return Object The value of the requested property, or null if it does not exist.
       */
      final Object getMQMDProperty(String name) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(this, tc, "getMQMDProperty", name);
  
          Object result = null;
  
          // First check in the table of explictly set properties
          result = getMQMDSetPropertiesMap().get(name);
  
          // If we didn't find it there, look in the real JMS System Properties map
          // which will resolve through to the MQMD.
          if (result == null) {
              if (mayHaveMappedJmsSystemProperties()) {
                  result = getJmsSystemPropertyMap().get(name);
              }
          }
  
          // If we get a byte[], we had better return a safe copy so the app can't mess
          // up the one in the message.
          if ((result != null) && (result.getClass() == byte[].class)) {
              // Would have used Arrays.copyOf(), but the Thin Client still supports Java 1.5
              result = copyOf((byte[]) result, ((byte[]) result).length);
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(this, tc, "getMQMDProperty", result);
          return result;
      }
  
      /**
       * setMQMDProperty
       * Set the JMS_IBM_MQMD_ into the table of explicitly set properties.
       * <p>
       * The method has package level visibility as it is used by JsJmsMessageImpl
       * and JsSdoMessageImpl.
       * 
       * @param name The name of the property
       * @param value The new non-null value of the property
       */
      final void setMQMDProperty(String name, Object value) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(this, tc, "setMQMDProperty", new Object[] { name, value });
  
          // byte arrays aren't immutable so we need to take a copy of them. two
          // Both the ones we support need to be 24 bytes for MQ, so we take the opportunity
          // to pad or truncate if necessary.
          // Would have used Arrays.copyOf(), but the Thin Client still supports Java 1.5
          if ((value instanceof byte[])) {
              value = copyOf((byte[]) value, 24);
          }
  
          // Both of the Strings we support must not be longer than 48 characters, so we
          // truncate them if necessary.
          else if ((value instanceof String)) {
              if (((String) value).length() > 48) {
                  value = (((String) value).substring(0, 48));
              }
          }
  
          // Now set it into the table of explicitly set properties.
          getMQMDSetPropertiesMap().put(name, value);
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(this, tc, "setMQMDProperty");
      }