Make the AbstractBehavior builder mutable (#26309)
* Make the AbstractBehavior builder mutable #26260 * Use mutable builder style in first sample, mention that fluent is an option * A bit of rework of the Java builders: * onAnyMessage added * use the japi SAMs throughout in the APIs * avoid wrapping the japi functions in Scala functions for the most common cases * more Java test coverage * Not just any exception * Works on 2.11 as well as 2.12
This commit is contained in:
parent
ddada9a8e1
commit
8fabb73f2b
22 changed files with 391 additions and 268 deletions
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
package akka.actor.typed.javadsl;
|
||||
|
||||
import akka.actor.testkit.typed.javadsl.TestKitJunitResource;
|
||||
import akka.actor.testkit.typed.javadsl.TestProbe;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
|
|
@ -15,9 +18,13 @@ import java.util.ArrayList;
|
|||
|
||||
import static akka.actor.typed.javadsl.Behaviors.same;
|
||||
import static akka.actor.typed.javadsl.Behaviors.stopped;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/** Test creating [[Behavior]]s using [[BehaviorBuilder]] */
|
||||
public class BehaviorBuilderTest extends JUnitSuite {
|
||||
|
||||
@ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource();
|
||||
|
||||
interface Message {}
|
||||
|
||||
static final class One implements Message {
|
||||
|
|
@ -28,7 +35,6 @@ public class BehaviorBuilderTest extends JUnitSuite {
|
|||
|
||||
static final class MyList<T> extends ArrayList<T> implements Message {};
|
||||
|
||||
@Test
|
||||
public void shouldCompile() {
|
||||
Behavior<Message> b =
|
||||
Behaviors.receive(Message.class)
|
||||
|
|
@ -54,6 +60,86 @@ public class BehaviorBuilderTest extends JUnitSuite {
|
|||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caseSelectedInOrderAdded() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
BehaviorBuilder.create()
|
||||
.onMessage(
|
||||
String.class,
|
||||
(context, msg) -> {
|
||||
probe.ref().tell("handler 1: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.onMessage(
|
||||
String.class,
|
||||
(context, msg) -> {
|
||||
probe.ref().tell("handler 2: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("handler 1: message");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMessageBasedOnEquality() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
BehaviorBuilder.create()
|
||||
.onMessageEquals(
|
||||
"message",
|
||||
(context) -> {
|
||||
probe.ref().tell("got it");
|
||||
return Behaviors.same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("got it");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyPredicate() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
BehaviorBuilder.create()
|
||||
.onMessage(
|
||||
String.class,
|
||||
(msg) -> "other".equals(msg),
|
||||
(context, msg) -> {
|
||||
probe.ref().tell("handler 1: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.onMessage(
|
||||
String.class,
|
||||
(context, msg) -> {
|
||||
probe.ref().tell("handler 2: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("handler 2: message");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catchAny() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
BehaviorBuilder.create()
|
||||
.onAnyMessage(
|
||||
(context, msg) -> {
|
||||
probe.ref().tell(msg);
|
||||
return same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("message");
|
||||
}
|
||||
|
||||
interface CounterMessage {};
|
||||
|
||||
static final class Increase implements CounterMessage {};
|
||||
|
|
@ -92,6 +178,12 @@ public class BehaviorBuilderTest extends JUnitSuite {
|
|||
|
||||
@Test
|
||||
public void testImmutableCounter() {
|
||||
Behavior<CounterMessage> immutable = immutableCounter(0);
|
||||
ActorRef<CounterMessage> ref = testKit.spawn(immutableCounter(0));
|
||||
TestProbe<Got> probe = testKit.createTestProbe();
|
||||
ref.tell(new Get(probe.getRef()));
|
||||
assertEquals(0, probe.expectMessageClass(Got.class).n);
|
||||
ref.tell(new Increase());
|
||||
ref.tell(new Get(probe.getRef()));
|
||||
assertEquals(1, probe.expectMessageClass(Got.class).n);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,23 @@
|
|||
|
||||
package akka.actor.typed.javadsl;
|
||||
|
||||
import akka.actor.testkit.typed.javadsl.TestKitJunitResource;
|
||||
import akka.actor.testkit.typed.javadsl.TestProbe;
|
||||
import akka.actor.typed.ActorRef;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.scalatest.junit.JUnitSuite;
|
||||
|
||||
import akka.actor.typed.Behavior;
|
||||
|
||||
import static akka.actor.typed.javadsl.Behaviors.same;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/** Test creating [[MutableActor]]s using [[ReceiveBuilder]] */
|
||||
public class ReceiveBuilderTest extends JUnitSuite {
|
||||
|
||||
@ClassRule public static final TestKitJunitResource testKit = new TestKitJunitResource();
|
||||
|
||||
@Test
|
||||
public void testMutableCounter() {
|
||||
Behavior<BehaviorBuilderTest.CounterMessage> mutable =
|
||||
|
|
@ -36,7 +43,7 @@ public class ReceiveBuilderTest extends JUnitSuite {
|
|||
|
||||
@Override
|
||||
public Receive<BehaviorBuilderTest.CounterMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(BehaviorBuilderTest.Increase.class, this::receiveIncrease)
|
||||
.onMessage(BehaviorBuilderTest.Get.class, this::receiveGet)
|
||||
.build();
|
||||
|
|
@ -56,7 +63,7 @@ public class ReceiveBuilderTest extends JUnitSuite {
|
|||
@Override
|
||||
public Receive<BehaviorBuilderTest.CounterMessage> createReceive() {
|
||||
assertEquals(42, value);
|
||||
return receiveBuilder().build();
|
||||
return newReceiveBuilder().build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,4 +72,67 @@ public class ReceiveBuilderTest extends JUnitSuite {
|
|||
MyAbstractBehavior mutable = new MyAbstractBehavior(42);
|
||||
assertEquals(Behaviors.unhandled(), mutable.receive(null, new BehaviorBuilderTest.Increase()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caseSelectedInOrderAdded() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
ReceiveBuilder.<Object>create()
|
||||
.onMessage(
|
||||
String.class,
|
||||
msg -> {
|
||||
probe.ref().tell("handler 1: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.onMessage(
|
||||
String.class,
|
||||
msg -> {
|
||||
probe.ref().tell("handler 2: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("handler 1: message");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyPredicate() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
ReceiveBuilder.create()
|
||||
.onMessage(
|
||||
String.class,
|
||||
msg -> "other".equals(msg),
|
||||
msg -> {
|
||||
probe.ref().tell("handler 1: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.onMessage(
|
||||
String.class,
|
||||
msg -> {
|
||||
probe.ref().tell("handler 2: " + msg);
|
||||
return Behaviors.same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("handler 2: message");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catchAny() {
|
||||
final TestProbe<Object> probe = testKit.createTestProbe();
|
||||
Behavior<Object> behavior =
|
||||
ReceiveBuilder.create()
|
||||
.onAnyMessage(
|
||||
msg -> {
|
||||
probe.ref().tell(msg);
|
||||
return same();
|
||||
})
|
||||
.build();
|
||||
ActorRef<Object> ref = testKit.spawn(behavior);
|
||||
ref.tell("message");
|
||||
probe.expectMessage("message");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ public class InteractionPatternsTest extends JUnitSuite {
|
|||
|
||||
@Override
|
||||
public Receive<Command> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(
|
||||
Translate.class,
|
||||
cmd -> {
|
||||
|
|
@ -603,7 +603,7 @@ public class InteractionPatternsTest extends JUnitSuite {
|
|||
|
||||
@Override
|
||||
public Receive<Object> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(
|
||||
Wallet.class,
|
||||
(wallet) -> {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,11 @@
|
|||
package jdocs.akka.typed;
|
||||
|
||||
// #imports
|
||||
import akka.NotUsed;
|
||||
import akka.actor.typed.ActorRef;
|
||||
import akka.actor.typed.ActorSystem;
|
||||
import akka.actor.typed.Behavior;
|
||||
import akka.actor.typed.Terminated;
|
||||
import akka.actor.typed.javadsl.AbstractBehavior;
|
||||
import akka.actor.typed.javadsl.ActorContext;
|
||||
import akka.actor.typed.javadsl.Behaviors;
|
||||
import akka.actor.typed.javadsl.Receive;
|
||||
import akka.actor.typed.javadsl.*;
|
||||
// #imports
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
|
@ -112,29 +108,32 @@ public class OOIntroTest {
|
|||
|
||||
@Override
|
||||
public Receive<RoomCommand> createReceive() {
|
||||
return receiveBuilder()
|
||||
.onMessage(
|
||||
GetSession.class,
|
||||
getSession -> {
|
||||
ActorRef<SessionEvent> client = getSession.replyTo;
|
||||
ActorRef<SessionCommand> ses =
|
||||
context.spawn(
|
||||
session(context.getSelf(), getSession.screenName, client),
|
||||
URLEncoder.encode(getSession.screenName, StandardCharsets.UTF_8.name()));
|
||||
// narrow to only expose PostMessage
|
||||
client.tell(new SessionGranted(ses.narrow()));
|
||||
sessions.add(ses);
|
||||
return this;
|
||||
})
|
||||
.onMessage(
|
||||
PublishSessionMessage.class,
|
||||
pub -> {
|
||||
NotifyClient notification =
|
||||
new NotifyClient((new MessagePosted(pub.screenName, pub.message)));
|
||||
sessions.forEach(s -> s.tell(notification));
|
||||
return this;
|
||||
})
|
||||
.build();
|
||||
ReceiveBuilder<RoomCommand> builder = newReceiveBuilder();
|
||||
|
||||
builder.onMessage(
|
||||
GetSession.class,
|
||||
getSession -> {
|
||||
ActorRef<SessionEvent> client = getSession.replyTo;
|
||||
ActorRef<SessionCommand> ses =
|
||||
context.spawn(
|
||||
session(context.getSelf(), getSession.screenName, client),
|
||||
URLEncoder.encode(getSession.screenName, StandardCharsets.UTF_8.name()));
|
||||
// narrow to only expose PostMessage
|
||||
client.tell(new SessionGranted(ses.narrow()));
|
||||
sessions.add(ses);
|
||||
return this;
|
||||
});
|
||||
|
||||
builder.onMessage(
|
||||
PublishSessionMessage.class,
|
||||
pub -> {
|
||||
NotifyClient notification =
|
||||
new NotifyClient((new MessagePosted(pub.screenName, pub.message)));
|
||||
sessions.forEach(s -> s.tell(notification));
|
||||
return this;
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,13 +42,13 @@ abstract class AbstractBehavior[T] extends ExtensibleBehavior[T] {
|
|||
|
||||
/**
|
||||
* Implement this to define how messages and signals are processed. Use the
|
||||
* [[AbstractBehavior.receiveBuilder]] to define the message dispatch.
|
||||
* [[AbstractBehavior.newReceiveBuilder]] to define the message dispatch.
|
||||
*/
|
||||
def createReceive: Receive[T]
|
||||
|
||||
/**
|
||||
* Create a [[ReceiveBuilder]] to define the message dispatch of the `Behavior`.
|
||||
* Create a new [[ReceiveBuilder]] to define the message dispatch of the `Behavior`.
|
||||
* Typically used from [[AbstractBehavior.createReceive]].
|
||||
*/
|
||||
def receiveBuilder: ReceiveBuilder[T] = ReceiveBuilder.create
|
||||
def newReceiveBuilder: ReceiveBuilder[T] = ReceiveBuilder.create
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,29 +5,34 @@
|
|||
package akka.actor.typed.javadsl
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
import akka.japi.function.{ Function, Function2, Predicate }
|
||||
import akka.japi.function.{ Function ⇒ JFunction }
|
||||
import akka.japi.function.{ Function2 ⇒ JFunction2 }
|
||||
import akka.japi.function.{ Predicate ⇒ JPredicate }
|
||||
import akka.annotation.InternalApi
|
||||
import akka.actor.typed
|
||||
import akka.actor.typed.{ Behavior, ExtensibleBehavior, Signal }
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
import akka.actor.typed.ExtensibleBehavior
|
||||
import akka.actor.typed.Signal
|
||||
import akka.actor.typed.TypedActorContext
|
||||
import akka.actor.typed.Behavior.unhandled
|
||||
|
||||
import BehaviorBuilder._
|
||||
import akka.util.OptionVal
|
||||
|
||||
/**
|
||||
* Used for creating a [[Behavior]] by 'chaining' message and signal handlers.
|
||||
* Immutable builder used for creating a [[Behavior]] by 'chaining' message and signal handlers.
|
||||
*
|
||||
* When handling a message or signal, this [[Behavior]] will consider all handlers in the order they were added,
|
||||
* looking for the first handler for which both the type and the (optional) predicate match.
|
||||
*
|
||||
* @tparam T the common superclass of all supported messages.
|
||||
*/
|
||||
class BehaviorBuilder[T] private (
|
||||
private val messageHandlers: List[Case[T, T]],
|
||||
private val signalHandlers: List[Case[T, Signal]]
|
||||
final class BehaviorBuilder[T] private (
|
||||
messageHandlers: List[Case[T, T]],
|
||||
signalHandlers: List[Case[T, Signal]]
|
||||
) {
|
||||
|
||||
/**
|
||||
* Build a Behavior from the current state of the builder
|
||||
*/
|
||||
def build(): Behavior[T] = new BuiltBehavior(messageHandlers.reverse, signalHandlers.reverse)
|
||||
|
||||
/**
|
||||
|
|
@ -36,10 +41,10 @@ class BehaviorBuilder[T] private (
|
|||
* @param type type of message to match
|
||||
* @param handler action to apply if the type matches
|
||||
* @tparam M type of message to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onMessage[M <: T](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage(`type`, None, (i1: ActorContext[T], msg: T) ⇒ handler.apply(i1, msg.asInstanceOf[M]))
|
||||
def onMessage[M <: T](`type`: Class[M], handler: JFunction2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage(OptionVal.Some(`type`), OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new predicated case to the message handling.
|
||||
|
|
@ -48,14 +53,13 @@ class BehaviorBuilder[T] private (
|
|||
* @param test a predicate that will be evaluated on the argument if the type matches
|
||||
* @param handler action to apply if the type matches and the predicate returns true
|
||||
* @tparam M type of message to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onMessage[M <: T](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
def onMessage[M <: T](`type`: Class[M], test: JPredicate[M], handler: JFunction2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage(
|
||||
`type`,
|
||||
Some((t: T) ⇒ test.test(t.asInstanceOf[M])),
|
||||
(i1: ActorContext[T], msg: T) ⇒ handler.apply(i1, msg.asInstanceOf[M])
|
||||
)
|
||||
OptionVal.Some(`type`),
|
||||
OptionVal.Some((t: T) ⇒ test.test(t.asInstanceOf[M])),
|
||||
handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling without compile time type check.
|
||||
|
|
@ -65,20 +69,36 @@ class BehaviorBuilder[T] private (
|
|||
*
|
||||
* @param type type of message to match
|
||||
* @param handler action to apply when the type matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onMessageUnchecked[M <: T](`type`: Class[_ <: T], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage(`type`, None, (i1: ActorContext[T], msg: T) ⇒ handler.apply(i1, msg.asInstanceOf[M]))
|
||||
def onMessageUnchecked[M <: T](`type`: Class[_ <: T], handler: JFunction2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage[M](
|
||||
OptionVal.Some(`type`.asInstanceOf[Class[M]]), OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling matching equal messages.
|
||||
*
|
||||
* @param msg the message to compare to
|
||||
* @param handler action to apply when the message matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onMessageEquals(msg: T, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage(msg.getClass, Some(_.equals(msg)), (ctx: ActorContext[T], _: T) ⇒ handler.apply(ctx))
|
||||
def onMessageEquals(msg: T, handler: JFunction[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage[T](
|
||||
OptionVal.Some(msg.getClass.asInstanceOf[Class[T]]),
|
||||
OptionVal.Some(_.equals(msg)),
|
||||
new JFunction2[ActorContext[T], T, Behavior[T]] {
|
||||
override def apply(ctx: ActorContext[T], msg: T): Behavior[T] = handler.apply(ctx)
|
||||
})
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling matching any message. Subsequent `onMessage` clauses will
|
||||
* never see any messages.
|
||||
*
|
||||
* @param handler action to apply for any message
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onAnyMessage(handler: JFunction2[ActorContext[T], T, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withMessage(OptionVal.None, OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the signal handling.
|
||||
|
|
@ -86,10 +106,13 @@ class BehaviorBuilder[T] private (
|
|||
* @param type type of signal to match
|
||||
* @param handler action to apply if the type matches
|
||||
* @tparam M type of signal to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onSignal[M <: Signal](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withSignal(`type`, None, (ctx: ActorContext[T], signal: Signal) ⇒ handler.apply(ctx, signal.asInstanceOf[M]))
|
||||
def onSignal[M <: Signal](`type`: Class[M], handler: JFunction2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withSignal(
|
||||
`type`,
|
||||
OptionVal.None,
|
||||
handler.asInstanceOf[JFunction2[ActorContext[T], Signal, Behavior[T]]])
|
||||
|
||||
/**
|
||||
* Add a new predicated case to the signal handling.
|
||||
|
|
@ -98,168 +121,87 @@ class BehaviorBuilder[T] private (
|
|||
* @param test a predicate that will be evaluated on the argument if the type matches
|
||||
* @param handler action to apply if the type matches and the predicate returns true
|
||||
* @tparam M type of signal to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onSignal[M <: Signal](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
def onSignal[M <: Signal](`type`: Class[M], test: JPredicate[M], handler: JFunction2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withSignal(
|
||||
`type`,
|
||||
Some((t: Signal) ⇒ test.test(t.asInstanceOf[M])),
|
||||
(ctx: ActorContext[T], signal: Signal) ⇒ handler.apply(ctx, signal.asInstanceOf[M])
|
||||
OptionVal.Some((t: Signal) ⇒ test.test(t.asInstanceOf[M])),
|
||||
handler.asInstanceOf[JFunction2[ActorContext[T], Signal, Behavior[T]]]
|
||||
)
|
||||
|
||||
/**
|
||||
* Add a new case to the signal handling without compile time type check.
|
||||
*
|
||||
* Should normally not be used, but when matching on class with generic type
|
||||
* argument it can be useful, e.g. <code>GenMsg.class</code> and <code>(ActorContext<Message> ctx, GenMsg<String> list) -> {...}</code>
|
||||
*
|
||||
* @param type type of signal to match
|
||||
* @param handler action to apply when the type matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def onSignalUnchecked[M <: Signal](`type`: Class[_ <: Signal], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
withSignal(`type`, None, (ctx: ActorContext[T], signal: Signal) ⇒ handler.apply(ctx, signal.asInstanceOf[M]))
|
||||
|
||||
/**
|
||||
* Add a new case to the signal handling matching equal signals.
|
||||
*
|
||||
* @param signal the signal to compare to
|
||||
* @param handler action to apply when the message matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return a new behavior builder with the specified handling appended
|
||||
*/
|
||||
def onSignalEquals(signal: Signal, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
|
||||
withSignal(signal.getClass, Some(_.equals(signal)), (ctx: ActorContext[T], _: Signal) ⇒ handler.apply(ctx))
|
||||
withSignal(
|
||||
signal.getClass,
|
||||
OptionVal.Some(_.equals(signal)),
|
||||
new JFunction2[ActorContext[T], Signal, Behavior[T]] {
|
||||
override def apply(ctx: ActorContext[T], signal: Signal): Behavior[T] = {
|
||||
handler.apply(ctx)
|
||||
}
|
||||
})
|
||||
|
||||
private def withMessage(`type`: Class[_ <: T], test: Option[T ⇒ Boolean], handler: (ActorContext[T], T) ⇒ Behavior[T]): BehaviorBuilder[T] =
|
||||
new BehaviorBuilder[T](Case[T, T](`type`, test, handler) +: messageHandlers, signalHandlers)
|
||||
private def withMessage[M <: T](clazz: OptionVal[Class[M]], test: OptionVal[M ⇒ Boolean], handler: JFunction2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] = {
|
||||
val newCase = Case(
|
||||
clazz,
|
||||
test,
|
||||
handler
|
||||
)
|
||||
new BehaviorBuilder[T](newCase.asInstanceOf[Case[T, T]] +: messageHandlers, signalHandlers)
|
||||
}
|
||||
|
||||
private def withSignal[M <: Signal](`type`: Class[M], test: Option[Signal ⇒ Boolean], handler: (ActorContext[T], Signal) ⇒ Behavior[T]): BehaviorBuilder[T] =
|
||||
new BehaviorBuilder[T](messageHandlers, Case[T, Signal](`type`, test, handler) +: signalHandlers)
|
||||
private def withSignal[M <: Signal](`type`: Class[M], test: OptionVal[Signal ⇒ Boolean], handler: JFunction2[ActorContext[T], Signal, Behavior[T]]): BehaviorBuilder[T] = {
|
||||
new BehaviorBuilder[T](
|
||||
messageHandlers,
|
||||
Case(OptionVal.Some(`type`), test, handler).asInstanceOf[Case[T, Signal]] +: signalHandlers
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object BehaviorBuilder {
|
||||
def create[T]: BehaviorBuilder[T] = new BehaviorBuilder[T](Nil, Nil)
|
||||
|
||||
private val _empty = new BehaviorBuilder[Nothing](Nil, Nil)
|
||||
|
||||
// used for both matching signals and messages so we throw away types after they are enforced by the builder API above
|
||||
/** INTERNAL API */
|
||||
@InternalApi
|
||||
private[javadsl] final case class Case[BT, MT](`type`: Class[_ <: MT], test: Option[MT ⇒ Boolean], handler: (ActorContext[BT], MT) ⇒ Behavior[BT])
|
||||
private[javadsl] final case class Case[BT, MT](`type`: OptionVal[Class[_ <: MT]], test: OptionVal[MT ⇒ Boolean], handler: JFunction2[ActorContext[BT], MT, Behavior[BT]])
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with this case.
|
||||
*
|
||||
* @param type type of message to match
|
||||
* @param handler action to apply if the type matches
|
||||
* @tparam T type of behavior to create
|
||||
* @tparam M type of message to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return new empty immutable behavior builder.
|
||||
*/
|
||||
def message[T, M <: T](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onMessage(`type`, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with this predicated case.
|
||||
*
|
||||
* @param type type of message to match
|
||||
* @param test a predicate that will be evaluated on the argument if the type matches
|
||||
* @param handler action to apply if the type matches and the predicate returns true
|
||||
* @tparam T type of behavior to create
|
||||
* @tparam M type of message to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def message[T, M <: T](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onMessage(`type`, test, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with a handler without compile time type check.
|
||||
*
|
||||
* Should normally not be used, but when matching on class with generic type
|
||||
* argument it can be useful, e.g. <code>List.class</code> and <code>(List<String> list) -> {...}</code>
|
||||
*
|
||||
* @param type type of message to match
|
||||
* @param handler action to apply when the type matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def messageUnchecked[T, M <: T](`type`: Class[_ <: T], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onMessageUnchecked(`type`, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with a handler for equal messages.
|
||||
*
|
||||
* @param msg the message to compare to
|
||||
* @param handler action to apply when the message matches
|
||||
* @tparam T type of behavior to create
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def messageEquals[T](msg: T, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onMessageEquals(msg, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with this signal case.
|
||||
*
|
||||
* @param type type of signal to match
|
||||
* @param handler action to apply if the type matches
|
||||
* @tparam T type of behavior to create
|
||||
* @tparam M type of signal to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def signal[T, M <: Signal](`type`: Class[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onSignal(`type`, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with this predicated signal case.
|
||||
*
|
||||
* @param type type of signals to match
|
||||
* @param test a predicate that will be evaluated on the argument if the type matches
|
||||
* @param handler action to apply if the type matches and the predicate returns true
|
||||
* @tparam T type of behavior to create
|
||||
* @tparam M type of signal to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def signal[T, M <: Signal](`type`: Class[M], test: Predicate[M], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onSignal(`type`, test, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with this unchecked signal case.
|
||||
*
|
||||
* Should normally not be used, but when matching on class with generic type
|
||||
* argument it can be useful, e.g. <code>GenMsg.class</code> and <code>(ActorContext<Message> ctx, GenMsg<String> list) -> {...}</code>
|
||||
*
|
||||
* @param type type of signal to match
|
||||
* @param handler action to apply when the type matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def signalUnchecked[T, M <: Signal](`type`: Class[_ <: Signal], handler: Function2[ActorContext[T], M, Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onSignalUnchecked(`type`, handler)
|
||||
|
||||
/**
|
||||
* Start a new behavior chain starting with a handler for this specific signal.
|
||||
*
|
||||
* @param signal the signal to compare to
|
||||
* @param handler action to apply when the message matches
|
||||
* @tparam T type of behavior to create
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def signalEquals[T](signal: Signal, handler: Function[ActorContext[T], Behavior[T]]): BehaviorBuilder[T] =
|
||||
BehaviorBuilder.create[T].onSignalEquals(signal, handler)
|
||||
|
||||
def create[T]: BehaviorBuilder[T] = _empty.asInstanceOf[BehaviorBuilder[T]]
|
||||
}
|
||||
|
||||
private class BuiltBehavior[T](
|
||||
private val messageHandlers: List[Case[T, T]],
|
||||
private val signalHandlers: List[Case[T, Signal]]
|
||||
/**
|
||||
* The concrete behavior
|
||||
*
|
||||
* INTERNAL API
|
||||
*/
|
||||
@InternalApi
|
||||
private final class BuiltBehavior[T](
|
||||
messageHandlers: List[Case[T, T]],
|
||||
signalHandlers: List[Case[T, Signal]]
|
||||
) extends ExtensibleBehavior[T] {
|
||||
|
||||
override def receive(ctx: typed.TypedActorContext[T], msg: T): Behavior[T] = receive[T](ctx.asJava, msg, messageHandlers)
|
||||
override def receive(ctx: TypedActorContext[T], msg: T): Behavior[T] = receive(ctx.asJava, msg, messageHandlers)
|
||||
|
||||
override def receiveSignal(ctx: typed.TypedActorContext[T], msg: Signal): Behavior[T] = receive[Signal](ctx.asJava, msg, signalHandlers)
|
||||
override def receiveSignal(ctx: TypedActorContext[T], msg: Signal): Behavior[T] = receive(ctx.asJava, msg, signalHandlers)
|
||||
|
||||
@tailrec
|
||||
private def receive[M](ctx: ActorContext[T], msg: M, handlers: List[Case[T, M]]): Behavior[T] =
|
||||
handlers match {
|
||||
case Case(cls, predicate, handler) :: tail ⇒
|
||||
if (cls.isAssignableFrom(msg.getClass) && (predicate.isEmpty || predicate.get.apply(msg))) handler(ctx, msg)
|
||||
else receive[M](ctx, msg, tail)
|
||||
case _ ⇒
|
||||
if ((cls.isEmpty || cls.get.isAssignableFrom(msg.getClass)) && (predicate.isEmpty || predicate.get.apply(msg)))
|
||||
handler(ctx, msg)
|
||||
else receive(ctx, msg, tail)
|
||||
case Nil ⇒
|
||||
unhandled[T]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,26 +5,29 @@
|
|||
package akka.actor.typed.javadsl
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import akka.japi.function.{ Creator, Function, Predicate }
|
||||
import akka.japi.function.Creator
|
||||
import akka.japi.function.{ Function ⇒ JFunction }
|
||||
import akka.japi.function.{ Predicate ⇒ JPredicate }
|
||||
import akka.actor.typed.{ Behavior, Signal }
|
||||
import akka.annotation.InternalApi
|
||||
import akka.util.OptionVal
|
||||
|
||||
/**
|
||||
* Used when implementing [[AbstractBehavior]].
|
||||
* Mutable builder used when implementing [[AbstractBehavior]].
|
||||
*
|
||||
* When handling a message or signal, this [[Behavior]] will consider all handlers in the order they were added,
|
||||
* looking for the first handler for which both the type and the (optional) predicate match.
|
||||
*
|
||||
* @tparam T the common superclass of all supported messages.
|
||||
*/
|
||||
class ReceiveBuilder[T] private (
|
||||
private val messageHandlers: List[ReceiveBuilder.Case[T, T]],
|
||||
private val signalHandlers: List[ReceiveBuilder.Case[T, Signal]]
|
||||
final class ReceiveBuilder[T] private (
|
||||
private var messageHandlers: List[ReceiveBuilder.Case[T, T]],
|
||||
private var signalHandlers: List[ReceiveBuilder.Case[T, Signal]]
|
||||
) {
|
||||
|
||||
import ReceiveBuilder.Case
|
||||
|
||||
def build(): Receive[T] = new BuiltReceive(messageHandlers.reverse, signalHandlers.reverse)
|
||||
def build(): Receive[T] = new BuiltReceive[T](messageHandlers.reverse, signalHandlers.reverse)
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling.
|
||||
|
|
@ -32,10 +35,10 @@ class ReceiveBuilder[T] private (
|
|||
* @param type type of message to match
|
||||
* @param handler action to apply if the type matches
|
||||
* @tparam M type of message to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onMessage[M <: T](`type`: Class[M], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(`type`, None, msg ⇒ handler.apply(msg.asInstanceOf[M]))
|
||||
def onMessage[M <: T](`type`: Class[M], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(OptionVal.Some(`type`), OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new predicated case to the message handling.
|
||||
|
|
@ -44,14 +47,10 @@ class ReceiveBuilder[T] private (
|
|||
* @param test a predicate that will be evaluated on the argument if the type matches
|
||||
* @param handler action to apply if the type matches and the predicate returns true
|
||||
* @tparam M type of message to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onMessage[M <: T](`type`: Class[M], test: Predicate[M], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(
|
||||
`type`,
|
||||
Some((t: T) ⇒ test.test(t.asInstanceOf[M])),
|
||||
msg ⇒ handler.apply(msg.asInstanceOf[M])
|
||||
)
|
||||
def onMessage[M <: T](`type`: Class[M], test: JPredicate[M], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(OptionVal.Some(`type`), OptionVal.Some(test), handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling without compile time type check.
|
||||
|
|
@ -61,20 +60,35 @@ class ReceiveBuilder[T] private (
|
|||
*
|
||||
* @param type type of message to match
|
||||
* @param handler action to apply when the type matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onMessageUnchecked[M <: T](`type`: Class[_ <: T], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(`type`, None, msg ⇒ handler.apply(msg.asInstanceOf[M]))
|
||||
def onMessageUnchecked[M <: T](`type`: Class[_ <: T], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage[M](OptionVal.Some(`type`.asInstanceOf[Class[M]]), OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling matching equal messages.
|
||||
*
|
||||
* @param msg the message to compare to
|
||||
* @param handler action to apply when the message matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onMessageEquals(msg: T, handler: Creator[Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(msg.getClass, Some(_.equals(msg)), _ ⇒ handler.create())
|
||||
withMessage(OptionVal.Some(msg.getClass), OptionVal.Some(new JPredicate[T] {
|
||||
override def test(param: T): Boolean = param == (msg)
|
||||
}), new JFunction[T, Behavior[T]] {
|
||||
// invoke creator without the message
|
||||
override def apply(param: T): Behavior[T] = handler.create()
|
||||
})
|
||||
|
||||
/**
|
||||
* Add a new case to the message handling matching any message. Subsequent `onMessage` clauses will
|
||||
* never see any messages.
|
||||
*
|
||||
* @param handler action to apply for any message
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onAnyMessage(handler: JFunction[T, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withMessage(OptionVal.None, OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the signal handling.
|
||||
|
|
@ -82,10 +96,10 @@ class ReceiveBuilder[T] private (
|
|||
* @param type type of signal to match
|
||||
* @param handler action to apply if the type matches
|
||||
* @tparam M type of signal to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onSignal[M <: Signal](`type`: Class[M], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withSignal(`type`, None, signal ⇒ handler.apply(signal.asInstanceOf[M]))
|
||||
def onSignal[M <: Signal](`type`: Class[M], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withSignal(`type`, OptionVal.None, handler)
|
||||
|
||||
/**
|
||||
* Add a new predicated case to the signal handling.
|
||||
|
|
@ -94,60 +108,55 @@ class ReceiveBuilder[T] private (
|
|||
* @param test a predicate that will be evaluated on the argument if the type matches
|
||||
* @param handler action to apply if the type matches and the predicate returns true
|
||||
* @tparam M type of signal to match
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onSignal[M <: Signal](`type`: Class[M], test: Predicate[M], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withSignal(
|
||||
`type`,
|
||||
Some((t: Signal) ⇒ test.test(t.asInstanceOf[M])),
|
||||
signal ⇒ handler.apply(signal.asInstanceOf[M])
|
||||
)
|
||||
|
||||
/**
|
||||
* Add a new case to the signal handling without compile time type check.
|
||||
*
|
||||
* Should normally not be used, but when matching on class with generic type
|
||||
* argument it can be useful, e.g. <code>GenMsg.class</code> and <code>(ActorContext<Message> ctx, GenMsg<String> list) -> {...}</code>
|
||||
*
|
||||
* @param type type of signal to match
|
||||
* @param handler action to apply when the type matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
*/
|
||||
def onSignalUnchecked[M <: Signal](`type`: Class[_ <: Signal], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withSignal(`type`, None, signal ⇒ handler.apply(signal.asInstanceOf[M]))
|
||||
def onSignal[M <: Signal](`type`: Class[M], test: JPredicate[M], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] =
|
||||
withSignal(`type`, OptionVal.Some(test), handler)
|
||||
|
||||
/**
|
||||
* Add a new case to the signal handling matching equal signals.
|
||||
*
|
||||
* @param signal the signal to compare to
|
||||
* @param handler action to apply when the message matches
|
||||
* @return a new behavior with the specified handling appended
|
||||
* @return this behavior builder
|
||||
*/
|
||||
def onSignalEquals(signal: Signal, handler: Creator[Behavior[T]]): ReceiveBuilder[T] =
|
||||
withSignal(signal.getClass, Some(_.equals(signal)), _ ⇒ handler.create())
|
||||
withSignal(signal.getClass, OptionVal.Some(new JPredicate[Signal] {
|
||||
override def test(param: Signal): Boolean = param == signal
|
||||
}), new JFunction[Signal, Behavior[T]] {
|
||||
override def apply(param: Signal): Behavior[T] = handler.create()
|
||||
})
|
||||
|
||||
private def withMessage(`type`: Class[_ <: T], test: Option[T ⇒ Boolean], handler: T ⇒ Behavior[T]): ReceiveBuilder[T] =
|
||||
new ReceiveBuilder[T](Case[T, T](`type`, test, handler) +: messageHandlers, signalHandlers)
|
||||
private def withMessage[M <: T](`type`: OptionVal[Class[M]], test: OptionVal[JPredicate[M]], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] = {
|
||||
messageHandlers = Case[T, M](`type`, test, handler).asInstanceOf[Case[T, T]] +: messageHandlers
|
||||
this
|
||||
}
|
||||
|
||||
private def withSignal[M <: Signal](`type`: Class[M], test: Option[Signal ⇒ Boolean], handler: Signal ⇒ Behavior[T]): ReceiveBuilder[T] =
|
||||
new ReceiveBuilder[T](messageHandlers, Case[T, Signal](`type`, test, handler) +: signalHandlers)
|
||||
private def withSignal[M <: Signal](`type`: Class[M], test: OptionVal[JPredicate[M]], handler: JFunction[M, Behavior[T]]): ReceiveBuilder[T] = {
|
||||
signalHandlers = Case[T, M](OptionVal.Some(`type`), test, handler).asInstanceOf[Case[T, Signal]] +: signalHandlers
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
object ReceiveBuilder {
|
||||
/** Create a new mutable receive builder */
|
||||
def create[T]: ReceiveBuilder[T] = new ReceiveBuilder[T](Nil, Nil)
|
||||
|
||||
/** INTERNAL API */
|
||||
@InternalApi
|
||||
private[javadsl] final case class Case[BT, MT](`type`: Class[_ <: MT], test: Option[MT ⇒ Boolean], handler: MT ⇒ Behavior[BT])
|
||||
private[javadsl] final case class Case[BT, MT](`type`: OptionVal[Class[_ <: MT]], test: OptionVal[JPredicate[MT]], handler: JFunction[MT, Behavior[BT]])
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive type for [[AbstractBehavior]]
|
||||
*
|
||||
* INTERNAL API
|
||||
*/
|
||||
@InternalApi
|
||||
private final class BuiltReceive[T](
|
||||
private val messageHandlers: List[ReceiveBuilder.Case[T, T]],
|
||||
private val signalHandlers: List[ReceiveBuilder.Case[T, Signal]]
|
||||
messageHandlers: List[ReceiveBuilder.Case[T, T]],
|
||||
signalHandlers: List[ReceiveBuilder.Case[T, Signal]]
|
||||
) extends Receive[T] {
|
||||
import ReceiveBuilder.Case
|
||||
|
||||
|
|
@ -159,7 +168,7 @@ private final class BuiltReceive[T](
|
|||
private def receive[M](msg: M, handlers: List[Case[T, M]]): Behavior[T] =
|
||||
handlers match {
|
||||
case Case(cls, predicate, handler) :: tail ⇒
|
||||
if (cls.isAssignableFrom(msg.getClass) && (predicate.isEmpty || predicate.get.apply(msg))) handler(msg)
|
||||
if ((cls.isEmpty || cls.get.isAssignableFrom(msg.getClass)) && (predicate.isEmpty || predicate.get.test(msg))) handler(msg)
|
||||
else receive[M](msg, tail)
|
||||
case _ ⇒
|
||||
Behaviors.unhandled
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package akka.japi.function
|
|||
/**
|
||||
* A Function interface. Used to create first-class-functions is Java.
|
||||
* `Serializable` is needed to be able to grab line number for Java 8 lambdas.
|
||||
* Supports throwing `Exception` in the apply, which the `java.util.function.Function` counterpart does not.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
trait Function[-T, +R] extends java.io.Serializable {
|
||||
|
|
@ -17,6 +18,7 @@ trait Function[-T, +R] extends java.io.Serializable {
|
|||
/**
|
||||
* A Function interface. Used to create 2-arg first-class-functions is Java.
|
||||
* `Serializable` is needed to be able to grab line number for Java 8 lambdas.
|
||||
* Supports throwing `Exception` in the apply, which the `java.util.function.BiFunction` counterpart does not.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
trait Function2[-T1, -T2, +R] extends java.io.Serializable {
|
||||
|
|
@ -27,6 +29,7 @@ trait Function2[-T1, -T2, +R] extends java.io.Serializable {
|
|||
/**
|
||||
* A Procedure is like a Function, but it doesn't produce a return value.
|
||||
* `Serializable` is needed to be able to grab line number for Java 8 lambdas.
|
||||
* Supports throwing `Exception` in the apply, which the `java.util.function.Consumer` counterpart does not.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
trait Procedure[-T] extends java.io.Serializable {
|
||||
|
|
@ -37,9 +40,11 @@ trait Procedure[-T] extends java.io.Serializable {
|
|||
/**
|
||||
* An executable piece of code that takes no parameters and doesn't return any value.
|
||||
* `Serializable` is needed to be able to grab line number for Java 8 lambdas.
|
||||
* Supports throwing `Exception` in the apply, which the `java.util.function.Effect` counterpart does not.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
trait Effect extends java.io.Serializable {
|
||||
|
||||
@throws(classOf[Exception])
|
||||
def apply(): Unit
|
||||
}
|
||||
|
|
@ -47,6 +52,7 @@ trait Effect extends java.io.Serializable {
|
|||
/**
|
||||
* Java API: Defines a criteria and determines whether the parameter meets this criteria.
|
||||
* `Serializable` is needed to be able to grab line number for Java 8 lambdas.
|
||||
* Supports throwing `Exception` in the apply, which the `java.util.function.Predicate` counterpart does not.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
trait Predicate[-T] extends java.io.Serializable {
|
||||
|
|
@ -55,6 +61,7 @@ trait Predicate[-T] extends java.io.Serializable {
|
|||
|
||||
/**
|
||||
* A constructor/factory, takes no parameters but creates a new value of type T every call.
|
||||
* Supports throwing `Exception` in the apply, which the `java.util.function.Creator` counterpart does not.
|
||||
*/
|
||||
@SerialVersionUID(1L)
|
||||
trait Creator[+T] extends Serializable {
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ public class ReplicatorTest extends JUnitSuite {
|
|||
|
||||
@Override
|
||||
public Receive<ClientCommand> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(Increment.class, this::onIncrement)
|
||||
.onMessage(InternalUpdateResponse.class, msg -> Behaviors.same())
|
||||
.onMessage(GetValue.class, this::onGetValue)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public class ReceptionistExampleTest extends JUnitSuite {
|
|||
|
||||
@Override
|
||||
public Receive<Object> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(
|
||||
Receptionist.Listing.class,
|
||||
listing -> listing.isForKey(serviceKey),
|
||||
|
|
@ -131,7 +131,7 @@ public class ReceptionistExampleTest extends JUnitSuite {
|
|||
|
||||
@Override
|
||||
public Receive<Object> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(
|
||||
Receptionist.Listing.class,
|
||||
listing -> listing.isForKey(serviceKey),
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ The console output may look like this:
|
|||
|
||||
The next example is more realistic and demonstrates some important patterns:
|
||||
|
||||
* Using a sealed trait and case class/objects to represent multiple messages an actor can receive
|
||||
* Using @scala[a sealed trait and case class/objects]@java[an interface and classes implementing that interface] to represent multiple messages an actor can receive
|
||||
* Handle sessions by using child actors
|
||||
* Handling state by changing behavior
|
||||
* Using multiple typed actors to represent different parts of a protocol in a type safe way
|
||||
|
|
@ -357,6 +357,10 @@ As the state is mutable, we never return a different behavior from the message l
|
|||
the `AbstractBehavior` instance itself (`this`) as a behavior to use for processing the next message coming in.
|
||||
We could also return `Behavior.same` to achieve the same.
|
||||
|
||||
@java[In this sample we make separate statements for creating the behavior builder, but it also returns the builder
|
||||
itself from each step so a more fluent behavior definition style is also possible. What you should prefer depends on
|
||||
how big the set of messages the actor accepts is.]
|
||||
|
||||
It is also possible to return a new different `AbstractBehavior`, for example to represent a different state in a
|
||||
finite state machine (FSM), or use one of the functional behavior factories to combine the object oriented
|
||||
with the functional style for different parts of the lifecycle of the same Actor behavior.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class PrintMyActorRefActor extends AbstractBehavior<String> {
|
|||
|
||||
@Override
|
||||
public Receive<String> createReceive() {
|
||||
return receiveBuilder().onMessageEquals("printit", this::printIt).build();
|
||||
return newReceiveBuilder().onMessageEquals("printit", this::printIt).build();
|
||||
}
|
||||
|
||||
private Behavior<String> printIt() {
|
||||
|
|
@ -60,7 +60,7 @@ class StartStopActor1 extends AbstractBehavior<String> {
|
|||
|
||||
@Override
|
||||
public Receive<String> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessageEquals("stop", Behaviors::stopped)
|
||||
.onSignal(PostStop.class, signal -> postStop())
|
||||
.build();
|
||||
|
|
@ -84,7 +84,7 @@ class StartStopActor2 extends AbstractBehavior<String> {
|
|||
|
||||
@Override
|
||||
public Receive<String> createReceive() {
|
||||
return receiveBuilder().onSignal(PostStop.class, signal -> postStop()).build();
|
||||
return newReceiveBuilder().onSignal(PostStop.class, signal -> postStop()).build();
|
||||
}
|
||||
|
||||
private Behavior<String> postStop() {
|
||||
|
|
@ -113,7 +113,7 @@ class SupervisingActor extends AbstractBehavior<String> {
|
|||
|
||||
@Override
|
||||
public Receive<String> createReceive() {
|
||||
return receiveBuilder().onMessageEquals("failChild", this::failChild).build();
|
||||
return newReceiveBuilder().onMessageEquals("failChild", this::failChild).build();
|
||||
}
|
||||
|
||||
private Behavior<String> failChild() {
|
||||
|
|
@ -134,7 +134,7 @@ class SupervisedActor extends AbstractBehavior<String> {
|
|||
|
||||
@Override
|
||||
public Receive<String> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessageEquals("fail", this::fail)
|
||||
.onSignal(PreRestart.class, signal -> preRestart())
|
||||
.onSignal(PostStop.class, signal -> postStop())
|
||||
|
|
@ -174,7 +174,7 @@ class Main extends AbstractBehavior<String> {
|
|||
|
||||
@Override
|
||||
public Receive<String> createReceive() {
|
||||
return receiveBuilder().onMessageEquals("start", this::start).build();
|
||||
return newReceiveBuilder().onMessageEquals("start", this::start).build();
|
||||
}
|
||||
|
||||
private Behavior<String> start() {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class IotSupervisor extends AbstractBehavior<Void> {
|
|||
// No need to handle any messages
|
||||
@Override
|
||||
public Receive<Void> createReceive() {
|
||||
return receiveBuilder().onSignal(PostStop.class, signal -> postStop()).build();
|
||||
return newReceiveBuilder().onSignal(PostStop.class, signal -> postStop()).build();
|
||||
}
|
||||
|
||||
private IotSupervisor postStop() {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class Device extends AbstractBehavior<DeviceMessage> {
|
|||
|
||||
@Override
|
||||
public Receive<DeviceMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(RecordTemperature.class, this::recordTemperature)
|
||||
.onMessage(ReadTemperature.class, this::readTemperature)
|
||||
.onSignal(PostStop.class, signal -> postStop())
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class Device extends AbstractBehavior<DeviceMessage> {
|
|||
|
||||
@Override
|
||||
public Receive<DeviceMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(ReadTemperature.class, this::readTemperature)
|
||||
.onSignal(PostStop.class, signal -> postStop())
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class Device extends AbstractBehavior<DeviceMessage> {
|
|||
|
||||
@Override
|
||||
public Receive<DeviceMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(RecordTemperature.class, this::recordTemperature)
|
||||
.onMessage(ReadTemperature.class, this::readTemperature)
|
||||
.onMessage(Passivate.class, m -> Behaviors.stopped())
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public class DeviceGroup extends AbstractBehavior<DeviceGroupMessage> {
|
|||
|
||||
@Override
|
||||
public Receive<DeviceGroupMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(RequestTrackDevice.class, this::onTrackDevice)
|
||||
// #device-group-register
|
||||
// #device-group-remove
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class DeviceManager extends AbstractBehavior<DeviceManagerMessage> {
|
|||
}
|
||||
|
||||
public Receive<DeviceManagerMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(RequestTrackDevice.class, this::onTrackDevice)
|
||||
.onMessage(RequestDeviceList.class, this::onRequestDeviceList)
|
||||
.onMessage(DeviceGroupTerminated.class, this::onTerminated)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class Device extends AbstractBehavior<DeviceMessage> {
|
|||
|
||||
@Override
|
||||
public Receive<DeviceMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(RecordTemperature.class, this::recordTemperature)
|
||||
.onMessage(ReadTemperature.class, this::readTemperature)
|
||||
.onMessage(Passivate.class, m -> Behaviors.stopped())
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class DeviceGroup extends AbstractBehavior<DeviceGroupMessage> {
|
|||
|
||||
@Override
|
||||
public Receive<DeviceGroupMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
// #query-added
|
||||
.onMessage(RequestTrackDevice.class, this::onTrackDevice)
|
||||
.onMessage(RequestDeviceList.class, r -> r.groupId.equals(groupId), this::onDeviceList)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public class DeviceGroupQuery extends AbstractBehavior<DeviceGroupQueryMessage>
|
|||
// #query-state
|
||||
@Override
|
||||
public Receive<DeviceGroupQueryMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(WrappedRespondTemperature.class, this::onRespondTemperature)
|
||||
.onMessage(DeviceTerminated.class, this::onDeviceTerminated)
|
||||
.onMessage(CollectionTimeout.class, this::onCollectionTimeout)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class DeviceManager extends AbstractBehavior<DeviceManagerMessage> {
|
|||
}
|
||||
|
||||
public Receive<DeviceManagerMessage> createReceive() {
|
||||
return receiveBuilder()
|
||||
return newReceiveBuilder()
|
||||
.onMessage(RequestTrackDevice.class, this::onTrackDevice)
|
||||
.onMessage(RequestDeviceList.class, this::onRequestDeviceList)
|
||||
.onMessage(RequestAllTemperatures.class, this::onRequestAllTemperatures)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue