Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the AbstractBehavior builder mutable #26309

Merged
Diff settings

Always

Just for now

@@ -36,7 +36,7 @@ public void testMutableCounter() {

@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 +56,7 @@ public MyAbstractBehavior(int initialValue) {
@Override
public Receive<BehaviorBuilderTest.CounterMessage> createReceive() {
assertEquals(42, value);
return receiveBuilder().build();
return newReceiveBuilder().build();
}
}

@@ -207,7 +207,7 @@ else if (rsp instanceof Backend.JobCompleted)

@Override
public Receive<Command> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(
Translate.class,
cmd -> {
@@ -603,7 +603,7 @@ public PrepareToLeaveHome(

@Override
public Receive<Object> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(
Wallet.class,
(wallet) -> {
@@ -5,7 +5,6 @@
package jdocs.akka.typed;

// #imports
import akka.NotUsed;
import akka.actor.typed.ActorRef;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Behavior;
@@ -112,7 +111,7 @@ public ChatRoomBehavior(ActorContext<RoomCommand> context) {

@Override
public Receive<RoomCommand> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(
GetSession.class,
getSession -> {
@@ -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
}
@@ -16,7 +16,7 @@ import akka.actor.typed.Behavior.unhandled
import BehaviorBuilder._

/**
This conversation was marked as resolved by johanandren

This comment has been minimized.

Copy link
@patriknw

patriknw Jan 31, 2019

Member

we use

import akka.japi.function.{ Function, Function2, Predicate }

I think that is confusing. Function and Function2 exists in scala. I'd rather rename those imports to JFunction for clarity.

Related question is if we have a good reason for using japi here but java.util.function in persistence?

This comment has been minimized.

Copy link
@johanandren

johanandren Jan 31, 2019

Author Member

I never quite understood why we use japi ones, something about scala version expanding lambas compatibility something?

I agree if there is no reason we should use the built in Java types

This comment has been minimized.

Copy link
@johanandren

johanandren Jan 31, 2019

Author Member

Ah, I may know now:

unreported exception java.io.UnsupportedEncodingException; must be caught or declared to be thrown

The java ones does not allow for throwing while our japi ones does.

This comment has been minimized.

Copy link
@patriknw

patriknw Jan 31, 2019

Member

isn't it something with variance too? I think we introduced the new function types because of that in Streams.

since the typical usage is to delegate to a method we could try if it makes any difference for combinations of things like this:

.onMessage(Orange.class, this::receiveFruit)
.onMessage(Banana.class, this::receiveFruit)
.onMessage(Apple.class, this::receiveFruit)
.onMessage(Fruit.class, this::receiveFruit)

private void receiveFruit(Orange o);
private void receiveFruit(Banana o);
private void receiveFruit(Fruit f);

This comment has been minimized.

Copy link
@johanandren

johanandren Jan 31, 2019

Author Member

Yes, you are right, variance as well. The java ones are invariant since they are defined in Java.

* 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.
@@ -10,16 +10,16 @@ import akka.actor.typed.{ Behavior, Signal }
import akka.annotation.InternalApi

/**
* 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]]
private var messageHandlers: List[ReceiveBuilder.Case[T, T]],
private var signalHandlers: List[ReceiveBuilder.Case[T, Signal]]
) {

This comment has been minimized.

Copy link
@patriknw

patriknw Feb 1, 2019

Member

do you thing we should add orElse? command and event builders have that
(separate PR)

This comment has been minimized.

Copy link
@johanandren

johanandren Feb 1, 2019

Author Member

I think it is less useful here than in typed persistence. Could be nice for consistency though.

This comment has been minimized.

Copy link
@renatocaval

renatocaval Feb 13, 2019

Contributor

Coming late to the party, but...

I don't think it's less useful. You can define different ReceiveBuilder and compose then differently depending on each state/behavior your are. That was a common pattern in untyped, no? Combining different Receive PF.

Or am I missing something here?


import ReceiveBuilder.Case
@@ -32,7 +32,7 @@ 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] =

This comment has been minimized.

Copy link
@patriknw

patriknw Jan 31, 2019

Member

should we align the naming?
In CommandHandlerBuilder it's matchCommand. Should this be matchMessage, or should it be onCommand in CommandHandlerBuilder?

This comment has been minimized.

Copy link
@johanandren

johanandren Jan 31, 2019

Author Member

I think we should align. The only argument I can find either way for selecting which name is that onMessage is shorter than matchMessage.

This comment has been minimized.

Copy link
@patriknw

patriknw Jan 31, 2019

Member

perhaps also because pattern matching is not a thing

This comment has been minimized.

Copy link
@renatocaval

renatocaval Jan 31, 2019

Contributor

I prefer on then match. Just because it expresses better the fact that we are reacting to the arrival of something.

The match variant aligns better to what happens behind, ie: we lookup for a handler that matches the criteria. We could even argue that match is leaking the implementation behind while on is descriptive to what should happen.

Making it short: +1 for onMessage, onCommand and onEvent.

This comment has been minimized.

Copy link
@patriknw

patriknw Jan 31, 2019

Member

decided then!

This comment has been minimized.

Copy link
@johanandren

johanandren Jan 31, 2019

Author Member

Created an issue to do that separately #26313

withMessage(`type`, None, msg handler.apply(msg.asInstanceOf[M]))
@@ -44,7 +44,7 @@ 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(
@@ -61,7 +61,7 @@ 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]))
@@ -71,7 +71,7 @@ class ReceiveBuilder[T] private (
*
* @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())
This conversation was marked as resolved by johanandren

This comment has been minimized.

Copy link
@patriknw

patriknw Jan 31, 2019

Member

CommandHandlerBuilder has matchAny, should we add that to the message builders too?

This comment has been minimized.

Copy link
@johanandren

johanandren Jan 31, 2019

Author Member

Sounds good

@@ -82,7 +82,7 @@ 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]))
@@ -94,7 +94,7 @@ 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(
@@ -111,7 +111,7 @@ class ReceiveBuilder[T] private (
*
* @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
* @return this behavior builder
*/
def onSignalUnchecked[M <: Signal](`type`: Class[_ <: Signal], handler: Function[M, Behavior[T]]): ReceiveBuilder[T] =
withSignal(`type`, None, signal handler.apply(signal.asInstanceOf[M]))
@@ -121,16 +121,20 @@ class ReceiveBuilder[T] private (
*
* @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())

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(`type`: Class[_ <: T], test: Option[T Boolean], handler: T Behavior[T]): ReceiveBuilder[T] = {
messageHandlers = Case[T, T](`type`, test, handler) +: 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: Option[Signal Boolean], handler: Signal Behavior[T]): ReceiveBuilder[T] = {
signalHandlers = Case[T, Signal](`type`, test, handler) +: signalHandlers
this
}
}

object ReceiveBuilder {
@@ -134,7 +134,7 @@

@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 RouterBehavior(

@Override
public Receive<Object> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(
Receptionist.Listing.class,
listing -> listing.isForKey(serviceKey),
@@ -131,7 +131,7 @@ private void updateReachable() {

@Override
public Receive<Object> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(
Receptionist.Listing.class,
listing -> listing.isForKey(serviceKey),
@@ -36,7 +36,7 @@ private PrintMyActorRefActor(ActorContext<String> context) {

@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 @@ private StartStopActor1() {

@Override
public Receive<String> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessageEquals("stop", Behaviors::stopped)
.onSignal(PostStop.class, signal -> postStop())
.build();
@@ -84,7 +84,7 @@ private StartStopActor2() {

@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 @@ private SupervisingActor(ActorContext<String> context) {

@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 @@ private SupervisedActor() {

@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 @@ private Main(ActorContext<String> context) {

@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 IotSupervisor(ActorContext<Void> context) {
// 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 Device(ActorContext<DeviceMessage> context, String groupId, String device

@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 Device(ActorContext<DeviceMessage> context, String groupId, String device

@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 Device(ActorContext<DeviceMessage> context, String groupId, String device

@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 @@ private DeviceGroup onTerminated(DeviceTerminated t) {

@Override
public Receive<DeviceGroupMessage> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(RequestTrackDevice.class, this::onTrackDevice)
// #device-group-register
// #device-group-remove
@@ -74,7 +74,7 @@ private DeviceManager onTerminated(DeviceGroupTerminated t) {
}

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 Device(ActorContext<DeviceMessage> context, String groupId, String device

@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 @@ private DeviceGroup onAllTemperatures(RequestAllTemperatures r) {

@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 DeviceGroupQuery(
// #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 @@ private DeviceManager onTerminated(DeviceGroupTerminated t) {
}

public Receive<DeviceManagerMessage> createReceive() {
return receiveBuilder()
return newReceiveBuilder()
.onMessage(RequestTrackDevice.class, this::onTrackDevice)
.onMessage(RequestDeviceList.class, this::onRequestDeviceList)
.onMessage(RequestAllTemperatures.class, this::onRequestAllTemperatures)
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.