Runtime.getRuntime().availableProcessors()
.An actor is a computational entity that, in response to a message it receives, can concurrently:
send a finite number of messages to other actors; create a finite number of new actors; designate the behavior to be used for the next message it receives.There is no assumed sequence to the above actions and they could be carried out in parallel.
playground
. If you use Maven, place a dependency into your playground’s pom.xml
file.build.gradle
.x.y.z
is a semantic version number and reflects the version of xoom-actors JAR file that you depend on. This number may be something such as 1.8.0
. You will find the available versions, including the most recent version, available on one of the supported public repositories.Pinger
interface with a single method definition, named ping()
, which takes a Ponger
as a parameter.Ponger
interface the same way, but with a pong()
method that takes a Pinger
as a parameter.Pinger
is a client of Ponger
, and Ponger
is a client of Pinger
.Pinger
protocol.Ponger
protocol.Pinger
and Ponger
interface definitions, you will notice that both of these protocols extend the Stoppable
protocol. Thus, they can both be stopped by other actors that have a Stoppable
reference to them. We use that capability from within PingerActor
to cause both actors to stop when the count
reaches 10
.stop()
methods. That’s because the abstract base class, Actor
, implements stop()
for them. You could override stop()
to find out when your actor is being stopped, but that’s not necessarily a good idea. What if you forgot to invoke the super’s stop()
? That would make you think that your actor was going to stop, but the actor would never shut down because the Actor
base class behavior would never be run. If you want to know when you are being stopped, you can override one of the four life cycle methods instead of stop()
.beforeStart()
afterStop()
beforeRestart(final Throwable reason)
afterRestart(final Throwable reason)
beforeResume(final Throwable reason)
Exception
, it can take a number of actions. Your supervisor can tell your actor to resume, to stop, or to restart. If it tells your actor to resume, the beforeResume()
is invoked. When it tells your actor to restart, the beforeRestart()
is invoked first, and then the afterRestart()
is invoked. Since your actor has failed, it may have been left in an invalid state. In such cases, these three life cycle methods give your actor the opportunity to clean up after the problem that caused the Exception
and also reinitialize itself before reacting to its next available protocol message.Exception
recovery methods DO NOT cause the Actor
instance to be completely discarded and recreated. Therefore, it is the responsibility of the Actor
to set its state to a safe point before message processing resumes.afterStop()
method shows two additional perks of XOOM Actors. All actors have a personal address, which is available through your inherited address()
method. Also, all actors have a Logger
available via its logger()
method. Any information that you log will be output asynchronously through a registered Logger
actor, so your actor won't block while file output is performed.World
for your actors to play in.World
is created. The World
is a major component of XOOM Actors. In a nutshell, a World
is the primary container within which actors live and play. Generally you would create only one World
per service instance. In DDD terms, a World
is the root of a Bounded Context. (Don't worry about the use of pauseThisThread()
; it is explained below.)World
is started, two actors are created, and a reference to their respective protocol is returned. Each actor is created by passing its protocol and the concrete actor type. You may also create actors by means of a Definition
. The Definition
indicates the class of the actor that implements the protocol, such as PingerActor.class
, which implements the Pinger.class
protocol. There are four ways to instantiate an actor by means of its constructor:actorFor()
as seen above, and also pass each constructor parameter following the implementation class type. This has the advantage of making the parameters visible, but they are passed as varargs each of type Object
, and are thus not type-safe.Definition
object as the second parameter to actorFor()
. The Definition
contains the class of the actor implementation and a possibly one or more constructor parameters, or it can pass Definition.NoParameters
. See the next code example for how to pass constructor parameters. This approach is also not type safe.ActorInstantiator
, a functional interface included in the xoom-actors
SDK and runtime. Construct your specific ActorInstantiator
type, pass any actor constructor parameters into the ActorInstantiator
constructor. When the actor is ready to be created by the runtime, the ActorInstantiator
method blah will be called. At that time call the specific actor's constructor when using new
. (See example provided below.)PingerActor
taking two parameters, a String
and an int
, using the Definition
approach:ActorInstantiator
, which does not require the use of reflection:PingerInstantiator
, each of which is demonstrated separately in the follow example:pauseThisThread()
is used.stop()
for both actors, have been delivered.Thread.sleep()
in your tests or your production services.io.vlingo.xoom.actors.testkit.TestUntil
component. This example also demonstrates how actors take constructor parameters. In the test method, create an instance of the TestUntil
to pass to the PingerActor
constructor.PingerActor
to take a TestUntil
instance as a constructor parameter. Proxy
ProtocolPinger
and Ponger
instances. These are not direct references to the underlying PingerActor
and PongerActor
instances, but are instead proxies. Invoking a method on a proxy causes a message to be created and enqueued for the actor that backs the proxy.Proxy
type. This can be used to access the Address
of the actor using address()
. In addition, all proxies supports working equals()
, hashCode()
, and toString()
implementations.Proxy
interface is not available by way of the Pinger
protocol (or any other actor protocols), there is a way to obtain the Proxy instance:final Address address = Proxy.from(pinger).address();
Proxy
type is available in the io.vlingo.xoom.actors
package.PingerActor
must cause a happened()
in its afterStop()
method to signal to the test that the Pinger
has stopped:afterStop()
method causes the until.happened()
the test method will block. As soon as the until.happened()
causes its state to transition from 1
to 0
, the test will unblock and the World
will terminate. This enables the test to complete.TestUntil
in code that will be used in production. Further, as is seen later in this chapter, you should actually use AccessSafely
rather than TestUntil
. It is not only thread safe, but also provides memory fences/gates around multi-threaded state modifications during tests.TestUntil
and other io.vlingo.xoom.actors.testkit
tools.ping()
and pong()
methods.Completes<T>
protocol with its backing implementation. Being cited in this chapter and others, it's best to understand how it works. To do so, refer to our discussion provided in the Completes<T>
documentation.World
object as follows.World
with normal runtime defaults in which Actor
instances are created and run. For many uses of XOOM Actors the defaults are the easiest and safest way to use the Actor
runtime.World
. The following is a summary.World
by loading configurations from the file named xoom-actors.properties
. The name
is used to name the World
instance.World
with your own name-value pairs using java.util.Properties
defined in code. The details of the xoom-actors.properties
file are discussed below.World
can be started using fluent configuration.Configuration
are discussed below.World
, you should use the following to terminate the Actor
runtime.World::terminate()
method is currently a synchronous operation, but in the future will become asynchronous. When the World
terminates asynchronously there will be a Completes<T>
or callback construct to inform the client when the termination has completed.Actor
runtime may be configured by means of a file that adheres to the java.util.Properties
conventions. Each property is defined by a name
followed by =
and then a value
. For the XOOM Actors toolkit the file must be named xoom-actors.properties
and be located in the runtime classpath
. The following shows how the standard ConcurrentQueueMailbox
can be defined in this properties file.\
at the end of the line to be continued on the next line. To see example properties that can be used, you should review: xoom-actors/src/test/resources/xoom-actors.properties
World
instance.World
provides the following. All concrete Actor
instance may obtain both their Stage
and their World
instances as follows, which enables the Actor
to reach specific facilities offered by each.Actor
addresses or a way to produce an address from a primitive or String
value. To do so, request it by means of the method addressFactory()
.Configuration
of the World
runtime. Even if you load your runtime properties from the xoom-actors.properties
or your own java.util.Properties
definition, all of your runtime configurations are placed in the Configuration
.Actor
instances that cannot be delivered for any reasons, such as the Actor
instance has previously been stopped, are delivered to the special Actor
know as DeadLetters
. You may subscribe to receive DeadLetters
messages.DeadLetters
protocol method to subscribe to its received messages. Your listener Actor must implement the DeadLettersListener
protocol.Logger
that is provided to all Actor
instances, use the following method.Actor
is a child of the default parent Actor
.Actor
must be assigned to a overarching supervisor. The following provides a reference to the default supervisor of all newly created Actor
instance.Logger
is available through the World
interface, there may also be a number of named Logger
instances. If you use non-default Logger
instances, they may be obtained via the following World
facility. All Logger
instances obtained through the standard XOOM Actors plugins are backed by actors, and are thus asynchronous by default.Logger
implementation is based on SLF4J. You may configure your Logger
to your standards. The following is a simple example that outputs strictly to the console.Logger
protocol provides several facilities for logging application/service output and exceptions.World
interface provides some additional facilities, but ones that are useful only for plugins. Those are documented below.World
you are able to create Actor
instance to run in it. As a reminder, there is nothing mysterious about actors. Actors are basically objects, but their behaviors are requested by sending messages asynchronously rather than through directly invoking their methods. The following shows you how to create an actor.World
is used to create a new instance of the SimpleActor
type. The SimpleActor
type implements the protocol defined by the interface named Simple
. Thus, actors provide protocol implementations, and they can implement multiple protocols.p1
, p2
, and p3
. These parameters could be of any type, and each parameter is required to follow the convention that the constructor contract requires.World
method actorFor()
returns, the requesting client is given a reference to the protocol that provides asynchronous access to the Actor
. This reference is used to send messages via methods on the protocol. The Simple
protocol is defined as follows.simpleSay()
. Messages are sent asynchronously to the concrete Actor
instance, which in the case of the above example is an instance of SimpleActor
. This behavior is demonstrated by the following expression.Actor
. The reification is accomplished by creating a Function
that represents the method invocation and enqueuing the Function
instance on to the Actor
's Mailbox
. The Function
will later be applied to the Actor
instance when all previously enqueued messages have been processed and a thread becomes available to process the message at the front of the Mailbox
. Thus, the method invocation is temporally decoupled from the sending side and the receiving side.World
does not directly create an Actor
. Instead, the World
dispatches actorFor()
requests to the default Stage
. It is the Stage
that provides the concrete implementation of actorFor()
.Stage
is the abstraction within which Actor
instances are maintained. A World
has at least one Stage
, which is known as the default Stage
, and its name is "__defaultStage"
. If you need to query the instance of the default stage, you use the following World
query:Stage
supports two important behaviors, actorFor()
and actorOf()
. The actorFor()
is a creational method, and there are several overloads supporting various parameter options. Using the method various implementations of the method actorFor()
will create a new Actor
instance and answer one or more protocols. Once you have a reference to a protocol you are able to send messages to the given Actor
that implements the protocol(s).actorOf()
method has a different purpose. Given an existing Actor
that is contained within a given Stage
, you may use actorOf()
to find the existing Actor
by passing the Address
of the Actor
. The actorOf()
answers a Completes<T>
because it is an asynchronous operation, answering the requested protocol eventually.Actor
may obtain its containing Stage
and its World
as follows.Stage
instances within the World
, but the default Stage
is automatically provided when a World
starts. The World
default Stage
is responsible for holding the private root actor and the public root actor. These two instances act as default supervisors, which are responsible for protecting a World
from catastrophic failures. Supervision is explained in detail below.World
must create some default operational Actor
instances when it is started, it may be best to segregate your application/service Actor
instances into another Stage
. It's simple to accomplish this. To obtain a reference to an existing Stage
by an other name, or to obtain or a newly created Stage
by name if non-existing, use one of the two following queries:Stage
instance with the given name
does not yet exist, a new Stage
with that name is created and returned.Stage
instances. It's likely that the default Stage
and one application/service Stage
instance will be sufficient. Yet, we don't set a limit on the number of Stage
instances in case more than two would be useful.Grid
type is an extension of the Stage
.Stage
has its own Schedular
, which may be used to produce time-lapsed events that are sent to an Actor
.Actor
can schedule itself or another Actor
, such as one or more of its children, for some timed event notification. In other words, it need not pass itself as the Scheduled
instance.Actor
scheduling itself for notifications must implement the Scheduled
protocol in order for it to be scheduled to receiver interval signals.100
milliseconds of the registration and will repeat every 1
second (1_000
milliseconds). The Actor
scheduling itself for notifications must implement the Scheduled
protocol, and it passes an Actor
enabled reference to that effect using the runtime method selfAs()
. The Actor
can associate some specific data with which it will be notified on each event, which in this example is the DataPacket
instance packet
. This DataPacket
type is only used for the example, and would be replaced with your own type, or you can pass null
if the data is unused.Scheduled
object you are provided a Cancellable
instance. You may use this instance to cancel one-time or repeating occurrences.Actor
receiving the timed event will be notified using the intervalSignal()
method of the Scheduled
protocol.Scheduler
is instantiated it creates a thread pool of 1 thread to manage all scheduling for a single Stage
. You can create a unique Scheduler
that has its own thread pool in case the Stage
's Scheduler
is already heavily used. Yet, note that if used as intended, the Scheduled
interface registered with the Scheduler
is generally implemented by an Actor
(as explained above). Thus, in that case, the Scheduler
thread is not used to run the ScheduledTask
instances. That is, the Scheduler
has a thread for tracking all ScheduledTask
instances under its care.Scheduler
itself. There seem to be two areas of potential latency that are related to the actor receiving the timed interval messages:Scheduler
would be related to how many messages are already in the Actor
's mailbox before the Scheduled#intervalSignal()
is delivered. The more messages in the mailbox, the more latency to the arrival of the message of concern, intervalSignal()
.Scheduled#intervalSignal()
messages arrive more rapidly than each previous one can be processed. In other words, are the full number of intervalSignal()
messages stacking up in the actor's mailbox? If so, consider increasing the time between timer intervals, or finding a way to increase performance of the computing done while handling each message, or both. If the actor's message handling is blocking on I/O you might want to use XOOM Streams to introduce backpressure and a "work stealing" approach to requesting intervals. Additionally, you could employ the Scheduler
's scheduleOnce()
service and reschedule for one interval each time the actor's processing ends.Mailbox
type to increase the number of thread pools for a runtime. You will find this discussion below under Plugins.Stage
's actor eviction service.Actor
is not limited to implementing a single protocol or interface
. It could have two or more. Consider again the Scheduler
example (above). An Actor implementation that wants to schedule timed event signals will be more than a Scheduled
type. Its primary purpose is to be another type of Actor
, and to its clients that protocol type is the focus. Consider this example.Actor
is to be a DocumentProcessor
. The fact that it is capable of being scheduled for timer signals is an internal concern only. The clients of this Actor
will not know that it can be a Scheduled
, only that it is a DocumentProcessor
. DocumentProcessor
requires it to schedule itself to receives timer signals. The DocumentProcessorActor
may be registered with the Scheduler
by passing a reference to itself as a Scheduled
. You saw this in the above example, but to make it stand out here is another one that isolates the use of the essential selfAs()
.Actor
types inherit the method selfAs(Class<T>)
to request itself as an Actor
that supplies the protocol represented by the generic parameter T
. The Actor must implement the T
protocol.this
. Passing this
, for example to scheduleOnce()
, will cause intervalSignal()
notifications to be received as direct method invocations rather than as asynchronous messages. Even though you may think that this could be a good idea, it is definitely a very bad idea. You will eventually experience a race condition in your Actor
where a message delivered asynchronously on one thread will collide by accessing data simultaneously with the Scheduler
thread that makes the direct method invocations.this
. Always use the Actor
inherited methodselfAs(SomeProtocol.class)
to comply with the proper use of the Actor Model.Actor
may implement a number of protocols, such as when representing itself as protocols specifically supported by its children or other collaborators. This makes for loose coupling and least knowledge by design. Even so, care should be used to avoid creating an Actor
that supports more protocols than is necessary to meet its primary responsibility.Completes<T>
, where T
is the type of the outcome. If your actor's message protocol that provides a return value answer does not return a Completes<T>
value, then the platform runtime will reject the protocol.