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

com.zink.scala.fly.stub.MethodCodec.scala Maven / Gradle / Ivy

package com.zink.scala.fly.stub

import com.zink.scala.fly.kit.Logging

import scala.actors.Actor
import com.zink.scala.fly.{ FlyPrime, FieldCodec, FlyAccessException }
import java.io.{ ByteArrayOutputStream, DataOutputStream }
import java.net.InetAddress
import java.nio.ByteBuffer

/**
 * Method Codec
 *
 * This is where methods on the implementing interface get converted into into
 * the binary format calls. The binary formats of the methods are then passsed
 * to the remoter to carry them over to the fly host.
 *
 * This does not get involved in the evolution of Entries - that happens in the
 * VersionLink which is twined' with the MethodCodec.
 *
 * A non evolvable stub could eassily be made by excluding the VersionLink and
 * implementing the evolution preamble to the space direcly in this class.
 * Performance, of course, could be improved in this case.
 *
 */
class MethodCodec(remoter: Remoter, fieldCodec: FieldCodec) extends Logging {

  def this(hostname: String, fieldCodec: FieldCodec) = this(new Remoter(hostname, FlyPrime.FLY_PORT), fieldCodec)
  def this(address: InetAddress, fieldCodec: FieldCodec) = this(new Remoter(address, FlyPrime.FLY_PORT), fieldCodec)

  private val typeChain = new TypeChain(remoter, fieldCodec)
  private val nmd = new NotifyMessageDispatcher(typeChain)

  remoter.setNotifyMessageDispatcher(nmd);

  private val bos = new ByteArrayOutputStream(FlyPrime.DEFAULT_BUFFER_SIZE)
  private val dos = new DataOutputStream(bos)
  private val inBuffer = ByteBuffer.allocateDirect(FlyPrime.DEFAULT_BUFFER_SIZE)

  def read[T <: AnyRef](template: T, timeout: Long): Option[T] = retrieve(template, timeout, FlyPrime.FLY_HEADER ^ FlyPrime.READ)

  def take[T <: AnyRef](template: T, timeout: Long): Option[T] = retrieve(template, timeout, FlyPrime.FLY_HEADER ^ FlyPrime.TAKE)

  private def theClass[T <: AnyRef](obj: T) = obj.getClass.asInstanceOf[Class[T]]

  private def retrieve[T <: AnyRef](template: T, timeout: Long, methodCodecHeader: Int): Option[T] = remoterOp {
    writeRetrieveHeader(template, methodCodecHeader)
    dos.writeLong(timeout)

    remoter.sendOperation(bos.toByteArray) match {
      case size if (size <= 0) => None
      case size => typeChain.readObject(size, theClass(template))
    }
  }

  private def writeRetrieveHeader[T <: AnyRef](template: T, methodCodecHeader: Int) {
    bos.reset()
    dos.writeInt(methodCodecHeader)
    dos.writeInt(typeChain.getChannel(template))
    typeChain.writeObject(dos, template)
  }

  def snapshot(template: AnyRef): AnyRef = template

  def write(obj: AnyRef, lease: Long): Long = writeOrNotify(obj, lease, FlyPrime.WRITE)

  def readMany[T <: AnyRef](template: T, matchLimit: Long, ignore: Long): Iterable[T] = remoterOp {
    writeRetrieveHeader(template, FlyPrime.FLY_HEADER ^ FlyPrime.READ_MANY)
    dos.writeLong(matchLimit)
    dos.writeLong(ignore)
    retrieveMany(template)
  }

  def takeMany[T <: AnyRef](template: T, matchLimit: Long): Iterable[T] = remoterOp {
    writeRetrieveHeader(template, FlyPrime.FLY_HEADER ^ FlyPrime.TAKE_MANY)
    dos.writeLong(matchLimit)
    retrieveMany(template)
  }

  private def retrieveMany[T <: AnyRef](template: T): Iterable[T] = {
    val entryCount = remoter.sendOperation(bos.toByteArray)
    for (i <- 0 until entryCount.toInt; o <- typeChain.readObject(theClass(template))) yield o
  }

  def notifyWrite(template: AnyRef, leaseTime: Long, actor: Actor, returnsEntry: Boolean): Boolean =
    notify(template, leaseTime, actor, if (returnsEntry) FlyPrime.NOTIFY_WRITE_OBJECT else FlyPrime.NOTIFY_WRITE)

  def notifyTake(template: AnyRef, leaseTime: Long, actor: Actor, returnsEntry: Boolean): Boolean = 
    notify(template, leaseTime, actor, if (returnsEntry) FlyPrime.NOTIFY_TAKE_OBJECT else FlyPrime.NOTIFY_TAKE)
  
  private def notify(template: AnyRef, leaseTime: Long, actor: Actor, op:Int) = {
    val notifyToken = writeOrNotify(template, leaseTime, op)
    remoter.addNotifyDetails(notifyToken, actor, theClass(template))
    remoter.setMessageComplete()
    true
  }

  /**
   *  Write and notify are identical except for the header, this method encapsulates the behaviour.
   */
  private def writeOrNotify(obj: AnyRef, lease: Long, header: Int): Long = remoterOp {
    bos.reset()
    dos.writeInt(FlyPrime.FLY_HEADER ^ header)
    dos.writeInt(typeChain.getChannel(obj))
    typeChain.writeObject(dos, obj)
    dos.writeLong(lease)
    remoter.sendOperation(bos.toByteArray)
  }

  private def remoterOp[X](op: => X): X = synchronized {
    try {
      op
    } catch {
      case e => throw new FlyAccessException(e)
    } finally {
      remoter.setMessageComplete()
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy