Reactor Core - Mono - onErrorFlatmap - java

Is there a way in Mono to return a flatMap while there is an error(onErrorFlatMap)
My scenario is, i would need the SubscriberContext when there is an error after processing i need the same error propagated down the chain
String test = "test";
Mono.just(test)
.map(Integer::valueOf)
.onErrorMap(error -> Mono.subscriberContext()
.map(context -> {
System.out.println(error + " -- " + context.getOrDefault("APPID", null));
return error;
}))
.subscriberContext(of("APPID", "APP-101"))
.block();
This is the way, i found to fix it, but is there a better way?
String test = "test";
Mono.just(test)
.map(Integer::valueOf)
.onErrorResume(error -> Mono.subscriberContext()
.flatMap(context -> {
System.out.println(error + " -- " + context.getOrDefault("APPID", null));
return Mono.error(error);
}))
.subscriberContext(of("APPID", "APP-101"))
.block();

Using onErrorResume and ultimately returning a Mono.error is the correct and recommended pattern for this use case.

Related

Need a way to get to get the pattern in a string matched with List.stream().anyMatch()

I'm matching a list of strings against a list of keywords. The goal is to get a string only one time if it has any of the keywords in it.
Actually I'm doing it with this loop:
for (int i = 0; i < keywords.size(); i++) {
if (text.toLowerCase().contains(keywords.get(i))) {
System.out.println("keyword >>> " + keywords.get(i)
+ " text >>> " + text);
break;
}
}
I like to know if there is a way to get the keyword if I'm using the java stream API like so:
if (keywords.stream().anyMatch(text.toLowerCase()::contains)) {
System.out.println("keyword >>> " + "how to get the keyword here"
+ " text >>> " + text);
}
anyMatch returns a boolean
Using .filter() & findFirst() you can retrieve a value. Note that if you use findFirst() you get an Optional<String> and could either use ifPresent() like this:
keywords.stream()
.filter(keyword -> text.toLowerCase().contains(keyword))
.findFirst()
.ifPresent(keyWord -> System.out.println("[2] keyword >>> " + keyWord + " text >>> " + text));
Or with Optional.isPresent() & Optional.get():
Optional<String> firstKey = keywords.stream()
.filter(keyword -> text.toLowerCase().contains(keyword))
.findFirst();
if (firstKey.isPresent()) {
System.out.println("[2] keyword >>> " + firstKey.get() + " | text >>> " + text);
}
As a less fancy alternative to stream there is a nicer for syntax in Java 1.6+
for(String keyword: keywords) {
if (text.toLowerCase().contains(keyword)) {
System.out.println("keyword >>> " + keyword
+ "text >>> " + text);
break;
}
}
I find the for syntax easier to read than the stream syntax. In a previous job I did a lot of C++ lambda functions with BOOST and the code was hard to parse, and the errors hard to trace.
Another option would be to print an informational message if the keyword was not found. This is easily done by moving the isPresent test inside the print statement.
List<String> keywords =
Arrays.asList("now", "is", "the", "time");
String text = "what are your names?";
Optional<String> opt = keywords.stream()
.filter(keyword -> text
.toLowerCase().contains(keyword))
.findFirst();
System.out.println(!opt.isPresent() ? "No keyword was found!" :
"keyword >>> " + opt.get()
+ " text >>> " + text);
Prints
No keyword was found!

How to return null when list is empty in Java 8?

How can I change this method so that it returns null if the list passed as a parameter is empty without using an if statement?
default String getFiltersExpression(List<WorklistViewDto.Filter> filters) {
return Optional.ofNullable(filters)
.map(Collection::stream)
.orElseGet(Stream::empty)
.map(WorkListViewMapper::formatValue)
.map(f -> f.getCriteria() + f.getOperator() + f.getValue())
.collect(Collectors.joining(" AND ", "(", ")"));
}
You can do it with Collectors.collectingAndThen .
.collect(
Collectors.collectingAndThen(
Collectors.joining(),
str->{
if(str.isEmpty()) return null;
return str;
}
)
);
Given OP's joining statement, Collectors.joining(" AND ", "(", ")") we could modify the above.
Collectors.collectingAndThen(
Collectors.joining(" AND "),
str->{
if(str.isEmpty()) return null;
return "(" + str + ")";
})
I would recommend not returning null and rather returning a "()" string as the filter expression for this you can just append a filter for an empty list there as :
String getFiltersExpression(List<Filter> filters) {
return Optional.ofNullable(filters)
.filter(l -> !l.isEmpty())
.map(Collection::stream)
.orElseGet(Stream::empty)
.map(WorkListViewMapper::formatValue)
.map(f -> f.getCriteria() + f.getOperator())
.collect(Collectors.joining(" AND ", "(", ")"));
}
Using Java-9 syntax :
String getFiltersExpressions(List<Filter> filters) {
return Stream.ofNullable(filters)
.flatMap(Collection::stream)
.map(WorkListViewMapper::formatValue)
.map(f -> f.getCriteria() + f.getOperator() + f.getValue())
.collect(Collectors.joining(" AND ", "(", ")"));
}
An alternative way is to start streaming only if the list is non null and non empty:
default String getFiltersExpression(List<WorklistViewDto.Filter> filters) {
return Optional.ofNullable(filters)
.filter(fs -> !fs.isEmpty())
.map(fs -> fs.stream()
.map(WorkListViewMapper::formatValue)
.map(f -> f.getCriteria() + f.getOperator() + f.getValue())
.collect(Collectors.joining(" AND ", "(", ")")))
.orElse(null);
}
Then you get null instead of ().

using system.out.print with java streams

I have the below code to map each entry to a print statement, But it shows error.
Is something wrong in the way I understood Stream().map()?
How do I use System.out.println() within the streams API? how do I correct the following code?
public static void main(String[] args) {
Properties p = new Properties();
p.setProperty("name", "XYX");
p.setProperty("email", "xyx#mail.com");
p.setProperty("address", "addr-street-city");
p.entrySet().stream().map(e ->
System.out.println(" " + e.getKey().toString() + " " + e.getValue().toString() + ""));
}
If you want to use map:
p.entrySet().stream()
.map(e -> " "+e.getKey()+" "+e.getValue())
.forEach(System.out::println);
p.entrySet().forEach(e -> System.out.println(e.getKey() + " " + e.getValue()));
or
p.forEach((key, value) -> System.out.println(key + " " + value));
properties.entrySet().stream()
.map(entry -> String.format("%s : %s", entry.getKey(), entry.getValue()))
.forEach(System.out::println);
.map(...) - convert you key-value to a string format
.forEach(...) - prints your string
Your output should look like that :
address : addr-street-city
email : xyx#mail.com
name : XYX
p.entrySet().forEach(System.out::println)
map() should be used only if you want to alter/modify stream otherwise
for printing to the console forEach would do the work as it accepts BiConsumer to print key and value

Java Flux GroupedFlux count() print

Hi rectors all aver the world. I'm doing reactivi programming in Java. I'm a java/grails/react developer but first steps with reactive programming with Spring Boot, version 2 M7 in this case. In the next code:
#GetMapping(API_BASE_PATH + "/flux4")
public String flux4() {
StringBuilder sb = new StringBuilder();
Flux.just("alpha", "bravo", "charlie")
.map(String::toUpperCase)
.flatMap(s -> Flux.fromArray(s.split("")))
.groupBy(String::toString)
.sort(Comparator.comparing(GroupedFlux::key))
.map(group -> group.key() + " => " + group.count() + "; ")
.subscribe(sb::append);
return "flux 4: " + sb.toString();
}
I get the group.key() printed but how caon I get printed the group.count() ?
Any help is welcome
Cheers
Juan
I think I am reading the same book as you :)
Here is the solution using block().
Flux.just("alpha", "beta", "charlie")
.map(String::toUpperCase)
.flatMap(s -> Flux.fromArray(s.split("")))
.groupBy(String::toString)
.sort((o1,o2) -> o1.key().compareTo(o2.key()))
.flatMap(group -> Mono.just(Tuples.of(group.key(), group.count().block())))
.map(keyAndCount -> keyAndCount.getT1() + " => " + keyAndCount.getT2() + "; ")
I am wondering if there is an alternate way that doesn't call block()?. Now group.count() returns a Mono<Long>, and group.key() returns a String. It would be good if we could combine the two to form a Mono<Tuple2<String, Long>> without having to evaluate the result of the Mono. Seems like there should be a general method for doing that?
The book tried to use:
Mono.just(group.key()).and(group.count())
But that just listens for the completion events and returns a Mono<Void>, and therefore gives me compile errors...
Addendum: found it! Use the zip method:
Flux.just("alpha", "beta", "charlie")
.map(String::toUpperCase)
.flatMap(s -> Flux.fromArray(s.split("")))
.groupBy(String::toString)
.sort((o1,o2) -> o1.key().compareTo(o2.key()))
.flatMap(group -> Mono.zip(Mono.just(group.key()), group.count()))
.map(keyAndCount -> keyAndCount.getT1() + " => " + keyAndCount.getT2() + "; ")

How to apply OR filters to a java.util.stream?

I have a List:
List<String> mylist=Arrays.asList("Abla","Tbla","Cbla");
I have 2 filters prepared for my stream:
Predicate<String> startswith_A= s-> s.startsWith("A");
Predicate<String> startswith_T= s-> s.startsWith("T");
Following example filters for "A":
mylist.stream().filter(startswith_A).forEach(mystring -> {
System.out.println("filtered : " + mystring);
});
How can I apply the same for a filter "A" or "T"?
Something like:
mylist.stream().filter(startswith_A || startswith_T).forEach(mystring -> {
System.out.println("filtered : " + mystring);
});
I don't want to create a new predicate like:
Predicate<String> startswith_A_or_T= s-> s.startsWith("T") || s.startsWith("A");
How can I do this with 2 seperate Predicates?
You could simply use .or between the 2 Predicates:
mylist.stream()
.filter(startswith_A.or(startswith_T))
.forEach(mystring -> { System.out.println("filtered : " + mystring); });
Result:
filtered : Abla
filtered : Tbla

Categories