I am trying to implement a retry mechanism for HTTP request with a HTTPClient library. For the retry mechanism, I have a list of website to try and I will try each website for retries times. The process ends when I receive a status 200 from any of the requests.
My idea is to use a boolean requestSuccess and a label requestLabel. I will set requestSuccess = true and break the requestLabel when I receive a status 200. However, variable must be (effectively) final and break label is not available inside lambda expression.
Is there any workaround to implement such retry mechanism?
boolean requestSuccess = false;
requestLabel:
for(String site: sites) {
for(int i = 0; i < retries; i++) {
client.request(site, data, requestOptions, (err, res, content) -> {
if(err == null) {
requestSuccess = true;
break requestLabel;
} else {
log(...);
}
})
}
}
if(!requestSuccess) {
log("request failed");
}
One possible answer
Inspired by a submitted then deleted answer, I can use a wrapped-class-like solution. I think it would work, but it seems dirty?
boolean[] requestSuccess = new boolean[1];
requestLabel:
for(String site: sites) {
for(int i = 0; i < retries; i++) {
if(requestSuccess[0] == true) {
break requestLabel;
}
client.request(site, data, requestOptions, (err, res, content) -> {
if(err == null) {
requestSuccess[0] = true;
} else {
log(...);
}
})
}
}
if(!requestSuccess) {
log("request failed");
}
A lambda gets turned into it's own class under the hood. It would be as if you have two classes as far as the java interpreter is concerned.
class Main {
public void runstuff() {
labelX:
for(...) {
client.request(new Main$Foo().xyz(.....));
}
}
class Foo {
public xyz(....) {
break labelX; // There is no labelX to break to here in Foo.xyz
}
}
}
Related
I am using Vert.x in my project, I used future() to get the results from a MongoDB query. However when I do future().result it returns "null". I want the result to be saved in the future and I will use it for other APIs. Is there any guide for me, I will be very grateful and appreciate if someone give me a solution. Thanks
router.class
rivate void getClazzById(RoutingContext rc) {
Future<JsonObject> future = Future.future();
String clazzId = rc.request().getParam("clazzId");
classService.getClazzById(clazzId, res -> {
System.out.println(res.result());
if (res.succeeded()) {
JsonObject result = res.result();
if (result != null) {
future.complete(res.result());
rc.response().setStatusCode(200).putHeader("Content-Type", "application/json; charset=utf-8")
.end(result.encodePrettily());
} else {
rc.response().setStatusCode(400).putHeader("Content-Type", "application/json; charset=utf-8")
.end(new JsonObject().put("error", "Class not found!").encodePrettily());
}
} else
rc.fail(res.cause());
});
future.setHandler(s -> {
if (s.succeeded()) {
System.out.println("sss: " +s.result()); // print: {"_id":"123", "name":"abc"}
}
else {
System.out.println("fail");
}
});
System.out.println("hhhhhh: " + future.result()); // I want to print {"_id":"123", "name":"abc"}
}
service.class
public void getClazzById(String clazzId, Handler<AsyncResult<JsonObject>> resultHandler) {
JsonObject query = new JsonObject().put("_id", clazzId);
client.findOne(Collection.CLAZZ, query, null, ar -> {
if (ar.succeeded()) {
if (ar.succeeded()) {
JsonObject result = ar.result();
if (result == null) {
resultHandler.handle(Future.failedFuture("Error"));
} else {
resultHandler.handle(Future.succeededFuture(result));
}
} else {
ar.cause();
}
}
});
}
When writing asynchronous code, you are carried to use the framework / runtime semantics and tools for communication.
You are already leveraging one of Vert.x's way of async communication, the Future - but in the wrong manner trying to retrieve its result inline.
Instead of having the Future result accessed within your method, you need to return it as your mean of communication to a caller, which would be able to set a completion handler (Handler) to it:
private Future<JsonObject> getClazzById(RoutingContext rc) {
Future<JsonObject> future = Future.future();
String clazzId = rc.request().getParam("clazzId");
classService.getClazzById(clazzId, res -> {
if (res.succeeded()) {
JsonObject result = res.result();
if (result != null) {
future.complete(res.result()); // set the retrieved result
rc.response().setStatusCode(200).putHeader("Content-Type", "application/json; charset=utf-8")
.end(result.encodePrettily());
} else {
future.complete(null); // you need to provide 'null' result to avoid caller blocking
rc.response().setStatusCode(400).putHeader("Content-Type", "application/json; charset=utf-8")
.end(new JsonObject().put("error", "Class not found!").encodePrettily());
}
} else
rc.fail(res.cause());
});
return future; // return the future to caller
}
An interested caller would be able to set a handler for Future completion as needed:
getClassById(rc).setHandler(s -> {
if (s.succeeded()) {
System.out.println("sss: " +s.result()); // print: {"_id":"123", "name":"abc"}
}
else {
System.out.println("fail");
}
});
As a side note: you are not setting the API boundaries properly in your business logic as you are trying to resolve the HTTP Response result which generally denotes the request processing end while still returning the query result to be handled in some other manner.
I want to create a unit test for a client service.
The function of the client service is to call the webservice, get data, and update the database as scheduled.
The scheduled method return void.
How to create unit test for a
client service
schedule method
void returning methods
The client is like this:
#ApplicationScoped
public class ClientClass {
private static final Logger LOGGER = Logger.getLogger(VdsClient.class);
#Inject
Client client;
VMR vmr;
CommandService commandService;
public VdsClient(VMR vmr,
CommandService commandService) {
this.vmr = vmr;
this.commandService = commandService;
}
#Scheduled(XXX)
public void getVal() {
var monitoringStateFilter =
new VMF.vmf(true, true);
var monoResultList =
vmr.fvms(monitoringStateFilter)
.collectList();
if (monoResultList != null) {
var resultList = monoResultList.block();
if (resultList != null) {
resultList.stream()
.map(row -> row.getValue("val", val.class))
.map(vin -> this.updateEstimate(val.getValue()))
}
}
}
public Tuple2<String, Boolean> updateEstimate(String val) {
List<route> routeList;
try {
routeList = vdsClient.getval(val)
.getItem();
boolean hasDealerDestination = false;
for (Route route : routeList) {
if (vd.DestLocationType._00.value()
.equals(route.getTransportConnectionPointTyp())) {
hasDealerDestination = true;
var estimate = DateTimeUtil.convertToInstantWithOffset(route.getArrivalDate(),
route.getArrivalTime(), route.getTimezone(), DateTimeUtil.PATTERN_LONG_DATE);
if (estimate == null) {
return Tuples.of(val, false);
}
var result = this.updateVehicleEstimate(val, estimate);
return Tuples.of(val, result);
}
}
if (!hasDealerDestination) {
return Tuples.of(val, false);
} else {
return Tuples.of(val, false);
}
} catch (route e) {
return Tuples.of(val, false);
} catch (Exception e) {
return Tuples.of(val, false);
}
}
public Boolean updateVehicleEstimate(String val, Instant estimate) {
var vehicleUpdate = vu.vuc.builder()
.val(new Val(val))
.Estimate(estimate)
.build();
return (Boolean) cs.ec(vu).block();
}
A unit should only be testing that a particular unit of code is working fine. In your case, for the unit test, you should assume that the webservice will return the data and the db updation works fine. We can accomplish this through mocking the response for each of these calls.
For void returning methods, you can actually verify if the call was indeed made or not.
For example:
Mockito.verify(mockedObject, Mockito.times(1)).callWebService(mockedParameter1, mockedParameter2);
There is another way, though I personally don't prefer that:
You can declare a class variable and make sure the value updates itself whenever the scheduled method reaches the end of the code. Read that value in your test and assert on its value. If the value is updated, then your code worked fine, else NO and it's a failure.
Also, in case you want to actually make sure the webservice returned the correct response / db entry was updated, then those should be part of integration tests and not unit tests.
Is there an operator in RxJava, an external library or a way I'm missing to create a flowable/observable that recieves a function that controls the emission of data, like a valve?
I have a huge json file I need to process but I have to get a portion of the file, a list of entities, process it and then get another portion, I have tried using windows(), buffer() but the BiFunction I pass to Flowable.generate() keeps executing after I recieved the first list and I haven't finished processing it. I also tried FlowableTransformers.valve() from hu.akarnokd.rxjava3.operators but it just piles up the items before the flatMap() function that process the list
private Flowable<T> flowable(InputStream inputStream) {
return Flowable.generate(() -> jsonFactory.createParser(new GZIPInputStream(inputStream)), (jsonParser, emitter) -> {
final var token = jsonParser.nextToken();
if (token == null) {
emitter.onComplete();
}
if (JsonToken.START_ARRAY.equals(token) || JsonToken.END_ARRAY.equals(token)) {
return jsonParser;
}
if (JsonToken.START_OBJECT.equals(token)) {
emitter.onNext(reader.readValue(jsonParser));
}
return jsonParser;
}, JsonParser::close);
}
Edit: I need to control de emission of items to don't overload the memory and the function that process the data, because that function reads and writes to database, also the processing needs to be sequentially. The function that process the data it's not entirely mine and it's written in RxJava and it's expected that I use Rx.
I managed to solve it like this but if there is another way let me know please:
public static <T> Flowable<T> flowable(InputStream inputStream, JsonFactory jsonFactory, ObjectReader reader, Supplier<Boolean> booleanSupplier) {
return Flowable.generate(() -> jsonFactory.createParser(new GZIPInputStream(inputStream)), (jsonParser, emitter) -> {
if (booleanSupplier.get()) {
final var token = jsonParser.nextToken();
if (token == null) {
emitter.onComplete();
}
if (JsonToken.START_ARRAY.equals(token) || JsonToken.END_ARRAY.equals(token)) {
return jsonParser;
}
if (JsonToken.START_OBJECT.equals(token)) {
emitter.onNext(reader.readValue(jsonParser));
}
}
return jsonParser;
}, JsonParser::close);
}
Edit2: This is one of the ways I'm currently consuming the function
public Flowable<List<T>> paging(Function<List<T>, Single<List<T>>> function) {
final var atomicInteger = new AtomicInteger(0);
final var atomicBoolean = new AtomicBoolean(true);
return flowable(inputStream, jsonFactory, reader, atomicBoolean::get)
.buffer(pageSize)
.flatMapSingle(list -> {
final var counter = atomicInteger.addAndGet(1);
if (counter == numberOfPages) {
atomicBoolean.set(false);
}
return function.apply(list)
.doFinally(() -> {
if (atomicInteger.get() == numberOfPages) {
atomicInteger.set(0);
atomicBoolean.set(true);
}
});
});
}
Managed to solve it like this
public static Flowable<Object> flowable(JsonParser jsonParser, ObjectReader reader, PublishProcessor<Boolean> valve) {
return Flowable.defer(() -> {
final var token = jsonParser.nextToken();
if (token == null) {
return Completable.fromAction(jsonParser::close)
.doOnError(Throwable::printStackTrace)
.onErrorComplete()
.andThen(Flowable.empty());
}
if (JsonToken.START_OBJECT.equals(token)) {
final var value = reader.readValue(jsonParser);
final var just = Flowable.just(value).compose(FlowableTransformers.valve(valve, true));
return Flowable.concat(just, flowable(jsonParser, reader, valve));
}
return flowable(jsonParser, reader, valve);
});
}
I have a method which does multiple validations which are dependent on earlier one. This is purely a REST Service with no form/frontend. e.g.
public Json processPayment(User user, Amount amount, CardData cardData) {
Json result = new Json();
Json userResult = validateUser(user);
if (userResult.isNotValid())
result.put("errorCode", userResult.get("errorCode");
result.put("message", userResult.get("message");
return result;
}
Merchant merchant = getMerchant(user);
Json merchantResult = validateMerchant(user);
if (merchantResult.isNotValid())
result.put("errorCode", merchantResult.get("errorCode");
result.put("message", merchantResult.get("message");
return result;
}
Json limitsResult = validateLimits(user, merchant, amount);
if (limitsResult.isNotValid())
result.put("errorCode", limitsResult.get("errorCode");
result.put("message", limitsResult.get("message");
return result;
}
// Like above there are few more steps.
.
.
.
// All validations are fine process transaction.
Json transactionResult = processTransaction(user, merchant, amount, cardData);
if (transactionResult.isNotValid())
result.put("errorCode", transactionResult.get("errorCode");
result.put("message", transactionResult.get("message");
} else {
result.put("message", "Transaction Successful");
result.put("referenceNumber, transactionResult.get("rrn");
}
return result;
}
In each step, if the results are invalid then it should return immediately with the error message otherwise continue to next step.
Due to multiple steps, this method has become too big and almost impossible to do unit testing.
I want to break this method into smaller ones. I have already moved all the business logic of each step into separate methods but still the flow remains in this big method.
Sonarlint CC is 47 which is a big worry.
Please suggest what would be the right approach to handle this.
Thank you.
Here is a little example which could be a solution for you.
The main idea is each validation step shares one common context. This context holds every information of your validation process.
Next you have a queue of validators. Each represents one validation step. A validator changes the context (like adding the merchant object), calls your validation methods and changes the result of the context if necessary.
The validation process itself just iterates over the queue looking for a failing validator.
Just run this code. Maybe it helps:
import java.util.*;
interface PaymentValidatorInterface {
public boolean validate(PaymentValidationContext context);
}
class PaymentValidationContext {
String result = "";
String user;
int cardData;
String merchant;
public PaymentValidationContext(String user, int cardData) {
this.user = user;
this.cardData = cardData;
}
}
class PaymentValidator {
public static boolean validateUser(PaymentValidationContext context) {
if (context.user == null) {
context.result += "User is wrong\n";
return false;
}
return true;
}
public static boolean validateMerchant(PaymentValidationContext context) {
context.merchant = context.user + "#" + context.cardData;
if (context.merchant.length() <= 3) {
context.result += "Marchant is wrong\n";
return false;
}
return true;
}
public static boolean finishValidation(PaymentValidationContext context) {
context.result += "Everything is fine.\n";
return true;
}
}
public class Processor {
private final static Queue<PaymentValidatorInterface> validators = new LinkedList<>();
static {
validators.add(PaymentValidator::validateUser);
validators.add(PaymentValidator::validateMerchant);
validators.add(PaymentValidator::finishValidation);
}
public String processPayment(String user, int cardData) {
PaymentValidationContext context = new PaymentValidationContext(user, cardData);
validators.stream().anyMatch(validator -> !validator.validate(context));
return context.result;
}
// For testing -------
public static void main(String[] args) {
Processor p = new Processor();
System.out.print(p.processPayment("Foobar", 1337)); // ok
System.out.print(p.processPayment(null, 1337)); // fails
System.out.print(p.processPayment("", 1)); // fails
}
}
You can write doValidation() function like the following.
private doValidation(Json validationResult, Json result) {
if (validationResult.isNotValid())
result.put("errorCode", validationResult.get("errorCode");
result.put("message", validationResult.get("message");
return false;//validation failed
}
return true;//validation passed
}
and Call this method from processPayment() method.
public Json processPayment(User user, Amount amount, CardData cardData) {
Json result = new Json();
if( !doAllValidations(user,amount,cardData, result) )
return result;
// All validations are fine process transaction.
Json transactionResult = processTransaction(user, merchant, amount, cardData);
if (transactionResult.isNotValid())
result.put("errorCode", transactionResult.get("errorCode");
result.put("message", transactionResult.get("message");
} else {
result.put("message", "Transaction Successful");
result.put("referenceNumber, transactionResult.get("rrn");
}
return result;
}
Finally you can move all validations to some other method if you want.
public bool doAllValidations(User user, Amount amount, CardData cardData, result) {
Json userResult = validateUser(user);
if (!doValidation(userResult, result))
return result;
Merchant merchant = getMerchant(user);
Json merchantResult = validateMerchant(user);
if (!doValidation(merchantResult, result))
return result;
Json limitsResult = validateLimits(user, merchant, amount);
if (!doValidation(limitsResult, result))
return result;
....
}
I send a message using EventBus and i want to get the reply message into a variable then will return it.this is the code block.
public class MessageExecute {
private static final Logger logger = LoggerFactory.getLogger(MessageExecute.class);
public static <T> T sendMessage(Vertx vertx,String address,T message){
Future<Message<T>> future = Future.future();
vertx.eventBus().send(address, message, future.completer());
future.setHandler(new Handler<AsyncResult<Message<T>>>() {
#Override
public void handle(AsyncResult<Message<T>> event) {
logger.info("received reply message | thread - " + Thread.currentThread().getName());
}
});
boolean notFound = true;
while(notFound){
try{
if(future.result()!= null){
notFound = false;
}
}catch(Exception e){
}
}
return message;
}
}
Actually this is working fine.But some times While block never exit.Its mean future.result() not getting the value ,even after the reply message is received.I don't know this the correct way and I don't have clear idea about how the Futures work in vert.x .Is there any other way to implement these kind of scenario.
I recommend you to read about the Vertx-Sync project - http://vertx.io/docs/vertx-sync/java/
In examples, have the follow example that appears very similar to you case:
EventBus eb = vertx.eventBus();
HandlerReceiverAdaptor<Message<String>> adaptor = streamAdaptor();
eb.<String>consumer("some-address").handler(adaptor);
// Receive 10 messages from the consumer:
for (int i = 0; i < 10; i++) {
Message<String> received1 = adaptor.receive();
System.out.println("got message: " + received1.body());
}