/**
* Copyright 2016 Hortonworks.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 com.hortonworks.registries.schemaregistry.serdes.avro.kafka;
import com.hortonworks.registries.schemaregistry.serdes.avro.AvroSnapshotDeserializer;
import org.apache.kafka.common.serialization.Deserializer;
import java.io.ByteArrayInputStream;
import java.util.Collections;
import java.util.Map;
/**
* This class can be configured as key or value deserializer for kafka consumer. This can be used like below with kafka consumers.
*
* <pre>{@code
// consumer configs
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.GROUP_ID_CONFIG, consumerGroup);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");
// key deserializer
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// schema registry config
props.putAll(Collections.singletonMap(SchemaRegistryClient.Configuration.SCHEMA_REGISTRY_URL.name(), registryUrl));
// configure reader versions for topics to be consumed.
Map<String, Integer> readerVersions = new HashMap<>();
readerVersions.put("clicks", 2);
readerVersions.put("users", 1);
props.put(KafkaAvroDeserializer.READER_VERSIONS, readerVersions);
// value deserializer
// current props are passed to KafkaAvroDeserializer instance by invoking #configure(Map, boolean) method.
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class.getName());
KafkaConsumer<String, Object> consumer = new KafkaConsumer<>(props);
* }</pre>
*
*/
public class KafkaAvroDeserializer implements Deserializer<Object> {
/**
* This property represents the version of a reader schema to be used in deserialization for each topic in
* {@link #deserialize(String, byte[])}. This property should be passed with configs argument to {@link #configure(Map, boolean)}.
* <p>
* For example, to set reader version of different topics which should be handled by this Deserializer.
* <pre>{@code
* Map<String, Object> configs = ...
* Map<String, Integer> readerVersions = new HashMap<>();
* readerVersions.put("clicks", 2);
* readerVersions.put("users", 1);
* configs.put(READER_VERSIONS, readerVersions);
* }</pre>
*/
public static final String READER_VERSIONS = "schemaregistry.reader.schema.versions";
private final AvroSnapshotDeserializer avroSnapshotDeserializer;
private Map<String, Integer> readerVersions;
public KafkaAvroDeserializer() {
avroSnapshotDeserializer = new AvroSnapshotDeserializer();
}
@Override
public void configure(Map<String, ?> configs, boolean isKey) {
avroSnapshotDeserializer.init(configs);
Map<String, Integer> versions = (Map<String, Integer>) ((Map<String, Object>) configs).get(READER_VERSIONS);
readerVersions = versions != null ? versions : Collections.emptyMap();
}
@Override
public Object deserialize(String topic, byte[] data) {
return avroSnapshotDeserializer.deserialize(new ByteArrayInputStream(data), readerVersions.get(topic));
}
@Override
public void close() {
try {
avroSnapshotDeserializer.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}