Modern Akka DI with Guice
Asked Answered
G

6

12

Java 8, Guice 4.0 and Akka 2.3.9 here. I am trying to figure out how to annotate my actor classes with JSR330-style @Inject annotations, and then wire them all up via Guice.

But literally every single article I have read (some examples below) either uses Scala code examples, a criminally-old version of Guice, or a criminally-old version of Akka:

So, given the following Guice module:

public interface MyService {
    void doSomething();
}

public class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("Something has been done!");
    }
}

public class MyActorSystemModule extends AbstractModule {
    @Override
    public void configure() {
        bind(MyService.class).to(MyServiceImpl.class);
    }
}

And given the FizzActor that gets injected with a MyService:

public class FizzActor extends UntypedActor {
    private final MyService myService;

    @Inject
    public FizzActor(MyService myService) {
        super();

        this.myService = myService;
    }

    @Override
    public void onReceive(Object message) {
        // .. Do fizz stuff inside here.
    }
}

Then I ask: How do I rig up MyActorSystemModule to create instances of FizzActor and properly inject them with Java (not Scala!)?

Please note: FizzActor is not the only actor in my actor system!

Gladden answered 16/6, 2015 at 9:39 Comment(0)
B
8

Use Creator to create ActorRefs in provider methods of your guice module. To distinguish between the different ActorRefs, which are untyped, use annotations on your provider methods and injection points as you would any guice system. For example,

In your guice module:

@Override
protected void configure() {
    bind(ActorSystem.class).toInstance(ActorSystem.apply());
    bind(FizzService.class).toInstance(new FizzServiceImpl());
}

@Provides @Singleton @Named("fizzActor")
ActorRef serviceActorRef(final ActorSystem system, final FizzService fizzService) {
    return system.actorOf(Props.create(new Creator<Actor>() {
        @Override
        public Actor create() throws Exception {
            return new FizzActor(fizzService);
        }
    }));
}

Then to use the actor service, inject a specific ActorRef:

class ClientOfFizzActor {
    @Inject
    ClientOfFizzActor(@Named("fizzActor") ActorRef fizzActorRef) {..}
}

It looks cleaner if the Props.create(..) clause is a static factory method in your actor class.

Biforate answered 17/6, 2015 at 20:30 Comment(5)
how to recover akka failover stratergies using google guice ?Xylem
Can you provide more details or an example?Biforate
Hey, i have requirement for processing concurrency where, create multiple instance of same actor. If i remove the scope of @Singleton i am getting an error. If my actor will fail, with the help of Supervision Statergies, i need to create new actor for handling process.Xylem
Sounds like this could be a new question. Post & I'll take a stab.Biforate
In this case, the @Singleton refers to the ActorRef -- which is correct, there's only one instance of the ActorRef. The means to re-create the instance of the actor are contained in the Props object used by system.actorOf(). So even if the underlying actor instance is restarted, the singleton ActorRef is still valid.Eastsoutheast
C
3

Unless you are trying to bind UntypedActor to FizzActor, then you can just inject it into other classes as is:

class SomeOtherClass {

    @Inject 
    public SomeOtherClass(FizzActor fizzActor) {
        //do stuff
    }
}

If you're trying to bind it to the interface, you'll need to specifically do that in the module:

public class MyActorSystemModule extends AbstractModule {
    @Override
    public void configure() {
        bind(MyService.class).to(MyServiceImpl.class);
        bind(UntypedActor.class).to(FizzActor.class);
    }
}

Edit:

What about using @Named to distinguish the UntypedActor, e.g.:

class SomeOtherClass {

    @Inject 
    public SomeOtherClass(@Named("fizzActor")UntypedActor fizzActor, @Named("fooActor") UntypedActor fooActor) {
        //do stuff
    }
}

Then in your module you could do the akka lookups:

public class MyActorSystemModule extends AbstractModule {

    ActorSystem system = ActorSystem.create("MySystem");

    @Override
    public void configure() {
        bind(MyService.class).to(MyServiceImpl.class);
    }

