com.github.mizosoft.methanol.adapter.jackson.internal.JacksonSubscriber Maven / Gradle / Ivy
/*
* Copyright (c) 2019, 2020 Moataz Abdelnasser
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.mizosoft.methanol.adapter.jackson.internal;
import static java.util.Objects.requireNonNull;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.async.ByteArrayFeeder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import com.github.mizosoft.methanol.TypeRef;
import com.github.mizosoft.methanol.internal.flow.Prefetcher;
import com.github.mizosoft.methanol.internal.flow.Upstream;
import java.io.IOException;
import java.net.http.HttpResponse.BodySubscriber;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow.Subscription;
public final class JacksonSubscriber implements BodySubscriber {
private final ObjectMapper mapper;
private final ObjectReader objReader;
private final JsonParser parser;
private final ByteArrayFeeder feeder;
private final TokenBuffer tokenBuffer;
private final CompletableFuture valueFuture;
private final Upstream upstream;
private final Prefetcher prefetcher;
public JacksonSubscriber(ObjectMapper mapper, TypeRef type, JsonParser parser) {
this.mapper = mapper;
this.objReader = mapper.readerFor(mapper.constructType(type.type()));
this.parser = parser;
feeder = (ByteArrayFeeder) parser.getNonBlockingInputFeeder();
tokenBuffer = new TokenBuffer(this.parser);
valueFuture = new CompletableFuture<>();
upstream = new Upstream();
prefetcher = new Prefetcher();
}
@Override
public CompletionStage getBody() {
return valueFuture;
}
@Override
public void onSubscribe(Subscription subscription) {
requireNonNull(subscription);
if (upstream.setOrCancel(subscription)) {
prefetcher.initialize(upstream);
}
}
@Override
public void onNext(List item) {
requireNonNull(item);
byte[] bytes = JacksonAdapterUtils.collectBytes(item);
try {
feeder.feedInput(bytes, 0, bytes.length);
flushParser();
} catch (Throwable t) {
valueFuture.completeExceptionally(t);
upstream.cancel();
return;
}
prefetcher.update(upstream);
}
@Override
public void onError(Throwable throwable) {
requireNonNull(throwable);
upstream.clear();
valueFuture.completeExceptionally(throwable);
}
@Override
public void onComplete() {
upstream.clear();
feeder.endOfInput();
try {
flushParser();
valueFuture.complete(objReader.readValue(tokenBuffer.asParser(mapper)));
} catch (Throwable t) {
valueFuture.completeExceptionally(t);
}
}
private void flushParser() throws IOException {
JsonToken token;
while ((token = parser.nextToken()) != null && token != JsonToken.NOT_AVAILABLE) {
tokenBuffer.copyCurrentEvent(parser);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy