I have multiple Monos that are required for a task as shown below
Mono<A> aMono = aService.getMonoA();
Mono<B> bMono = bService.getMonoB();
Mono<C> cMono = cService.getMonoC();
Mono<Task> task = taskService.executeTask(b, c);
Now my method has following structure
Working code without zipWith
public Mono<MyTask> getMyTask() {
try {
Mono<A> aMono = aService.getMonoA();
return aMono.flatMap(
a -> {
return bService.getMonoB()
.flatMap(
b ->
taskService.executeTask(a, b));
});
} catch (Exception e) {
log.error("Execute Task: " + e.getMessage());
return Mono.error(e);
}
}
Now if I call the above method with subscribe then the executeTask gets executed
taskService.getMyTask().subscribe();
But as soon as I use zipWith as shown below with an additional mono the execute Task does not get executed and I am not sure why. Is there an additional subscribe needed somewhere
Not working with zipWith and an additional Mono
public Mono<MyTask> getMyTask() {
try {
Mono<A> aMono = aService.getMonoA();
return aMono.flatMap(
a -> {
Mono<C> cMono = cService.getMonoC();
return bService.getMonoB()
.zipWith(cMono)
.flatMap(
r ->
taskService.executeTask(a, r.getT1(), r.getT2()));
});
} catch (Exception e) {
log.error("Unable to execute Task: " + e.getMessage());
return Mono.error(e);
}
}
Any insight would be appreciated. Note, the actual names of methods are different. Used these method names just for explanation purpose
Ok, I will answer my own question. I made stupid mistake to not verify whether the Monos I am zipping with is empty or not and apparently one of them was.
Also this post gave me a hint Spring Reactor: Mono.zip fails on empty Mono
Related
My requirement is as follows.
Get ApptReq object which will have apptId. Get Appt object from DB and update Appt object with the data from ApptReq and update the table.
Mono<User> monoUser = retrieveUser();
public Mono<ServerResponse> updateAppt(ServerRequest request) {
return apptRepository.findById(request.bodyToMono(ApptReq.class).map(ApptReq::getApptId)).flatMap(appt -> {
return updateAppt(appt, request.bodyToMono(ApptReq.class)).flatMap(apptRepository::save).flatMap(
res -> ServerResponse.created(URI.create(String.format(APPT_URI_FORMAT, res.getApptId()))).build());
});
}
private Mono<Appt> updateAppt(Appt appt, Mono<ApptReq> apptReq) {
return apptReq.map(req -> {
appt.setNotes(req.getNotes());
return monoUser.map((usr) -> {
appt.setUpdatedBy(usr.getUserId());
return appt;
});
});
}
Here getting error in updateAppt method that
can not convert from Mono<Object> to Mono<Appt>.
Is there any better approach?
You've got it almost. I changed nothing in your updateAppt(ServerRequest request) method but made just a slight adjustment in your updateAppt(Appt appt, Mono<ApptReq> apptReq) method, as follows:
private Mono<Appt> updateAppt(Appt appt, Mono<ApptReq> apptReq) {
return apptReq.flatMap(req -> {
appt.setNotes(req.getNotes());
return retrieveUser().map((usr) -> {
appt.setUpdatedBy(usr.getUserId());
return appt;
});
});
}
Watch out for the apptReq.flatMap instead of your apptReq.map and everything works fine. Give it a try!
Reminder: Be careful with nested Monos in other Monos or more generally said nested Publishers.
I'm currently working on a frontend for visualizing the results out ouf some searches in foreign systems. At the moment the programm is asking one system by another and only continues, when alle foreign systems have answered.
The frontend is written in Vaadin 13 and this should be able to refresh the page by push.
I have six controller classes for six foreign systems to question and want to start all questions at the same time without having to wait for the privious controller to finish.
My problem is that I can't find a tutorial which helps me with this special problem. All tutorials are about starting the same process for more than once but at the same time.
This is how I start the searches at the moment:
public static void performSingleSearch(ReferenceSystem referenceSystem, String searchField, List<String> searchValues, SystemStage systemStage) throws Exception {
if(!isAvailable(referenceSystem, systemStage)) return;
Map<String, ReferenceElement> result = new HashMap<>();
try {
Class<?> classTemp = Class.forName(referenceSystem.getClassname());
Method method = classTemp.getMethod("searchElements", String.class , List.class, SystemStage.class);
result = (Map<String, ReferenceElement>) method.invoke(classTemp.newInstance(), searchField, searchValues, systemStage);
} catch (Exception e) {
return;
}
if(result != null) orderResults(result, referenceSystem);
}
I hope you can provide me an tutorial on how to, or better a book over multithreading.
Best regards
Daniel
Seems to me the simplest approach is using CompletableFuture. Ignoring your atrocious use of reflection, I'm going to assume
interface ReferenceSystem {
public Map<String,ReferenceElement> searchElements(List<String> args);
}
List<ReferenceSystem> systems = getSystems();
List<String> searchArguments = getSearchArguments();
so you can do
List<CompletableFuture<Map<String, ReferenceElement>>> futures = new ArrayList<>();
for (ReferenceSystem system : systems) {
futures.add(CompletableFuture.supplyAsync(() -> system.searchElements(searchArguments)));
}
or with Java 8 Streams
List<CompletableFuture<Map<String, ReferenceElement>>> futures =
systems.stream()
.map(s -> CompletableFuture.supplyAsync(
() -> system.searchElements(searchArguments)))
.collect(Collectors.toList());
Now the futures contains a list of futures which will eventually return the Map you're looking for; you can access them with #get() which will block until the result is present:
for (CompletableFuture<Map<String,ReferenceElement>> future : futures) {
System.out.printf("got a result: %s%n", future.get());
}
With your primitive case all you would need is either list of threads and just wait on them to finish or even easier, use thread pool and use that:
private static ExecutorService service = Executors.newFixedThreadPool(6); // change to whatever you want
public static void someMethod() {
queueActions(Arrays.asList(
() -> {
try {
performSingleSearch(null, null, null, null); // fill your data
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
},
() -> {
try {
performSingleSearch(null, null, null, null); // fill your data #2 etc
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
));
}
public static void queueActions(List<Runnable> actions) {
Semaphore wait = new Semaphore((-actions.size()) + 1);
for (Runnable action : actions) {
service.execute(() -> {
try {
action.run();
} finally {
wait.release();
}
});
}
try {
wait.acquire();
} catch (InterruptedException e) {
}
}
The question remains whether you want to orders be executed at the same time or one at a time or something else (join orders into one big order etc).
I have two Observers that are merged with a flatMap. The first observer returns a value that is used when the second is called.
Observable<Integer> mergedObservers = firstAPI.getFirstInfo(userLat, userLong)
.flatMap(resultFirstObservable -> {
try {
return secondApi.getSecondInfo(resultFirstObservable.body().string(), "3")
.onErrorResumeNext(e -> {
e.printStackTrace();
return secondApi.getSecondInfo("defaultValue", "3");
});
} catch (IOException e) {
e.printStackTrace();
secondApi.getSecondInfo("defaultValue", "3")
.onErrorResumeNext(e -> {
e.printStackTrace();
return secondApi.getSecondInfo("defaultValue", "3");
});
});
}
}, (resultFirstObservable, resultSecondObservable) -> {
try {
return transformToWhatINeed(resultSecondObservable.body().string());
} catch (IOException ex) {
ex.printStackTrace();
return transformToWhatINeed([]);
}
});
userLat and userLong are declared outside my method and are changed during the time the activity is open, but my Subscription takes into account only the first value of these. I would have expected that each time there's a new call, they will take the newest values.
What am I doing wrong ?
If I understand you problem correctly using Observable.defer should solve problem
Observable<Integer> mergedObservers = Observable.defer {
firstAPI.getFirstInfo(userLat, userLong)
}.flatMap ...
Your method should be like:
Observable.combineLatest(userLatObservable, userLongObervable, yourmergerfunction).flatmap( lat, long –> firstApi.get(lat, long))... Etc..
I think you problem is that how do you get the value of userLat amd userLong... Those values should first be converted to Observables to join them in the chain.
I'm finding myself writing alot of retry loops that look like
int triesRemaining = 3;
while (triesRemaining > 0) {
try {
<MY FUNCTION CALL>
LOGGER.info("success");
break;
} catch (Exception e) {
if (e.getCause() instanceof SocketTimeoutException) {
triesRemaining--;
LOGGER.info(e.getMessage() + " trying again. Tries Remaining: " + triesRemaining);
} else {
LOGGER.error(e.getMessage(), e);
return;
}
}
}
if (triesRemaining == 0) {
LOGGER.error("Failed over too many times");
}
I want to write a generic function that accepts a Lambda and only retries on a specific error (in the above case thats SocketTimeoutException). I've seen some functions that accept a Runnable which is fine, but they don't seem to allow limiting to specific exceptions.
Any advice?
Well it's already done. It also accepts list of exceptions on which you want to retry. It also provides linear/exponential retry strategies.
Have a look https://github.com/rholder/guava-retrying
A simple example from it's readme, you can compose and use a retryer like:-
Callable<Boolean> callable = new Callable<Boolean>() {
public Boolean call() throws Exception {
return true; // do something useful here
}};
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfResult(Predicates.<Boolean>isNull())
.retryIfExceptionOfType(IOException.class)
.retryIfRuntimeException()
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
try {
retryer.call(callable);
} catch (RetryException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Have a look to org.springframework.retry
There is an annotation #Retryable which corresponding to your need. You can specify the type of exception to retry and configure the number of attempt, etc...
Check out Failsafe:
RetryPolicy retryPolicy = new RetryPolicy()
.retryOn(SocketTimeoutException.class)
.withMaxRetries(3);
Failsafe.with(retryPolicy)
.onRetry((c, f, ctx) -> log.warn("Failure #{}. Retrying.", ctx.getExecutions()))
.onFailure(e -> LOGGER.error(e.getMessage(), e))
.run(() -> myFunctionCall());
What's the problem of just making this function to accept a Runnable argument and then run it in <MY FUNCTION CALL>?
public static void retry(Runnable r) {
// ...
while (triesRemaining > 0) {
try {
r.run();
LOGGER.info("success");
break;
}
// ...
}
then call it (if you prefer - with a lambda):
retry(() -> {
connectToServer();
// todo what-ever-you-want
});
I believe you're looking for pure Java based solution. Based on assumption, I would say Java 8 uses functional interface, an interface with single abstract method. I would create a new RetryCommand class that has a run method which takes in a function.
I am using spring-cloud-starter (ie.. spring boot with all the microservices features). When I create hystrix method in a component annotated using the javanica #HystrixCommand, follow the directions on the javanica github site (https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica) to make that method run async, regardless of whether I use their 'Future<>' or Reactive execution 'Observable<>', nothing runs/executes and I get
java.lang.ClassCastException: springbootdemo.EricComponent$1 cannot be cast to springbootdemo.Eric whenever I attempt to pull the result (in the case of Future<>) or get a callback (in case of Reactive Execution .. and println's dont trigger so it really didnt run).
public class Application { ...
}
#RestController
#RequestMapping(value = "/makebunchofcalls/{num}")
class EricController { ..
#RequestMapping(method={RequestMethod.POST})
ArrayList<Eric> doCalls(#PathVariable Integer num) throws IOException {
ArrayList<Eric> ale = new ArrayList<Eric>(num);
for (int i =0; i<num; i++) {
rx.Observable<Eric> oe = this.ericComponent.doRestTemplateCallAsync(i);
oe.subscribe(new Action1<Eric>() {
#Override
public void call(Eric e) { // AT RUNTIME, ClassCastException
ale.add(e);
}
});
}
return ale;
}
#Component
class EricComponent { ...
// async version =========== using reactive execution via rx library from netflix ==============
#HystrixCommand(fallbackMethod = "defaultRestTemplateCallAsync", commandKey = "dogeAsync")
public rx.Observable<Eric> doRestTemplateCallAsync(int callNum) {
return new ObservableResult<Eric>() {
#Override
public Eric invoke() { // NEVER CALLED
try {
ResponseEntity<String> result = restTemplate.getForEntity("http://doges/doges/24232/photos", String.class); // actually make a call
System.out.println("*************** call successfull: " + new Integer(callNum).toString() + " *************");
} catch (Exception ex) {
System.out.println("=============== call " + new Integer(callNum).toString() + " not successfull: " + ex.getMessage() + " =============");
}
return new Eric(new Integer(callNum).toString(), "ok");
}
};
}
public rx.Observable<Eric> defaultRestTemplateCallAsync(int callNum) {
return new ObservableResult<Eric>() {
#Override
public Eric invoke() {
System.out.println("!!!!!!!!!!!!! call bombed " + new Integer(callNum).toString() + "!!!!!!!!!!!!!");
return new Eric(new Integer(callNum).toString(), "bomb");
}
};
}
}
Why would I be getting back an EricComponent$1 instead of a Eric? btw, Eric is just a simple class with 2 strings... its ommitted.
I am figuring that I must have to explicitly execute, but that alludes me because: 1) Doing it with Future<> the queue() method is not available as the documentation claims and 2) doing it with Observable<> there really isn't a way to execute it that I get.
Do you have the #EnableHystrix annotation on you application class?
The subscribe method is asynchronous and you are trying to populate a list in a synchronous controller method so there may be a problem there. Can you change the subscribe to toBlockingObservable().forEach() and see if that helps?
Update #1
I was able to duplicate. Your default method should not return an Observable<Eric>, just an Eric.
public Eric defaultRestTemplateCallAsync(final int callNum) {
System.out.println("!!!!!!!!!!!!! call bombed " + new Integer(callNum) + "!!!!!!!!!!!!!");
return new Eric(new Integer(callNum).toString(), "bomb");
}
Update #2
See my code here https://github.com/spencergibb/communityanswers/tree/so26372319
Update #3
When I commented out the fallbackMethod attribute, it complained that it couldn't find a public version of EricComponent for AOP. I made EricComponent public static and it worked. A top level class in its own file would work to. My code, linked above, works (assuming the restTemplate call works) and returns n OK.