Serialization options for Java services

You do not need to handle serialization for messages. Akka Serverless functions serve gRPC interfaces, and the input and output messages are protobuf messages that get serialized to the protobuf format.

The gRPC services are also exposed as HTTP endpoints with JSON messages. See Transcoding HTTP.

For messages consumed from or published to topics it can be needed to use another format than protobuf. Akka Serverless provides a utility to serialize and deserialize JSON messages.

Primitive types

Akka Serverless supports serializing the following primitive types:

Protobuf type Java type

string

java.lang.String

bytes

com.google.protobuf.ByteString

int32

java.lang.Integer

int64

java.lang.Long

float

java.lang.Float

double

java.lang.Double

bool

java.lang.Boolean

JSON

Akka Serverless provides the JsonSupport new tab utility to serialize and deserialize JSON messages.

A proto definition of an Action that consumes JSON messages and produces JSON messages can look like this:

src/main/proto/com/example/json/json_api.proto
import "akkaserverless/annotations.proto";
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";

option java_outer_classname = "MyServiceApi";

message KeyValue {
  string key = 1;
  int32 value = 2;
}

service MyService {
  option (akkaserverless.service) = {
    type : SERVICE_TYPE_ACTION
  };

  rpc Consume(google.protobuf.Any) returns (google.protobuf.Empty) { (1)
    option (akkaserverless.method).eventing.in = {
      topic:  "notifications"
    };
  }

  rpc Produce(KeyValue) returns (google.protobuf.Any) { (2)
    option (akkaserverless.method).eventing.out = {
      topic:  "notifications"
    };
  }

}
1 When consuming JSON messages from a topic the input type must be google.protobuf.Any.
2 When producing a JSON message to a topic the return type must be google.protobuf.Any.
The type_url in the google.protobuf.Any must start with json.akkaserverless.com/. The suffix of the type_url is a type hint of the concrete message type that is encoded.

The corresponding implementation class:

src/main/java/com/example/json/MyServiceAction.java
import com.akkaserverless.javasdk.JsonSupport;
import com.akkaserverless.javasdk.action.ActionCreationContext;
import com.google.protobuf.Any;
import com.google.protobuf.Empty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyServiceAction extends AbstractMyServiceAction {

  private static final Logger LOG = LoggerFactory.getLogger(MyServiceAction.class);

  public MyServiceAction(ActionCreationContext creationContext) {}

  @Override
  public Effect<Empty> consume(Any any) {
    JsonKeyValueMessage jsonMessage =
        JsonSupport.decodeJson(JsonKeyValueMessage.class, any); (1)
    LOG.info("Consumed " + jsonMessage);
    return effects().reply(Empty.getDefaultInstance());
  }

  @Override
  public Effect<Any> produce(MyServiceApi.KeyValue keyValue) {
    JsonKeyValueMessage jsonMessage =
        new JsonKeyValueMessage(keyValue.getKey(), keyValue.getValue()); (2)
    Any jsonAny = JsonSupport.encodeJson(jsonMessage); (3)
    return effects().reply(jsonAny);
  }
}
1 Decode the JSON message to a Java class JsonKeyValueMessage.
2 Convert the Protobuf message KeyValue to a Java class JsonKeyValueMessage.
3 Encode the Java class JsonKeyValueMessage to JSON.

Akka Serverless uses Jackson to serialize JSON.

Receiving CloudEvents

Akka Serverless uses the CloudEvents standard when receiving from and publishing to topics. The CloudEvents specification standardizes message metadata so that systems can integrate more easily.

Describing the structure of the message payload is the CloudEvents feature most important to Akka Serverless.

An example of that is the capability to send serialized Protobuf messages and have Akka Serverless deserialize them accordingly.

To allow proper reading of Protobuf messages from topics, the messages need to specify the message attributes:

  • Content-Type = application/protobuf

  • ce-specversion = 1.0

  • ce-type = fully qualified protobuf message name (eg. shopping.cart.api.TopicOperation)

(The ce- prefixed attributes are part of the CloudEvents specification.)

The Protobuf rpc declaration uses the expected Protobuf message type and specifies the topic to subscribe to. You’ll normally want to share the exact Protobuf message declaration with the sending system.

syntax = "proto3";

import "google/protobuf/empty.proto";
import "google/protobuf/any.proto";
import "akkaserverless/annotations.proto";

package shopping.cart.api;

message TopicOperation {
    string operation = 1;
}

service ShoppingCartTopicService {

    rpc ProtobufFromTopic(TopicOperation) returns (google.protobuf.Empty) {
        option (akkaserverless.method).eventing.in = {
            topic:  "shopping-cart-protobuf-cloudevents"
        };
    }
}