All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.axonframework.kafka.eventhandling.DefaultKafkaMessageConverter Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2018. Axon Framework
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.axonframework.kafka.eventhandling;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.Headers;
import org.apache.kafka.common.header.internals.RecordHeader;
import org.axonframework.common.Assert;
import org.axonframework.eventhandling.EventMessage;
import org.axonframework.eventhandling.GenericEventMessage;
import org.axonframework.eventhandling.async.SequencingPolicy;
import org.axonframework.eventhandling.async.SequentialPerAggregatePolicy;
import org.axonframework.eventsourcing.GenericDomainEventMessage;
import org.axonframework.messaging.MetaData;
import org.axonframework.serialization.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.BiFunction;

import static org.axonframework.kafka.eventhandling.HeaderUtils.*;
import static org.axonframework.messaging.Headers.*;
import static org.axonframework.serialization.MessageSerializer.serializePayload;

/**
 * Converts: {@link EventMessage} to {@link ProducerRecord kafkaMessage} and {@link ConsumerRecord message read from
 * Kafka} back
 * to {@link EventMessage} (if possible).
 * 

* During conversion it passes all meta-data entries with {@code 'axon-metadata-'} prefix to {@link Headers}. Other * message-specific attributes are added as metadata. The payload is serialized using the * configured {@link Serializer} and passed as the message body. *

* This implementation will suffice in most cases. * * @author Nakul Mishra * @since 3.0 */ public class DefaultKafkaMessageConverter implements KafkaMessageConverter { private static final Logger logger = LoggerFactory.getLogger(KafkaMessageConverter.class); private final Serializer serializer; private final SequencingPolicy> sequencingPolicy; private final BiFunction headerValueMapper; /** * Initializes the KafkaMessageConverter with the given {@code serializer}. * * @param serializer The serializer to serialize the Event Message's payload with. */ public DefaultKafkaMessageConverter(Serializer serializer) { this(serializer, SequentialPerAggregatePolicy.instance(), byteMapper()); } /** * Initializes the KafkaMessageConverter with the given {@code serializer}, {@code sequencingPolicy} and * {@code objectMapper}. * * @param serializer The serializer to serialize the Event Message's payload and Meta Data with * @param sequencingPolicy The policy to generate the key of the {@link ProducerRecord}. * @param headerValueMapper The Function for mapping values to Kafka headers. */ public DefaultKafkaMessageConverter(Serializer serializer, SequencingPolicy> sequencingPolicy, BiFunction headerValueMapper) { Assert.notNull(serializer, () -> "Serializer may not be null"); Assert.notNull(sequencingPolicy, () -> "SequencingPolicy may not be null"); Assert.notNull(headerValueMapper, () -> "HeaderValueMapper may not be null"); this.serializer = serializer; this.sequencingPolicy = sequencingPolicy; this.headerValueMapper = headerValueMapper; } @Override public ProducerRecord createKafkaMessage(EventMessage eventMessage, String topic) { SerializedObject serializedObject = serializePayload(eventMessage, serializer, byte[].class); byte[] payload = serializedObject.getData(); return new ProducerRecord<>(topic, null, null, key(eventMessage), payload, toHeaders(eventMessage, serializedObject, headerValueMapper)); } private String key(EventMessage eventMessage) { Object identifier = sequencingPolicy.getSequenceIdentifierFor(eventMessage); return identifier != null ? identifier.toString() : null; } @Override public Optional> readKafkaMessage(ConsumerRecord consumerRecord) { try { Headers headers = consumerRecord.headers(); if (isAxonMessage(headers)) { byte[] messageBody = consumerRecord.value(); SerializedMessage message = extractSerializedMessage(headers, messageBody); return buildMessage(headers, message); } } catch (Exception e) { logger.trace("Error converting {} to axon", consumerRecord, e); } return Optional.empty(); } private Optional> buildMessage(Headers headers, SerializedMessage message) { long timestamp = valueAsLong(headers, MESSAGE_TIMESTAMP); return headers.lastHeader(AGGREGATE_ID) != null ? domainEvent(headers, message, timestamp) : event(message, timestamp); } private SerializedMessage extractSerializedMessage(Headers headers, byte[] messageBody) { SimpleSerializedObject serializedObject = new SimpleSerializedObject<>( messageBody, byte[].class, valueAsString(headers, MESSAGE_TYPE), valueAsString(headers, MESSAGE_REVISION, null) ); return new SerializedMessage<>( valueAsString(headers, MESSAGE_ID), new LazyDeserializingObject<>(serializedObject, serializer), new LazyDeserializingObject<>(MetaData.from(extractAxonMetadata(headers)))); } private boolean isAxonMessage(Headers headers) { return keys(headers).containsAll(Arrays.asList(MESSAGE_ID, MESSAGE_TYPE)); } private Optional> domainEvent(Headers headers, SerializedMessage message, long timestamp) { return Optional.of(new GenericDomainEventMessage<>(valueAsString(headers, AGGREGATE_TYPE), valueAsString(headers, AGGREGATE_ID), valueAsLong(headers, AGGREGATE_SEQ), message, () -> Instant.ofEpochMilli(timestamp))); } private Optional> event(SerializedMessage message, long timestamp) { return Optional.of(new GenericEventMessage<>(message, () -> Instant.ofEpochMilli(timestamp))); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy