diff --git a/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala b/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala
index e360df53..e574c0ec 100644
--- a/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala
+++ b/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub.scala
@@ -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)
diff --git a/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub2.scala b/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub2.scala
new file mode 100644
index 00000000..7769b696
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/guid/NumberPoolHub2.scala
@@ -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
+ }
+ }
+}
diff --git a/common/src/main/scala/net/psforever/objects/guid/actor/UniqueNumberSystem.scala b/common/src/main/scala/net/psforever/objects/guid/actor/UniqueNumberSystem.scala
new file mode 100644
index 00000000..9e37624f
--- /dev/null
+++ b/common/src/main/scala/net/psforever/objects/guid/actor/UniqueNumberSystem.scala
@@ -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.
+ *
+ * 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.
+ *
+ * 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
+ }
+}