Esto es algo así como un tiro en la oscuridad en caso de que alguien conocedor de la implementación Java de Apache Avro esté leyendo esto.En Java, ¿cómo puedo crear un equivalente de un archivo contenedor Apache Avro sin tener que utilizar un archivo como medio?
Mi objetivo de alto nivel es tener alguna forma de transmitir algunas series de datos avro a través de la red (digamos HTTP por ejemplo, pero el protocolo en particular no es tan importante para este propósito). En mi contexto, tengo una HttpServletResponse. Necesito escribir estos datos de alguna manera.
inicialmente intentó escribir los datos como lo que equivalía a una versión virtual de un archivo contenedor Avro (suponer que la "respuesta" es de tipo HttpServletResponse):
response.setContentType("application/octet-stream");
response.setHeader("Content-transfer-encoding", "binary");
ServletOutputStream outStream = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(outStream);
Schema someSchema = Schema.parse(".....some valid avro schema....");
GenericRecord someRecord = new GenericData.Record(someSchema);
someRecord.put("somefield", someData);
...
GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(someSchema);
DataFileWriter<GenericRecord> fileWriter = new DataFileWriter<GenericRecord>(datumWriter);
fileWriter.create(someSchema, bos);
fileWriter.append(someRecord);
fileWriter.close();
bos.flush();
Esto era todo lo fino y elegante, con la excepción que resulta que en realidad no se Avro proporcionar una manera de leer un archivo contenedor aparte de un archivo real: el DataFileReader solamente tiene dos constructores:
public DataFileReader(File file, DatumReader<D> reader);
y
public DataFileReader(SeekableInput sin, DatumReader<D> reader);
donde SeekableInput es una forma personalizada específica de avro cuya creación también termina leyendo de un archivo. Ahora bien, dado que, a menos que haya alguna forma de forzar de algún modo un InputStream en un archivo (http://stackoverflow.com/questions/578305/create-a-java-file-object-or-equivalent-using-a-byte- array-in-memory-without-a sugiere que no existe, y he intentado buscar en la documentación de Java también), este enfoque no funcionará si el lector en el otro extremo de OutputStream recibe ese archivo contenedor avro (No estoy seguro de por qué permitieron que uno generara archivos contenedores avro binarios en un OutputStream arbitrario sin proporcionar una manera de leerlos desde el InputStream correspondiente en el otro extremo, pero eso está al lado del punto). Parece que la implementación del lector de archivos contenedores requiere la funcionalidad "buscable" que proporciona un archivo concreto.
De acuerdo, así que no parece que ese enfoque hará lo que quiero. ¿Qué tal crear una respuesta JSON que imite el archivo contenedor avro?
public static Schema WRAPPER_SCHEMA = Schema.parse(
"{\"type\": \"record\", " +
"\"name\": \"AvroContainer\", " +
"\"doc\": \"a JSON avro container file\", " +
"\"namespace\": \"org.bar.foo\", " +
"\"fields\": [" +
"{\"name\": \"schema\", \"type\": \"string\", \"doc\": \"schema representing the included data\"}, " +
"{\"name\": \"data\", \"type\": \"bytes\", \"doc\": \"packet of data represented by the schema\"}]}"
);
No estoy seguro si esto es la mejor manera de abordar esto, dadas las limitaciones anteriores, pero parece que esto podría hacer el truco. Pondré el esquema (de "Schema someSchema" desde arriba, por ejemplo) como una Cadena dentro del campo "esquema", y luego coloco en la forma avro-binaria-serializada de un registro que corresponde a ese esquema (es decir, "GenericRecord" someRecord ") dentro del campo" datos ".
En realidad, quería saber acerca de un detalle específico de lo que se describe a continuación, pero pensé que valdría la pena dar un contexto más grande también, por lo que si hay un mejor enfoque de alto nivel que podría estar tomando (Este enfoque funciona, pero simplemente no se siente óptimo) por favor házmelo saber.
Mi pregunta es, suponiendo que vaya con este enfoque basado en JSON, ¿cómo escribo la representación binaria avro de mi registro en el campo "datos" del esquema de AvroContainer? Por ejemplo, llegué hasta aquí:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(someSchema);
Encoder e = new BinaryEncoder(baos);
datumWriter.write(resultsRecord, e);
e.flush();
GenericRecord someRecord = new GenericData.Record(someSchema);
someRecord.put("schema", someSchema.toString());
someRecord.put("data", ByteBuffer.wrap(baos.toByteArray()));
datumWriter = new GenericDatumWriter<GenericRecord>(WRAPPER_SCHEMA);
JsonGenerator jsonGenerator = new JsonFactory().createJsonGenerator(baos, JsonEncoding.UTF8);
e = new JsonEncoder(WRAPPER_SCHEMA, jsonGenerator);
datumWriter.write(someRecord, e);
e.flush();
PrintWriter printWriter = response.getWriter(); // recall that response is the HttpServletResponse
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
printWriter.print(baos.toString("UTF-8"));
inicialmente traté omitiendo la cláusula ByteBuffer.wrap, pero entonces la línea
datumWriter.write(someRecord, e);
inició una excepción que no podía emitir una matriz de bytes en ByteBuffer.Bastante, parece que cuando se llama a la clase Encoder (de la cual JsonEncoder es una subclase) para escribir un objeto avro Bytes, se requiere un ByteBuffer como argumento. Por lo tanto, he intentado encapsular el byte [] con java.nio.ByteBuffer.wrap, pero cuando los datos se imprimen, se imprimió como una serie lineal de bytes, sin ser pasado a través de la representación hexadecimal Avro:
"data": {"bytes": ".....some gibberish other than the expected format...}
Eso no parece correcto. De acuerdo con la documentación de avro, el objeto de bytes de ejemplo que dan dice que necesito poner un objeto json, cuyo ejemplo parece "\ u00FF", y lo que he puesto allí claramente no es de ese formato. Lo que ahora quiero saber es lo siguiente:
- ¿Qué es un ejemplo de formato de bytes avro? ¿Se ve algo así como "\ uDEADBEEFDEADBEEF ..."?
- ¿Cómo forzar mis datos avro binarios (como los genera el BinaryEncoder en una matriz byte []) en un formato que puedo incluir en el objeto GenericRecord y hacer que se imprima correctamente en JSON? Por ejemplo, quiero un Object DATA para el que pueda llamar en algunos registros genéricos "someRecord.put (" data ", DATA);" con mi avro datos serializados adentro?
- ¿Cómo volvería a leer esos datos en una matriz de bytes en el otro extremo (consumidor), cuando se le dé la representación JSON de texto y desee recrear el registro genérico representado por el formato Jro de AvroContainer?
- (reiterando la pregunta anterior) ¿Hay alguna manera mejor de que yo pueda estar haciendo todo esto?
org.apache.avro.file.DataFileStream? – Chikei
SeekableInput no es solo una forma personalizada específica de avro cuya creación termina leyendo de un archivo. Hay [SeekableByteArrayInput] (http://avro.apache.org/docs/current/api/java/org/apache/avro/file/SeekableByteArrayInput.html) que se lee desde una matriz de bytes en la memoria. –
Muy buena pregunta, y el requisito de necesitar acceso aleatorio es muy extraño, ya que es imposible satisfacerlo sin una gran cantidad de memoria intermedia. Y, sin embargo, parece innecesario hacerlo también ... No sé por qué se sintió que se necesitaba acceso aleatorio. Muchos otros formatos de datos no agregan tales requisitos para el procesamiento. – StaxMan