All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jeroenr.tepkin.protocol.message.Reply.scala Maven / Gradle / Ivy

The newest version!
package com.github.jeroenr.tepkin.protocol.message

import java.nio.{ByteBuffer, ByteOrder}

import akka.util.ByteString
import com.github.jeroenr.bson.BsonDocument
import com.github.jeroenr.bson.reader.BsonDocumentReader

import scala.collection.mutable.ArrayBuffer

/**
 * The OP_REPLY message is sent by the database in response to an OP_QUERY or OP_GET_MORE message.
 * The format of an OP_REPLY message is:
 *
 * {{{
 * struct {
 *     MsgHeader header;         // standard message header
 *     int32     responseFlags;  // bit vector
 *     int64     cursorID;       // cursor id if client needs to do get more's
 *     int32     startingFrom;   // where in the cursor this reply is starting
 *     int32     numberReturned; // number of documents in the reply
 *     document* documents;      // documents
 * }
 * }}}
 */
case class Reply(responseTo: Int,
                 responseFlags: Int,
                 cursorID: Long,
                 startingFrom: Int,
                 numberReturned: Int,
                 documents: List[BsonDocument]) extends Message {

  override def opCode: Int = Reply.OP_CODE

  override def encodeBody: ByteString = {
    val builder = ByteString.newBuilder
      .putInt(responseFlags)
      .putLong(cursorID)
      .putInt(startingFrom)
      .putInt(numberReturned)

    documents.foreach(doc => builder.append(doc.encode))

    builder.result()
  }

  def queryFailed: Boolean = (responseFlags & ResponseFlags.QueryFailure) != 0

  def cursorNotFound: Boolean = (responseFlags & ResponseFlags.CursorNotFound) != 0
}

object Reply {

  val OP_CODE = 1

  def apply(buffer: ByteBuffer): Option[Reply] = {
    buffer.order(ByteOrder.LITTLE_ENDIAN)
    val length = buffer.getInt

    if (length != buffer.remaining() + 4) {
      None
    } else {
      buffer.getInt // requestID, unused for Reply
      val responseTo = buffer.getInt
      val opCode = buffer.getInt
      if (opCode == OP_CODE) {
        val responseFlags = buffer.getInt
        val cursorID = buffer.getLong
        val startingFrom = buffer.getInt
        val numberReturned = buffer.getInt

        val documents = ArrayBuffer[BsonDocument]()

        while (buffer.hasRemaining) {
          documents ++= BsonDocumentReader.read(buffer)
        }

        Some(Reply(responseTo, responseFlags, cursorID, startingFrom, numberReturned, documents.toList))
      } else {
        None
      }
    }
  }

}


object ResponseFlags {

  /**
   * Set when getMore is called but the cursor id is not valid at the server. Returned with zero results.
   */
  val CursorNotFound: Int = 1

  /**
   * Set when query failed. Results consist of one document containing an $$err field describing the failure.
   */
  val QueryFailure: Int = 2

  /**
   * Set when the server supports the AwaitData Query option. If it doesn’t, a client should sleep a little
   * between getMore’s of a Tailable cursor. Mongod version 1.6 supports AwaitData and thus always sets AwaitCapable.
   */
  val AwaitCapable: Int = 8
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy