docs: private boolean internalPut(
                                  SIMPMessage msg,
                                  TransactionCommon tran,
                                  InputHandlerStore inputHandlerStore,
                                  boolean storedByIH,
                                  boolean storedByCD,
                                  boolean firstPut) throws SIResourceException, SIDiscriminatorSyntaxException
      {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(
                          tc,
                          "internalPut",
                          new Object[] {
                                        msg,
                                        tran,
                                        inputHandlerStore,
                                        Boolean.valueOf(storedByIH),
                                        Boolean.valueOf(storedByCD),
                                        Boolean.valueOf(firstPut),
                                        msg.getMessage().getSystemMessageId() });
  
          long lastSpecificVersion = 0;
          // Used to determine if the consumer set has changed
          boolean finalMatch = false;
          // Once this is true it doesn't matter if the consumers change
          boolean continueSearch = true;
          DispatchableKey readyConsumer = null;
          boolean newMatchRequired = false;
          boolean grabCurrentReadyVersion = false;
          boolean statsNeedUpdating = firstPut;
          // Stats are only updated if firstPut
          long newestReadyVersion = Long.MAX_VALUE;
          // The newest ready consumer we're interested in
          MatchingConsumerPoint[] matchResults = null;
          // The search results object is used to retrieve the list of matching consumer keys
          // if needed and the result is stored in the matchResults object.
          MessageProcessorSearchResults searchResults = null;
  
          boolean specificConsumer = false;
          // Used to determine what kind of consumer we have
          boolean eligibleForDelivery = true;
          // Set to false if our noLocal check means we don`t dispatch the msg
          boolean shortLivedProducerSeedIncremented = false;
          // Set to true to ensure we do not double-increment the shortLivedProducerSeed
  
          // Private list of ready forward scanning consumers
          java.util.ArrayList<DispatchableKey> forwardScanningReadyConsumers = null;
  
          // Indicates that a messageItemReference was created early before the message
          // was stored.
          boolean referenceCreatedEarly = false;
  
          if (storedByCD)
          {
              // As we've already stored the message we only need to check the consumers that
              // were ready at the time (actually just after) we stored it. Any consumers
              // becoming ready since then will have to have checked the message store on
              // the way in and will see the message if its still there.
              grabCurrentReadyVersion = true;
          }
  
          while (continueSearch)
          {
              synchronized (_baseDestHandler.getReadyConsumerPointLock())
              {
  
                  //Once the message has been stored there is no need in trying to give a message
                  //to a newly ready consumer (in fact it could cause us to loop indefinitely). So
                  //once we've stored the message we grab the ready consumer set version (updated
                  //everytime a consumer goes from notready to ready)
                  if (grabCurrentReadyVersion)
                  {
                      newestReadyVersion = readyConsumerVersion;
                      grabCurrentReadyVersion = false;
                  }
                  //Search the non-specific ready list (performed under the lock to ensure we
                  //see the current ready consumers). popFirstReadyConsumer() will only return
                  //a consumer that was made ready as or before readyConsumerVersion exceeded
                  //the newestReadyVersion value.
                  //readyConsumer = nonSpecificReadyConsumerPoints.pop(newestReadyVersion);
                  readyConsumer = (DispatchableKey) nonSpecificReadyCPs.getFirst();
                  if (readyConsumer != null)
                  {
                      if (readyConsumer.getVersion() > newestReadyVersion)
                      {
                          readyConsumer = null;
                      }
                  }
  
                  if (readyConsumer != null)
                  {
                      // We have a nonSpecificReadyConsumer
                      specificConsumer = false;
  
                      // We may have a key group in our hands. This is a good point to resolve
                      // down to an individual member of the group
                      readyConsumer = readyConsumer.resolvedKey();
  
                      // We have a ready consumer, we have to release the readyList lock before
                      // we can try to deliver the message as this will attempt to get the
                      // consumer's lock and a consumer can hold this while trying to add
                      // themselves to the QP's ready list. This would cause deadlock.
  
                      // popFirstReadyConsumer() will have removed this consumer from the ready
                      // list as there are only two possible outcomes, either they take the
                      // message or they're no longer ready. In either case they can be removed
                      // from the list.
  
                      // As we've set readyConsumer we need do nothing here, just drop through
                  }
                  else
                  {
                      // We try for a specificReadyConsumer
                      specificConsumer = true;
  
                      // Otherwise, look to see if there are any ready specific consumers (performed
                      // under the lock to ensure we see the correct value)
                      if (specificReadyConsumerCount > 0)
                      {
                          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                              SibTr.debug(tc, "sepcificReadyConsumerCount:" + specificReadyConsumerCount);
  
                          // If the specific consumer set has changed since the last time we were
                          // here (or if it's our first time) then we have to get matchspace to
                          // re-parse the message. Otherwise we can use the results from the
                          // previous time round the loop. Again, this is under the lock to make sure
                          // we see the right version).
                          if ((specificConsumerVersion != lastSpecificVersion)
                              && (!finalMatch))
                          {
                              if ((lastSpecificVersion > 0) // i.e. we've matched before
                                  && (!storedByCD))
                              {
                                  // If we already have a set of match results and the consumer set has changed
                                  // since then but we haven't put the message into the Message Store yet we may as
                                  // well skip the match here and store the message otherwise, if the consumer
                                  // set keeps changing, we'll go round this match loop forever.
                              }
                              else
                              {
                                  newMatchRequired = true;
                                  lastSpecificVersion = specificConsumerVersion;
  
                                  // If the message is already in the Message Store it is visible to new consumers
                                  // therefore, once we've performed this match it doesn't matter if new consumers
                                  // come along later as they'll have the chance to see the message without our
                                  // help. So this match can be our last.
                                  if (storedByCD)
                                      finalMatch = true;
                              }
                          }
                          else
                          {
                              // If we are here and matchResults are null, then we'll have
                              // driven the MatchSpace to analyseMessages for XD but have yet
                              // to extract ConsumerPoint results from the SearchResults
                              if (matchResults == null)
                              {
                                  // Extract the ConsumerPoint results
                                  Object allResults[] =
                                                  searchResults.getResults(_baseDestHandler.getName());
                                  Set matchSet =
                                                  (Set) allResults[MessageProcessorMatchTarget.JS_CONSUMER_TYPE];
                                  matchResults =
                                                  (MatchingConsumerPoint[]) matchSet.toArray(new MatchingConsumerPoint[0]);
                              }
                              // Now process the matchResults
                              if (matchResults.length > 0)
                              {
                                  // We probably have ready specific consumers and we have a valid set
                                  // of matchspace results so we need to pick one to deliver the
                                  // message to.
  
                                  // We need a seed to allow us to attempt a round-robin selection
                                  // from the set of ready consumers. For a long lived producer,
                                  // this is based on the number of messages produced in the session.
                                  int seed = msg.getProducerSeed();
  
                                  // When the producer is short lived (common in J2EE) we cannot use
                                  // the count of messages from that producer to provide even distribution.
                                  // In this case we increment our own dispatcher-wide seed count.
                                  // Negative counts mean an producer which has wrapped passed maxint.
                                  if (seed < SIMPConstants.LONG_LIVED_PRODUCER_THRESHOLD && seed >= 0) {
                                      // PK74905 This producer has sent few messages - use local seed count.
                                      seed = shortLivedProducerSeed;
                                      // Ensure we only increment the seed once, as we might go round
                                      // the loop again and re-drive this logic (for a non-transacted
                                      // send where we find we need to call store before we deliver).
                                      // 630988.1 defect information
                                      // Let us increment the seed value only if the match results return more than 1.
                                      // When there are two MDB instances (two WPS app servers in a cluster), each
                                      // matching request message would have two waiting consumers, but each reply message
                                      // would only have one matching consumer, And as they arrive interleaved, incrementing
                                      // the seed for every message would always result in the same request message consumer being
                                      // chosen first. As there is no need to use the seed when there is only one matching consumer
                                      // (the reply messages), then avoiding the seed increment would prevent this anomaly
                                      if (!shortLivedProducerSeedIncremented && matchResults.length > 1) { // 630988.1
                                          shortLivedProducerSeed++;
                                          shortLivedProducerSeedIncremented = true;
                                      }
                                  }
  
                                  // Use the seed to determine the startpoint in the matchspace results array
                                  int startPoint = seed % matchResults.length;
  
                                  // Protect against a negative index. Mod can give negative so we need
                                  // this here, but we don't have to worry about Integer.MIN_VALUE problems.
                                  if (startPoint < 0)
                                      startPoint = (0 - startPoint);
  
                                  int index = startPoint;
  
                                  // Check for a ready consumer
                                  while (readyConsumer == null)
                                  {
                                      // Retrieve the DispatchableKey from the match result.
                                      final DispatchableKey match = matchResults[index].
                                                      getConsumerPointData();
  
                                      // Find a ready consumer that is old enough for us
                                      if ((match.isKeyReady()) && (match.getVersion() <= newestReadyVersion))
                                      {
                                          // Remember this consumer but we won't try to deliver the message
                                          // until the lock is released.
                                          readyConsumer = match;
                                      }
                                      else
                                      {
                                          // Move on to the next consumer but check we haven't looped round
                                          // to the start.
                                          index = (index + 1) % matchResults.length;
                                          if (index == startPoint)
                                              break;
                                      }
                                  }
                              }
                          }
                      }
                  }
  
                  // If we have a consumer, and while we still have the lock, we need to check if
                  // the consumer has specified noLocal and is still eligible for the message. (i.e.
                  // it is not on the same connection as the producer). If ineligible then we
                  // stop looking for consumers, do not store, and drop through to the end of the
                  // method.
  
                  if (readyConsumer != null)
                  {
                      // If noLocal is set to true and...
                      // If the ProducerConnectionID is the same as the ConsumerConnection ID then we
                      // do not deliver or store this message.
                      if (dispatcherState.isNoLocal()
                          && readyConsumer.getConnectionUuid().equals(msg.getProducerConnectionUuid()))
                      {
                          // We don`t want to deliver to the consumer since the message was
                          // produced on the same connection. Therefore flag that we want to
                          // "drop" the message from this consumer
                          eligibleForDelivery = false;
                          continueSearch = false;
  
                      }
                      else
                      {
                          // If we haven`t stored the message yet, check whether we need to.
                          if ((!storedByCD)
                              && (readyConsumer.requiresRecovery(msg) || msg.isTransacted()))
                          {
                              // If we have a transacted consumer or producer and we haven`t stored the message yet,
                              // then do not deliver yet. Wait until the message is stored.
                              readyConsumer = null;
  
                              if (msg.isTransacted())
                              {
                                  // If producer transacted then drop out. We don`t want to deliver yet,
                                  // we leave this until the postCommit call. We know that there is an eligible
                                  // consumer (after the noLocal checks) and we therefore need to store the message.
                                  continueSearch = false;
                              }
                          }
                          else
                          {
                              // Make the consumer unready because we are going to use it.
                              readyConsumer.markNotReady();
  
                              // and forget about them for now
                              removeReadyConsumer(readyConsumer.getParent(), specificConsumer);
  
                              // PM31067 start
                              // If we are pub/sub and we have an unrecoverable message (BENP) or not transacted
                              // then we will be trying to give the message straight to the consumers rather than storing
                              // the message first. This means we will be bypassing the storeMessage method which
                              // would have created the MessageItemReference, this also means we don't reply on any 
                              // transactional callbacks, such as in the BENP transacted case.
                              // We need to create a MsgItemReference for each interested consumer as when we deliver
                              // the message to the consumer the LCP will be registering for message events. If each
                              // LCP (consumer) does this on the same MessageItem then we throw an InternalException.
                              // We need each LCP to register for message events on there own MessageItemReference.
                              //
                              // If we do the below but then the consumer does not take the message (giveMessagetoConsumer
                              // returns false) then we will store the message. In this case we need to revert back to the
                              // original messageItem to store rather than this new messageItemReference.
                              if (isPubSub())
                              {
                                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                                      SibTr.debug(tc, "An non-recoverable or non-transacted pubsub message, create MsgReference");
  
                                  // there is a chance that we might be passed in a MessageItemReference to start with if
                                  // we have gone down this path before but the consumer became unready to take the message 
                                  if (msg instanceof MessageItem)
                                  {
                                      referenceCreatedEarly = true;
                                      MessageItemReference msgRef = new MessageItemReference((MessageItem) msg, true);
                                      msg = msgRef;
                                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                                          SibTr.debug(tc, "MsgReference created: " + msg);
                                  }
                                  else
                                  {
                                      // the msg is probably already a msgItemReference so no need to do anything
                                  }
                              }
                          }
                      }
                  }
                  // If we have no normal consumer ready we can see if there are any forward scanning
                  // consumers ready. If there are and the message is already in the message store we
                  // can just wake them all up and let them fight for the message.
                  // We have to wake them all up as we don't know which if any of them has a cursor
                  // behind this message, if we picked one and their cursor was already ahead of the message
                  // the consumer would accept the put but not see the message and this method would
                  // assume it had sccessfully given the message to someone and not try another
                  // forward scanning consumer. So instead by waking them all up and they
                  // all check for the message and at most one locks it. If this consumer then chooses
                  // not to keep it they will unlock it and this method will be redriven, giving the
                  // other consumers a chance.
                  //TODO Currently the MS cursor will actually move all cursors over the new message
                  //     so when we wake them up the second time they won't see the message. MS promise
                  //     to fix this soon.
                  else
                  {
                      if (storedByCD)
                      {
                          // We don't hold the list lock while we wake up the consumers so we have to
                          // make a copy of the list of ready forward scanning consumers while we still hold
                          // the lock.
                          DispatchableKey readyFSConsumer = (DispatchableKey) readyFwdScanningCPs.getFirst();
                          if (readyFSConsumer != null)
                          {
                              DispatchableKey nextReadyFSConsumer;
                              // Make the copy list
                              forwardScanningReadyConsumers = new java.util.ArrayList<DispatchableKey>();
                              while (readyFSConsumer != null)
                              {
                                  // Remember the next entry in the list
                                  nextReadyFSConsumer = (DispatchableKey) ((SimpleEntry) readyFSConsumer).next();
                                  // Add the current entry to the copy list
                                  forwardScanningReadyConsumers.add(readyFSConsumer);
                                  // Mark this consumer as no longer ready
                                  readyFSConsumer.markNotReady();
                                  //Remove them from the ready list
                                  ((SimpleEntry) readyFSConsumer).remove();
                                  // Move the cursor on to the next entry
                                  readyFSConsumer = nextReadyFSConsumer;
                              }
                          }
                      }
                  }
  
              } // synchronized
  
              // Now we are no longer synchronized we can do the actual work...
  
              // If our noLocal check showed that we don`t want to deliver to this
              // consumer, then drop out of the method.
              if (eligibleForDelivery)
              {
  
                  // If we managed to find a ready consumer we can try to deliver it to them.
                  // It is possible that they are no longer interested so we'll have to do all
                  // the above again.
                  if (readyConsumer != null)
                  {
                      // We must NOT hold the readyList lock as this method will attempt to lock
                      // the consumer. The consumer can attempt to lock the readyList while it
                      // holds this lock. This prevents deadlock
                      if (!giveMessageToConsumer(msg,
                                                 tran,
                                                 readyConsumer.getConsumerPoint(),
                                                 storedByCD))
                      {
                          continueSearch = true;
                      }
                      else
                      {
                          continueSearch = false;
                      }
  
                      // If the consumer rejects the message (they are no longer ready) we need
                      // to do it all over again.
                      if (continueSearch)
                      {
                          readyConsumer = null;
                      }
  
                  }
                  // If we have a set of forward scanning consumers we can wake them all up now
                  // and let them fight for the message.
                  else if (forwardScanningReadyConsumers != null)
                  {
                      Iterator<DispatchableKey> consumerIterator =
                                      forwardScanningReadyConsumers.iterator();
                      while (consumerIterator.hasNext())
                      {
                          giveMessageToConsumer(
                                                msg,
                                                tran,
                                                (consumerIterator.next()).getConsumerPoint(),
                                                storedByCD);
                      }
                      continueSearch = false;
                  }
                  // If we need matchspace to parse the message to get a set of matching
                  // consumers we should do it now, then we can go back round and see if any
                  // of the matching consumers are ready.
                  else if (newMatchRequired)
                  {
                      // Get a search results object to use
                      searchResults =
                                      (MessageProcessorSearchResults) _messageProcessor
                                                      .getSearchResultsObjectPool()
                                                      .remove();
  
                      // Search the MatchSpace to find the set of matching consumers
                      searchMatchSpace(msg, searchResults);
  
                      // Extract the ConsumerPoint results
                      Object allResults[] =
                                      searchResults.getResults(_baseDestHandler.getName());
                      Set matchSet =
                                      (Set) allResults[MessageProcessorMatchTarget.JS_CONSUMER_TYPE];
                      matchResults =
                                      (MatchingConsumerPoint[]) matchSet.toArray(new MatchingConsumerPoint[0]);
                      newMatchRequired = false;
                  }
                  // If neither of the above were true we have checked all consumers and none
                  // of them were ready. If we haven't yet written the message out to the
                  // message store we should do so now, then we need to go back round to see
                  // if anyone became ready while we were doing it.
                  else if (!storedByCD)
                  {
                      if (referenceCreatedEarly)
                      {
                          try
                          {
                              msg = (MessageItem) ((MessageItemReference) msg).getReferredItem();
                          } catch (SevereMessageStoreException e)
                          {
                              FFDCFilter.processException(
                                                          e,
                                                          "com.ibm.ws.sib.processor.impl.ConsumerDispatcher.internalPut",
                                                          "1:1553:1.280.5.25",
                                                          this);
  
                              if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
                                  SibTr.exit(tc, "internalPut", "SevereMessageStoreException");
  
                              throw new SIResourceException(e);
                          }
                      }
  
                      storedByIH =
                                      storeMessage(
                                                   (MessageItem) msg,
                                                   tran,
                                                   inputHandlerStore,
                                                   storedByIH);
                      storedByCD = true;
  
                      // As we've now stored the message we only need to check the consumers that
                      // were ready at the time (actually just after) we stored it. Any consumers
                      // becoming ready since then will have to have checked the message store on
                      // the way in and will see the message if its still there.
                      grabCurrentReadyVersion = true;
  
                      // If we have stored the message as part of a transaction we have to drop
                      // out of the search and wait until the message has been committed before
                      // we can choose a consumer for it.
                      // A transaction = null means that it is Auto Commit
                      if (tran != null && !tran.isAutoCommit())
                          continueSearch = false;
                  }
                  // Otherwise we've checked everyone was ready after we'd written the
                  // message to the message store so we can stop trying. If a consumer comes
                  // along now ooking for a message it will find it in the message store with
                  // no help from us.
                  else
                  {
                      continueSearch = false;
                  }
              }
          }
  
          // If a MessageProcessorSearchResults object was created, add it back into the pool
          // at this point as it is now safe to do so.
          if (searchResults != null)
              _messageProcessor.getSearchResultsObjectPool().add(searchResults);
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(tc, "internalPut", Boolean.valueOf(storedByIH));
  
          return storedByIH;
      }