Serialization (Java)

Serialization (Java)

Akka has a built-in Extension for serialization, and it is both possible to use the built-in serializers and to write your own.

The serialization mechanism is both used by Akka internally to serialize messages, and available for ad-hoc serialization of whatever you might need it for.

Usage

Configuration

For Akka to know which Serializer to use for what, you need edit your Configuration, in the “akka.actor.serializers”-section you bind names to implementations of the akka.serialization.Serializer you wish to use, like this:

val config = ConfigFactory.parseString("""
  akka {
    actor {
      serializers {
        java = "akka.serialization.JavaSerializer"
        proto = "akka.serialization.ProtobufSerializer"
        myown = "akka.docs.serialization.MyOwnSerializer"
      }
    }
  }
""")

After you’ve bound names to different implementations of Serializer you need to wire which classes should be serialized using which Serializer, this is done in the “akka.actor.serialization-bindings”-section:

val config = ConfigFactory.parseString("""
  akka {
    actor {
      serializers {
        java = "akka.serialization.JavaSerializer"
        proto = "akka.serialization.ProtobufSerializer"
        myown = "akka.docs.serialization.MyOwnSerializer"
      }

      serialization-bindings {
        "java.lang.String" = java
        "akka.docs.serialization.Customer" = java
        "com.google.protobuf.Message" = proto
        "akka.docs.serialization.MyOwnSerializable" = myown
        "java.lang.Boolean" = myown
      }
    }
  }
""")

You only need to specify the name of an interface or abstract base class of the messages. In case of ambiguity, i.e. the message implements several of the configured classes, the most specific configured class will be used, i.e. the one of which all other candidates are superclasses. If this condition cannot be met, because e.g. java.io.Serializable and MyOwnSerializable both apply and neither is a subtype of the other, a warning will be issued.

Akka provides serializers for java.io.Serializable and protobuf com.google.protobuf.GeneratedMessage by default (the latter only if depending on the akka-remote module), so normally you don’t need to add configuration for that; since com.google.protobuf.GeneratedMessage implements java.io.Serializable, protobuf messages will always by serialized using the protobuf protocol unless specifically overridden. In order to disable a default serializer, map its marker type to “none”:

akka.actor.serialization-bindings {
  "java.io.Serializable" = none
}

Verification

If you want to verify that your messages are serializable you can enable the following config option:

val config = ConfigFactory.parseString("""
  akka {
    actor {
      serialize-messages = on
    }
  }
""")

Warning

We only recommend using the config option turned on when you’re running tests. It is completely pointless to have it turned on in other scenarios.

If you want to verify that your Props are serializable you can enable the following config option:

val config = ConfigFactory.parseString("""
  akka {
    actor {
      serialize-creators = on
    }
  }
""")

Warning

We only recommend using the config option turned on when you’re running tests. It is completely pointless to have it turned on in other scenarios.

Programmatic

If you want to programmatically serialize/deserialize using Akka Serialization, here’s some examples:

import akka.actor.*;
import akka.remote.RemoteActorRefProvider;
import akka.serialization.*;
import com.typesafe.config.*;

    ActorSystem system = ActorSystem.create("example");

    // Get the Serialization Extension
    Serialization serialization = SerializationExtension.get(system);

    // Have something to serialize
    String original = "woohoo";

    // Find the Serializer for it
    Serializer serializer = serialization.findSerializerFor(original);

    // Turn it into bytes
    byte[] bytes = serializer.toBinary(original);

    // Turn it back into an object,
    // the nulls are for the class manifest and for the classloader
    String back = (String) serializer.fromBinary(bytes);

    // Voilá!
    assertEquals(original, back);

For more information, have a look at the ScalaDoc for akka.serialization._

Customization

So, lets say that you want to create your own Serializer, you saw the akka.docs.serialization.MyOwnSerializer in the config example above?

Creating new Serializers

First you need to create a class definition of your Serializer, which is done by extending akka.serialization.JSerializer, like this:

import akka.actor.*;
import akka.remote.RemoteActorRefProvider;
import akka.serialization.*;
import com.typesafe.config.*;

    public static class MyOwnSerializer extends JSerializer {

      // This is whether "fromBinary" requires a "clazz" or not
      @Override public boolean includeManifest() {
          return false;
      }

      // Pick a unique identifier for your Serializer,
      // you've got a couple of billions to choose from,
      // 0 - 16 is reserved by Akka itself
      @Override public int identifier() {
          return 1234567;
      }

      // "toBinary" serializes the given object to an Array of Bytes
      @Override public byte[] toBinary(Object obj) {
        // Put the code that serializes the object here
        // ... ...
      }

      // "fromBinary" deserializes the given array,
      // using the type hint (if any, see "includeManifest" above)
      @Override public Object fromBinaryJava(byte[] bytes,
                     Class<?> clazz) {
        // Put your code that deserializes here
        // ... ...
      }
    }

