CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-typesafe-akka--akka-serialization-jackson

Jackson-based JSON and CBOR serialization capabilities for the Akka toolkit, enabling efficient serialization and deserialization of Akka messages and persistent events.

Pending
Overview
Eval results
Files

akka-types.mddocs/

Akka Type Serialization

Akka Serialization Jackson provides built-in serialization support for Akka-specific types through specialized Jackson modules. These modules handle the complexities of serializing distributed system references and Akka-specific data types.

Core Jackson Modules

AkkaJacksonModule

The comprehensive module that includes support for all core Akka types.

/**
 * Complete module with support for all custom serializers.
 * Includes ActorRef, Address, and FiniteDuration serialization.
 */
class AkkaJacksonModule extends JacksonModule 
  with ActorRefModule with AddressModule with FiniteDurationModule {
  override def getModuleName = "AkkaJacksonModule"
}

object AkkaJacksonModule extends AkkaJacksonModule

AkkaTypedJacksonModule

Module for Akka Typed ActorRef serialization support.

/**
 * Module for Akka Typed ActorRef serialization.
 * Only available if akka-actor-typed is on the classpath.
 */
class AkkaTypedJacksonModule extends JacksonModule with TypedActorRefModule {
  override def getModuleName = "AkkaTypedJacksonModule"
}

object AkkaTypedJacksonModule extends AkkaTypedJacksonModule

AkkaStreamJacksonModule

Module for Akka Streams reference serialization.

/**
 * Module for Akka Streams serialization (SourceRef, SinkRef).
 * Only available if akka-streams is on the classpath.
 */
class AkkaStreamJacksonModule extends JacksonModule with StreamRefModule {
  override def getModuleName = "AkkaStreamJacksonModule"
}

object AkkaStreamJacksonModule extends AkkaStreamJacksonModule

Supported Akka Types

ActorRef Serialization

Classic ActorRef references are serialized as strings using their serialization format.

import akka.actor.{ActorRef, ActorSystem}

// ActorRef is automatically serialized to string format
val myActor: ActorRef = system.actorOf(Props[MyActor], "myActor")

// JSON representation:
// "akka://MySystem@hostname:2552/user/myActor"

Serialization behavior:

  • Uses ActorRef.path.toSerializationFormatWithAddress()
  • Includes the full actor system address for remote resolution
  • Handles both local and remote ActorRef instances

Typed ActorRef Serialization

Akka Typed ActorRef instances are serialized using the ActorRefResolver.

import akka.actor.typed.{ActorRef, ActorSystem}
import akka.actor.typed.scaladsl.Behaviors

// Typed ActorRef is automatically serialized
val typedActor: ActorRef[String] = 
  system.systemActorOf(Behaviors.receiveMessage[String](msg => Behaviors.same), "typedActor")

// JSON representation:
// "akka://MySystem@hostname:2552/system/typedActor#1234567890"

Serialization behavior:

  • Uses ActorRefResolver.toSerializationFormat()
  • Maintains type safety during deserialization
  • Supports both system and user-created typed actors

Address Serialization

Akka Address instances are serialized as URI strings.

import akka.actor.{Address, AddressFromURIString}

val address = Address("akka", "MySystem", "hostname", 2552)

// JSON representation:
// "akka://MySystem@hostname:2552"

Serialization behavior:

  • Uses Address.toString() for serialization
  • Uses AddressFromURIString() for deserialization
  • Handles all address components (protocol, system, host, port)

FiniteDuration Serialization

Scala FiniteDuration instances are serialized using Java 8 time format.

import scala.concurrent.duration._

val duration = 30.seconds

// JSON representation (ISO-8601 duration format):
// "PT30S"

Serialization behavior:

  • Delegates to Jackson's DurationSerializer from jackson-modules-java8
  • Converts between Scala FiniteDuration and Java Duration
  • Uses ISO-8601 duration format for JSON representation

StreamRef Serialization

Akka Streams SourceRef and SinkRef are serialized using StreamRefResolver.

import akka.stream.{SourceRef, SinkRef}
import akka.stream.scaladsl.{Source, Sink}

// SourceRef serialization
val sourceRef: SourceRef[String] = Source(1 to 100).map(_.toString).runWith(StreamRefs.sourceRef())

// SinkRef serialization  
val sinkRef: SinkRef[String] = StreamRefs.sinkRef[String]().run()

// JSON representations:
// SourceRef: "sourceRef://MySystem@hostname:2552/temp/$a#1234567890"
// SinkRef: "sinkRef://MySystem@hostname:2552/temp/$b#0987654321"

Serialization behavior:

  • Uses StreamRefResolver.toSerializationFormat() for serialization
  • Uses StreamRefResolver.resolveSourceRef() / resolveSinkRef() for deserialization
  • Maintains stream materialization context

Module Configuration

Automatic Module Registration

The Jackson modules are automatically registered based on classpath detection:

