REST Streaming Integration
TokenReader, TokenWriter, RecordReader, and RecordWriter are first-class
@RestOp-method parameter types. Declare one in your method signature and the framework picks
the matching parser/serializer based on the request Content-Type / response Accept and
hands you the cursor.
See the per-format capability matrix for which formats can satisfy a
RecordReader/TokenReader/RecordWriter/TokenWriterparameter.
Server-side reader parameter
@Rest(parsers = JsonParser.class)
public class MyResource {
@RestPost
public String process(RecordReader r) throws Exception {
Bean b = r.read(Bean.class);
return b.name;
}
}
Concrete cursor types work too — declaring JsonlTokenReader means only JsonlParser is
eligible:
@Rest(parsers = JsonlParser.class, defaultAccept = "application/jsonl")
public class JsonlIngest {
@RestPost
public int countRecords(JsonlTokenReader r) throws Exception {
int n = 0;
while (r.canRead()) {
r.read(Bean.class);
n++;
}
return n;
}
}
Server-side writer parameter
Declaring a writer parameter flips the response into "user owns the body" mode — the framework will not later serialize a return value over the same stream:
@RestGet
public void exportLargeArray(RecordWriter w) throws IOException {
for (Bean b : sourceOfBeans()) {
w.write(b);
}
// close() is the framework's responsibility on return.
}
Two-layer resolution
Cursor parameter resolution uses two layers:
- Declared type filters eligible parsers/serializers. A
RecordReaderparameter accepts any parser that is aRecordReadable; aTokenReaderparameter accepts only parsers that are aTokenReadable; a concreteJsonlTokenReaderparameter accepts only parsers whosereadTokens(...)returns aJsonlTokenReader. Capability is detected withinstanceofagainst the role interface — there is no capability enum. - HTTP
Content-Type/Acceptnegotiates among the eligible set. Standard Juneau media-type matching applies.
Mismatch returns HTTP 415 Unsupported Media Type (or 406 Not Acceptable on the writer side):
- The matched parser/serializer does not implement the required role interface
(
RecordReadable/TokenReadable/ArrayRecordReadableand their*Writablemirrors). - The matched cursor type isn't assignable to the declared parameter.
- No parser/serializer matched the requested media type at all.
Multi-parser example
@Rest(parsers = {JsonParser.class, CborParser.class}, defaultAccept = "application/json")
public class MultiFormat {
@RestPost
public String process(TokenReader r) throws Exception {
Bean b = r.read(Bean.class);
return r.getClass().getSimpleName() + ":" + b.name;
// → "JsonTokenReader:..." for application/json
// → "CborTokenReader:..." for application/cbor
}
}
Argument resolvers
The four arg resolvers are registered in DefaultConfig.restOpArgs:
Reader-side input is getReader() for ReaderParser, getInputStream() for binary parsers.
Writer-side output is getNegotiatedWriter() / getNegotiatedOutputStream() similarly.