    @Provides
    @Named("fizzActor")
    public UntypedActor getFizzActor() {
        return system.actorOf(Props.create(FizzActor.class), "fizzActor");
    }

    @Provides
    @Named("fooActor")
    public UntypedActor getFooActor() {
        return system.actorOf(Props.create(FooActor.class), "fooActor");
    }
}
Choate answered 16/6, 2015 at 9:50 Comment(4)
Thanks @Choate (+1) - first off, I realized that I left out a crucial fact in my original question, please see my update. Essentially, there will be hundreds of different UntypedActor impls in my actor system, and so I can't just blindly bind FizzActor to it. Second, and more importantly, are you at all familiar with Akka? If you're not, then your code example above makes sense to me: in Akka you never create actors outside the ActorSystem, you let Akka do this for you. And so SomeOtherClass would never have a reference to a FizzActor. Ever!Gladden
Hey @Gladden - Ok gotcha. I am not super familiar with it, but just had a look at the akka page. Using their greeter example, would my edit work?Choate
Thanks again @Choate (+1) - so believe it or not, no, that solution won't work either. It will only give you an ActorRef instance. Again, in Akka, the framework deals directly with UntypedActor instances. If you want to interact with the system (or a specific actor) from the outside, you use an Inbox. If you want to interact with a specific actor from the inside, then you use an ActorRef, but you never directly deal with the UntypedActor impl yourself. So the solution here will likely involve some Akka-Guice plugin that allows Guice to inject the guts of the actor system.Gladden
@Gladden yep, sorry - that makes sense. You're doing a better job educating me on Akka than I am trying to help with the Guice stuff! I'll stop wasting your time, good luck!Choate
H
1

Use an akka Creator:

public class GuiceCreator<T> implements Creator<T> {
 Class<T> clz;
 Module module;
 /*Constructor*/

 public T create() {
    Injector injector = Guice.createInjector(this.module);
    return injector.getInstance(this.clz);
  }
}

Then use Props.create with your shiny new guice-based creator.

Disclaimer: I don't actually know Akka, the mentioned information comes from browsing the documentation and JavaDoc.

However answered 16/6, 2015 at 9:50 Comment(2)
Thanks @However (+1) - please see my comment underneath acanby's answer, same applies to you! It is not as simple as you might think, because you can never instantiate an actor outside of the actor system. Ergo, the answer to this must lie in an Akka-Guice plugin or something that wires Guice up to the actor system's internals.Gladden
@Gladden I see. I browsed the JavaDoc for Props and found the Creator interface. Changed my answer accordingly. I don't actually know akka, as you have rightly guessed ;-)However
F
0

In case anyone found this question, you need to use IndirectActorProducer, I referred to the Spring example and changed it to use Guice instead.

/**
 * An actor producer that lets Guice create the Actor instances.
 */
public class GuiceActorProducer implements IndirectActorProducer {
    final String actorBeanName;
    final Injector injector;
    final Class<? extends Actor> actorClass;

    public GuiceActorProducer(Injector injector, String actorBeanName, Class<? extends Actor> actorClass) {
        this.actorBeanName = actorBeanName;
        this.injector = injector;
        this.actorClass = actorClass;
    }

    @Override
    public Actor produce() {
        return injector.getInstance(Key.get(Actor.class, Names.named(actorBeanName)));
    }

    @Override
    public Class<? extends Actor> actorClass() {
        return actorClass;
    }
}

In the module

public class BookingModule extends AbstractModule {

    @Override
    protected void configure() {               
        // Raw actor class, meant to be used by GuiceActorProducer.
        // Do not use this directly
        bind(Actor.class).annotatedWith(
                Names.named(BookingActor.ACTOR_BEAN_NAME)).to(
                BookingActor.class);
    }

    @Singleton
    @Provides
    @Named(BookingActor.ACTOR_ROUTER_BEAN_NAME)
    ActorRef systemActorRouter(Injector injector, ActorSystem actorSystem) {
      Props props = Props.create(GuiceActorProducer.class, injector, BookingActor.ACTOR_BEAN_NAME, actorClass);
      actorSystem.actorOf(props.withRouter(new RoundRobinPool(DEFAULT_ROUTER_SIZE)), BookingActor.ACTOR_ROUTER_BEAN_NAME);
    }
}
Fairly answered 6/5, 2016 at 2:52 Comment(1)
Typesafe (or whoever they are now) does endorse IndirectActorProducer though I've never understood why this class is needed.Biforate
N
0

So I have been playing around with Akka and Guice recently alot and I feel that those two don't play too well together.

What I suggest is you take a similar approach what Play is doing.

Kutschkem's answer comes closest to that.

  1. use the ActorCreator interface
  2. make sure you have an argumentless Creator. Don't try to do @AssisstedInject in your Creator as this will imply that you will need a new creator for every Actor that you want to create. Personally I believe that initializing this in the actor is better done through messaging.
  3. let the ActorCreator consume an injector such that you can easily create the Actor Object within the Creator.

Here is a code example using current Akka 2.5. This is the preferred setup we chose for our Akka 2.5 deployment. For brevity I did not provide the Module, but it should be clear from the way the Members are injected, what you want to provide.

Code:

 class ActorCreator implements Creator<MyActor>
   @Inject
   Injector injector;
   public MyActor create() {
     return injector.getInstance(MyActor.class);
   }
 }

 class MyActor extends AbstractActor {
   @Inject
   SomeController object;

   @Nullable
   MyDataObject data;

   public ReceiveBuilder createReceiveBuilder() {
    return receiveBuilder()
      .match(MyDataObject.class, m -> { /* doInitialize() */ })
      .build(); 
   }
}

class MyParentActor extends AbstractActor {
   @Inject
   ActorCreator creator;

   void createChild() {
     getContext().actorOf(new Props(creator));
   }

   void initializeChild(ActorRef child, MyDataObject obj) {
     child.tell(obj);
   }
}
Notice answered 17/11, 2017 at 17:43 Comment(0)
T
0

Generic Akka Guice integration without dependency on Play, keeping in mind, not the only actor should be created in the actor system.

import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import com.google.inject.AbstractModule;
import com.google.inject.Provider;
import com.google.inject.name.Names;

public abstract class AkkaGuiceModule extends AbstractModule {

    protected <T extends Actor> void bindActor(Class<T> actorClass, String name) {
        bind(actorClass);

        Provider<ActorSystem> actorSystemProvider = getProvider(ActorSystem.class);
        Provider<T> actorProvider = getProvider(actorClass);

        bind(ActorRef.class)
                .annotatedWith(Names.named(name))
                .toProvider(ActorRefProvider.of(actorSystemProvider, actorProvider, name))
                .asEagerSingleton();
    }

}

Generic ActorRefProvider to create ActorRef for each Actor

import akka.actor.Actor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.japi.Creator;
import com.google.inject.Provider;
import lombok.Value;

@Value(staticConstructor = "of")
public class ActorRefProvider<T extends Actor> implements Provider<ActorRef> {

    private Provider<ActorSystem> actorSystemProvider;
    private Provider<T> actorProvider;
    private String name;

    public final class ActorCreator implements Creator<Actor> {
        @Override
        public Actor create() {
            return actorProvider.get();
        }
    }

    @Override
    public ActorRef get() {
        return actorSystemProvider.get().actorOf(Props.create(new ActorCreator()), name);
    }

}

Usage example

import akka.actor.ActorSystem;
import com.google.inject.Provides;
import com.typesafe.config.Config; // optional

public class MyAkkaModule extends AkkaGuiceModule {

    @Provides
    @Singleton
    ActorSystem actorSystem(Config config) {
        return ActorSystem.create("actor-system-name", config);
    }

    @Override
    protected void configure() {
        bindActor(SomeActor1.class, "actorName1");
        bindActor(SomeActor2.class, "actorName2");
    }

}
Tempietempla answered 24/9, 2019 at 19:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.