Questo è un po 'uno sparo al buio nel caso in cui qualcuno esperto nell'implementazione Java di Apache Avro stia leggendo questo.In Java, come posso creare un equivalente di un file contenitore Apro Avro senza dover utilizzare un file come supporto?
Il mio obiettivo di alto livello è avere un modo per trasmettere alcune serie di dati avro sulla rete (diciamo semplicemente HTTP per esempio, ma il particolare protocollo non è così importante per questo scopo). Nel mio contesto ho un HttpServletResponse che ho bisogno di scrivere questi dati in qualche modo.
inizialmente ho tentato di scrivere i dati come quello che ammontano a una versione virtuale di un file contenitore Avro (supporre che "risposta" è di 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();
questo era tutto bene e dandy, ad eccezione che si scopre Avro in realtà non fornire un modo per leggere un file contenitore a parte da un file vero e proprio: l'DataFileReader ha solo due costruttori:
public DataFileReader(File file, DatumReader<D> reader);
e
public DataFileReader(SeekableInput sin, DatumReader<D> reader);
dove SeekableInput è un modulo personalizzato avro-specifico la cui creazione finisce anche per leggere da un file. Detto questo, a meno che non ci sia un modo per forzare in qualche modo un InputStream in un file (http://stackoverflow.com/questions/578305/create-a-java-file-object-or-equivalent-using-a-byte- array-in-memory-without-a suggerisce che non c'è, e ho provato anche a cercare intorno alla documentazione Java, questo approccio non funzionerà se il lettore all'altra estremità di OutputStream riceve quel file avro container (Non sono sicuro del motivo per cui hanno permesso a uno di generare file avro binary container su un OutputStream arbitrario senza fornire un modo per leggerli dal corrispondente InputStream dall'altra parte, ma questo è oltre il punto). Sembra che l'implementazione del lettore di file contenitore richieda la funzionalità "ricercabile" fornita da un file concreto.
Ok, quindi non sembra che quell'approccio farà quello che voglio. Che ne dici di creare una risposta JSON che imita il file del contenitore 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\"}]}"
);
Non sono sicuro se questo è il modo migliore per affrontare questo dato i vincoli di cui sopra, ma sembra che questo potrebbe fare il trucco. Metterò lo schema (di "Schema someSchema" dall'alto, per esempio) come una stringa all'interno del campo "schema", e quindi inserirò la forma serializzata in avro-binario di un record che corrisponde a quello schema (ad esempio "GenericRecord"). someRecord ") all'interno del campo" data ".
In realtà volevo sapere un dettaglio specifico di ciò che è descritto di seguito, ma ho pensato che valesse la pena dare un contesto più ampio, così che se ci fosse un migliore approccio di alto livello potrei prendere (questo approccio funziona ma non mi sembra ottimale) per favore fatemelo sapere.
La mia domanda è, supponendo che io vada con questo approccio basato su JSON, come posso scrivere la rappresentazione avrobinary del mio Record nel campo "data" dello schema di AvroContainer? Ad esempio, sono arrivato fino a qui:
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"));
ho inizialmente provato omettendo la clausola ByteBuffer.wrap, ma poi poi la linea
datumWriter.write(someRecord, e);
ha generato un'eccezione che non ho potuto lanciare un array di byte in ByteBuffer.Abbastanza corretto, sembra che quando la classe Encoder (di cui JsonEncoder è una sottoclasse) viene chiamata per scrivere un oggetto avro Bytes, richiede un ByteBuffer da fornire come argomento. Così, ho provato incapsulando il byte [] con java.nio.ByteBuffer.wrap, ma quando i dati sono stati stampati, è stato stampato come una serie di byte rettilineo, senza essere passato attraverso la rappresentazione esadecimale avro:
"data": {"bytes": ".....some gibberish other than the expected format...}
Questo non sembra giusto. Secondo la documentazione di avro, l'oggetto byte di esempio che danno dice che ho bisogno di inserire un oggetto json, un esempio del quale sembra "\ u00FF", e quello che ho inserito non è chiaramente di quel formato. Quello che voglio sapere ora è il seguente:
- Che cos'è un formato di byte avro? Assomiglia a "\ uDEADBEEFDEADBEEF ..."?
- Come faccio a forzare i miei dati avro binari (come output da BinaryEncoder in un array byte []) in un formato che posso inserire nell'oggetto GenericRecord e farlo stampare correttamente in JSON? Ad esempio, voglio un oggetto DATI per il quale posso chiamare su alcuni GenericRecord "someRecord.put (" data ", DATA);" con i miei dati avro serializzati all'interno?
- Come potrei quindi leggere quei dati in una matrice di byte sull'altra estremità (di consumo), quando viene fornita la rappresentazione JSON del testo e vuole ricreare GenericRecord come rappresentato dal formato JSON di AvroContainer?
- (reiterando la domanda di prima) C'è un modo migliore in cui potrei fare tutto questo?
org.apache.avro.file.DataFileStream? – Chikei
SeekableInput non è solo un modulo personalizzato avro-specifico la cui creazione finisce per essere letto da un file. C'è [SeekableByteArrayInput] (http://avro.apache.org/docs/current/api/java/org/apache/avro/file/SeekableByteArrayInput.html) che legge da una matrice di byte in memoria. –
Ottima domanda - e l'esigenza di aver bisogno dell'accesso casuale è molto strana, dal momento che è impossibile soddisfarla senza possibilmente un enorme buffer. Eppure sembra che non sia necessario fare altrettanto ... Non so perché sia stato ritenuto necessario un accesso casuale. Molti altri formati di dati non aggiungono tali requisiti per l'elaborazione. – StaxMan