Using Akka With Java
Follow along with this walkthrough of a simple example of using Akka AbstractActor.
Join the DZone community and get the full member experience.
Join For FreeIn the past few days, I've been working on a project with Akka using Java. It was an amazing experience, so I'm going to discuss how to use Akka in Java and write a test case.
If we look at the Akka's documentation, there is a class named UntypedActor to create an actor. But here, we're going to discuss the AbstractActor, which seems pretty concise.
First, add the following dependency for Akka and the test case:
"com.typesafe.akka" %% "akka-slf4j" % "2.4.8"
Then, to create an actor, we first we need to create Props:
public static Props props() {
return Props.create(HappyBdayActor.class);
}
Then, we write the responsibility of an actor as:
class HappyBdayActor extends AbstractActor {
public static Props props() {
return Props.create(HappyBdayActor.class);
}
@Override
public PartialFunction<Object, BoxedUnit> receive() {
//responsiblity of this actor can be here
}
}
Now we need to find out the message passed to this actor and react accordingly. We can do this as:
@Override
public PartialFunction<Object, BoxedUnit> receive() {
return ReceiveBuilder
.match(String.class, message -> {
logger.info("Message received from someone : {}"+ message);
})
.matchAny(message -> logger.error("Some unknown things happened : {}", message))
.build();
}
Now everybody will need a Supervisor. As we know, a Supervisor is an Actor that supervises other actors. Here's how we'll implement it.
private final SupervisorStrategy strategy = new OneForOneStrategy(false,
match(InterruptedException.class, e ->
SupervisorStrategy.resume()
).
match(Throwable.class, e ->
SupervisorStrategy.restart()
).build());
@Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
Now we have an architecture for an Akka actor.
After that, I decided to introduce a Google Guice injection. To proceed with this, we need to add the following dependency:
“com.google.inject” % “guice” % “4.1.0”
We will inject our service to the actor, so we create a simple service:
@Singleton
public class MessageHandlerService {
static final String BEAN_NAME = "messageHandlerService";
public final String substr(String message , int index){
return "Happy bday "+message.substring(index);
}
}
Now create an another class to define injections:
public class Config extends AbstractModule {
@Override
protected final void configure() {
bind(ActorSystem.class).toInstance(ActorSystem.apply());
bind(MessageHandlerService.class).annotatedWith(Names.named(MessageHandlerService.BEAN_NAME)).
to(MessageHandlerService.class);
}
@Singleton
@Provides
@Named(value = Supervisor.BEAN_NAME)
public final ActorRef supervisorRef(final ActorSystem system) {
return system.actorOf(Supervisor.props());
}
@Provides
@Inject
@Named(value = HappyBdayActor.BEAN_NAME)
public final ActorRef HappyBdayActorRef(@Named(Supervisor.BEAN_NAME) final ActorRef supervisor,
@Named(MessageHandlerService.BEAN_NAME) final MessageHandlerService messageHandlerService) throws Exception {
CompletionStage<Object> eventFuture = ask(supervisor, HappyBdayActor.props(messageHandlerService),
Timeout.apply(50, TimeUnit.MILLISECONDS));
return (ActorRef) eventFuture.toCompletableFuture().get(60, TimeUnit.MILLISECONDS);
}
}
And also modify the actor as:
final LoggingAdapter logger = Logging.getLogger(context().system(), this);
public static final String BEAN_NAME = "HappyBdayActorRef";
private final MessageHandlerService messageHandlerService;
HappyBdayActor(MessageHandlerService messageHandlerServie) {
this.messageHandlerService = messageHandlerServie;
}
public static Props props(MessageHandlerService messageHandlerService) {
return Props.create(HappyBdayActor.class, messageHandlerService);
}
Now we can write our launcher to start the actor’s execution as:
public class Launcher {
private ActorRef happyBdayActorRef;
@Inject
public Launcher(@Named(HappyBdayActor.BEAN_NAME) ActorRef happyBdayActorRef) {
this.happyBdayActorRef = happyBdayActorRef;
}
public ActorRef getHappyBdayActorRef() {
return happyBdayActorRef;
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new Config());
Launcher app = injector.getInstance(Launcher.class);
app.getHappyBdayActorRef().tell("Hello",ActorRef.noSender());
}
}
Now, we developers are always curious about how to write unit test cases. Add the following sbt dependency:
"junit" % "junit" % "4.12" % "test" exclude("hamcrest-core", "org.hamcrest"),
"com.typesafe.akka" %% "akka-http-testkit" % "2.4.8" % "test",
"org.mockito" % "mockito-core" % "1.10.19" % "test" exclude("hamcrest-core", "org.hamcrest"),
"org.hamcrest" % "hamcrest-all" % "1.3" % "test"
And now, our unit test case:
public classHappyBdayActor {
private static ActorSystem system;
private static MessageHandlerService messageHandlerService;
@BeforeClass
public static void setup() {
system = ActorSystem.create();
messageHandlerService = mock(MessageHandlerService.class);
}
@AfterClass
public static void teardown() {
JavaTestKit.shutdownActorSystem(system);
system = null;
}
@Test
public void testProps() {
final JavaTestKit probe = new JavaTestKit(system);
Props props = HappyBdayActor.props(messageHandlerService);
assertThat(props.actorClass(), is(equalTo(HappyBdayActor.class)));
}
@Test
public void testEventLoggerActorFirebaseRequestMatch() {
TestProbe probe = TestProbe.apply(system);
final Props props = HappyBdayActor.props(messageHandlerService);
when(messageHandlerService.substr("RahulKumar",5))
.thenReturn(String.valueOf("Kumar"));
final TestActorRef<HappyBdayActor>happyBdayActorTestActorRef = TestActorRef.create(system, props);
happyBdayActorTestActorRef.tell("RahulKumar", happyBdayActorTestActorRef);
probe.expectNoMsg();
}
}
For the complete code, click here.
Published at DZone with permission of Rahul Kumar, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments