mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-26 01:33:35 +00:00
combined NumberPoolHubActor and NumberPoolAccessorActor into UniqueNumberSystem; fixed issue with using NumberPoolHub straight-up
This commit is contained in:
parent
be0902fbc4
commit
cc383420c8
3 changed files with 662 additions and 1 deletions
|
|
@ -203,8 +203,8 @@ class NumberPoolHub(private val source : NumberSource) {
|
|||
val slctr = pool.Selector
|
||||
import net.psforever.objects.guid.selector.SpecificSelector
|
||||
val specific = new SpecificSelector
|
||||
specific.SelectionIndex = number
|
||||
pool.Selector = specific
|
||||
specific.SelectionIndex = number
|
||||
pool.Get()
|
||||
pool.Selector = slctr
|
||||
register_GetAvailableNumberFromSource(number)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,313 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.guid
|
||||
|
||||
import net.psforever.objects.entity.{IdentifiableEntity, NoGUIDException}
|
||||
import net.psforever.objects.guid.key.LoanedKey
|
||||
import net.psforever.objects.guid.pool.{ExclusivePool, GenericPool, NumberPool}
|
||||
import net.psforever.objects.guid.source.NumberSource
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class NumberPoolHub2(private val source : NumberSource) {
|
||||
import scala.collection.mutable
|
||||
private val hash : mutable.HashMap[String, NumberPool] = mutable.HashMap[String, NumberPool]()
|
||||
private val bigpool : mutable.LongMap[String] = mutable.LongMap[String]()
|
||||
hash += "generic" -> new GenericPool(bigpool, source.Size)
|
||||
source.FinalizeRestrictions.foreach(i => bigpool += i.toLong -> "") //these numbers can never be pooled; the source can no longer restrict numbers
|
||||
|
||||
def apply(number : PlanetSideGUID) : Option[IdentifiableEntity] = this(number.guid)
|
||||
|
||||
def apply(number : Int) : Option[IdentifiableEntity] = source.Get(number).orElse(return None).get.Object
|
||||
|
||||
def Numbers : List[Int] = bigpool.keys.map(key => key.toInt).toList
|
||||
|
||||
def AddPool(name : String, pool : List[Int]) : NumberPool = {
|
||||
if(hash.get(name).isDefined) {
|
||||
throw new IllegalArgumentException(s"can not add pool $name - name already known to this hub?")
|
||||
}
|
||||
if(source.Size <= pool.max) {
|
||||
throw new IllegalArgumentException(s"can not add pool $name - max(pool) is greater than source.size")
|
||||
}
|
||||
val collision = bigpool.keys.map(n => n.toInt).toSet.intersect(pool.toSet)
|
||||
if(collision.nonEmpty) {
|
||||
throw new IllegalArgumentException(s"can not add pool $name - it contains the following redundant numbers: ${collision.toString}")
|
||||
}
|
||||
pool.foreach(i => bigpool += i.toLong -> name)
|
||||
hash += name -> new ExclusivePool(pool)
|
||||
hash(name)
|
||||
}
|
||||
|
||||
def RemovePool(name : String) : List[Int] = {
|
||||
if(name.equals("generic") || name.equals("")) {
|
||||
throw new IllegalArgumentException("can not remove pool - generic or restricted")
|
||||
}
|
||||
val pool = hash.get(name).orElse({
|
||||
throw new IllegalArgumentException(s"can not remove pool - $name does not exist")
|
||||
}).get
|
||||
if(pool.Count > 0) {
|
||||
throw new IllegalArgumentException(s"can not remove pool - $name is being used")
|
||||
}
|
||||
|
||||
hash.remove(name)
|
||||
pool.Numbers.foreach(number => bigpool -= number)
|
||||
pool.Numbers
|
||||
}
|
||||
|
||||
def GetPool(name : String) : Option[NumberPool] = if(name.equals("")) { None } else { hash.get(name) }
|
||||
|
||||
def Pools : mutable.HashMap[String, NumberPool] = hash
|
||||
|
||||
def WhichPool(number : Int) : Option[String] = {
|
||||
val name = bigpool.get(number)
|
||||
if(name.contains("")) { None } else { name }
|
||||
}
|
||||
|
||||
def WhichPool(obj : IdentifiableEntity) : Option[String] = {
|
||||
try {
|
||||
val number : Int = obj.GUID.guid
|
||||
val entry = source.Get(number)
|
||||
if(entry.isDefined && entry.get.Object.contains(obj)) { WhichPool(number) } else { None }
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def register(obj : IdentifiableEntity) : Try[Int] = register(obj, "generic")
|
||||
|
||||
def register(obj : IdentifiableEntity, number : Int) : Try[Int] = {
|
||||
bigpool.get(number.toLong) match {
|
||||
case Some(name) =>
|
||||
register_GetSpecificNumberFromPool(name, number) match {
|
||||
case Success(key) =>
|
||||
key.Object = obj
|
||||
Success(obj.GUID.guid)
|
||||
case Failure(ex) =>
|
||||
Failure(new Exception(s"trying to register an object to a specific number but, ${ex.getMessage}"))
|
||||
}
|
||||
case None =>
|
||||
import net.psforever.objects.guid.selector.SpecificSelector
|
||||
hash("generic").Selector.asInstanceOf[SpecificSelector].SelectionIndex = number
|
||||
register(obj, "generic")
|
||||
}
|
||||
}
|
||||
|
||||
private def register_GetSpecificNumberFromPool(name : String, number : Int) : Try[LoanedKey]= {
|
||||
hash.get(name) match {
|
||||
case Some(pool) =>
|
||||
val slctr = pool.Selector
|
||||
import net.psforever.objects.guid.selector.SpecificSelector
|
||||
val specific = new SpecificSelector
|
||||
specific.SelectionIndex = number
|
||||
pool.Selector = specific
|
||||
pool.Get()
|
||||
pool.Selector = slctr
|
||||
register_GetAvailableNumberFromSource(number)
|
||||
case None =>
|
||||
Failure(new Exception(s"number pool $name not defined"))
|
||||
}
|
||||
}
|
||||
|
||||
private def register_GetAvailableNumberFromSource(number : Int) : Try[LoanedKey] = {
|
||||
source.Available(number) match {
|
||||
case Some(key) =>
|
||||
Success(key)
|
||||
case None =>
|
||||
Failure(new Exception(s"number $number is unavailable"))
|
||||
}
|
||||
}
|
||||
|
||||
def register(obj : IdentifiableEntity, name : String) : Try[Int] = {
|
||||
try {
|
||||
register_CheckNumberAgainstDesiredPool(obj, name, obj.GUID.guid)
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
register_GetPool(name) match {
|
||||
case Success(key) =>
|
||||
key.Object = obj
|
||||
Success(obj.GUID.guid)
|
||||
case Failure(ex) =>
|
||||
Failure(new Exception(s"trying to register an object but, ${ex.getMessage}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def register_CheckNumberAgainstDesiredPool(obj : IdentifiableEntity, name : String, number : Int) : Try[Int] = {
|
||||
val directKey = source.Get(number)
|
||||
if(directKey.isEmpty || !directKey.get.Object.contains(obj)) {
|
||||
Failure(new Exception("object already registered, but not to this source"))
|
||||
}
|
||||
else if(!WhichPool(number).contains(name)) {
|
||||
//TODO obj is not registered to the desired pool; is this okay?
|
||||
Success(number)
|
||||
}
|
||||
else {
|
||||
Success(number)
|
||||
}
|
||||
}
|
||||
|
||||
private def register_GetPool(name : String) : Try[LoanedKey] = {
|
||||
hash.get(name) match {
|
||||
case Some(pool) =>
|
||||
register_GetNumberFromDesiredPool(pool)
|
||||
case _ =>
|
||||
Failure(new Exception(s"number pool $name not defined"))
|
||||
}
|
||||
}
|
||||
|
||||
private def register_GetNumberFromDesiredPool(pool : NumberPool) : Try[LoanedKey] = {
|
||||
pool.Get() match {
|
||||
case Success(number) =>
|
||||
register_GetMonitorFromSource(number)
|
||||
case Failure(ex) =>
|
||||
Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
private def register_GetMonitorFromSource(number : Int) : Try[LoanedKey] = {
|
||||
source.Available(number) match {
|
||||
case Some(key) =>
|
||||
Success(key)
|
||||
case _ =>
|
||||
throw NoGUIDException(s"a pool gave us a number $number that is actually unavailable") //stop the show; this is terrible!
|
||||
}
|
||||
}
|
||||
|
||||
def register(number : Int) : Try[LoanedKey] = {
|
||||
WhichPool(number) match {
|
||||
case None =>
|
||||
import net.psforever.objects.guid.selector.SpecificSelector
|
||||
hash("generic").Selector.asInstanceOf[SpecificSelector].SelectionIndex = number
|
||||
register_GetPool("generic")
|
||||
case Some(name) =>
|
||||
register_GetSpecificNumberFromPool(name, number)
|
||||
}
|
||||
}
|
||||
|
||||
def register(name : String) : Try[LoanedKey] = register_GetPool(name)
|
||||
|
||||
def latterPartRegister(obj : IdentifiableEntity, number : Int) : Try[IdentifiableEntity] = {
|
||||
register_GetMonitorFromSource(number) match {
|
||||
case Success(monitor) =>
|
||||
monitor.Object = obj
|
||||
Success(obj)
|
||||
case Failure(ex) =>
|
||||
Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
def unregister(obj : IdentifiableEntity) : Try[Int] = {
|
||||
unregister_GetPoolFromObject(obj) match {
|
||||
case Success(pool) =>
|
||||
val number = obj.GUID.guid
|
||||
pool.Return(number)
|
||||
source.Return(number)
|
||||
obj.Invalidate()
|
||||
Success(number)
|
||||
case Failure(ex) =>
|
||||
Failure(new Exception(s"can not unregister this object: ${ex.getMessage}"))
|
||||
}
|
||||
}
|
||||
|
||||
def unregister_GetPoolFromObject(obj : IdentifiableEntity) : Try[NumberPool] = {
|
||||
WhichPool(obj) match {
|
||||
case Some(name) =>
|
||||
unregister_GetPool(name)
|
||||
case None =>
|
||||
Failure(throw new Exception("can not find a pool for this object"))
|
||||
}
|
||||
}
|
||||
|
||||
private def unregister_GetPool(name : String) : Try[NumberPool] = {
|
||||
hash.get(name) match {
|
||||
case Some(pool) =>
|
||||
Success(pool)
|
||||
case None =>
|
||||
Failure(new Exception(s"no pool by the name of '$name'"))
|
||||
}
|
||||
}
|
||||
|
||||
def unregister(number : Int) : Try[Option[IdentifiableEntity]] = {
|
||||
if(source.Test(number)) {
|
||||
unregister_GetObjectFromSource(number)
|
||||
}
|
||||
else {
|
||||
Failure(new Exception(s"can not unregister a number $number that this source does not own") )
|
||||
}
|
||||
}
|
||||
|
||||
private def unregister_GetObjectFromSource(number : Int) : Try[Option[IdentifiableEntity]] = {
|
||||
source.Return(number) match {
|
||||
case Some(obj) =>
|
||||
unregister_ReturnObjectToPool(obj)
|
||||
case None =>
|
||||
unregister_ReturnNumberToPool(number) //nothing is wrong, but we'll check the pool
|
||||
}
|
||||
}
|
||||
|
||||
private def unregister_ReturnObjectToPool(obj : IdentifiableEntity) : Try[Option[IdentifiableEntity]] = {
|
||||
val number = obj.GUID.guid
|
||||
unregister_GetPoolFromNumber(number) match {
|
||||
case Success(pool) =>
|
||||
pool.Return(number)
|
||||
obj.Invalidate()
|
||||
Success(Some(obj))
|
||||
case Failure(ex) =>
|
||||
source.Available(number) //undo
|
||||
Failure(new Exception(s"started unregistering, but ${ex.getMessage}"))
|
||||
}
|
||||
}
|
||||
|
||||
private def unregister_ReturnNumberToPool(number : Int) : Try[Option[IdentifiableEntity]] = {
|
||||
unregister_GetPoolFromNumber(number) match {
|
||||
case Success(pool) =>
|
||||
pool.Return(number)
|
||||
Success(None)
|
||||
case _ => //though everything else went fine, we must still fail if this number was restricted all along
|
||||
if(!bigpool.get(number).contains("")) {
|
||||
Success(None)
|
||||
}
|
||||
else {
|
||||
Failure(new Exception(s"can not unregister this number $number"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def unregister_GetPoolFromNumber(number : Int) : Try[NumberPool] = {
|
||||
WhichPool(number) match {
|
||||
case Some(name) =>
|
||||
unregister_GetPool(name)
|
||||
case None =>
|
||||
Failure(new Exception(s"no pool using number $number"))
|
||||
}
|
||||
}
|
||||
|
||||
def latterPartUnregister(number : Int) : Option[IdentifiableEntity] = source.Return(number)
|
||||
|
||||
def isRegistered(obj : IdentifiableEntity) : Boolean = {
|
||||
try {
|
||||
source.Get(obj.GUID.guid) match {
|
||||
case Some(monitor) =>
|
||||
monitor.Object.contains(obj)
|
||||
case None =>
|
||||
false
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : NoGUIDException =>
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
def isRegistered(number : Int) : Boolean = {
|
||||
source.Get(number) match {
|
||||
case Some(monitor) =>
|
||||
monitor.Policy == AvailabilityPolicy.Leased
|
||||
case None =>
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,348 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.guid.actor
|
||||
|
||||
import akka.actor.{Actor, ActorContext, ActorRef, Props}
|
||||
import net.psforever.objects.entity.IdentifiableEntity
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
/**
|
||||
* An `Actor` that wraps around converted `NumberPool`s and synchronizes a portion of the number registration process.
|
||||
* The ultimate goal is to manage a coherent group of unique identifiers for a given "region" (`Zone`).
|
||||
* Both parts of the UID system sit atop the `Zone` for easy external access.
|
||||
* The plain part - the `NumberPoolHub` here - is used for low-priority requests such as checking for existing associations.
|
||||
* This `Actor` is the involved portion that paces registration and unregistration.<br>
|
||||
* <br>
|
||||
* A four part process is used for object registration tasks.
|
||||
* First, the requested `NumberPool` is located among the list of known `NumberPool`s.
|
||||
* Second, an asynchronous request is sent to that pool to retrieve a number.
|
||||
* (Only any number. Only a failing case allows for selection of a specific number.)
|
||||
* Third, the asynchronous request returns and the original information about the request is recovered.
|
||||
* Fourth, both sides of the contract are completed by the object being assigned the number and
|
||||
* the underlying "number source" is made to remember an association between the object and the number.
|
||||
* Short circuits and recoveries as available on all steps though reporting is split between logging and callbacks.
|
||||
* The process of removing the association between a number and object (unregistering) is a similar four part process.<br>
|
||||
* <br>
|
||||
* The important relationship between this `Actor` and the `Map` of `NumberPoolActors` is an "gate."
|
||||
* A single `Map` is constructed and shared between multiple entry points to the UID system where requests are messaged.
|
||||
* Multiple entry points send messages to the same `NumberPool`.
|
||||
* That `NumberPool` deals with the messages one at a time and sends reply to each entry point that communicated with it.
|
||||
* This process is almost as fast as the process of the `NumberPool` selecting a number.
|
||||
* (At least, both should be fast.)
|
||||
* @param guid the `NumberPoolHub` that is partially manipulated by this `Actor`
|
||||
* @param poolActors a common mapping created from the `NumberPool`s in `guid`;
|
||||
* there is currently no check for this condition save for requests failing
|
||||
*/
|
||||
class UniqueNumberSystem(private val guid : NumberPoolHub, private val poolActors : Map[String, ActorRef]) extends Actor {
|
||||
/** Information about Register and Unregister requests that persists between messages to a specific `NumberPool`. */
|
||||
private val requestQueue : collection.mutable.LongMap[UniqueNumberSystem.GUIDRequest] = new collection.mutable.LongMap()
|
||||
/** The current value for the next request entry's index. */
|
||||
private var index : Long = Long.MinValue
|
||||
private[this] val log = org.log4s.getLogger
|
||||
|
||||
def receive : Receive = {
|
||||
case Register(obj, Some(pname), None, call) =>
|
||||
val callback = call.getOrElse(sender())
|
||||
try {
|
||||
obj.GUID //stop if object already has a GUID; sometimes this happens
|
||||
AlreadyRegistered(obj, pname)
|
||||
callback ! Success(obj)
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
val id : Long = index
|
||||
index += 1
|
||||
requestQueue += id -> UniqueNumberSystem.GUIDRequest(obj, pname, callback)
|
||||
RegistrationProcess(pname, id)
|
||||
}
|
||||
|
||||
//this message is automatically sent by NumberPoolActor
|
||||
case NumberPoolActor.GiveNumber(number, id) =>
|
||||
id match {
|
||||
case Some(nid : Long) =>
|
||||
RegistrationProcess(requestQueue.remove(nid), number, nid)
|
||||
case _ =>
|
||||
log.warn(s"received a number but there is no request to process it; returning number to pool")
|
||||
NoCallbackReturnNumber(number) //recovery?
|
||||
//no callback is possible
|
||||
}
|
||||
|
||||
//this message is automatically sent by NumberPoolActor
|
||||
case NumberPoolActor.NoNumber(ex, id) =>
|
||||
id match {
|
||||
case Some(nid : Long) =>
|
||||
requestQueue.remove(nid) match {
|
||||
case Some(entry) =>
|
||||
entry.replyTo ! Failure(ex) //ONLY callback that is possible
|
||||
case None => ;
|
||||
log.warn(s"awkward no number error $ex") //neither a successful request nor an entry of making the request
|
||||
}
|
||||
case None => ;
|
||||
log.warn(s"awkward no number error $ex") //neither a successful request nor an entry of making the request
|
||||
case _ => ;
|
||||
log.warn(s"unrecognized request $id accompanying a no number error $ex")
|
||||
}
|
||||
|
||||
case Unregister(obj, call) =>
|
||||
val callback = call.getOrElse(sender())
|
||||
try {
|
||||
val number = obj.GUID.guid
|
||||
guid.WhichPool(number) match {
|
||||
case Some(pname) =>
|
||||
val id : Long = index
|
||||
index += 1
|
||||
requestQueue += id -> UniqueNumberSystem.GUIDRequest(obj, pname, callback)
|
||||
UnregistrationProcess(pname, number, id)
|
||||
case None =>
|
||||
callback ! Failure(new Exception(s"the GUID of object $obj - $number - is not a part of this number pool"))
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
log.info(s"$obj is already unregistered")
|
||||
callback ! Success(obj)
|
||||
}
|
||||
|
||||
//this message is automatically sent by NumberPoolActor
|
||||
case NumberPoolActor.ReturnNumberResult(number, None, id) =>
|
||||
id match {
|
||||
case Some(nid : Long) =>
|
||||
UnregistrationProcess(requestQueue.remove(nid), number, nid)
|
||||
case _ =>
|
||||
log.error(s"returned a number but there is no request to process it; recovering the number from pool")
|
||||
NoCallbackGetSpecificNumber(number) //recovery?
|
||||
//no callback is possible
|
||||
}
|
||||
|
||||
//this message is automatically sent by NumberPoolActor
|
||||
case NumberPoolActor.ReturnNumberResult(number, Some(ex), id) => //if there is a problem when returning the number
|
||||
id match {
|
||||
case Some(nid : Long) =>
|
||||
requestQueue.remove(nid) match {
|
||||
case Some(entry) =>
|
||||
entry.replyTo ! Failure(new Exception(s"for ${entry.target} with number $number, ${ex.getMessage}"))
|
||||
case None => ;
|
||||
log.error(s"could not find original request $nid that caused error $ex, but pool was $sender")
|
||||
//no callback is possible
|
||||
}
|
||||
case None => ;
|
||||
log.error(s"could not find original request $id that caused error $ex, but pool was $sender")
|
||||
//no callback is possible
|
||||
}
|
||||
|
||||
case msg =>
|
||||
log.warn(s"unexpected message received - $msg")
|
||||
}
|
||||
|
||||
/**
|
||||
* A step of the object registration process.
|
||||
* Send a message to the `NumberPool` to request a number back.
|
||||
* @param poolName the pool to which the object is trying to register
|
||||
* @param id a potential identifier to associate this request
|
||||
*/
|
||||
private def RegistrationProcess(poolName : String, id : Long) : Unit = {
|
||||
poolActors.get(poolName) match {
|
||||
case Some(pool) =>
|
||||
pool ! NumberPoolActor.GetAnyNumber(Some(id))
|
||||
case None =>
|
||||
//do not log; use callback
|
||||
requestQueue.remove(id).get.replyTo ! Failure(new Exception(s"can not find pool $poolName; nothing was registered"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step of the object registration process.
|
||||
* If there is a successful request object to be found, continue the registration request.
|
||||
* @param request the original request data
|
||||
* @param number the number that was drawn from a `NumberPool`
|
||||
*/
|
||||
private def RegistrationProcess(request : Option[UniqueNumberSystem.GUIDRequest], number : Int, id : Long) : Unit = {
|
||||
request match {
|
||||
case Some(entry) =>
|
||||
processRegisterResult(entry, number)
|
||||
case None =>
|
||||
log.error(s"returned a number but the rest of the request is missing (id:$id)")
|
||||
if(id != Long.MinValue) { //check to ignore endless loop of error-catching
|
||||
log.warn("returning number to pool")
|
||||
NoCallbackReturnNumber(number) //recovery?
|
||||
//no callback is possible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step of the object registration process.
|
||||
* This step completes the registration by asking the `NumberPoolHub` to sort out its `NumberSource`.
|
||||
* @param entry the original request data
|
||||
* @param number the number to use
|
||||
*/
|
||||
private def processRegisterResult(entry : UniqueNumberSystem.GUIDRequest, number : Int) : Unit = {
|
||||
val obj = entry.target
|
||||
guid.latterPartRegister(obj, number) match {
|
||||
case Success(_) =>
|
||||
entry.replyTo ! Success(obj)
|
||||
case Failure(ex) =>
|
||||
//do not log; use callback
|
||||
NoCallbackReturnNumber(number, entry.targetPool) //recovery?
|
||||
entry.replyTo ! Failure(ex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step of the object unregistration process.
|
||||
* Send a message to the `NumberPool` to restore the availability of one of its numbers.
|
||||
* @param poolName the pool to which the number will try to be returned
|
||||
* @param number the number that was previously drawn from the specified `NumberPool`
|
||||
* @param id a potential identifier to associate this request
|
||||
*/
|
||||
private def UnregistrationProcess(poolName : String, number : Int, id : Long) : Unit = {
|
||||
poolActors.get(poolName) match {
|
||||
case Some(pool) =>
|
||||
pool ! NumberPoolActor.ReturnNumber(number, Some(id))
|
||||
case None =>
|
||||
//do not log; use callback
|
||||
requestQueue.remove(id).get.replyTo ! Failure(new Exception(s"can not find pool $poolName; nothing was de-registered"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step of the object unregistration process.
|
||||
* If there is a successful request object to be found, continue the registration request.
|
||||
* @param request the original request data
|
||||
* @param number the number that was drawn from the `NumberPool`
|
||||
*/
|
||||
private def UnregistrationProcess(request : Option[UniqueNumberSystem.GUIDRequest], number : Int, id : Long) : Unit = {
|
||||
request match {
|
||||
case Some(entry) =>
|
||||
processUnregisterResult(entry, number)
|
||||
case None =>
|
||||
log.error(s"returned a number but the rest of the request is missing (id:$id)")
|
||||
if(id != Long.MinValue) { //check to ignore endless loop of error-catching
|
||||
log.error("recovering the number from pool")
|
||||
NoCallbackGetSpecificNumber(number) //recovery?
|
||||
//no callback is possible
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step of the object unregistration process.
|
||||
* This step completes revoking of the object's registration by consulting the `NumberSource`.
|
||||
* @param entry the original request data
|
||||
* @param number the number to use
|
||||
*/
|
||||
private def processUnregisterResult(entry : UniqueNumberSystem.GUIDRequest, number : Int) : Unit = {
|
||||
val obj = entry.target
|
||||
guid.latterPartUnregister(number) match {
|
||||
case Some(_) =>
|
||||
obj.Invalidate()
|
||||
entry.replyTo ! Success(obj)
|
||||
case None =>
|
||||
//do not log; use callback
|
||||
NoCallbackGetSpecificNumber(number, entry.targetPool) //recovery?
|
||||
entry.replyTo ! Failure(new Exception(s"failed to unregister a number; this may be a critical error"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a relevant logging message for an object that is trying to register is actually already registered.
|
||||
* @param obj the object that was trying to register
|
||||
* @param poolName the pool to which the object was trying to register
|
||||
*/
|
||||
private def AlreadyRegistered(obj : IdentifiableEntity, poolName : String) : Unit = {
|
||||
val msg =
|
||||
guid.WhichPool(obj) match {
|
||||
case Some(pname) =>
|
||||
if(poolName.equals(pname)) {
|
||||
s"to pool $poolName"
|
||||
}
|
||||
else {
|
||||
s"but to different pool $pname"
|
||||
}
|
||||
case None =>
|
||||
"but not to any pool known to this system"
|
||||
}
|
||||
log.warn(s"$obj already registered $msg")
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a specific `NumberPool` in a way that doesn't invoke a callback and reset one of its numbers.
|
||||
* @param number the number that was drawn from a `NumberPool`
|
||||
*/
|
||||
private def NoCallbackReturnNumber(number : Int) : Unit = {
|
||||
guid.WhichPool(number) match {
|
||||
case Some(pname) =>
|
||||
NoCallbackReturnNumber(number, pname)
|
||||
case None =>
|
||||
log.error(s"critical: tried to return number $number but could not find containing pool")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a specific `NumberPool` in a way that doesn't invoke a callback and reset one of its numbers.
|
||||
* To avoid fully processing the callback, an id of `Long.MinValue` is used to short circuit the routine.
|
||||
* @param number the number that was drawn from a `NumberPool`
|
||||
* @param poolName the `NumberPool` from which the `number` was drawn
|
||||
* @see `UniqueNumberSystem.UnregistrationProcess(Option[GUIDRequest], Int, Int)`
|
||||
*/
|
||||
private def NoCallbackReturnNumber(number : Int, poolName : String) : Unit = {
|
||||
poolActors.get(poolName) match {
|
||||
case Some(pool) =>
|
||||
pool ! NumberPoolActor.ReturnNumber(number, Some(Long.MinValue))
|
||||
case None =>
|
||||
log.error(s"critical: tried to return number $number but did not find pool $poolName")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a specific `NumberPool` in a way that doesn't invoke a callback and claim one of its numbers.
|
||||
* @param number the number to be drawn from a `NumberPool`
|
||||
*/
|
||||
private def NoCallbackGetSpecificNumber(number : Int) : Unit = {
|
||||
guid.WhichPool(number) match {
|
||||
case Some(pname) =>
|
||||
NoCallbackGetSpecificNumber(number, pname)
|
||||
case None =>
|
||||
log.error(s"critical: tried to re-register number $number but could not find containing pool")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a specific `NumberPool` in a way that doesn't invoke a callback and claim one of its numbers.
|
||||
* To avoid fully processing the callback, an id of `Long.MinValue` is used to short circuit the routine.
|
||||
* @param number the number to be drawn from a `NumberPool`
|
||||
* @param poolName the `NumberPool` from which the `number` is to be drawn
|
||||
* @see `UniqueNumberSystem.RegistrationProcess(Option[GUIDRequest], Int, Int)`
|
||||
*/
|
||||
private def NoCallbackGetSpecificNumber(number : Int, poolName : String) : Unit = {
|
||||
poolActors.get(poolName) match {
|
||||
case Some(pool) =>
|
||||
pool ! NumberPoolActor.GetSpecificNumber(number, Some(Long.MinValue))
|
||||
case None =>
|
||||
log.error(s"critical: tried to re-register number $number but did not find pool $poolName")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object UniqueNumberSystem {
|
||||
/**
|
||||
* Persistent record of the important information between the time fo request and the time of reply.
|
||||
* @param target the object
|
||||
* @param targetPool the name of the `NumberPool` being used
|
||||
* @param replyTo the callback `ActorRef`
|
||||
*/
|
||||
private final case class GUIDRequest(target : IdentifiableEntity, targetPool : String, replyTo : ActorRef)
|
||||
|
||||
/**
|
||||
* Transform `NumberPool`s into `NumberPoolActor`s and pair them with their name.
|
||||
* @param poolSource where the raw `NumberPools` are located
|
||||
* @param context used to create the `NumberPoolActor` instances
|
||||
* @return a `Map` of the pool names to the `ActorRef` created from the `NumberPool`
|
||||
*/
|
||||
def AllocateNumberPoolActors(poolSource : NumberPoolHub)(implicit context : ActorContext) : Map[String, ActorRef] = {
|
||||
poolSource.Pools.map({ case ((pname, pool)) =>
|
||||
pname -> context.actorOf(Props(classOf[NumberPoolActor], pool), pname)
|
||||
}).toMap
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue