Having spat the Google Guava kool-aid back out of our mouths, and diving head-first into our new infatuation with VAVR and its sparkling ideals, say we are map()ping a Stream, performing foldLeft() on a Traversable, or similar, and one of the inner functions throws a checked exception. Now we are staring down at compiler errors, unhandled exceptions.
How aught such exceptions be handled ideally, with idiomatic VAVR. What does the resulting code look like, what are the patterns. We have Options and Trys... How does it all come together.
Oh, and tricks like sneaky exceptions disinterest us.
You can use CheckedFunction[0-8].liftTry() to convert a function that throws checked exceptions to a total function that returns the result of the original function wrapped in a Try. If the original function returns a value without throwing, it will be wrapped in a Success, if it throws, the exception will be wrapped in a Failure.
You will then need to take a decision on how to handle errors in the context of multiple values. Here are some examples on what you could do with a bunch of Try values.
Array<String> input = Array.of(
"123", "456", "789", "not a number", "1111", "another non-number"
);
// try and parse all the strings
Array<Try<Integer>> trys = input.map(CheckedFunction1.liftTry(Integer::parseInt));
// you can take just the successful values
Array<Integer> values = trys.flatMap(Try::iterator);
// you can look just for the failures
Array<Throwable> failures = trys.filter(Try::isFailure).map(Try::getCause);
// you can partition by the outcome and extract values/errors
Tuple2<Traversable<Integer>, Traversable<Throwable>> partition =
trys.partition(Try::isSuccess)
.map(
seq -> seq.map(Try::get),
seq -> seq.map(Try::getCause)
);
// you can do a short-circuiting parse of the original sequence
// this will stop at the first error and return it as a failure
// or take all success values and wrap them in a Seq wrapped in a Try
Try<Seq<Integer>> shortCircuit = Try.sequence(
input.iterator() //iterator is lazy, so it's not fully evaluated if not needed
.map(CheckedFunction1.liftTry(Integer::parseInt))
);
// Failure(java.lang.NumberFormatException: For input string: "not a number")
Of course, you can use any other vavr collection in place of Array.
Related
Runnable returns void in Java. Why does Mono.fromRunnable return Mono<T> instead of Mono<Void>?
API documentation of Mono#fromRunnable states about the type parameter:
The generic type of the upstream, which is preserved by this operator
It allows to use it as part of an operation chain without altering the resulting type.
Example:
This code:
Mono<String> myMono = Mono.empty();
myMono = myMono.switchIfEmpty(Mono.fromRunnable(()
-> System.err.println("WARNING, empty signal !")));
String value = myMono.block(Duration.ofSeconds(1));
System.out.println("Exported value is "+value);
Produces:
WARNING, empty signal !
Exported value is null
The code above compiles fine, and provide a Mono for a String without having to add additional casts.
The posted example is not very good, but I suppose that this signature allows to use fromRunnable to launch side-effects in some case, without disturbing value type of the overall operation chain.
It's kinda like Mono.empty() with additional computing embedded with it.
I want to use Optional for handling null values, the "tricky" part which I cannot think of what is the best way to do - is that I want to do logging if value is null. I can achieve that with following code - but it feels awkward.
(Update: I have posted my own answer, with Optional from Java 9)
Lets say code looks like this:
// logLine.getSomeProperty returns Optional<String>
List<LogDetails> logDetails = logLine.getSomeProperty()
.map(this::extractLogDetails)
.orElseGet(() -> logError(logLine));
List<LogDetails> extractLogDetails(String s) {
List<LogDetails> logDetails = new ArrayList<>();
String sp = "(?:([A-Z0-9]{5,7})-([A-Z0-9]{9})-(.{4}))";
Pattern p = Pattern.compile(sp, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(s);
while (m.find()) {
logDetails.add(new LogDetails(m.group(1), m.group(2), m.group(3)));
}
return logDetails;
}
List<LogDetails> logError(LogLine logLine) {
log.error("Error while ... {} ", logLine));
persistence.setErrorStatus(logLine, FAILED_PARSING);
return new ArrayList<>();
}
It would do what I want, but I have several "problems" with it.
I found it odd, that method called orElseGet is used for logging
errors.
I could replace orElseGet with orElseThrow and logError there and DO NOT throw anything - which I don't like either.
logError method returns List which I don't use and it looks weird to return something from method which should be void.
Simply there must be better way
Cases where someProperty is not null, but there are no matches - I would like to log as well, but for that I would need another line of code to check if logDetails.size() == 0
The orElseGet is not really intended as an error handling mechanism, but as a way to generate a different default value in case the Optional instance is not carrying any.
If you want to check if the Optional is empty explicitly, simply use the Optional.isPresent() check, and do the logError() in that case.
What you need to think first is, if the Optional is empty, what do you want to do? Apart from logging the error, do you want to proceed with an empty list?
If yes then you could have something like this:
List<LogDetails> logDetails = logLine.getSomeProperty()
.map(this::extractLogDetails)
.orElseGet(Collections::emptyList);
After which you could do:
if (logDetails.isEmpty()) {
logError(logline);
}
Alternatively, if you do not want to have an empty list at all, you could keep things at optional level. This way, both cases where the getSomeProperty() is empty or when the generated list is empty are handled in the same way.
Optional<List<LogDetails>> logDetailsOpt = logLine.getSomeProperty()
.map(this::extractLogDetails)
.filter(list -> !list.isEmpty());
if (!logDetailsOpt.isPresent()) {
logError(logLine);
}
In both cases, logError() is not expected to return anything. It is doing what it is intended to do in its name, logging the error.
Rather than trying to overuse the functionality of Optional, try to make your intentions in your code clear. There is more value in readability.
Rather than changing result type or logging inside stream you can simply return partitioned Map. Then after obtaining the result, execute log function on the resulting map accordingly.
Map<Boolean, List<String>> map = Stream.of("a", "aaa", "aaaa")
----
.collect(() -> Collectors.partitioningBy(predicate))
----
While I am grateful for the answers, but I just recently find out, that Java 9 introduced new method to Optional and I like it best.
Here is example.
Optional.ofNullable(user).ifPresentOrElse( u -> logger.info("User is:" + u.getEmail()),
() -> logger.info("User not found"));
Lets say I have a very long string:
trillions of chunks
|
v
/asdf/........./bar/baz/foo
^
|
what I try to find is closer to the right:
the data after 9999999th '/'
I need all the chunks of data up to this slash, but not any slashes. I see this as a stream and want to do the following:
I start to read symbols from the back and count slashes.
Anything but slash I put into Last-In-First-Out data structure.
In order not to wait for the whole operation to finish, I start reading data from the lifo datastructure as it becomes available.
I terminate after the 9999999th '/'
Can something like this be accomplished with reactive streams and how?
I think the following code will achve what you want
#Test
public void reactiveParser() throws InterruptedException {
ConnectableFlux<String> letters = Flux.create((Consumer<? super FluxSink<String>>) t -> {
char[] chars = "sfdsfsdf/sdf/sdfs/dfsdfsd/fsd/fsd/fs/df/sdf".toCharArray();
for (char c : chars) {
t.next(String.valueOf(c));
}
}).publish();
letters
.window(
letters.filter( t -> t.equals("/"))
)
.flatMap( t -> t.collectList())
.map( t -> t.stream().collect(Collectors.joining()))
.subscribe(t -> {
System.out.println(t);
});
letters.connect();
}
The example above utilizes the project reactor. Which is pretty cool way of doing the reactive stuff inside of java.
There is plenty of optimization that can be done in the following code. Not using Strings to represent a single letter would be one of them.
But the basic idea is there. You create flux/observable that emits a letters as they come in and make that observable shareable (you have to window over emitting values) and then just collect them in to a single sting. The code bellow should give the following output:
sfdsfsdf
/sdf
/sdfs
/dfsdfsd
/fsd
/fsd
/fs
/df
Of course you have to utilize non-blocking connection so the bytes could be read asynchronously.
I have some data stored in a JPA Repository that I am trying to process. I would like to be able to use Java 8 Streams to do so, but can not figure out how to get the required information. This particular 'Entity' is actually only for recovery, so it holds items that would need to be processed after something like a power-fail/restart.
Using pre-Java 8 for-loops the code would look like:
List<MyEntity> deletes = myEntityJpaRepository.findByDeletes();
for (MyEntity item : deletes) {
String itemJson = item.getData();
// use a Jackson 'objectMapper' already setup to de-serialize
MyEventClass deleteEvent = objectMapper.readValue(itemJson, MyEventClass.class);
processDelete(deleteEvent, item.getId());
}
The problem arises from the two parameter method called at the very end. Using Streams, I believe I would do:
// deletes.stream()
// .map(i -> i.getData())
// .map(event -> objectMapper.readValue(event, MyEventClass.class))
// .forEach(??? can't get 'id' here to invoke 2 parameter method);
I have a solution (without Streams) that I can live with. However I would think this problem comes up a lot, thus my question is: IN GENERAL, is there a way using Streams to accomplish what I am trying to do?
Why not a Pair return on your map operation:
.map(i -> new Pair<>(i.getData(), i.getId()))
.map(pair -> new Pair<>(objectMapper.readValue(pair.getLeft(), MyEventClass.class), pair.getRight())
.forEach(p -> processDelete(pair.getLeft(), pair.getRight()))
I did not compile this, so there might be minor things to fix. But in general, you would need a Holder to pass your objects to the next stage in such a case. Either a Pair or some type or even a array.
Why not doing it simply this way?
deletes.forEach(item ->
processDelete(objectMapper.readValue(item.getData(), MyEventClass.class),
item.getId()));
This is a start at least, I guess it is dependent on why you want to use stream and how much you want to make it more functional
List<MyEntity> deletes = myEntityJpaRepository.findByDeletes();
deletes.stream().foreach(item -> {
String itemJson = item.getData();
// use a Jackson 'objectMapper' already setup to de-serialize
MyEventClass deleteEvent = objectMapper.readValue(itemJson, MyEventClass.class);
processDelete(deleteEvent, item.getId());
});
I'm writing a custom Hive UDF to parse key/value pairs in a Map according to the values defined in a properties file. The format being validate(Map<String, String>, String). The intent being to have the first argument be the Map to evaluate, and the second to contain the properties file.
The issue I'm having is that the GenericUDF class appears to expect both of these values to be dynamic for the query, as the initialize() function cast arguments as ObjectInspectors, which from the looks of things give no possible option to return the object they're inspecting.
I want the initialize function to load the properties file, and the evaluate function to return pass/fail. This isn't nearly enough code to cover everything I've tried, but hopefully it gives someone who knows what they're doing a good idea of the issue:
public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
mapOI = (MapObjectInspector) arguments[0];
StringObjectInspector stringOI = (StringObjectInspector) arguments[1];
try {
// Begin Debug
System.out.println(stringOI.getPrimitiveJavaObject(((DeferredObject) arguments[1]).get()));
// End Debug
loadProperties(stringOI.getPrimitiveJavaObject(((DeferredObject) arguments[1]).get()));
}
catch (HiveException exception) {
throw new UDFArgumentTypeException(1, "Failed to cast properties file path for evaluation by loadProperties... What did you do?");
}
The initialize method is called only once and therefore the parameters can only be considered as metadata describing the the input types that the evaluate method expects to receive. Thus the only place where you can actually see the input values is in the evaluate method which is potentially called multiple times, usually once per row. You should really only use the initialize function to validate the types rather than the actual values. That is, in your case, check that the input is of type map with primitive-category elements of type string. Validation for the actual values should be carried out in the evaluate method.