My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Concepts  

Featured
Updated Jul 27, 2009 by sergio.b...@gmail.com

Concepts

Actorom 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.

Topologies

Topologies (com.googlecode.actorom.Topology) are the main access point to a system composed by actor objects.

There are currently two different topology implementations:

  • Local topologies.
  • Client/Server topologies.

Regardless of the actual implementation, all topologies have the following responsibilities:

  • Spawn and retrieve actors.
  • Apply the proper threading policy.
  • Apply the proper fail-and-restart policy.

Local topologies

Local 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.

Here's how to create a local topology:

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.
There are also other constructors for specifying the threading and failover policies, as described later.

Client/Server topologies

Client/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
There are also other constructors for specifying the threading and failover policies, as described later.

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 actors

Actor 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 policies

Threading policies (com.googlecode.actorom.ThreadingPolicy) define the threading model used by actors to handle incoming messages.

There are currently two different policies:

  • Green threads, using a single native thread.
  • Operating-system threads, using a fixed pool of native threads.

You can set the desired policy by using the proper LocalTopology or ServerTopology constructor.

Fail-over policies

Fail-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.

There are currently two different policies:

  • No fail-over: the topology makes no action and let the actor exit.
  • One-for-one: the topology tries to restart the killed/failed actor, depending on its RestartType (see below).
  • All-for-one: the topology tries to restart all its actors (not just the killed/failed one), each depending on its RestartType (see below).

You can set the desired policy by using the proper LocalTopology or ServerTopology constructor.

Actors

While 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.

You will be able to define:

  • On spawn behavior: for implementing custom behavior to execute after the actor is spawned by its topology.
  • On swap behavior: for implementing custom behavior to execute after having swapped the old handler with the current one.
  • On kill behavior: for implementing custom behavior to execute after the actor is killed.
  • Restart type: for defining how the actor has to behave after it has been killed.
  • On restart behavior: for implementing custom behavior to execute after the actor has been restarted.
  • Message handling: for matching received messages and implementing custom behavior on matching message.
  • Exit traps: for catching exit messages sent by linking actors and implement custom behavior.

Handling on spawn behavior

On 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 behavior

Actors 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 behavior

Actors can be killed at any time as a result of:

  • An un-trapped (see below) com.googlecode.actorom.ExitActorMessage, sent by a linking actor.
  • A com.googlecode.actorom.KillActorException manually thrown inside a message handling or exit trap method (see below).

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 behavior

Every 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.

There are three restart types (com.googlecode.actorom.RestartType):

  • Permanent, the default one, defining that the actor should be always restarted, whether if killed or failed.
  • Transient, defining that the actor should be restarted only if failed.
  • Temporary, defining that the actor should never be restarted.

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 sending

There 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.

Here is how you can access the result, whether it is an actual object returned by the message handling method (see below), or an exception (com.googlecode.actorom.support.ActorExecutionException):

Object result = future.getResult();
ActorExecutionException exception = future.getException();

Message handling

Messages sent to actors are processed by implementing handler methods annotated with the com.googlecode.actorom.annotation.OnMessage annotation.

The OnMessage annotation must specify a type attribute, representing the Java class of the matching message: that is, the annotated method will match and process all messages whose class is assignable to the Java class specified above.

Here is an example:

@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.

Linking

Actors 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.

You can then unlink the two by simply calling:

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 traps

Actors can catch com.googlecode.actorom.ExitActorMessage instances sent by linking actors, avoiding so to immediately exit.

Just implement an exit trap method annotated with the com.googlecode.actorom.annotation.TrapExit annotation:

@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.

Injections

Actor handlers fields can be injected with instances of:

  • The actor address (com.googlecode.actorom.Address), by annotating an handler field with the com.googlecode.actorom.annotation.AddressInstance annotation.
  • The actor topology (com.googlecode.actorom.Topology), by annotating an handler field with the com.googlecode.actorom.annotation.TopologyInstance annotation.

Messaging DSL

Actorom comes with a tiny DSL to (even more) easily manipulate actors.
Given a certain topology (which may be local, client or server):

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);

Sign in to add a comment
Powered by Google Project Hosting