Skip to content

Commit

Permalink
initial code
Browse files Browse the repository at this point in the history
  • Loading branch information
pjfanning committed Aug 12, 2023
1 parent 2845b37 commit 7182184
Show file tree
Hide file tree
Showing 19 changed files with 2,315 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target

30 changes: 30 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ThisBuild / version := "0.1.0-SNAPSHOT"

val scala12Version = "2.12.18"
val scala13Version = "2.13.11"
val scala3Version = "3.3.0"
ThisBuild / scalaVersion := scala13Version
ThisBuild / crossScalaVersions := Seq(scala12Version, scala13Version, scala3Version)

val pekkoVersion = "1.0.1"
val jacksonVersion = "2.15.2"

lazy val root = (project in file("."))
.settings(
name := "pekko-serialization-jackson215",
libraryDependencies ++= Seq(
"org.apache.pekko" %% "pekko-actor" % pekkoVersion,
"org.apache.pekko" %% "pekko-actor-typed" % pekkoVersion % Optional,
"org.apache.pekko" %% "pekko-stream" % pekkoVersion % Optional,
"com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion,
"com.fasterxml.jackson.core" % "jackson-core" % jacksonVersion,
"com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % jacksonVersion,
"com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % jacksonVersion,
"com.fasterxml.jackson.module" % "jackson-module-parameter-names" % jacksonVersion,
"com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion,
"org.lz4" % "lz4-java" % "1.8.0",
"org.scala-lang.modules" %% "scala-java8-compat" % "1.0.2",
"org.scalatest" %% "scalatest" % "3.2.16" % Test,
"ch.qos.logback" % "logback-classic" % "1.2.12" % Test
)
)
1 change: 1 addition & 0 deletions project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version = 1.9.3
259 changes: 259 additions & 0 deletions src/main/resources/reference.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# SPDX-License-Identifier: Apache-2.0

###########################################
# Pekko Serialization Jackson Config File #
###########################################

# This is the reference config file that contains all the default settings.
# Make your edits/overrides in your application.conf.

#//#jackson-modules
pekko.serialization.jackson215 {

# The Jackson JSON serializer will register these modules.
jackson-modules += "com.github.pjfanning.pekko.serialization.jackson215.PekkoJacksonModule"
# PekkoTypedJacksonModule optionally included if pekko-actor-typed is in classpath
jackson-modules += "com.github.pjfanning.pekko.serialization.jackson215.PekkoTypedJacksonModule"
# PekkoStreamsModule optionally included if pekko-streams is in classpath
jackson-modules += "com.github.pjfanning.pekko.serialization.jackson215.PekkoStreamJacksonModule"
jackson-modules += "com.fasterxml.jackson.module.paramnames.ParameterNamesModule"
jackson-modules += "com.fasterxml.jackson.datatype.jdk8.Jdk8Module"
jackson-modules += "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"
jackson-modules += "com.fasterxml.jackson.module.scala.DefaultScalaModule"
}
#//#jackson-modules

pekko.serialization.jackson215 {
# When enabled and pekko.loglevel=DEBUG serialization time and payload size
# is logged for each messages.
verbose-debug-logging = off

# Define data migration transformations of old formats to current
# format here as a mapping between the (old) class name to be
# transformed to the JacksonJsonMigration class that implements
# the transformation.
migrations {
}

}

#//#stream-read-constraints
pekko.serialization.jackson215 {
read {
# see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.15.2/com/fasterxml/jackson/core/StreamReadConstraints.html
# these defaults are the same as the defaults in `StreamReadConstraints`
max-nesting-depth = 1000
max-number-length = 1000
max-string-length = 20000000
}
}
#//#stream-read-constraints

