/* * Copyright 2014-2016 CyberVision, Inc. * * 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.kaaproject.kaa.server.common.log.shared; import org.apache.avro.io.BinaryDecoder; import org.apache.avro.io.BinaryEncoder; import org.apache.avro.io.DecoderFactory; import org.apache.avro.io.EncoderFactory; import org.apache.avro.specific.SpecificDatumReader; import org.apache.avro.specific.SpecificDatumWriter; import org.apache.avro.specific.SpecificRecordBase; import java.io.ByteArrayOutputStream; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.HashMap; import java.util.Map; /** * Utility class that allow usage of Avro records in Apache Spark and other frameworks that require * support of Java serialization. * * @param <T> avro generated record * @author Andrew Shvayka */ public class AvroSerializationWrapper<T extends SpecificRecordBase> implements Externalizable { private static final ThreadLocal<Map<String, AvroReader<? extends SpecificRecordBase>>> recordReaderMap = //NOSONAR new ThreadLocal<Map<String, AvroReader<? extends SpecificRecordBase>>>() { protected Map<String, AvroReader<? extends SpecificRecordBase>> initialValue() { return new HashMap<String, AvroReader<? extends SpecificRecordBase>>(); } }; private static final ThreadLocal<Map<String, AvroWriter<? extends SpecificRecordBase>>> recordWriterMap = //NOSONAR new ThreadLocal<Map<String, AvroWriter<? extends SpecificRecordBase>>>() { protected Map<String, AvroWriter<? extends SpecificRecordBase>> initialValue() { return new HashMap<String, AvroWriter<? extends SpecificRecordBase>>(); } }; private final Class<T> clazz; private final String className; private T avroObject; public AvroSerializationWrapper(Class<T> clazz) { this(clazz, null); } /** * Create new instance of <code>AvroSerializationWrapper</code>. * * @param clazz the appender * @param avroObject the avro object */ public AvroSerializationWrapper(Class<T> clazz, T avroObject) { this.clazz = clazz; this.className = clazz.getName(); this.avroObject = avroObject; } @Override public void writeExternal(ObjectOutput out) throws IOException { @SuppressWarnings("unchecked") AvroWriter<T> writer = (AvroWriter<T>) recordWriterMap.get().get(className); if (writer == null) { ByteArrayOutputStream os = new ByteArrayOutputStream(); writer = new AvroWriter<T>( new SpecificDatumWriter<T>(clazz), EncoderFactory.get().binaryEncoder(os, null) ); recordWriterMap.get().put(className, writer); } ByteArrayOutputStream os = new ByteArrayOutputStream(); BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(os, writer.getEncoder()); writer.getWriter().write(avroObject, encoder); out.write(os.size()); out.write(os.toByteArray()); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int size = in.readInt(); byte[] data = new byte[size]; in.read(data); @SuppressWarnings("unchecked") AvroReader<T> reader = (AvroReader<T>) recordReaderMap.get().get(className); if (reader == null) { reader = new AvroReader<T>( new SpecificDatumReader<T>(clazz), DecoderFactory.get().binaryDecoder(data, null) ); recordReaderMap.get().put(className, reader); } BinaryDecoder recordDataDecoder = DecoderFactory.get().binaryDecoder(data, reader.getDecoder()); avroObject = reader.getReader().read(null, recordDataDecoder); } public T get() { return avroObject; } public void set(T avroObject) { this.avroObject = avroObject; } private static class AvroReader<T extends SpecificRecordBase> { private final SpecificDatumReader<T> reader; private final BinaryDecoder decoder; public AvroReader(SpecificDatumReader<T> reader, BinaryDecoder decoder) { super(); this.reader = reader; this.decoder = decoder; } public SpecificDatumReader<T> getReader() { return reader; } public BinaryDecoder getDecoder() { return decoder; } } private static class AvroWriter<T extends SpecificRecordBase> { private final SpecificDatumWriter<T> writer; private final BinaryEncoder encoder; public AvroWriter(SpecificDatumWriter<T> writer, BinaryEncoder encoder) { super(); this.writer = writer; this.encoder = encoder; } public SpecificDatumWriter<T> getWriter() { return writer; } public BinaryEncoder getEncoder() { return encoder; } } }