/* * 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.common.avro; import org.apache.avro.Schema; import org.apache.avro.generic.GenericContainer; import org.apache.avro.generic.GenericDatumReader; import org.apache.avro.generic.GenericDatumWriter; import org.apache.avro.io.BinaryDecoder; import org.apache.avro.io.BinaryEncoder; import org.apache.avro.io.DatumReader; import org.apache.avro.io.DatumWriter; import org.apache.avro.io.DecoderFactory; import org.apache.avro.io.EncoderFactory; import org.apache.avro.io.JsonDecoder; import org.apache.avro.io.JsonEncoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; /** * The Class AvroByteArrayConverter is used to convert * {#link org.apache.avro.generic.GenericContainer specific avro records} to/from bytes. * NOT Thread safe. * * @param <T> the generic type that extends GenericContainer */ public class GenericAvroConverter<T extends GenericContainer> { private static final Logger LOG = LoggerFactory .getLogger(GenericAvroConverter.class); private static final Charset ENCODING_CHARSET = Charset.forName("UTF-8"); private static final Charset DECODING_CHARSET = Charset.forName("ISO-8859-1"); private Schema schema; private DatumReader<T> datumReader; private DatumWriter<T> datumWriter; private BinaryDecoder binaryDecoder; private BinaryEncoder binaryEncoder; private JsonDecoder jsonDecoder; private JsonEncoder jsonEncoder; /** * Instantiates a new generic Avro converter. * * @param schemaSrc the schemaSrc */ public GenericAvroConverter(String schemaSrc) { this(new Schema.Parser().parse(schemaSrc)); } /** * Instantiates a new generic avro converter. * * @param schema the schema */ public GenericAvroConverter(Schema schema) { this.schema = schema; datumReader = new GenericDatumReader<T>(this.schema); datumWriter = new GenericDatumWriter<T>(this.schema); } /** * Convert binary data using schema to Json. * * @param rawData the encoded data * @param dataSchema the encoded data schema * @return the string */ public static String toJson(byte[] rawData, String dataSchema) { Schema schema = new Schema.Parser().parse(dataSchema); GenericAvroConverter<GenericContainer> converter = new GenericAvroConverter<GenericContainer>(schema); String json; try { GenericContainer record = converter.decodeBinary(rawData); json = converter.encodeToJson(record); } catch (IOException ex) { LOG.warn("Can't parse json data", ex); throw new RuntimeException(ex); //NOSONAR } return json; } /** * Convert json string using schema to binary data. * * @param json the json string * @param dataSchema the encoded data schema * @return the byte[] */ public static byte[] toRawData(String json, String dataSchema) { Schema schema = new Schema.Parser().parse(dataSchema); GenericAvroConverter<GenericContainer> converter = new GenericAvroConverter<GenericContainer>(schema); byte[] rawData; try { GenericContainer record = converter.decodeJson(json); rawData = converter.encode(record); } catch (IOException ex) { LOG.warn("Can't parse json data", ex); throw new RuntimeException(ex); //NOSONAR } return rawData; } /** * Decode binary data. * * @param data the data * @return the decoded object * @throws IOException Signals that an I/O exception has occurred. */ public T decodeBinary(byte[] data) throws IOException { return decodeBinary(data, null); } /** * Decode binary data. * * @param data the data * @param reuse the reuse * @return the decoded object * @throws IOException Signals that an I/O exception has occurred. */ public T decodeBinary(byte[] data, T reuse) throws IOException { binaryDecoder = DecoderFactory.get().binaryDecoder(data, binaryDecoder); return datumReader.read(reuse, binaryDecoder); } /** * Decode json data. * * @param data the data * @return the decoded object * @throws IOException Signals that an I/O exception has occurred. */ public T decodeJson(String data) throws IOException { return decodeJson(data, null); } /** * Decode json data. * * @param data the data * @return the decoded object * @throws IOException Signals that an I/O exception has occurred. */ public T decodeJson(byte[] data) throws IOException { return decodeJson(new String(data, DECODING_CHARSET), null); } /** * Decode json data. * * @param data the data * @param reuse the reuse * @return the decoded object * @throws IOException Signals that an I/O exception has occurred. */ public T decodeJson(String data, T reuse) throws IOException { jsonDecoder = DecoderFactory.get().jsonDecoder(this.schema, data, true); return datumReader.read(null, jsonDecoder); } /** * Encode record to Json String. * * @param record the object to encode * @return the string * @throws IOException Signals that an I/O exception has occurred. */ public String encodeToJson(T record) throws IOException { return new String(encodeToJsonBytes(record), ENCODING_CHARSET); } /** * Encode record to Json and then convert to byte array. * * @param record the object to encode * @return the byte[] * @throws IOException Signals that an I/O exception has occurred. */ public byte[] encodeToJsonBytes(T record) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); jsonEncoder = EncoderFactory.get().jsonEncoder(this.schema, baos, true); datumWriter.write(record, jsonEncoder); jsonEncoder.flush(); baos.flush(); return baos.toByteArray(); } /** * Encode record to byte array. * * @param record the object to encode * @return the byte[] * @throws IOException Signals that an I/O exception has occurred. */ public byte[] encode(T record) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); binaryEncoder = EncoderFactory.get().binaryEncoder(baos, binaryEncoder); datumWriter.write(record, binaryEncoder); binaryEncoder.flush(); baos.flush(); return baos.toByteArray(); } }