|
Concepts
ConceptsActorom provides a full fledged object-oriented implementation of the actors concurrency model, with basic and advanced capabilities inspired by other well-known implementations such as the ones in the Scala and Erlang languages. TopologiesTopologies (com.googlecode.actorom.Topology) are the main access point to a system composed by actor objects. There are currently two different topology implementations:
Regardless of the actual implementation, all topologies have the following responsibilities:
Local topologiesLocal topologies (com.googlecode.actorom.local.LocalTopology) provide fast, in-memory, in-process, message-passing capabilities: they are your implementation of choice if you need to use the actor concurrency model in your single-process, local-only, Java application. Topology topology = new LocalTopology("local");As you may see, you only need one mandatory argument, the symbolic host name to bind the local topology to. The symbolic host name is needed to uniquely identify the topology itself (you cannot use the same name for more than one active topologies), and to create its address space. Client/Server topologiesClient/Server topologies (com.googlecode.actorom.remote.ClientTopology and com.googlecode.actorom.remote.ServerTopology) provide both in-memory message-passing capabilities, as well as remote communication capabilities based on TCP sockets, allowing you to spawn and access, from a client topology, actors hosted on a remote server topology: they are your implementation of choice if you need to distribute your actors on several computers, and remotely access and work with them. Here's how to create a server topology: Topology topology = new ServerTopology("127.0.0.1", 8000);As you may see, you only need two mandatory arguments: the host and port to listen to for client connections A client topology is connected to one and only one server topology, here's how to create it: Topology topology = new ClientTopology("127.0.0.1", 8000, 1, TimeUnit.SECONDS, 1, TimeUnit.SECONDS, 3);Other than the host and port arguments defining the server topology to connect to, it requires additional parameters to control the communication link, more specifically the communication and keepAlive timeout values, plus the number of failed retries (see javadocs for more details). Please note that Client/Server topologies are meant to work on trusted environments, so they have no security mechanisms. Spawn and retrieve actorsActor instances are created by passing the actor identifier (to be unique inside the topology itself) and handler to the proper topology method: Address address = topology.spawnActor(ACTOR_ADDRESS, new ActorHandler()); The call above returns an Address (com.googlecode.actorom.Address) instance, which must be used to retrieve the previously spawned actor: topology.getActor(address); The actor handler is the object that implements the actual actor behavior: we will talk about it in a moment. Threading policiesThreading policies (com.googlecode.actorom.ThreadingPolicy) define the threading model used by actors to handle incoming messages.
You can set the desired policy by using the proper LocalTopology or ServerTopology constructor. Fail-over policiesFail-over policies (com.googlecode.actorom.FailoverPolicy) define how the topology behaves when one of its actors exits, that is, cease its activity after being killed or failed.
You can set the desired policy by using the proper LocalTopology or ServerTopology constructor. ActorsWhile actors are actually created by topologies, you can easily control their life-cycle and implement their message handling logic by writing a so-called handler, a simple, properly annotated, plain old Java object.
Handling on spawn behaviorOn spawn behavior can be controlled by implementing a no-arg method annotated with the com.googlecode.actorom.annotation.OnSpawn annotation: @OnSpawn
public void onSpawn() {
// do something ...
}Swapping actor handlers and handling on swap behaviorActors logic is implemented by providing a properly annotated handler object: Actorom provides dynamic (sometimes called hot) handler swapping, allowing you to change the actor handler, and hence its behavior, while it's running and processing messages: actor.swap(newHandler); On swap behavior can be controlled by implementing a no-arg method annotated with the com.googlecode.actorom.annotation.OnSwap annotation: @OnSwap
public void onSwap() {
// do something ...
}Killing actors and handling on kill behaviorActors can be killed at any time as a result of:
On kill behavior can be controlled by implementing a no-arg method annotated with the com.googlecode.actorom.annotation.OnKill annotation: @OnKill
public void onKill() {
// do something ...
}Restart types and handling on restart behaviorEvery actor can be associated with a restart type, which defines when an actor should be actually restarted by its topology after having been killed or failed.
Restart types can be configured by annotating the handler class with the com.googlecode.actorom.annotation.Restart annotation: @Restart(type=RestartType.PERMANENT)
public class ActorHandler {
// do something ...
}On restart behavior, executed depending on both fail-over policies and restart types as described above, can be controlled by implementing a no-arg method annotated with the com.googlecode.actorom.annotation.OnRestart annotation: @OnRestart
public void onRestart() {
// do something ...
}Message sendingThere are two different methods, on the com.googlecode.actorom.Actor interface, that you can use to send messages. You can simply send an asynchronous message to your actor: actor.send(new MyMessage()); It's a non-blocking method: the message is en-queued into the actor's mailbox, and asynchronously processed. Or you can send an asynchronous message and wait for its result through a so-called future (com.googlecode.actorom.FutureResult): FutureResult future = actor.send(new MyMessage(), 10, TimeUnit.SECONDS); future.await(); Here you pass the message and a maximum timeout which defines how long you can wait for the message execution before it is considered to be expired; expired messaged will still be processed, but you will not be able to retrieve the eventual result of the computation. Object result = future.getResult(); ActorExecutionException exception = future.getException(); Message handlingMessages sent to actors are processed by implementing handler methods annotated with the com.googlecode.actorom.annotation.OnMessage annotation. @OnMessage(type = SampleMessage.class)
public void onMessage(SampleMessage message) {
// ....
}The properly annotated onMessage method will match and process messages of SampleMessage type (or sub-type); the SampleMessage argument is the message instance to be actually processed. LinkingActors can be linked in order to make the linked actors life-cycle dependent on the linking ones. Actors send an com.googlecode.actorom.ExitActorMessage instance to their links prior to exiting, requesting them to exit as well unless they explicitly catch the exit message (see below about exit traps). You can link two actors by simply calling: actor1.link(actor2); Here, actor1 will be linked to actor2, not vice-versa: links are unidirectional. actor1.unlink(actor2); Please note that you can only link actors belonging to the same topology (also called neighbors). You cannot link local actors with remote actors, nor remote actors belonging to different server topologies. Exit trapsActors can catch com.googlecode.actorom.ExitActorMessage instances sent by linking actors, avoiding so to immediately exit. @TrapExit
public void trap(ExitActorMessage message) {
// do something ...
}The method must have a com.googlecode.actorom.ExitActorMessage argument, representing the actual message sent by the linking actor. InjectionsActor handlers fields can be injected with instances of:
Messaging DSLActorom comes with a tiny DSL to (even more) easily manipulate actors. Topology topology = ... You will be able to: Send messages to actors: // With no future: on(topology).send(message).to(address); // With future: on(topology).send(message).withTimeout(1, TimeUnit.SECONDS).to(address); Link/unlink actors: // Link: on(topology).link(address1).to(address2); // Unlink: on(topology).unlink(address1).from(address2); Swap handlers: on(topology).swap(address).with(newHandler); |