One process is creating a log that is a sequence of json documents. Another process wants to read this log as it is being generated (similar to tail -f) and parse it using Jackson JsonParser (and ObjectReader). How do you ask JsonParser or MappingIterator not to close itself when Reader.read returns -1 temporarily?
Example that attempts to create JsonReader.readValues over a Reader that EOFs often:
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
public class ParitalParserTest {
/** Reader that temporarily returns -1 (EOF) to simulate growing file */
private static class StringReaderWithEofs extends Reader {
public final String string;
private int pos;
private int limit;
public StringReaderWithEofs(String string) {
this.string = string;
}
public void limit(int limit) {
this.limit = limit;
}
@Override public int read(char[] cbuf, int off, int len) throws IOException {
int newPos = Math.min(pos + len, limit);
string.getChars(pos, newPos, cbuf, off);
int r = newPos - this.pos;
if (r == 0) r = -1;
this.pos = newPos;
return r;
}
@Override public void close() throws IOException {}
}
/** POJO log event */
public static class LogEvent {
private final String message;
@JsonCreator
public LogEvent(@JsonProperty("message") String message) {
this.message = message;
}
public String getMessage() {
return message;
}
@Override public int hashCode() {
return Objects.hash(message);
}
@Override public boolean equals(Object obj) {
if (! (obj instanceof LogEvent) || obj.getClass() != this.getClass()) return false;
LogEvent o = (LogEvent) obj;
return Objects.equals(message, o.message);
}
@Override public String toString() {
return String.format("%s{message=%s}", getClass().getSimpleName(), message);
}
}
public void test(boolean withEofs) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ObjectReader jsonReader = mapper.reader(LogEvent.class);
String one = "{\"message\":\"one\"}\n";
String two = "{\"message\":\"two\"}\n";
String three = "{\"message\":\"three\"}\n";
StringReaderWithEofs reader = new StringReaderWithEofs(one + two + three);
MappingIterator<LogEvent> it = null;
List<LogEvent> values = new ArrayList<>();
int firstLimit = withEofs ? 1 : reader.string.length();
int lastLimit = reader.string.length();
for (int i = firstLimit; i <= lastLimit; i++) {
reader.limit(i);
if (it == null) {
it = jsonReader.readValues(reader);
}
while (it.hasNext()) {
LogEvent value = it.next();
values.add(value);
}
}
List<LogEvent> expected = Arrays.asList(new LogEvent("one"), new LogEvent("two"), new LogEvent("three"));
assertEquals(expected, values);
}
@Test public void testReadFullFile() throws IOException {
test(false);
}
@Test public void testReadPartialFile() throws IOException {
test(true);
}
}
This is bit tricky, but I would probably try to create a Reader
that actually blocks if no data is available, sleeps a bit, checks, and so on. This way Jackson need not concern itself with lack of content.
Return 0 from Reader
or InputStream
will not actually help, as there isn't much parser can do, as there is really no way to properly block. Or, put another way: underlying reader or stream MUST try to read at least one byte (if one or more requested), blocking if none is found; or return -1 if nothing will be available.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.