package mil.nga.giat.geowave.adapter.vector; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; import org.apache.avro.io.BinaryEncoder; import org.apache.avro.io.EncoderFactory; import org.apache.avro.specific.SpecificDatumWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import mil.nga.giat.geowave.adapter.vector.avro.AttributeValues; import mil.nga.giat.geowave.adapter.vector.avro.AvroSimpleFeature; import mil.nga.giat.geowave.adapter.vector.avro.FeatureDefinition; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.store.data.field.FieldWriter; public class AvroFeatureWriter implements FieldWriter<SimpleFeature, Object> { private static final Logger LOGGER = LoggerFactory.getLogger(AvroFeatureWriter.class); private final EncoderFactory ef = EncoderFactory.get(); private final SpecificDatumWriter<AvroSimpleFeature> datumWriter = new SpecificDatumWriter<AvroSimpleFeature>(); @Override public byte[] getVisibility( final SimpleFeature rowValue, final ByteArrayId fieldId, final Object fieldValue ) { return new byte[] {}; } @Override public byte[] writeField( final Object fieldValue ) { byte[] serializedAttributes = null; try { serializedAttributes = serializeAvroSimpleFeature( (SimpleFeature) fieldValue, null, null, ""); } catch (final IOException e) { e.printStackTrace(); LOGGER.error( "Error, failed to serialize SimpleFeature with id '" + ((SimpleFeature) fieldValue).getID() + "'", e); } // there is no need to preface the payload with the class name and a // length of the class name, the implementation is assumed to be known // on read so we can save space on persistence return serializedAttributes; } // Serialization logic /*** * @param avroObject * Avro object to serialized * @return byte array of serialized avro data * @throws IOException */ private byte[] serializeAvroSimpleFeature( final AvroSimpleFeature avroObject ) throws IOException { final ByteArrayOutputStream os = new ByteArrayOutputStream(); final BinaryEncoder encoder = ef.binaryEncoder( os, null); datumWriter.setSchema(avroObject.getSchema()); datumWriter.write( avroObject, encoder); encoder.flush(); return os.toByteArray(); } /*** * Converts a SimpleFeature to an avroSimpleFeature and then serializes it. * * @param sf * Simple Feature to be serialized * @param avroObjectToReuse * null or AvroSimpleFeature instance to be re-used. If null a * new instance will be allocated * @param defaultClassifications * null map of attribute names vs. classification. if null all * values will be set to the default classification * @param defaultClassification * null or default classification. if null and * defaultClassifications are not provided an exception will be * thrown * @return * @throws IOException */ public byte[] serializeAvroSimpleFeature( final SimpleFeature sf, AvroSimpleFeature avroObjectToReuse, final Map<String, String> defaultClassifications, final String defaultClassification ) throws IOException { if (sf == null) { throw new IOException( "Feature cannot be null"); } if ((defaultClassification == null) && (defaultClassifications == null)) { throw new IOException( "if per attribute classifications aren't provided then a default classification must be provided"); } final SimpleFeatureType sft = sf.getType(); if (avroObjectToReuse == null) { avroObjectToReuse = new AvroSimpleFeature(); } final FeatureDefinition fd = AvroFeatureUtils.buildFeatureDefinition( avroObjectToReuse.getFeatureType(), sft, defaultClassifications, defaultClassification); avroObjectToReuse.setFeatureType(fd); final AttributeValues av = AvroFeatureUtils.buildAttributeValue( sf, sft); avroObjectToReuse.setValue(av); return serializeAvroSimpleFeature(avroObjectToReuse); } }