docs: @Override
      public void processAckExpected(long stamp)
      {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.entry(this, tc, "processAckExpected", new Object[] { Long.valueOf(stamp) });
  
          List nackList = null;
          List msgList = null;
          boolean processNack = true;
  
          // Take lock on target stream and hold it until messages have been
          // added to batch by deliverOrderedMessages 
          synchronized (this)
          {
              synchronized (oststream) //see defect 289889
              {
                  long completedPrefix = oststream.getCompletedPrefix();
                  long blockingTick = -1;
                  // Send Ack message for ticks up to completedPrefix
                  sendAck();
                  if (isStreamBlocked())
                  {
                      // If the link was implicitly blocked due to an 'unexpected' problem when we tried
                      // to deliver a message (the most obvious reason is due to not being authorized)
                      // we can't keep the stream blocked as we need to allow messages in again to re-try
                      // the deliverery of the message to be able to tell if the problem still exists.
                      // However, rather than nack all messages currently blocked on the stream (which is
                      // what the source end will be ack-expecting) we only nack the blocking message,
                      // if that succeeds the stream will be left un-blocked, but if it fails the stream
                      // will be re-locked. If the stream is left un-blocked, the next time an ack-expected
                      // comes in we'll nack everything.
                      if (isStreamBlockedUnexpectedly())
                      {
                          stamp = linkBlockingTick;
                          blockingTick = linkBlockingTick;
  
                          // We need to unblock the stream, otherwise the failing message won't be re-delivered
                          // when it arrives (it'll get bounced in writeValue), and that's the only way to tell
                          // is the failure is still valid (as it's not detected by streamCanAcceptNewMessage).
                          setStreamIsBlocked(false, DestinationHandler.OUTPUT_HANDLER_FOUND, null, null);
                      }
                      else
                      {
                          // The stream has been marked as blocked. We should only send 
                          // NACKs if we are able to accept the resent message once it arrives.
                          // Because ACKs are for the stream as a whole, if this is a link
                          // then we need to check whether the link's exception destination is
                          // able to accept messages. If no exception destination is defined on the
                          // link then we check the link blocking destination.
                          int blockingReason = deliverer.checkStillBlocked();
                          if (blockingReason == DestinationHandler.OUTPUT_HANDLER_FOUND)
                          {
                              //the destination is no longer blocked
                              setStreamIsBlocked(false, DestinationHandler.OUTPUT_HANDLER_FOUND, null, null);
                          }
                          else
                          {
                              // Its possible that the reason for the blockage has changed. This code allows
                              // the link health state to be altered to reflect that change (but keeps the
                              // same blocking tick as that hasn't changed)
                              setStreamIsBlocked(true, blockingReason, null, streamBlockingAddress);
  
                              // It's possible that the blockage has been cleared on the sending side without
                              // us knowing about it (e.g. the offending message was deleted but the silence never
                              // made it to us). So, to err on the side of caution, we occasionally forget about
                              // the blockage and allow the blocking tick to be nack'd (just that one, not all
                              // of them, we don't want them all to be re-sent until we know the problem doesn't
                              // still exist). This will either cause the same problematic message to be re-sent
                              // and the link will stay blocked or a silence will arrive indicating the blockage
                              // has been cleared, allowing the subsequent ackExcepted to result in a full Nack
                              if (blockingCount++ > 3)
                              {
                                  stamp = linkBlockingTick;
                                  blockingCount = 0;
                                  blockingTick = linkBlockingTick;
  
                                  // Note: In this case (unlike the unexpected block case above) we can leave the
                                  // stream blocked as the original problem was detected by streamCanAcceptNewMessage,
                                  // if the mesasge is re-sent writeValue will have a chance of processing it if
                                  // the call to streamCanAcceptNewMessage thinks the problem has been resolved.
  
                                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                                      SibTr.debug(tc, "Nacking the first tick (" + stamp + ") of a blocked link");
                              }
                              // The stream is blocked (for now) so we don't want the blocking message (and all
                              // the ones built up behind it) to be resent because we nack them!
                              else
                                  processNack = false;
                          }
                      }
                  }
                  //We're allowed to send Nacks
                  if (processNack)
                  {
                      if (stamp <= completedPrefix)
                      {
                          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
                              SibTr.exit(tc, "processAckExpected");
                          return;
                      }
  
                      // walk the oststream from the completedPrefix to endstamp and Nack
                      // everything that is Unknown or Requested
                      // Change everything which is Unknown to Requested.
                      nackList = new ArrayList();
                      long startstamp = completedPrefix + 1;
                      long endstamp = stamp;
                      oststream.setCursor(startstamp);
                      TickRange tr2 = null;
                      TickRange tr1 = oststream.getNext();
                      do
                      {
                          if ((tr1.type == TickRange.Unknown)
                              || (tr1.type == TickRange.Requested))
                          {
                              long ss, es;
  
                              // Handle case where start or endstamps fall within a range
                              ss = tr1.startstamp > startstamp ? tr1.startstamp : startstamp;
                              es = endstamp > tr1.endstamp ? tr1.endstamp : endstamp;
  
                              TickRange tr = new TickRange(TickRange.Requested, ss, es);
  
                              // Only need to update stream if this is currently Unknown
                              if (tr1.type == TickRange.Unknown)
                              {
                                  oststream.writeRange(tr);
                              }
  
                              // SIB0105
                              // Set the head msg health state to AMBER if neccessary
                              synchronized (pendingAlarms)
                              {
                                  // If we have no current gaps then we must be missing msg at the head of the stream
                                  if (!getAlarms(this).hasNext())
                                  {
                                      getControlAdapter().getHealthState().updateHealth(HealthStateListener.MSG_LOST_ERROR_STATE,
                                                                                        HealthState.AMBER);
                                      lastNackTick = endstamp;
                                  }
                              }
  
                              nackList.add(tr);
                          }
                          // If the stream is blocked but we want to force the message (or a silence if it's
                          // been deleted on the source side) to be sent again then we need to add the blocking
                          // range to the nack list, even if it's in value state (which is possible if we tried
                          // to deliver it before but failed (so it's already added to the stream, but not
                          // delivered yet).
                          else if ((tr1.type == TickRange.Value) && (tr1.endstamp == blockingTick))
                          {
                              nackList.add(tr1);
                          }
                          tr2 = tr1;
                          tr1 = oststream.getNext();
                      } while ((tr1.startstamp <= endstamp) && (tr1 != tr2));
                  }//end processNack
                  else
                  {
                      //we did not process the AckExpected message for any nacks
                      //as we did not have room for the replies
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                          SibTr.debug(tc, "did not process AckExpected message for nacks.");
  
                  }
  
                  // PK57736 We should should this stimulus from the sending side
                  // to advance the doubt horizon, as it is possible they are not
                  // sending messages any longer (which is the other stimulus).
                  msgList = advanceDoubtHorizon(null);
  
              } // end synchronized
  
              // PK57736 Deliver messages outside of synchronise 
              // We do this because deliverOrderedMessages takes the 
              // BatchHandler lock and the BatchHandler callbacks require
              // the stream lock to update the completedPrefix. If we call
              // the BatchHandler when we hold the stream lock it could cause
              // a deadlock
              if (msgList != null)
              {
                  // Call the Input or Output Handler to deliver the messages
                  try
                  {
                      deliverer.deliverOrderedMessages(msgList, this, priority, reliability);
                  } catch (SINotPossibleInCurrentConfigurationException e)
                  {
                      // No FFDC code needed
                      SibTr.exception(tc, e);
  
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                          SibTr.debug(tc, "processAckExpected", "GDException");
  
                      // Dont rethrow the exception. The GD protocols will handle the resend of
                      // the message
                  } catch (SIException e)
                  {
                      // FFDC
                      FFDCFilter.processException(
                                                  e,
                                                  "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream.processAckExpected",
                                                  "1:1201:1.110",
                                                  this);
  
                      SibTr.exception(tc, e);
                      SibTr.error(tc, "INTERNAL_MESSAGING_ERROR_CWSIP0002",
                                  new Object[] {
                                                "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream",
                                                "1:1208:1.110",
                                                e });
  
                      if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
                          SibTr.exit(tc, "processAckExpected", "GDException");
  
                      throw new SIErrorException(
                                      nls.getFormattedMessage(
                                                              "INTERNAL_MESSAGING_ERROR_CWSIP0002",
                                                              new Object[] {
                                                                            "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream",
                                                                            "1:1218:1.110",
                                                                            e },
                                                              null),
                                      e);
                  }
              }
          } // end sync - release lock on target stream
  
          // send nacks
          for (int j = 0; processNack && j < nackList.size(); j++)
          {
              TickRange temptr = (TickRange) nackList.get(j);
  
              try
              {
                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                      SibTr.debug(tc, "processAckExpected sending Nack from: " + temptr.startstamp + " to " + temptr.endstamp);
  
                  upControl.sendNackMessage(
                                            streamSet.getRemoteMEUuid(),
                                            streamSet.getDestUuid(),
                                            streamSet.getBusUuid(),
                                            temptr.startstamp,
                                            temptr.endstamp,
                                            priority,
                                            reliability,
                                            streamSet.getStreamID());
              } catch (SIResourceException e)
              {
                  // FFDC
                  FFDCFilter.processException(
                                              e,
                                              "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream.processAckExpected",
                                              "1:1252:1.110",
                                              this);
  
                  SibTr.exception(tc, e);
                  SibTr.error(tc, "INTERNAL_MESSAGING_ERROR_CWSIP0002",
                              new Object[] {
                                            "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream",
                                            "1:1259:1.110",
                                            e });
  
                  if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
                      SibTr.exit(tc, "processAckExpected", e);
  
                  throw new SIErrorException(
                                  nls.getFormattedMessage(
                                                          "INTERNAL_MESSAGING_ERROR_CWSIP0002",
                                                          new Object[] {
                                                                        "com.ibm.ws.sib.processor.gd.GuaranteedTargetStream",
                                                                        "1:1270:1.110",
                                                                        e },
                                                          null),
                                  e);
              }
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
              SibTr.exit(tc, "processAckExpected");
      }