public int enqueue(int messageNo, IMessage message, MessageFuture future) throws IOException {
   QueueEntry qe = new QueueEntry();
   qe.messageNo = messageNo;
   qe.score = computeMessageScore(messageNo, message);
   qe.messageClass = message.getClass();
   qe.messageStream = messenger.serializeMessage(message);
   qe.future = future;
   if (LogUtil.isLogAvailable()) {
     // precompute the expected queue size...this is because the writer thread may quickly pick
     // up the queue item which, while swell, means the debug output may be confusing
     int newExpectedQueueSize = queue.size() + 1;
     Log.i(
         TAG,
         "Message "
             + qe.messageNo
             + " enqueued, it's a "
             + qe.messageClass.getSimpleName()
             + ", "
             + qe.messageStream.available()
             + " bytes in length, "
             + newExpectedQueueSize
             + " entries in the queue");
   }
   queue.add(qe);
   return qe.messageNo;
 }
  /**
   * Write one message (or part of a message) to the connected output stream.
   *
   * <p>This may write a partial message, if the message is bigger than our output buffer. If this
   * is the case, the queue entry is readded to the priority queue and given another chance to write
   * more data. This is repeated until the message is completely sent. Note that this means other,
   * higher priority messages may jump the line while this message is in the middle of sending its
   * data. This is ok, and how we allow the system to send command messages when long transfer
   * messages are in progress.
   *
   * @throws IOException
   */
  public void writeOne() throws IOException {
    // process canceled entires first
    processCanceled();

    // then process the next entry to be sent
    QueueEntry qe = nextQueueEntry();
    if (qe != null) {
      int read = qe.messageStream.read(outBytes);
      if (LogUtil.isLogAvailable()) {
        Log.d(
            TAG,
            "Message "
                + qe.messageNo
                + " written, it's a "
                + qe.messageClass.getSimpleName()
                + ", "
                + read
                + " bytes in length");
      }
      outStream.write(outBytes, 0, read);
      int left = qe.messageStream.available();
      // if there are bytes left to write, add this message back into the queue
      // to write at the next opportunity
      if (left > 0) {
        if (LogUtil.isLogAvailable()) {
          Log.d(TAG, "Message " + qe.messageNo + ", " + left + " bytes left to write");
        }
        queue.add(qe);
      } else {
        qe.future.setFinished(true);
        // otherwise, we're done
        if (LogUtil.isLogAvailable()) {
          Log.i(TAG, "Message " + qe.messageNo + " finished writing");
        }
      }
    }
  }
 private void processCanceled() throws IOException {
   Set<Integer> canceled = new HashSet<Integer>();
   synchronized (queueLock) {
     canceled.addAll(this.canceled);
     this.canceled.clear();
   }
   for (int messageNo : canceled) {
     QueueEntry found = removeQueueEntry(messageNo);
     if (found != null) {
       if (LogUtil.isLogAvailable()) {
         Log.d(TAG, "Message " + messageNo + " canceled");
       }
       PacketFormat cancelPacket = new PacketFormat(messageNo, new byte[0]);
       cancelPacket.addControlCode(ControlCode.Cancelled);
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       cancelPacket.serialize(baos);
       found.messageStream = new ByteArrayInputStream(baos.toByteArray());
       found.score = messageNo * CANCEL_TRANSFER_SCORE_MULTIPLIER;
       queue.add(found);
     }
   }
 }