I want to handle the exception without using try catch clauses.
void test() {
list.stream().map(obj -> {
Batch batch = x.findByBatchId(obj.getBatchId())
/// exception is thrown here
.orElseThrow(() -> new ResourceNotFoundException(""));
if (obj.getBatchStatus() != null) {
batchHelper.updateBatchStatus(batch, obj.getBatchStatus());
}
});
}
You can't throw checked exception that way.
You can:
Throw RuntimeException and handle it later, maybe in another method, or don't handle it at all:
public void test() {
list.stream().map(obj -> {
Batch batch = x.findByBatchId(obj.getBatchId())
/// exception is thrown here
.orElseThrow(() -> new RuntimeException(""));
if (obj.getBatchStatus() != null) {
batchHelper.updateBatchStatus(batch, obj.getBatchStatus());
}
});
Use for statement and add your exception to method signature:
public void test() throws ResourceNotFoundException {
for (BatchInfo bi: list) {
.findByBatchId(bi.getBatchId()).orElseThrow(
() -> new ResourceNotFoundException(""));
}
It also depends on what you want to do in case of exception.
If you just want to let users know they do something wrong - use RuntimeException
If you want just update Batch status, use forEach, not map :
list.stream().forEach(obj -> {
Batch batch = x.findByBatchId(obj.getBatchId())
/// exception is thrown here
.orElseThrow(() -> new RuntimeException(""));
if (obj.getBatchStatus() != null) {
batchHelper.updateBatchStatus(batch, obj.getBatchStatus());
}
});
Related
I have the code snippet below. But i was wondering how to try catch exception with method references. I want to write try catch block for getUserByUserId method, probably log it and catch with NotFoundException. How do i refactor this code in case of method reference userService::getUserByUserId?
List<String> listofIds= ldapUsers.stream()
.map(PersonDTO::getUserId)
.map(userService::getUserByUserId)
.filter(Optional::isPresent)
.map(Optional::get)
.map(User::get_id)
.collect(Collectors.toList());
You could write a mapper function in the class you are doing the chaining of calls:
private Optional<User> getUser(PersonDTO personDTO) {
try {
return userService.getUserByUserId(personDTO.getUserId());
} catch (Exception ex) {
log.error("Your message here", ex);
throw new NotFoundException();
}
}
And use it like this:
List<String> listofIds = ldapUsers.stream()
.map(PersonDTO::getUserId)
.map(this::getUser)
.filter(Optional::isPresent)
.map(Optional::get)
.map(User::get_id)
.collect(Collectors.toList());
Leave the stream like that add the logic you want in the getUserByUserId method. If it doesn't find a user it logs the error and throws the exception.
EDIT: since you can't modify the method, you can do the following:
List<String> listofIds= ldapUsers.stream()
.map(PersonDTO::getUserId)
.map(userId -> {
User user = userService.getUserByUserId(userId);
if(user == null) {
log.error("User not found");
throw new NotFoundException();
}
return user;
})
.filter(Optional::isPresent)
.map(Optional::get)
.map(User::get_id)
.collect(Collectors.toList());
If it's unchecked exception, you don't need to do anything. But if its checked exception then you can do something like this:
..
.map((userService) -> {
try{
...//call userService#getUserByUserId
}catch(NotFoundException e){
//log or do something else
}
}) ...
Getting an UndeliverableException while using completable
public Completable createBucketWithStorageClassAndLocation() {
return Completable.complete()
.doFinally(() -> {
Bucket bucket =
storage.create(
BucketInfo.newBuilder(googleUploadObjectConfiguration.bucketName())
.setStorageClass(storageClass)
.setLocation(googleUploadObjectConfiguration.locationName())
.build());
}).doOnError(error -> LOG.error(error.getMessage()));
}
The exception is thrown from the Google storage which is correct, But trying to handle on doOnError method
Caused by: com.google.cloud.storage.StorageException: You already own this bucket. Please select another name.
RXJava exception
io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | com.google.cloud.storage.StorageException: You already own this bucket. Please select another name.
at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
at io.reactivex.internal.operators.completable.CompletableDoFinally$DoFinallyObserver.runFinally(CompletableDoFinally.java:99)
at io.reactivex.internal.operators.completable.CompletableDoFinally$DoFinallyObserver.onComplete(CompletableDoFinally.java:79)
at io.micronaut.reactive.rxjava2.RxInstrumentedCompletableObserver.onComplete(RxInstrumentedCompletableObserver.java:64)
at io.reactivex.internal.disposables.EmptyDisposable.complete(EmptyDisposable.java:68)
at io.reactivex.internal.operators.completable.CompletableEmpty.subscribeActual(CompletableEmpty.java:27)
at io.reactivex.Completable.subscribe(Completable.java:2309)
at io.micronaut.reactive.rxjava2.RxInstrumentedCompletable.subscribeActual(RxInstrumentedCompletable.java:51)
at io.reactivex.Completable.subscribe(Completable.java:2309)
at io.reactivex.internal.operators.completable.CompletableDoFinally.subscribeActual(CompletableDoFinally.java:43)
at io.reactivex.Completable.subscribe(Completable.java:2309)
at io.micronaut.reactive.rxjava2.RxInstrumentedCompletable.subscribeActual(RxInstrumentedCompletable.java:51)
at io.reactivex.Completable.subscribe(Completable.java:2309)
at io.reactivex.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
at io.reactivex.Completable.subscribe(Completable.java:2309)
at io.micronaut.reactive.rxjava2.RxInstrumentedCompletable.subscribeActual(RxInstrumentedCompletable.java:51)
at io.reactivex.Completable.subscribe(Completable.java:2309)
at io.reactivex.Completable.subscribe(Completable.java:2410)
at fete.bird.StartUp.onApplicationEvent(StartUp.java:24)
at fete.bird.StartUp.onApplicationEvent(StartUp.java:12)
at io.micronaut.context.DefaultBeanContext.notifyEventListeners(DefaultBeanContext.java:1323)
at io.micronaut.context.DefaultBeanContext.publishEvent(DefaultBeanContext.java:1308)
at io.micronaut.http.server.netty.NettyHttpServer.fireStartupEvents(NettyHttpServer.java:507)
at io.micronaut.http.server.netty.NettyHttpServer.start(NettyHttpServer.java:350)
at io.micronaut.http.server.netty.NettyHttpServer.start(NettyHttpServer.java:113)
at io.micronaut.runtime.Micronaut.lambda$start$2(Micronaut.java:77)
at java.base/java.util.Optional.ifPresent(Optional.java:176)
at io.micronaut.runtime.Micronaut.start(Micronaut.java:75)
at io.micronaut.runtime.Micronaut.run(Micronaut.java:311)
at io.micronaut.runtime.Micronaut.run(Micronaut.java:297)
at fete.bird.FeteBirdServiceApplication.main(FeteBirdServiceApplication.java:16)
From the rxjava documentation https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling I need to handle the error in the application.
I need to write the below code,
/ If Java 8 lambdas are supported
RxJavaPlugins.setErrorHandler(e -> { });
My question is where should I write this code. I have a Micronaut application using java or this is the only way to handle the exception.
You should add this in you application class
RxJavaPlugins.setErrorHandler(e -> { });
RxJavaPlugins.setErrorHandler { e ->
if (e is UndeliverableException) {
// Merely log undeliverable exceptions
log.error(e.message)
} else {
// Forward all others to current thread's uncaught exception handler
Thread.currentThread().also { thread ->
thread.uncaughtExceptionHandler.uncaughtException(thread, e)
}
}
More info here https://github.com/instacart/truetime-android/issues/98
Use Completable.fromAction and perhaps try-catch the exception instead of that doFinally contraption:
Completable.fromAction(() -> {
try {
Bucket bucket = storage.create(
BucketInfo.newBuilder(googleUploadObjectConfiguration.bucketName())
.setStorageClass(storageClass)
.setLocation(googleUploadObjectConfiguration.locationName())
.build());
} catch (Throwable error) {
LOG.error(error.getMessage());
}
})
Like #jonathan said, to solve this, I simply add in my App class the following code :
RxJavaPlugins.setErrorHandler { error ->
var e = error
if (e is UndeliverableException) {
e = e.cause ?:let { e }
}
if (e is IOException || e is SocketException) {
// fine, irrelevant network problem or API that throws on cancellation
return#setErrorHandler
}
if (e is InterruptedException) {
// fine, some blocking code was interrupted by a dispose call
return#setErrorHandler
}
if (e is NullPointerException || e is IllegalArgumentException) {
// that's likely a bug in the application
Thread.currentThread().uncaughtExceptionHandler?.uncaughtException(Thread.currentThread(), e)?:let {
FirebaseCrashlytics.log(Log.ERROR, TAG, "RxJavaPlugins uncaughtExceptionHandler is null but error is NullPointerException || error is IllegalArgumentException : $e")
}
return#setErrorHandler
}
if (e is IllegalStateException) {
// that's a bug in RxJava or in a custom operator
Thread.currentThread().uncaughtExceptionHandler?.uncaughtException(Thread.currentThread(), e)?:let {
FirebaseCrashlytics.log(Log.ERROR, TAG, "RxJavaPlugins uncaughtExceptionHandler is null but error is IllegalStateException : $e")
}
return#setErrorHandler
}
FirebaseCrashlytics.log(Log.ERROR, TAG, "Undeliverable exception received, not sure what to do $e")
}
You can adapt this code with your own Log system if you don't use FirebaseCrashlytics.
This code is edited from the source code in https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-2.0#error-handling (URL indicate in the error itself).
I'm not sure whether there is something wrong with my code or is it a bug in the reactor-core.
What i'm trying to do is to handle errors such as OptimisticLockingFailureException that happens when different threads are trying to save the same database record/entity at the same moment.
the example below is just a simpler form of what i have in the code and assuming that i have a main stream that contains a lot of operations and a sub-stream with the issue i have that should be executing some database calls using reactive mongo which is represented here with "processFlux()" method.
I'm applying a retry using onErrorResume() to call the same method from inside the "errorFallBack()" and my code goes like this:
import reactor.core.publisher.Flux;
public class TestClass1 {
public static void main(String[] args) {
Flux.range(1,10)
.doOnNext(System.out::println)
.publish(integerFlux -> processFlux(integerFlux))
.doOnNext(System.out::println)
.onErrorContinue((throwable, o) -> System.out.println("on error continue!!!"))
.subscribe(x -> sleepMillis(100));
}
private static void sleepMillis(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static Flux<Integer> processFlux(Flux<Integer> input) {
return input
.map(integer -> {
if (integer == 5)
throw new NumberFormatException();
return integer;
})
.onErrorResume(NumberFormatException.class, e -> errorFallBack(input));
}
private static Flux<Integer> errorFallBack(Flux<Integer> input) {
return input
.doOnNext(integer -> System.out.println("inside errorFallBack()"))
.flatMap(integer -> processFlux(Flux.just(integer)));
}
}
in the "processFlux()" method i assume there will be an exception thrown at some point of time and i try to catch it with the "onErrorResume()".
Since i'm using "publish()" in the main stream to wrap around "processFlux()" i expect that when the error happens the "onErrorResume()" is the one that will be called...the actual behaviour is that the "onErrorContinue()" of the min stream is the one that is being called.
is this a bug? or shall i change something in my code?
I'm using reactive mongo with spring boot 2.1.13.RELEASE,
spring cloud version Greenwich.SR5
and reactor-core version 3.3.6.RELEASE
I have been converting some code to be asynchronous. The original unit test used the annotation #Test(expected = MyExcpetion.class) but I don't think this will work because the exception I want to assert on is wrapped in java.util.concurrent.ExcutionException . I did try calling my future like this but my assertion is still failing and I don't love that I had to add in return null
myApiCall.get(123).exceptionally((ex) -> {
assertEquals(ex.getCause(),MyCustomException.class)
return null
}
I also tried this flavor but still not working
myApiCall.get(123).exceptionally((ex) -> {
assertThat(ex.getCause())
.isInstanceOF(MyException.class)
.hasMessage("expected message etc")
return null;
}
My API just throws exception if it can't find id. How should I be properly testing this? Can I use that original annotation in anyway?
my api call reaches out to db when run. In this test I am setting up my future to return an error so it doesn't actually try to communicate with anything. the code under test looks like this
public class myApiCall {
public completableFuture get(final String id){
return myService.getFromDB(id)
.thenApply(
//code here looks at result and if happy path then returns it after
//doing some transformation
//otherwise it throws exception
)
}
}
in the unit test I force myService.getFromDB(id) to return bad data so I can test exception and also keep this a unit test don't reach out to db etc.
Let's assume your API throws if called with 0:
public static CompletableFuture<Integer> apiCall(int id) {
return CompletableFuture.supplyAsync(() -> {
if (id == 0) throw new RuntimeException("Please not 0!!");
else return id;
});
}
You can test that it works as expected with the following code (I'm using TestNG but I suspect it won't be too difficult to translate into a JUnit test):
#Test public void test_ok() throws Exception {
CompletableFuture<Integer> result = apiCall(1);
assertEquals(result.get(), (Integer) 1);
}
#Test(expectedExceptions = ExecutionException.class,
expectedExceptionsMessageRegExp = ".*RuntimeException.*Please not 0!!")
public void test_ex() throws Throwable {
CompletableFuture<Integer> result = apiCall(0);
result.get();
}
Note that the second test uses the fact that the ExecutionException message will contain the original exception type and message and captures the expectation with a regex. If you can't do that with JUnit, you can call result.get() in a try/catch block and call throw e.getCause(); in the catch block. In other words, something like this:
#Test(expectedExceptions = RuntimeException.class,
expectedExceptionsMessageRegExp = "Please not 0!!")
public void test_ex() throws Throwable {
CompletableFuture<Integer> result = apiCall(0);
try {
result.get();
} catch (ExecutionException e) {
throw e.getCause();
}
}
You can try also alternative option:
import org.hamcrest.core.IsInstanceOf;
import org.junit.rules.ExpectedException;
public class Test() {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void myApiCallTest() {
thrown.expect(ExcutionException.class);
thrown.expectCause(IsInstanceOf.instanceOf(MyException.class));
thrown.expectMessage("the message you expected");
myApiCall.get("");
}
}
Assuming that:
public class myApiCall {
public completableFuture get(final String id) {
// ...
throw new ExcutionException(new MyException("the message you expected"))
}
}
Assume that you have a class and you want to test a method which returns a completable future:
public class A {
private final Api api;
public A(Api api) { this.api = api;}
public CompletableFuture<Void> execute(Integer input) {
final CompletableFuture<Void> future = api.execute(input)
.thenApplyAsync(result -> doSomething())
.exceptionally(ex -> doFailure());
return future;
}
}
To test the execution of "doSomething()" then you may use mockito and do the following:
// prepare test
final Api api = mock(Api.class)
final A a = new A(api);
when(api.execute(any(Integer.class)))
.thenReturn(CompletableFuture.completedFuture(null));
// execute
final CompletableFuture<Void> result = a.execute(input);
// validate
...
To test "doFailure" do the following:
when(api.execute(any(Integer.class))).thenAnswer(answer -> {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
});
// execute
final CompletableFuture<Void> result = a.execute(input);
// validate
assertTrue(result.isCompletedExceptionally());
that is easy thing doing in junit-4. Are you remember the #RunWith annotation? Yes, write your own TestRunner to intercept the exception before the junit expected exception processor is invoked, for example:
public class ConcurrentRunner extends BlockJUnit4ClassRunner {
public ConcurrentRunner(Class<?> klass) throws InitializationError {
super(klass);
}
#Override
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test,
Statement next) {
return super.possiblyExpectingExceptions(
method, test, throwingActualException(next)
);
}
private Statement throwingActualException(Statement next) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
try {
next.evaluate();
} catch (ExecutionException | CompletionException source) {
throw theActualExceptionOf(source);
}
}
private Throwable theActualExceptionOf(Exception source) {
return source.getCause() != null ? source.getCause() : source;
}
};
}
}
just annotated with #RunWith(ConcurrentRunner.class) on the test, you needn't change your test code at all. for example:
#RunWith(ConcurrentRunner.class)
public class ConcurrentExpectedExceptionTest {
#Test(expected = IllegalArgumentException.class)
public void caughtTheActualException() throws Throwable {
myApiCall().join();
}
private CompletableFuture<Object> myApiCall() {
return CompletableFuture.supplyAsync(() -> {
throw new IllegalArgumentException();
});
}
}
I modified this code to do several tasks in one directory:
public class HDDSerialNumber
{
public void getHDDSerialNumber() throws IOException
{
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD Model
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/model")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
// Get HDD Vendor
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/vendor")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
// Get HDD Vendor
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/state")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
}
static <T, R> Function<T, R> wrap(IOFunction<T, R> f)
{
return t ->
{
try
{
return f.apply(t);
}
catch (IOException ex)
{
throw new UncheckedIOException(ex);
}
};
}
interface IOFunction<T, R>
{
R apply(T in) throws IOException;
}
}
But when I run the code I get this error stack:
run:
ST320LT012-9WS14
Exception in thread "main" java.lang.IllegalStateException: Iterator already obtained
at sun.nio.fs.UnixDirectoryStream.iterator(UnixDirectoryStream.java:118)
at sun.nio.fs.UnixSecureDirectoryStream.iterator(UnixSecureDirectoryStream.java:73)
at java.lang.Iterable.spliterator(Iterable.java:101)
at hardware.HDDSerialNumber.getHDDSerialNumber(HDDSerialNumber.java:25)
at hardware.Hardware.main(Hardware.java:12)
Java Result: 1
Can you help me to fix the code, please? I suppose that Iterator already obtained must be used only once in this example but I don't have idea how to fix this.
While DirectoryStream extends Iterable, it is not a general-purpose Iterable as it supports only a single Iterator; invoking the iterator method to obtain a second or subsequent iterator throws IllegalStateException.
(Source)
The iterator of the Iterable returned by Files.newDirectoryStream (DirectoryStream implements Iterable) can only be used once. You can solve it by calling Files.newDirectoryStream separately for each of the 3 streams you are creating.
Instead of creating one DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"); and using it in all 3 StreamSupport.stream calls, create 3 DirectoryStream<Path>.
Example :
public void getHDDSerialNumber() throws IOException
{
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD Model
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/model")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD Vendor
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/vendor")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD State
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/state")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
}
EDIT :
If you want to handle the case of a file that doesn't exist without interrupting the program execution, catch the exception thrown in that case.
For example :
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD State
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/state"))
.flatMap(wrap(path - > try {
return Files.lines(path);
} catch (IOException ioEx) {
return Stream.empty();
}))
.forEach(System.out::println);
}
This would catch the exception and return an empty Stream.