Whats the simplest code i can use to start an asynchronous thread to fetch json data and then parse it on the UI thread in android using retrofit and rxjava?
I have searched this for two days and every solution so far gives me an error.
I basically want to replace all the old async tasks in my app with the newer retrofit and rxjava way of doing things.
import okhttp3.ResponseBody;
import retrofit2.Response;
import rx.Subscriber;
PoloniexService.getInstance().getAllCoins()
.subscribe(new Subscriber<Response<ResponseBody>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
}
});
Something like this.. Except this gives me the error:
Cannot resolve method 'subscribe(anonymous rx.Subscriber<retrofit2.Response<okhttp3.ResponseBody>>)'
Related
Currently, I have code written in regular Java that gets a public-readable s3 object's InputStream and creates a thumbnail image.
Now I am looking to convert it to using Reactive Java using Project Reactor on Spring Webflux. The following is my code so far and I don't know how to convert it to a inpustream:
public ByteArrayOutputStream createThumbnail(String fileKey, String imageFormat) {
try {
LOG.info("fileKey: {}, endpoint: {}", fileKey, s3config.getSubdomain());
GetObjectRequest request = GetObjectRequest.builder()
.bucket(s3config.getBucket())
.key(fileKey)
.build();
Mono.fromFuture(s3client.getObject(request, new FluxResponseProvider()))
.map(fluxResponse -> new
ResponseInputStream(fluxResponse.sdkResponse, <ABORTABLE_INPUSTREAM?>))
I saw ResponseInputStream and I am thinking maybe that is the way to create a inputstream but I don't know what to put as AbortableInputStream in that constructor?
Is that even the way to create a inpustream?
Btw, I am using FluxResponseProvider from baeldung's documentation which is:
import reactor.core.publisher.Flux;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
class FluxResponseProvider implements AsyncResponseTransformer<GetObjectResponse,FluxResponse> {
private FluxResponse response;
#Override
public CompletableFuture<FluxResponse> prepare() {
response = new FluxResponse();
return response.cf;
}
#Override
public void onResponse(GetObjectResponse sdkResponse) {
this.response.sdkResponse = sdkResponse;
}
#Override
public void onStream(SdkPublisher<ByteBuffer> publisher) {
response.flux = Flux.from(publisher);
response.cf.complete(response);
}
#Override
public void exceptionOccurred(Throwable error) {
response.cf.completeExceptionally(error);
}
}
class FluxResponse {
final CompletableFuture<FluxResponse> cf = new CompletableFuture<>();
GetObjectResponse sdkResponse;
Flux<ByteBuffer> flux;
}
Any body know how to get a inpustream from s3 object in reactive java? I am using awssdk version 2.17.195.
I'm exploring reactive programming with Spring Webflux and therefore, I'm trying to make my code completely nonblocking to get all the benefits of a reactive application.
Currently my code for the method to parse a Json String to a JsonNode to get specific values (in this case the elementId) looks like this:
public Mono<String> readElementIdFromJsonString(String jsonString){
final JsonNode jsonNode;
try {
jsonNode = MAPPER.readTree(jsonString);
} catch (IOException e) {
return Mono.error(e);
}
final String elementId = jsonNode.get("elementId").asText();
return Mono.just(elementId);
}
However, IntelliJ notifies me that I'm using an inappropriate blocking method call with this code:
MAPPER.readTree(jsonString);
How can I implement this code in a nonblocking way? I have seen that since Jackson 2.9+, it is possible to parse a Json String in a nonblocking async way, but I don't know how to use that API and I couldn't find an example how to do it correctly.
I am not sure why it is saying it is a blocking call since Jackson is non blocking as far as I know. Anyway one way to resolve this issue is to use schedulers if you do not want to use any other library. Like this.
public Mono<String> readElementIdFromJsonString(String input) {
return Mono.just(Mapper.readTree(input))
.map(it -> it.get("elementId").asText())
.onErrorResume( it -> Mono.error(it))
.subscribeOn(Schedulers.boundedElastic());
}
Something along that line.
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.codec.json.AbstractJackson2Decoder;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
#FunctionalInterface
public interface MessageParser<T> {
Mono<T> parse(String message);
}
public class JsonNodeParser extends AbstractJackson2Decoder implements MessageParser<JsonNode> {
private static final MimeType MIME_TYPE = MimeTypeUtils.APPLICATION_JSON;
private static final ObjectMapper OBJECT_MAPPER = allocateDefaultObjectMapper();
private final DefaultDataBufferFactory factory;
private final ResolvableType resolvableType;
public JsonNodeParser(final Environment env) {
super(OBJECT_MAPPER, MIME_TYPE);
this.factory = new DefaultDataBufferFactory();
this.resolvableType = ResolvableType.forClass(JsonNode.class);
this.setMaxInMemorySize(100000); // 1MB
canDecodeJsonNode();
}
#Override
public Mono<JsonNode> parse(final String message) {
final byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
return decode(bytes);
}
private Mono<JsonNode> decode(final byte[] bytes) {
final DefaultDataBuffer defaultDataBuffer = this.factory.wrap(bytes);
return this.decodeToMono(Mono.just(defaultDataBuffer), this.resolvableType, MIME_TYPE, Map.of())
.ofType(JsonNode.class)
.subscribeOn(Schedulers.boundedElastic())
.doFinally((t) -> DataBufferUtils.release(defaultDataBuffer));
}
private void canDecodeJsonNode() {
if (!canDecode(this.resolvableType, MIME_TYPE)) {
throw new IllegalStateException(String.format("JsonNodeParser doesn't supports the given tar`enter code here`get " +
"element type [%s] and the MIME type [%s]", this.resolvableType, MIME_TYPE));
}
}
}
I'm loving vertx.io in my spare time. Just now I am switching from plain vertx to rxjava2-fied (vertx-rx-java2, 3.5.1) api version and my VertxUnitRunner tests doesn't complete anymore:
#Test
public void computeSomethingByNullPlan(TestContext ctx) {
query = null;
Async async = ctx.async();
vertx.eventBus().send(HANDLER_ADDRESS, query,
new DeliveryOptions().addHeader("action", ACTION), msg -> {
if (msg.failed())
log.error(msg.cause());
ctx.assertTrue(msg.succeeded());
ctx.assertTrue(new
JsonArray(msg.result().body().toString()).isEmpty());
async.complete();
});
}
test runs fine but once async.complete(); instruction is hit test does not return but hangs. Maybe it is because I'm mixing io.vertx.reactivex.core and io.vertx.core together (e.g.: io.vertx.reactivex.core.Vertx and io.vertx.core.TestContex) or maybe I'm not using the correct VertxUnitRunner.
What am I doing wrong? I search for a while but no success, Is there any example/doc about vertx.io rxjava2 and testing?
i'm able to reproduce the issue of the test "hanging" when this bit:
JsonArray(msg.result().body().toString())
...fails to deserialize the response body as an array of JSON. to verify this, comment out that entire assertion and see if the test completes afterwards. if so, that should narrow down the scope of that particular issue.
it's worth noting that since you're using the Rx-ified version of the API, you can represent asynchronous sources using the standard Rx types (instead of Handler). your test rewritten along those lines (minus the JsonArray stuff) might look something like this:
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import io.vertx.reactivex.core.Vertx;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(VertxUnitRunner.class)
public class Blah {
private Vertx vertx;
#Before
public void setUp(TestContext context) {
vertx = Vertx.vertx();
vertx.eventBus().consumer("HANDLER_ADDRESS").handler(message -> {
message.reply("PONG!");
});
}
#Test
public void computeSomethingByNullPlan(TestContext context) {
final String query = "PING?";
final Async async = context.async();
vertx.eventBus().rxSend("HANDLER_ADDRESS", query)
.subscribe(
message -> {
context.assertEquals("PONG!", message.body().toString());
async.complete();
},
context::fail
);
}
}
i hope that helps clear things up!
I am struggling on a simple task. I want to create a cxfrs consumer that simply consumes json.
The json should be converted to a simple map (key->value): I created a simple test:
#Test
public final void test() throws Exception {
MockEndpoint mockOut = context.getEndpoint(MOCK_OUT, MockEndpoint.class);
mockOut.expectedMessageCount(1);
context.addRoutes(createRouteBuilder());
context.start();
context.createProducerTemplate().sendBody(DIRECT_A, "{ \"ussdCode\":\"101#\",\"msisdn\":\"491234567\"}");
mockOut.assertIsSatisfied();
}
private RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from(DIRECT_A).to("cxfrs://http://localhost:8085/ussd");
from("cxfrs://http://localhost:8085/ussd")
.unmarshal().json(JsonLibrary.Jackson)
.process(to).to(MOCK_OUT);
}
};
}
The problem is on context.start() i get ServiceConstructionException: No resource classes found. I also tried to create the consumer this way (setting binding style):
private Endpoint fromCxfRsEndpoint() {
CxfRsEndpoint cxfRsEndpoint = context.getEndpoint("cxfrs://http://localhost:8085/ussd", CxfRsEndpoint.class);
cxfRsEndpoint.setBindingStyle(BindingStyle.SimpleConsumer);
return cxfRsEndpoint;
}
This didn't helped neither. So how to create a simple rest/json consumer and unmarshal to a simple map?
I am running a VertX HTTP Server. It understands requests when content type is HTML/forms, but when I try to post JSON data, it never even enters the request handler.
Is there something I need to do to make Vertx expect JSON? Is this supported?
Here is a java example. Note that the data handler which will be processing json is executed only for post request. Post a request with some json data to this and it will return with the same.
import org.vertx.java.core.Handler;
import org.vertx.java.core.buffer.Buffer;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.platform.Verticle;
/**
* Simple Http server
*/
public class HttpVerticle extends Verticle {
public void start() {
vertx.createHttpServer()
.requestHandler(new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
container.logger().info(
"Got request for " + request.path());
if (request.method().equalsIgnoreCase("POST")) {
request.dataHandler(new Handler<Buffer>() {
#Override
public void handle(Buffer data) {
request.response().end("got data " + data);
}
});
} else {
request.response().end("got request");
}
}
}).listen(8080);
container.logger().info("HttpVerticle started");
}
}
If you can, Check out the latest Vertx-web They provide a really neat way of handling various requests formats ( multipart-data , url-encoded params, file uploads etc ) using elegant syntax ala NodeJs routing
Though you would need to migrate your code to Vertx 3.0