#//#features
pekko.serialization.jackson215 {
# Configuration of the ObjectMapper serialization features.
# See com.fasterxml.jackson.databind.SerializationFeature
# Enum values corresponding to the SerializationFeature and their boolean value.
serialization-features {
# Date/time in ISO-8601 (rfc3339) yyyy-MM-dd'T'HH:mm:ss.SSSZ format
# as defined by com.fasterxml.jackson.databind.util.StdDateFormat
# For interoperability it's better to use the ISO format, i.e. WRITE_DATES_AS_TIMESTAMPS=off,
# but WRITE_DATES_AS_TIMESTAMPS=on has better performance.
WRITE_DATES_AS_TIMESTAMPS = off
WRITE_DURATIONS_AS_TIMESTAMPS = off
FAIL_ON_EMPTY_BEANS = off
}

# Configuration of the ObjectMapper deserialization features.
# See com.fasterxml.jackson.databind.DeserializationFeature
# Enum values corresponding to the DeserializationFeature and their boolean value.
deserialization-features {
FAIL_ON_UNKNOWN_PROPERTIES = off
}

# Configuration of the ObjectMapper mapper features.
# See com.fasterxml.jackson.databind.MapperFeature
# Enum values corresponding to the MapperFeature and their
# boolean values, for example:
#
# mapper-features {
# SORT_PROPERTIES_ALPHABETICALLY = on
# }
mapper-features {}

# Configuration of the ObjectMapper JsonParser features.
# See com.fasterxml.jackson.core.JsonParser.Feature
# Enum values corresponding to the JsonParser.Feature and their
# boolean value, for example:
#
# json-parser-features {
# ALLOW_SINGLE_QUOTES = on
# }
json-parser-features {}

# Configuration of the ObjectMapper JsonParser features.
# See com.fasterxml.jackson.core.JsonGenerator.Feature
# Enum values corresponding to the JsonGenerator.Feature and
# their boolean value, for example:
#
# json-generator-features {
# WRITE_NUMBERS_AS_STRINGS = on
# }
json-generator-features {}

# Configuration of the JsonFactory StreamReadFeature.
# See com.fasterxml.jackson.core.StreamReadFeature
# Enum values corresponding to the StreamReadFeatures and
# their boolean value, for example:
#
# stream-read-features {
# STRICT_DUPLICATE_DETECTION = on
# }
stream-read-features {}

# Configuration of the JsonFactory StreamWriteFeature.
# See com.fasterxml.jackson.core.StreamWriteFeature
# Enum values corresponding to the StreamWriteFeatures and
# their boolean value, for example:
#
# stream-write-features {
# WRITE_BIGDECIMAL_AS_PLAIN = on
# }
stream-write-features {}

# Configuration of the JsonFactory JsonReadFeature.
# See com.fasterxml.jackson.core.json.JsonReadFeature
# Enum values corresponding to the JsonReadFeatures and
# their boolean value, for example:
#
# json-read-features {
# ALLOW_SINGLE_QUOTES = on
# }
json-read-features {}

# Configuration of the JsonFactory JsonWriteFeature.
# See com.fasterxml.jackson.core.json.JsonWriteFeature
# Enum values corresponding to the JsonWriteFeatures and
# their boolean value, for example:
#
# json-write-features {
# WRITE_NUMBERS_AS_STRINGS = on
# }
json-write-features {}

# Configuration of the JsonFactory Visibility.
# See com.fasterxml.jackson.annotation.PropertyAccessor
# and com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility
# Enum values. For example, to serialize only public fields
# overwrite the default values with:
#
# visibility {
# FIELD = PUBLIC_ONLY
# }
# Default: all fields (including private and protected) are serialized.
visibility {
FIELD = ANY
}

# Deprecated, use `allowed-class-prefix` instead
whitelist-class-prefix = []

# Additional classes that are allowed even if they are not defined in `serialization-bindings`.
# This is useful when a class is not used for serialization any more and therefore removed
# from `serialization-bindings`, but should still be possible to deserialize.
allowed-class-prefix = ${pekko.serialization.jackson.whitelist-class-prefix}


# settings for compression of the payload
compression {
# Compression algorithm.
# - off : no compression
# - gzip : using common java gzip
algorithm = off

# If compression is enabled with the `algorithm` setting the payload is compressed
# when it's larger than this value.
compress-larger-than = 0 KiB
}

# Whether the type should be written to the manifest.
# If this is off, then either deserialization-type must be defined, or there must be exactly
# one serialization binding declared for this serializer, and the type in that binding will be
# used as the deserialization type. This feature will only work if that type either is a
# concrete class, or if it is a supertype that uses Jackson polymorphism (ie, the
# @JsonTypeInfo annotation) to store type information in the JSON itself. The intention behind
# disabling this is to remove extraneous type information (ie, fully qualified class names) when
# serialized objects are persisted in Pekko persistence or replicated using Pekko distributed
# data. Note that Pekko remoting already has manifest compression optimizations that address this,
# so for types that just get sent over remoting, this offers no optimization.
type-in-manifest = on

# The type to use for deserialization.
# This is only used if type-in-manifest is disabled. If set, this type will be used to
# deserialize all messages. This is useful if the binding configuration you want to use when
# disabling type in manifest cannot be expressed as a single type. Examples of when you might
# use this include when changing serializers, so you don't want this serializer used for
# serialization and you haven't declared any bindings for it, but you still want to be able to
# deserialize messages that were serialized with this serializer, as well as situations where
# you only want some sub types of a given Jackson polymorphic type to be serialized using this
# serializer.
deserialization-type = ""

# Specific settings for jackson-json binding can be defined in this section to
# override the settings in 'pekko.serialization.jackson'
jackson-json {}

# Specific settings for jackson-cbor binding can be defined in this section to
# override the settings in 'pekko.serialization.jackson'
jackson-cbor {}

# Issue #28918 for compatibility with data serialized with JacksonCborSerializer in
# Akka 2.6.4 or earlier, which was plain JSON format.
jackson-cbor-264 = ${pekko.serialization.jackson.jackson-cbor}

}
#//#features

