else {
else if (ack.isAck()) {
  public MessageAttributes forwardMessage (AttributedMessage msg)     throws UnregisteredNameException, NameLookupException,            CommFailureException, MisdeliveredMessageException  {    //  Sanity Check:  All messages must have a number    if (!MessageUtils.hasMessageNumber (msg))    {      //  NOTE:  This can happen if this aspect is called in the wrong order      String s = "Msg has no number! (Check the aspect order)";      log.error (s);        throw new CommFailureException (new Exception (s));    }    //  Special Case:  Local messages are completely excluded from acking    if (MessageUtils.isLocalMessage (msg)) return super.forwardMessage (msg);    //  Deal with new & retry/resend messages    Ack ack = MessageUtils.getAck (msg);    String msgString = MessageUtils.toString (msg);    String toNode = MessageUtils.getToAgentNode (msg);    if (ack == null)    {      //  A new message (to acking) - insert an ack into it      ack = new Ack (msg);      MessageUtils.setAck (msg, ack);      ack.setResendMultiplier (MessageAckingAspect.resendMultiplier);      //  This info is updated by the AdaptiveLinkSelection on retrys/resends      ack.addLinkSelection (link);  // the first selection (already made)      ack.setSendLink (link.getProtocolClass().getName());      ack.setRTT (MessageAckingAspect.getBestFullRTTForLink (link, toNode, msg));    }    else    {      //  We have a message being retried or resent.  If it has been acked in the meantime      //  we can just return success.      if (MessageAckingAspect.hasMessageBeenAcked (msg))      {        if (log.isDebugEnabled()) log.debug ("AckFrontend: Dropping acked resend " +msgString);        return success;      }      //  We drop any resends over excluded links as they manage their own resends      if (ack.getSendCount() > 0 && MessageAckingAspect.isExcludedLink(link))      {        if (log.isDebugEnabled()) log.debug ("AckFrontend: Dropping resend over excluded link " +msgString);        return success;      }    }    //  Set the specific acks based on the ack type    if (ack.isAck())    {      //  We set the specific acks here only once.  That is because we need to      //  know what acks have been acked when this message is acked, and if we      //  set (potentially) different acks with each resend of this message, we       //  are not going to know which send or resend made it to the other       //  side and thus what acks we can safely retire.      if (ack.getSendCount() == 0)       {        ack.setSpecificAcks (MessageAckingAspect.getAcksToSend (toNode));      }    }    else if (ack.isPureAck())    {      //  For pure ack messages we only send the latest acks      ack.setSpecificAcks (null);    }    else if (ack.isPureAckAck())    {      //  The specific acks within a pure ack-ack message have already been      //  set and they do not change.    }    //  We always set the latest acks    ack.setLatestAcks (MessageAckingAspect.getAcksToSend (toNode));    //  Special handling for pure acks    if (ack.isPureAck())    {      //  Simple filters that retire pure ack messages      PureAck pureAck = (PureAck) ack;            if (pureAck.getLatestAcks().isEmpty())       {        if (log.isDebugEnabled()) log.debug ("AckFrontend: Latest acks empty, dropping " +msgString);        return success;      }      if (!pureAck.stillAckingSrcMsg())      {        if (log.isDebugEnabled()) log.debug ("AckFrontend: Src msg out of acks, dropping " +msgString);        return success;      }      if (!MessageAckingAspect.findAckToSend ((PureAckMessage)msg))      {        if (log.isDebugEnabled()) log.debug ("AckFrontend: Pure ack acked, dropping " +msgString);        return success;      }      //  Check if some other message has beat the pure ack outta here, meaning we      //  don't need to send it as its information has been piggy-backed on the      //  other message via its regular (embedded) ack.      long lastSendTime = MessageAckingAspect.getLastSendTime (link, toNode);      if (lastSendTime > pureAck.getAckSendableTime())      {        //  No need to send the ack over this link, reschedule it to consider        //  another link.        float rtt = (float) ack.getRTT();        long deadline = lastSendTime + (long)(rtt * MessageAckingAspect.interAckSpacingFactor);        pureAck.setSendDeadline (deadline);        if (log.isDebugEnabled()) log.debug ("AckFrontend: Rescheduling " +msgString);        MessageAckingAspect.pureAckSender.add ((PureAckMessage)msg);        return success;      }    }    //  Last chance to bail before sending the message    if (MessageAckingAspect.hasMessageBeenAcked (msg))     {      if (log.isDebugEnabled()) log.debug ("AckFrontend: Dropping acked resend " +msgString);      return success;    }    //  Try sending the message    try    {      if (true) // MessageAckingAspect.showTraffic)      {        if (log.isInfoEnabled())        {          StringBuffer buf = new StringBuffer();          buf.append ("\n\n>");          buf.append (AdaptiveLinkSelectionPolicy.getLinkLetter(link));          buf.append (" ");          buf.append (MessageUtils.getMessageNumber(msg));          buf.append (".");          buf.append (ack.getSendCount()+1);  // what gets sent          buf.append (" ");          buf.append (MessageUtils.getMessageTypeLetter(msg));          buf.append (" ");          buf.append (MessageUtils.getFromAgent(msg).toShortString());          buf.append (" to ");          buf.append (MessageUtils.getToAgent(msg).toShortString());          buf.append ("\n");          log.info (buf.toString());                 }        else if (log.isDebugEnabled())        {          StringBuffer buf = new StringBuffer();          buf.append ("\n\n");          buf.append (ack.getSendCount()==0 ? "Sending  " : "REsending ");          buf.append (MessageUtils.toShortString (msg));          buf.append (" via ");          buf.append (MessageAckingAspect.getLinkType (ack.getSendLink()));          buf.append (" of ");          buf.append (MessageUtils.getAckingSequenceID (msg));          buf.append ("\n");          AckList.printAcks (buf, "specific", ack.getSpecificAcks());          AckList.printAcks (buf, "  latest", ack.getLatestAcks());          // System.err.println ("Outbound roundtrip time: " +ack.getSenderRoundtripTime());          log.debug (buf.toString());                 }      }      //  If the message has not yet been successfully sent on its first leg,      //  we set its send time and add it to the ack waiter.  The ack waiter      //  manages resending the message as needed.  We do this even for excluded links,      //  as we are doing this here before the send to handle the case where this      //  first send gets stuffed up down the line for some reason (a frozen socket?)       //  thus preventing the message to make it onto the ack waiter to enable it      //  to be resent around the obstruction if we did this after the send.  Above      //  we make sure than any resends don't go out on any excluded links in case      //  one is chosen.      if (ack.getSendCount() == 0)      {        ack.setSendTime (now());  // resends get their send time set in the ack waiter        MessageAckingAspect.ackWaiter.add (msg);      }        ack.incrementSendTry();      ack.incrementSendCount();      success = link.forwardMessage (msg);  // message send    }    catch (Exception e)    {      //  Ok, the send failed.  If this is the first send leg for this message, we       //  remove the message from the message resender and throw an exception, otherwise      //  we notify the message resender to reschedule the send.  Either way, a new link       //  selection will be made and the message will comes back thru and try again.      if (log.isInfoEnabled()) log.info ("Failure sending " +msgString+ ":\n" +stackTraceToString(e));      ack.decrementSendCount();  // didn't actually send      if (ack.getSendCount() == 0)       {        //  Cougaar is in control of retries        MessageAckingAspect.ackWaiter.remove (msg);        throw new CommFailureException (e);        }      else       {        //  We are in control of resends (over non-excluded links)        MessageAckingAspect.ackWaiter.scheduleImmediateResend (msg);        return success;  // not really of course      }    }    //  Ok, the first (and possibly only) leg of the message send has completed successfully    MessageAckingAspect.setLastSendTime (ack.getSendLink(), toNode, ack.getSendTime());    MessageAckingAspect.setLastSuccessfulLinkUsed (toNode, link.getProtocolClass());    if (ack.isAck() && MessageUtils.getMessageNumber(msg) != 0)    {      //  If the protocol link used to send the message is excluded from acking, we create      //  an ack for it so the ack waiter can process the message and not resend it.  It is      //  certainly a race to put this ack out there before the message gets resent, but in      //  most cases no resend should occur unless it really needed to.  Of course the       //  receiver is protecting himself from duplicates, so only some effort would be lost.      if (MessageAckingAspect.isExcludedLink (link))      {        //  Manufacture an ack for the message        AckList ackList = new AckList (MessageUtils.getFromAgent(msg), MessageUtils.getToAgent(msg));        ackList.add (MessageUtils.getMessageNumber (msg));        Vector v = new Vector();        v.add (ackList);        MessageAckingAspect.addReceivedAcks (toNode, v);      }    }    else if (ack.isPureAck())    {      //  We reschedule pure acks so they will be sent again if needed      PureAck pureAck = (PureAck) ack;      long lastSendTime = pureAck.getSendTime();      float rtt = (float) ack.getRTT();      long deadline = lastSendTime + (long)(rtt * MessageAckingAspect.interAckSpacingFactor);      pureAck.setSendDeadline (deadline);      MessageAckingAspect.pureAckSender.add ((PureAckMessage)msg);    }    return success;  // msg successfully sent (so far)  }