akka.serialization.jackson {
  jackson-modules += "akka.serialization.jackson.AkkaJacksonModule"
  # Conditionally loaded if akka-actor-typed is available
  jackson-modules += "akka.serialization.jackson.AkkaTypedJacksonModule"
  # Conditionally loaded if akka-streams is available  
  jackson-modules += "akka.serialization.jackson.AkkaStreamJacksonModule"
}

Manual Module Registration

For custom ObjectMapper instances:

import akka.serialization.jackson._
import com.fasterxml.jackson.databind.ObjectMapper

val mapper = new ObjectMapper()

// Register individual modules
mapper.registerModule(AkkaJacksonModule)
mapper.registerModule(AkkaTypedJacksonModule)
mapper.registerModule(AkkaStreamJacksonModule)

// Or register through custom factory
class MyObjectMapperFactory extends JacksonObjectMapperFactory {
  override def overrideConfiguredModules(bindingName: String, configuredModules: Seq[Module]): Seq[Module] = {
    configuredModules ++ Seq(
      AkkaJacksonModule,
      AkkaTypedJacksonModule,
      AkkaStreamJacksonModule
    )
  }
}

Usage Examples

Serializing Messages with ActorRef

import akka.actor.ActorRef

case class MessageWithActorRef(text: String, replyTo: ActorRef)

// Usage
val message = MessageWithActorRef("Hello", sender())

// JSON output:
// {
//   "text": "Hello",
//   "replyTo": "akka://MySystem@hostname:2552/user/sender#1234567890"
// }

Serializing Events with Duration

import scala.concurrent.duration._

case class TimedEvent(name: String, duration: FiniteDuration, timestamp: Long)

// Usage
val event = TimedEvent("processing", 5.minutes, System.currentTimeMillis())

// JSON output:
// {
//   "name": "processing",
//   "duration": "PT5M", 
//   "timestamp": 1640995200000
// }

Serializing Stream References

import akka.stream.{SourceRef, SinkRef}

case class StreamingJob(id: String, dataSource: SourceRef[String], resultSink: SinkRef[String])

// Usage - when serialized, refs become strings that can be resolved remotely
val job = StreamingJob("job-123", sourceRef, sinkRef)

// JSON output:
// {
//   "id": "job-123",
//   "dataSource": "sourceRef://MySystem@hostname:2552/temp/$a#1234567890",
//   "resultSink": "sinkRef://MySystem@hostname:2552/temp/$b#0987654321"
// }

Typed ActorRef Patterns

Message with Typed Reply

import akka.actor.typed.ActorRef

case class Request(data: String, replyTo: ActorRef[Response])
case class Response(result: String)

// The typed ActorRef maintains type information in the serialized form
val request = Request("process this", typedResponseActor)

System Actor References

import akka.actor.typed.{ActorSystem, SpawnProtocol}

val system: ActorSystem[SpawnProtocol.Command] = ActorSystem(Behaviors.setup[SpawnProtocol.Command] { ctx =>
  SpawnProtocol()
}, "MySystem")

// System actors are serialized with system path
val systemActor = system.systemActorOf(myBehavior, "systemService")

Deserialization Context

ActorSystem Access

All Akka type deserializers require access to the current ActorSystem:

import akka.serialization.Serialization

// The current ActorSystem is available during deserialization
// through Serialization.currentTransportInformation
val currentSystem = Serialization.currentTransportInformation.value.system

Note: This context is automatically managed by Akka's serialization system and doesn't require manual setup.

Error Handling

Invalid ActorRef References

// If an ActorRef path cannot be resolved during deserialization:
// - For classic ActorRef: returns deadLetters ActorRef
// - For typed ActorRef: may throw exception depending on resolver configuration

Missing Dependencies

// If akka-actor-typed is not on the classpath:
// - AkkaTypedJacksonModule will not be loaded
// - Typed ActorRef serialization will not be available

// If akka-streams is not on the classpath:
// - AkkaStreamJacksonModule will not be loaded  
// - StreamRef serialization will not be available

Performance Considerations

  1. ActorRef Resolution: Deserializing ActorRef instances requires actor system lookup
  2. Remote References: Cross-system ActorRef serialization includes full addresses
  3. StreamRef Lifecycle: Serialized StreamRef instances maintain stream materialization
  4. Duration Conversion: FiniteDuration serialization involves Scala/Java conversion

Best Practices

  1. Use appropriate modules - Only include modules for Akka features you use
  2. Handle resolution failures - ActorRef deserialization may fail if actors don't exist
  3. Consider ref lifecycles - Serialized ActorRef/StreamRef may outlive the original actors/streams
  4. Test across systems - Verify serialization works across different ActorSystem instances
  5. Monitor performance - Type serialization adds overhead compared to primitive types

Install with Tessl CLI

npx tessl i tessl/maven-com-typesafe-akka--akka-serialization-jackson

docs

akka-types.md

configuration.md

index.md

migration.md

object-mapper.md

tile.json