#//#compression
# Compression settings for the jackson-json binding
pekko.serialization.jackson215.jackson-json.compression {
# Compression algorithm.
# - off : no compression
# - gzip : using common java gzip
# - lz4 : using lz4-java
algorithm = gzip

# If compression is enabled with the `algorithm` setting the payload is compressed
# when it's larger than this value.
compress-larger-than = 32 KiB
}
#//#compression

pekko.actor {
serializers {
jackson-json = "com.github.pjfanning.pekko.serialization.jackson215.JacksonJsonSerializer"
jackson-cbor = "com.github.pjfanning.pekko.serialization.jackson215.JacksonCborSerializer"

# Issue #28918 for compatibility with data serialized with JacksonCborSerializer in
# Akka 2.6.4 or earlier, which was plain JSON format.
jackson-cbor-264 = "com.github.pjfanning.pekko.serialization.jackson215.JacksonJsonSerializer"
}
serialization-identifiers {
jackson-json = 31
jackson-cbor = 33

# Issue #28918 for compatibility with data serialized with JacksonCborSerializer in
# Akka 2.6.4 or earlier, which was plain JSON format.
jackson-cbor-264 = 32
}
serialization-bindings {
# Define bindings for classes or interfaces use Jackson serializer, e.g.
# "com.example.Jsonable" = jackson-json
# "com.example.MyMessage" = jackson-cbor
#
# For security reasons it is disallowed to bind the Jackson serializers to
# open ended types that might be target to be deserialization gadgets, such as
# java.lang.Object, java.io.Serializable, java.util.Comparable

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* license agreements; and to You under the Apache License, version 2.0:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* This file is part of the Apache Pekko project, which was derived from Akka.
*/

/*
* Copyright (C) 2019-2022 Lightbend Inc. <https://www.lightbend.com>
*/

package com.github.pjfanning.pekko.serialization.jackson

// FIXME maybe move many things to `com.github.pjfanning.pekko.serialization.jackson215.internal` package?

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonTokenId
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer
import org.apache.pekko
import org.apache.pekko.serialization.jackson215.ActorSystemAccess
import pekko.actor.ActorRef
import pekko.annotation.InternalApi

/**
* INTERNAL API: Adds support for serializing and deserializing [[ActorRef]].
*/
@InternalApi private[jackson] trait ActorRefModule extends JacksonModule {
addSerializer(classOf[ActorRef], () => ActorRefSerializer.instance, () => ActorRefDeserializer.instance)
}

/**
* INTERNAL API
*/
@InternalApi private[jackson] object ActorRefSerializer {
val instance: ActorRefSerializer = new ActorRefSerializer
}

/**
* INTERNAL API
*/
@InternalApi private[jackson] class ActorRefSerializer
extends StdScalarSerializer[ActorRef](classOf[ActorRef])
with ActorSystemAccess {
override def serialize(value: ActorRef, jgen: JsonGenerator, provider: SerializerProvider): Unit = {
val serializedActorRef = value.path.toSerializationFormatWithAddress(currentSystem().provider.getDefaultAddress)
jgen.writeString(serializedActorRef)
}
}

/**
* INTERNAL API
*/
@InternalApi private[jackson] object ActorRefDeserializer {
val instance: ActorRefDeserializer = new ActorRefDeserializer
}

/**
* INTERNAL API
*/
@InternalApi private[jackson] class ActorRefDeserializer
extends StdScalarDeserializer[ActorRef](classOf[ActorRef])
with ActorSystemAccess {

def deserialize(jp: JsonParser, ctxt: DeserializationContext): ActorRef = {
if (jp.currentTokenId() == JsonTokenId.ID_STRING) {
val serializedActorRef = jp.getText()
currentSystem().provider.resolveActorRef(serializedActorRef)
} else
ctxt.handleUnexpectedToken(handledType(), jp).asInstanceOf[ActorRef]
}
}

0 comments on commit 7182184

Please sign in to comment.