On Fri, Apr 25, 2014 at 8:43 PM, Justin du coeur <[email protected]> wrote:

Okay -- the key problem is that you have to remember that *everything*
> involving Actors is asynchronous.  You're not allowed to make any
> assumptions about what happens when.
>
> So take a look at your code.  Main creates A.  A **begins** to create
> supervisorActor -- but that will happen *someday*, not immediately.  You
> get the ActorRef back immediately, but doesn't say anything about when the
> Actor *itself* gets created and initialized.  That takes time -- truth to
> tell, I'm surprised you only "sometimes" get a NPE, since I would have
> expected it to almost always happen.
>

You will never get a NPE, the ActorRef you get back is fully operable, i.e.
you can send messages, even if the respective actor hasn't been created.
Essentially messages are buffered until the actor is up and ready to handle
these.

Heiko


>
> More importantly, the line
>
> aInstance.workerActor = context.actorOf(Props(new WorkerActor))
>
>
> violates one of the cardinal rules of Akka -- Actors should be
> self-contained.  An Actor should not modify state outside itself.  (And
> anything outside the Actor *must* not ever modify the Actor's state.)  So
> while this isn't *quite* strictly illegal, it's highly frowned upon.
>
> Personally, I would probably hide workerActor completely inside B (the
> supervisorActor), and would route messages to workerActor through the
> supervisor, something like this:
>
> --------
>
> class MainClass {
> val aInstance = new A()
>
> def submitJob {aInstance.submitJob()}
> }
>
> class A {
> val supervisorActor: B = MainClass.env.system.actorOf(Props(new B))
>
> def submitJob() {
>  supervisorActor! JobSubmitted   // Now it won't NPE, because you are
> using the Actor you built synchronously
>  }
> }
>
> class B {
> val myWorker = context.actorOf(Props(new WorkerActor))
>
>         def receive = {
>             // This won't happen until after myWorker's ActorRef has been
> created, and the Supervisor is ready to pass the message on:
>             case msg:JobSubmitted => myWorker.forward(msg)
>         }
> }
>
> --------
>
> That produces better separation of concerns -- for example, the supervisor
> can decide to spawn ten workers instead of one, and route to any of them,
> without the controlling code needing to know about it.
>
> If you strongly prefer having the outer shell communicate directly with
> the worker, you must *ask* the supervisor for it -- that allows the
> supervisor to respond when it's good and ready.  That would be something
> like (again, totally untested and probably needs tweaking):
>
> --------
>
> case object GetWorker
> case class MyWorker(worker:ActorRef)
>
> Class MainClass {
> val aInstance = new A()
>
> def submitJob {aInstance.submitJob()}
> }
>
> Class A {
> val supervisorActor: B = MainClass.env.system.actorOf(Props(new B))
>  val workerActor: ActorRef = getWorker()
>
>         def getWorker():ActorRef = {
>             val fut = supervisorActor ? GetWorker
>             // We have to *wait* until the Supervisor says that it's
> created the worker:
>             scala.concurrent.Await.result(fut, 5 seconds) match {
>                 case MyWorker(worker) => worker
>                 case _ => throw new Exception
>             }
>         }
>
> def submitJob() {
> workerActor ! JobSubmitted    // Shouldn't NPE, because now we've waited
> until we got the workerActor back properly
>  }
> }
>
> Class B (aInstance: A) {
> val myWorker = context.actorOf(Props(new WorkerActor))
>
>         def receive = {
>             case GetWorker => sender ! MyWorker(myWorker)
>         }
> }
>
> --------
>
> You know that's an inferior solution because of that Await sitting there
> in the middle -- that's blocking the main thread.  But I don't see any
> other way for it to work: the main thread has to wait until the supervisor
> has actually created the worker and says that it is ready.
>
> The first solution is better, though: the main thread makes no assumptions
> except that it has created the supervisor, and it trusts the supervisor to
> pass messages to the worker once it is good and ready to do so.  No
> blocking required.
>
> (Caveat: I am eliding all *kinds* of real-world problems like
> back-pressure and stuff.  This solution works for easy cases, but you need
> a totally different approach for high-volume problems.  Look up "work
> pulling" and "reactive streams" for more sophisticated approaches...)
>
>
> On Fri, Apr 25, 2014 at 12:57 PM, Nan Zhu <[email protected]> wrote:
>
>> Hi, Justin
>>
>> Thank you for the reply, here is a revised description of the question
>>
>> Class A is a non-actor class containing some methods in which some
>> messages are sent to the workerActor
>>
>> WorkerActor is to do some real work, e.g. forward messages to other
>> system components and monitor the task status...
>>
>> At the beginning, we initialize workerActor directly in the constructor
>> of A. Then, we added a new Class B which is supposed to be the supervisor
>> of the WorkerActor. We want this because in our scenario, we want to stop
>> the system upon the failure of WorkerActor, so we use Class B to achieve
>> this.
>>
>> We create B in the constructor of A and create workerActor in B's
>> constructor.
>>
>> However, the weird thing is we found that some methods in A (sending
>> messages to worker) are called before A is assigned with any non-null
>> value, so some NPEs are thrown
>>
>> I'm not sure about the reason of this, though I highly suspect that it's
>> because of the non-blocking actorOf...but by intuitive, since we create
>> supervisorActor as
>>
>> val supervisor = actorOf(Props(new B()))
>>
>> new B should be executed in the main thread, i.e. all statements
>> including the one create A should be executed in the main thread...
>>
>> Here is a bigger picture of our program
>>
>> Class MainClass {
>> val aInstance = new A()
>>
>> def submitJob {aInstance.submitJob()}
>> }
>>
>> Class A {
>> val workerActor: WorkerActor = null
>> val supervisorActor: B = MainClass.env.system.actorOf(Props(new B))
>>
>> def submitJob() {
>> workerActor ! JobSubmitted //NPE sometimes
>> }
>> }
>>
>> Class B (aInstance: A) {
>> aInstance.workerActor = context.actorOf(Props(new WorkerActor))
>> }
>>
>> user creates MainClass instance and calls submitJob to trigger everything
>>
>> Thank you!
>>
>>
>> On Friday, April 25, 2014 11:05:22 AM UTC-4, Justin du coeur wrote:
>>
>>> It's hard to give the right suggestion from what you say here, but the
>>> basic approach doesn't feel very "Akka-ish".  It sounds like you are trying
>>> to hand-manage a few Actors from some linear, synchronous code, and that's
>>> usually a recipe for trouble.  For example, the Supervisor should always be
>>> responsible for creating its Workers, and it's pretty common for the worker
>>> operations to route through the Supervisor.  The "external" code (outside
>>> the Actors) should usually be pretty minimal, if any is needed at all.
>>>
>>> But it's very hard to know from this snippet -- there isn't enough here
>>> to even understand what the code is doing, much less what you're trying to
>>> accomplish.  Can you describe your problem a bit better?
>>>
>>>
>>> On Fri, Apr 25, 2014 at 8:17 AM, Nan Zhu <[email protected]> wrote:
>>>
>>>> Hi, all
>>>>
>>>> I'm a newbie to akka, I have a question on the initialization process
>>>> of akka
>>>>
>>>> My question comes from the following scenario
>>>>
>>>> class A {
>>>>   val workerActor: ActorRef = _
>>>>   val supervisorActor: ActorRef = actorOf(Props[B])
>>>>
>>>>   def work() {workerActor ! message} //NPE line
>>>> }
>>>>
>>>> class B {
>>>>   val supervisorStrategy = ...
>>>>   def receive = {...}
>>>>   workerActor = context.actorOf(...) //buggy line
>>>> }
>>>>
>>>> it seems that the "buggy line" is executed in another thread (I think
>>>> it's true, as I found actorOf is non-blocking since akka 2.1), so sometimes
>>>> I will meet a NPE when the work() is called before B is fully started
>>>>
>>>> What I want is to block A's constructor thread until workerActor is not
>>>> null
>>>>
>>>> I can send a message to supervisorActor in a blocking way and returns
>>>>  a ActorRef from the supervisor in A's constructor, but it seems that I
>>>> have to define a timeout Value on that blocking operation....which may
>>>> involve some hard-coding
>>>>
>>>> The other option includes busy loop in A's constructor...but it's
>>>> obviously not a first option
>>>>
>>>> I also saw this https://gist.github.com/viktorklang/856818, but I'm
>>>> not sure if it is what I want
>>>>
>>>> Any suggestion is highly appreciated!
>>>>
>>>> Best,
>>>>
>>>> Nan
>>>>
>>>> --
>>>> >>>>>>>>>> Read the docs: http://akka.io/docs/
>>>> >>>>>>>>>> Check the FAQ: http://doc.akka.io/docs/akka/
>>>> current/additional/faq.html
>>>> >>>>>>>>>> Search the archives: https://groups.google.com/
>>>> group/akka-user
>>>> ---
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Akka User List" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>> an email to [email protected].
>>>> To post to this group, send email to [email protected].
>>>>
>>>> Visit this group at http://groups.google.com/group/akka-user.
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>
>>>  --
>> >>>>>>>>>> Read the docs: http://akka.io/docs/
>> >>>>>>>>>> Check the FAQ:
>> http://doc.akka.io/docs/akka/current/additional/faq.html
>> >>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "Akka User List" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To post to this group, send email to [email protected].
>> Visit this group at http://groups.google.com/group/akka-user.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>  --
> >>>>>>>>>> Read the docs: http://akka.io/docs/
> >>>>>>>>>> Check the FAQ:
> http://doc.akka.io/docs/akka/current/additional/faq.html
> >>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
> ---
> You received this message because you are subscribed to the Google Groups
> "Akka User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/akka-user.
> For more options, visit https://groups.google.com/d/optout.
>



-- 

Heiko Seeberger
Twitter: @hseeberger
Blog: blog.heikoseeberger.name

-- 
>>>>>>>>>>      Read the docs: http://akka.io/docs/
>>>>>>>>>>      Check the FAQ: 
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>>      Search the archives: https://groups.google.com/group/akka-user
--- 
You received this message because you are subscribed to the Google Groups "Akka 
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to