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