Then you only need to fill in the blanks, bind it to a name in your Configuration and then list which classes that should be serialized using it.

Serializing ActorRefs

All ActorRefs are serializable using JavaSerializer, but in case you are writing your own serializer, you might want to know how to serialize and deserialize them properly, here’s the magic incantation:

import akka.actor.*;
import akka.remote.RemoteActorRefProvider;
import akka.serialization.*;
import com.typesafe.config.*;

        // Serialize
        // (beneath toBinary)
        final Address transportAddress =
                Serialization.currentTransportAddress().value();
        String identifier;

        // If there is no transportAddress,
        // it means that either this Serializer isn't called
        // within a piece of code that sets it,
        // so either you need to supply your own,
        // or simply use the local path.
        if (transportAddress == null) identifier = theActorRef.path().toString();
        else identifier = theActorRef.path().toStringWithAddress(transportAddress);
        // Then just serialize the identifier however you like


        // Deserialize
        // (beneath fromBinary)
        final ActorRef deserializedActorRef = theActorSystem.actorFor(identifier);
        // Then just use the ActorRef

Note

ActorPath.toStringWithAddress only differs from toString if the address does not already have host and port components, i.e. it only inserts address information for local addresses.

This assumes that serialization happens in the context of sending a message through the remote transport. There are other uses of serialization, though, e.g. storing actor references outside of an actor application (database, durable mailbox, etc.). In this case, it is important to keep in mind that the address part of an actor’s path determines how that actor is communicated with. Storing a local actor path might be the right choice if the retrieval happens in the same logical context, but it is not enough when deserializing it on a different network host: for that it would need to include the system’s remote transport address. An actor system is not limited to having just one remote transport per se, which makes this question a bit more interesting.

In the general case, the local address to be used depends on the type of remote address which shall be the recipient of the serialized information. Use ActorRefProvider.getExternalAddressFor(remoteAddr) to query the system for the appropriate address to use when sending to remoteAddr:

public static class ExternalAddressExt implements Extension {
  private final ExtendedActorSystem system;

  public ExternalAddressExt(ExtendedActorSystem system) {
    this.system = system;
  }

  public Address getAddressFor(Address remoteAddress) {
    final scala.Option<Address> optAddr = system.provider()
        .getExternalAddressFor(remoteAddress);
    if (optAddr.isDefined()) {
      return optAddr.get();
    } else {
      throw new UnsupportedOperationException(
          "cannot send to remote address " + remoteAddress);
    }
  }
}

public static class ExternalAddress extends
    AbstractExtensionId<ExternalAddressExt> implements ExtensionIdProvider {
  public static final ExternalAddress ID = new ExternalAddress();

  public ExternalAddress lookup() {
    return ID;
  }

  public ExternalAddressExt createExtension(ExtendedActorSystem system) {
    return new ExternalAddressExt(system);
  }
}

This requires that you know at least which type of address will be supported by the system which will deserialize the resulting actor reference; if you have no concrete address handy you can create a dummy one for the right protocol using new Address(protocol, "", "", 0) (assuming that the actual transport used is as lenient as Akka’s RemoteActorRefProvider).

There is a possible simplification available if you are just using the default NettyRemoteTransport with the RemoteActorRefProvider, which is enabled by the fact that this combination has just a single remote address:

public static class DefaultAddressExt implements Extension {
  private final ExtendedActorSystem system;

  public DefaultAddressExt(ExtendedActorSystem system) {
    this.system = system;
  }

  public Address getAddress() {
    final ActorRefProvider provider = system.provider();
    if (provider instanceof RemoteActorRefProvider) {
      return ((RemoteActorRefProvider) provider).transport().address();
    } else {
      throw new UnsupportedOperationException("need RemoteActorRefProvider");
    }
  }
}

public static class DefaultAddress extends
    AbstractExtensionId<DefaultAddressExt> implements ExtensionIdProvider {
  public static final DefaultAddress ID = new DefaultAddress();

  public DefaultAddress lookup() {
    return ID;
  }

  public DefaultAddressExt createExtension(ExtendedActorSystem system) {
    return new DefaultAddressExt(system);
  }
}

This solution has to be adapted once other providers are used (like the planned extensions for clustering).

Deep serialization of Actors

The current recommended approach to do deep serialization of internal actor state is to use Event Sourcing, for more reading on the topic, see these examples:

Martin Krasser on EventSourcing Part1

Martin Krasser on EventSourcing Part2

Note

Built-in API support for persisting Actors will come in a later release, see the roadmap for more info:

Akka 2.0 roadmap

A Word About Java Serialization

When using Java serialization without employing the JavaSerializer for the task, you must make sure to supply a valid ExtendedActorSystem in the dynamic variable JavaSerializer.currentSystem. This is used when reading in the representation of an ActorRef for turning the string representation into a real reference. DynamicVariable is a thread-local variable, so be sure to have it set while deserializing anything which might contain actor references.

Contents