Migration Guide 2.4.x to 2.5.x
Actor (Java)
AbstractActor
AbstractActor
has been promoted from its experimental/may change state and while doing this we did some small, but important, improvements to the API that will require some mechanical changes of your source code.
Previously the receive behavior was set with the receive
method, but now an actor has to define its initial receive behavior by implementing the createReceive
method in the AbstractActor
. This has the advantages:
- It gives a clear entry point of what to implement. The compiler tells you that the abstract method must be implemented.
- It’s impossible to forget to set the receive behavior.
- It’s not possible to define the receive behavior more than once.
The return type of createReceive
is AbstractActor.Receive
. It defines which messages your Actor can handle, along with the implementation of how the messages should be processed. You can build such behavior with a builder named ReceiveBuilder
.
AbstractActor.Receive
can also be used in getContext().become
.
The old receive
method exposed Scala’s PartialFunction
and BoxedUnit
in the signature, which are unnecessary concepts for newcomers to learn. The new createReceive
requires no additional imports.
Note that The Receive
can still be implemented in other ways than using the ReceiveBuilder
since it in the end is a wrapper around a Scala PartialFunction
. For example, one could implement an adapter to Javaslang Pattern Matching DSL.
The mechanical source code change for migration to the new AbstractActor
is to implement the createReceive
instead of calling receive
(compiler will tell that this is missing).
Old:
import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
public class SomeActor extends AbstractActor {
public SomeActor() {
receive(ReceiveBuilder
.match(String.class, s -> System.out.println(s.toLowerCase())).
.build());
}
}
New:
import akka.actor.AbstractActor;
public class SomeActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> System.out.println(s.toLowerCase()))
.build();
}
}
See Receive messages documentation for more advice about how to implement createReceive
.
A few new methods have been added with deprecation of the old. Worth noting is preRestart
.
Old:
@Override
public void preRestart(Throwable reason, scala.Option<Object> message) {
super.preRestart(reason, message);
}
New:
@Override
public void preRestart(Throwable reason, java.util.Optional<Object> message) {
super.preRestart(reason, message);
}
AbstractPersistentActor
Similar change as described above for AbstractActor
is needed for AbstractPersistentActor
. Implement createReceiveRecover
instead of receiveRecover
, and createReceive
instead of receiveCommand
.
Old:
@Override
public PartialFunction<Object, BoxedUnit> receiveCommand() {
return ReceiveBuilder.
match(String.class, cmd -> {/* ... */}).build();
}
@Override
public PartialFunction<Object, BoxedUnit> receiveRecover() {
return ReceiveBuilder.
match(String.class, evt -> {/* ... */}).build();
}
New:
@Override
public Receive createReceive() {
return receiveBuilder().
match(String.class, cmd -> {/* ... */}).build();
}
@Override
public Receive createReceiveRecover() {
return receiveBuilder().
match(String.class, evt -> {/* ... */}).build();
}
UntypedActor
UntypedActor
has been deprecated in favor of AbstractActor
. As a migration path you can extend UntypedAbstractActor
instead of UntypedActor
.
Old:
import akka.actor.UntypedActor;
public class SomeActor extends UntypedActor {
public static class Msg1 {}
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof Msg1) {
Msg1 msg1 = (Msg1) msg;
// actual work
} else {
unhandled(msg);
}
}
}
New:
import akka.actor.UntypedAbstractActor;
public class SomeActor extends UntypedAbstractActor {
public static class Msg1 {}
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof Msg1) {
Msg1 msg1 = (Msg1) msg;
// actual work
} else {
unhandled(msg);
}
}
}
It’s recommended to migrate UntypedActor
to AbstractActor
by implementing createReceive
instead of onReceive
.
Old:
import akka.actor.UntypedActor;
public class SomeActor extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof String) {
String s = (String) msg;
System.out.println(s.toLowerCase());
} else {
unhandled(msg);
}
}
}
New:
import akka.actor.AbstractActor;
public class SomeActor extends AbstractActor {
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> {
System.out.println(s.toLowerCase());
})
.build();
}
}
See Receive messages documentation for more advice about how to implement createReceive
.
Similar with UntypedActorWithStash
, UntypedPersistentActor
, and UntypedPersistentActorWithAtLeastOnceDelivery
.
Actor (Scala)
Actor DSL deprecation
Actor DSL is a rarely used feature and thus will be deprecated and removed. Use plain system.actorOf
instead of the DSL to create Actors if you have been using it.
ExtensionKey Deprecation
ExtensionKey
is a shortcut for writing Akka Extensions but extensions created with it cannot be used from Java and it does in fact not save many lines of code over directly implementing ExtensionId
.
Old:
object MyExtension extends ExtensionKey[MyExtension]
New:
object MyExtension extends ExtensionId[MyExtension] with ExtensionIdProvider {
override def lookup = MyExtension
override def createExtension(system: ExtendedActorSystem): MyExtension =
new MyExtension(system)
// needed to get the type right when used from Java
override def get(system: ActorSystem): MyExtension = super.get(system)
}
Actor Mailbox
Scala 2.12 is using the standard JDK8 ForkJoinPool, which may cause performance regression for some Actor messaging scenarios. Therefore we have embedded the ForkJoinPool from Scala 2.11 in Akka. This breaks binary backwards compatibility for custom Mailbox
implementations that were compiled with Akka 2.4.
This is only a problem if you are using a library that provides a custom Mailbox
implementation. The change is source compatible and such library should be recompiled and released for Akka 2.5.
Streams
Removal of StatefulStage, PushPullStage
StatefulStage
and PushPullStage
were first introduced in Akka Streams 1.0, and later deprecated and replaced by GraphStage
in 2.0-M2. The GraphStage
API has all features (and even more) as the previous APIs and is even nicer to use.
Please refer to the GraphStage documentation, for details on building custom GraphStages.
StatefulStage
would be migrated to a simple GraphStage
that contains some mutable state in its GraphStageLogic
, and PushPullStage
directly translate to graph stages.
Removal of Source.transform
, replaced by via
Along with the removal of Stage
(as described above), the transform
methods creating Flows/Sources/Sinks from Stage
have been removed. They are replaced by using GraphStage
instances with via
, e.g.:
exampleFlow.transform(() => new MyStage())
would now be:
myFlow.via(new MyGraphStage)
as the GraphStage
itself is a factory of logic instances.
SubFlow.zip and SubSource.zip now emit akka.japi.Pair instead of Scala’s Pair
The the Java API’s zip
operator on SubFlow
and SubSource
has been emitting Scala’s Pair
(Tuple2
) instead of akka.japi.Pair
. This is fixed in Akka 2.5 where it emits the proper Java DSl type.
Please note that the zip
operator on Source
and Flow
has had the correct type, this change only affects the Sub...
versions of those classes.
Deprecation of ActorSubscriber and ActorPublisher
The classes ActorPublisher
and ActorSubscriber
were the first user-facing Reactive Streams integration API that we provided for end-users. Akka Streams APIs have evolved and improved a lot since then, and now there is no need to use these low-level abstractions anymore. It is easy to get things wrong when implementing them, and one would have to validate each implementation of such Actor using the Reactive Streams Technology Compatibility Kit.
The replacement API is the powerful GraphStage
. It has all features that raw Actors provided for implementing Stream stages and adds additional protocol and type-safety. You can learn all about it in the documentation: Custom stream processing.
You should also read the blog post series on the official team blog, starting with Mastering GraphStages, part I, which explains using and implementing GraphStages in more practical terms than the reference documentation.
Order of Attributes List
Imporant performance improvement could be achieved by reverting the order of the attributesList
in Attributes
.
The attributeList
is ordered with the most specific attribute first, least specific last. Note that the order was the opposite in Akka 2.4.x (but it was not really documented).
The semantics of the convenience methods, such as get
and getFirst
are the same, but if you use the attributesList
directly or via filtered
or getAttributeList
you need to take the new order into consideration.
Dispatcher attribute
The ActorAttributes.dispatcher
attribute is adding an async boundary in 2.5, since that is the typical desired behavior. In 2.4 an explicit async marker (AsyncBoundary
attribute) had to be added. For example, this means that Source
that defined blocking-io-dispatcher
as default followed by a map
will now be separated by an async boundary, which was not the case in 2.4.
Removal of the auto-fuse setting
In 2.4 fusing stages together into the same actor could be completely disabled with the setting akka.stream.materializer.auto-fusing
. The new materializer introduced in Akka 2.5 does not support disabling fusing, so this setting does not have any effect any more and has been deprecated. Running each stage in a stream on a separate actor can be done by adding explicit async boundaries around every stage. How to add asynchronous boundaries can be seen in Operator Fusion.
Remote
Mutual TLS authentication now required by default for netty-based SSL transport
Mutual TLS authentication is now required by default for the netty-based SSL transport.
Nodes that are configured with this setting to on
might not be able to receive messages from nodes that run on older versions of akka-remote. This is because in versions of Akka < 2.4.12 the active side of the remoting connection will not send over certificates even if asked to.
It is still possible to make a rolling upgrade from a version < 2.4.12 by doing the upgrade stepwise: : * first, upgrade Akka to the latest version but keep akka.remote.netty.ssl.require-mutual-authentication
at off
and do a first rolling upgrade * second, turn the setting to on
and do another rolling upgrade
For more information see the documentation for the akka.remote.netty.ssl.require-mutual-authentication
configuration setting in akka-remote’s reference.conf.
additional-serialization-bindings
From Akka 2.5.0 the additional-serialization-bindings
are enabled by default. That defines serializers that are replacing some Java serialization that were used in 2.4. This setting was disabled by default in Akka 2.4.16 but can also be enabled in an Akka 2.4 system.
To still be able to support rolling upgrade from a system with this setting disabled, e.g. default for 2.4.16, it is possible to disable the additional serializers and continue using Java serialization for those messages.
akka.actor {
# Set this to off to disable serialization-bindings defined in
# additional-serialization-bindings. That should only be needed
# for backwards compatibility reasons.
enable-additional-serialization-bindings = off
}
Please note that this setting must be the same on all nodes participating in a cluster, otherwise the mis-aligned serialization configurations will cause deserialization errors on the receiving nodes.
After performing a rolling upgrade from 2.4 to 2.5 with enable-additional-serialization-bindings = off
you can perform another rolling upgrade and change this setting to on
. See also Rolling upgrades.
With serialize-messages the deserialized message is actually sent
The flag akka.actor.serialize-messages = on
triggers serialization and deserialization of each message sent in the ActorSystem
. With this setting enabled the message actually passed on to the actor previously was the original message instance, this has now changed to be the deserialized message instance.
This may cause tests that rely on messages being the same instance (for example by having mutable messages with attributes that are asserted in the tests) to not work any more with this setting enabled. For such cases the recommendation is to either not rely on messages being the same instance or turn the setting off.
Wire Protocol Compatibility
It is possible to use Akka Remoting between nodes running Akka 2.4.16 and 2.5.x, but some settings have changed so you might need to adjust some configuration as described in Rolling Update.
Note however that if using Java serialization it will not be possible to mix nodes using Scala 2.11 and 2.12.
Cluster
Rolling Update
It is possible to do a rolling update from Akka 2.4.16 to 2.5.x, i.e. running a cluster of 2.4.16 nodes and join nodes running 2.5.x followed by shutting down the old nodes.
You must first update all nodes to 2.4.16. It’s not supported to update directly from an older version than 2.4.16 to 2.5.x. For example, if you are running 2.4.11 you must first do a rolling update to 2.4.16, shut down all 2.4.11 nodes, and then do the rolling update to 2.5.x.
For some configuration settings it’s important to use the same values on all nodes in the cluster. Some settings have changed default value in 2.5.0 and therefore you need to review your configuration before doing a rolling update to 2.5.x. Such settings are mentioned elsewhere in this migration guide and here is a summary of things to consider.
- akka.actor.additional-serialization-bindings
- akka.cluster.allow-weakly-up-members
- akka.cluster.sharding.state-store-mode
- akka.remote.netty.ssl.require-mutual-authentication
See also the rolling update guide for specifics about later patch releases.
Limit lookup of routees to nodes tagged with multiple roles
Starting with 2.5.4, cluster routing supports delivering messages to routees tagged with all specified roles using use-roles
(instead of use-role
in previous versions). When doing rolling upgrades and using this new feature, it is important to first upgrade the existing nodes to the latest version of Akka and then start using multiple roles in a separate rolling upgrade. Otherwise, if a new node sends a message with the restriction use-roles = ["a", "b"]
, that will only require the “a” role on old nodes.
Coordinated Shutdown
There is a new extension named CoordinatedShutdown
that will stop certain actors and services in a specific order and perform registered tasks during the shutdown process.
When using Akka Cluster, tasks for graceful leaving of cluster including graceful shutdown of Cluster Singletons and Cluster Sharding are now performed automatically.
Previously it was documented that things like terminating the ActorSystem
should be done when the cluster member was removed, but this was very difficult to get right. That is now taken care of automatically. This might result in changed behavior, hopefully to the better. It might also be in conflict with your previous shutdown code so please read the documentation for the Coordinated Shutdown and revisit your own implementations. Most likely your implementation will not be needed any more or it can be simplified.
More information can be found in the documentation.
For some tests it might be undesired to terminate the ActorSystem
via CoordinatedShutdown
. You can disable that by adding the following to the configuration of the ActorSystem
that is used in the test:
# Don't terminate ActorSystem via CoordinatedShutdown in tests
akka.coordinated-shutdown.terminate-actor-system = off
akka.coordinated-shutdown.run-by-jvm-shutdown-hook = off
akka.cluster.run-coordinated-shutdown-when-down = off
WeaklyUp
WeaklyUp is now enabled by default, but it can be disabled with configuration option:
akka.cluster.allow-weakly-up-members = off
You should not run a cluster with this feature enabled on some nodes and disabled on some. Therefore you might need to enable/disable it in configuration when performing rolling upgrade from 2.4.x to 2.5.0.
Cluster Sharding state-store-mode
Distributed Data mode, which was still experimental in 2.4.x, is now the default state-store-mode
for Cluster Sharding. The persistence mode is also supported. Read more in the documentation.
It’s important to use the same mode on all nodes in the cluster, i.e. if you perform a rolling upgrade from 2.4.16 you might need to change the state-store-mode
to be the same (persistence
is default in 2.4.x):
akka.cluster.sharding.state-store-mode = persistence
Note that the stored Remembering Entities data with persistence
mode cannot be migrated to the data
mode. Such entities must be started again in some other way when using ddata
mode.
Rolling upgrades from clusters that already used the (then-experimental) ddata
mode are not supported.
Cluster Sharding remember entities
To use remember entities with cluster sharding there are now an additional requirement added: the extractShardId
must be able to extract the shard id from the message Shard.StartEntity(EntityId)
. This is implies that it must be possible to calculate a shard id from an entity id when using remember entities.
This was added to be able to gracefully handle when persisted locations of entities does not match where the entities should live when a shard region starts up. Such states could be cause by changing the extractShardId
logic and restart a system using remember entities.
Cluster Management Command Line Tool
There is a new cluster management tool with HTTP API that has the same functionality as the command line tool. The HTTP API gives you access to cluster membership information as JSON including full reachability status between the nodes. It supports the ordinary cluster operations such as join, leave, and down.
See documentation of Akka Management.
The command line script for cluster management has been deprecated and is scheduled for removal in the next major version. Use the HTTP API with curl or similar instead.
Distributed Data
Distributed Data has been promoted to a stable module. This means that we will keep the API stable from this point. As a result the module name is changed from akka-distributed-data-experimental to akka-distributed-data and you need to change that in your build tool (sbt/mvn/…).
Map allow generic type for the keys
In 2.4 the key of any Distributed Data map always needed to be of type String. In 2.5 you can use any type for the key. This means that every map (ORMap, LWWMap, PNCounterMap, ORMultiMap) now takes an extra type parameter to specify the key type. To migrate existing code from 2.4 to 2.5 you simple add String as key type, for example: ORMultiMap[Foo] becomes ORMultiMap[String, Foo]. PNCounterMap didn’t take a type parameter in version 2.4, so PNCounterMap in 2.4 becomes PNCounterMap[String] in 2.5. Java developers should use <> instead of [], e.g: PNCounterMap
NOTE: Even though the interface is not compatible between 2.4 and 2.5, the binary protocol over the wire is (as long as you use String as key type). This means that 2.4 nodes can synchronize with 2.5 nodes.
Subscribers
When an entity is removed subscribers will not receive Replicator.DataDeleted
any more. They will receive Replicator.Deleted
instead.
Persistence
Binary incompatibility of PersistentActor and AtLeastOneDelivery
To be able to evolve the Java APIs AbstractPersistentActor
and AbstractPersistentActorWithAtLeastOnceDelivery
to work with Scala 2.12 we could find no other solution but to break the binary compatibility of the Scala versions (which the Java ones were based on).
This means that the Akka 2.5 artifact cannot be a class path drop in replacement of Akka 2.4 if you use PersistentActor
or AtLeastOnceDelivery
, to do this upgrade you must recompile your project with the new version of Akka.
Removal of PersistentView
After being deprecated for a long time, and replaced by Persistence Query PersistentView
has been removed now removed.
The corresponding query type is EventsByPersistenceId
. There are several alternatives for connecting the Source
to an actor corresponding to a previous PersistentView
. There are several alternatives for connecting the Source
to an actor corresponding to a previous PersistentView
actor which are documented in Integration.
The consuming actor may be a plain Actor
or an PersistentActor
if it needs to store its own state (e.g. fromSequenceNr
offset).
Please note that Persistence Query is not experimental/may-change anymore in Akka 2.5.0
, so you can safely upgrade to it.
Persistence Plugin Proxy
A new persistence plugin proxy was added, that allows sharing of an otherwise non-sharable journal or snapshot store. The proxy is available by setting akka.persistence.journal.plugin
or akka.persistence.snapshot-store.plugin
to akka.persistence.journal.proxy
or akka.persistence.snapshot-store.proxy
, respectively. The proxy supplants the Shared LevelDB journal.
Persistence Query
Persistence Query has been promoted to a stable module. As a result the module name is changed from akka-persistence-query-experimental to akka-persistence-query and you need to change that in your build tool (sbt/mvn/…). Only slight API changes were made since the module was introduced:
Query naming consistency improved
Queries always fall into one of the two categories: infinite or finite (“current”). The naming convention for these categories of queries was solidified and is now as follows:
- “infinite” - e.g.
eventsByTag
,persistenceIds
- which will keep emitting events as they are persisted and match the query. - “finite”, also known as “current” - e.g.
currentEventsByTag
,currentPersistenceIds
- which will complete the stream once the query completed, for the journal’s definition of “current”. For example in an SQL store it would mean it only queries the database once.
Only the AllPersistenceIdsQuery
class and method name changed due to this. The class is now called PersistenceIdsQuery
, and the method which used to be allPersistenceIds
is now persistenceIds
.
Queries now use Offset
instead of Long
for offsets
This change was made to better accommodate the various types of Journals and their understanding what an offset is. For example, in some journals an offset is always a time, while in others it is a numeric offset (like a sequence id).
Instead of the previous Long
offset you can now use the provided Offset
factories (and types):
akka.persistence.query.Offset.sequence(value: Long)
,akka.persistence.query.Offset.timeBasedUUID(value: UUID)
- and finally
NoOffset
if not offset should be used.
Journals are also free to provide their own specific Offset
types. Consult your journal plugin’s documentation for details.
Agents
Agents are now deprecated
Akka Agents are a very simple way of containing mutable state and allowing to access it safely from multiple threads. The abstraction is leaky though, as Agents do not work over the network (unlike Akka Actors).
As users were often confused by “when to use an Actor vs. when to use an Agent?” a decision was made to deprecate the Agents, as they rarely are really enough and do not fit the Akka spirit of thinking about distribution. We also anticipate to replace the uses of Agents by the upcoming Akka Typed, so in preparation thereof the Agents have been deprecated in 2.5.
If you use Agents and would like to take over the maintenance thereof, please contact the team on Gitter or GitHub.
Camel
akka-camel
has been deprecated in favour of Alpakka , the Akka Streams based collection of integrations to various endpoints (including Camel)
We acknowledge that Akka Camel is a very useful and important module. It will not be removed until Alpakka has reached the needed production quality to be a full replacement. The deprecation of Akka Camel should be seen as a signal that new development is to be invested in Alpakka instead of Akka Camel.
Contrib
akka-contrib
has been deprecated and is scheduled for removal in the next major version. The reason is to reduce the amount of things to maintain in the core Akka projects. Contributions to the core of Akka or its satellite projects are welcome. Contributions that don’t fit into existing modules can be hosted in new Akka GitHub repositories in the akka
GitHub organization or outside of it depending on what kind of library it is. Please ask.
Aggregator
Aggregator
has been deprecated. Feel free to copy the source into your project or create a separate library outside of Akka.
CircuitBreakerProxy
CircuitBreakerProxy
has been deprecated in favor of akka.pattern.CircuitBreaker
with explicit ask
requests.
JavaLogger
akka.contrib.jul.JavaLogger
has been deprecated and included in akka-actor
instead as akka.event.jul.JavaLogger
. See documentation.
The JavaLoggingAdapter
has also been deprecated, but not included in akka-actor
. Feel free to copy the source into your project or create a separate library outside of Akka.
PeekMailbox
PeekMailbox
has been deprecated. Use an explicit supervisor or proxy actor instead.
ReceivePipeline
ReceivePipeline
has been deprecated. Feel free to copy the source into your project or create a separate library outside of Akka.
ReliableProxy
ReliableProxy
has been deprecated. Use At-Least-Once Delivery instead. ReliableProxy
was only intended as an example and doesn’t have full production quality. If there is demand for a lightweight (non-durable) at-least once delivery mechanism we are open for a design discussion.
TimerBasedThrottler
TimerBasedThrottler
has been deprecated. Use the throttle
stage in Akka Streams instead.
Example in Scala:
import scala.concurrent.duration._
import akka.NotUsed
import akka.actor.ActorRef
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.OverflowStrategy
import akka.stream.ThrottleMode
import akka.stream.scaladsl.Sink
import akka.stream.scaladsl.Source
val system: ActorSystem = ??? // TODO real ActorSystem here
val target: ActorRef = ??? // TODO real target ActorRef here
implicit val materializer = ActorMaterializer.create(system)
val throttler: ActorRef =
Source.actorRef(bufferSize = 1000, OverflowStrategy.dropNew)
.throttle(100, 1.second)
.to(Sink.actorRef(target, NotUsed))
.run()
Example in Java:
import java.util.concurrent.TimeUnit;
import java.time.Duration;
import akka.NotUsed;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.stream.OverflowStrategy;
import akka.stream.ThrottleMode;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
final ActorSystem system = null; // TODO real ActorSystem here
final ActorRef target = null; // TODO real target ActorRef here
final Materializer materializer = ActorMaterializer.create(system);
final ActorRef throttler =
Source.actorRef(1000, OverflowStrategy.dropNew())
.throttle(100, Duration.ofSeconds(1))
.to(Sink.actorRef(target, NotUsed.getInstance()))
.run(materializer);
Note, that when using Sink.actorRef
the sender of the original message sent to the thottler
ActorRef will be lost and messages will arrive at the target
with the sender set to ActorRef.noSender
. Using this construct it is currently impossible to keep the original sender. Alternatively, you can manually specify a static sender by replacing Sink.actorRef
with Sink.foreach(msg -> target.tell(msg, whateverSender))
Sink.foreach(msg => target.tell(msg, whateverSender))
. You could also calculate a sender by inspecting the msg
. Be cautious not to use target.tell(msg, sender())
inside of Sink.foreach
because the result of sender()
is undefined or will fail when executed from within a stream.
Akka Typed
With the new term may change we will no longer have a different artifact for modules that are not stable, and akka-typed-experimental
has therefore been renamed to akka-typed
. Note that it is still not promoted to a stable module.
Experimental modules
We have previously marked modules that we did not want to freeze the APIs of a experimental, such modules will instead be marked as may change from now on.
Testkit
JavaTestKit
has been deprecated since it does all kinds of tricks to achieve what Scala testkit does. Use akka.testkit.javadsl.TestKit
instead which introduces nicer APIs.
Old:
new JavaTestKit(system) {{
final JavaTestKit probe = new JavaTestKit(system);
new Within(duration("1 second")) {
public void run() {
probe.expectMsgEquals("hello");
}
};
}};
New:
new TestKit(system) {{
final TestKit probe = new TestKit(system);
within(duration("1 second"), () -> probe.expectMsgEquals("hello"));
}};