Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling exceptions by Reactor Spring

I am using Reactor 2 and Spring 4. Here's the typical code that I have - a Consumer working with a repository

@Consumer
public class ApplicationService {

   @Selector(value="/applications/id", type = SelectorType.URI)
   @ReplyTo
   public Application byApplicationId(String id) throws ApplicationNotFoundException {
      Application app = appRepo.findOne(id);
      if(app == null) 
        throw new ApplicationNotFoundException("Application `" + id + "` could not be found.");
      return app;
   }
}

Then I have a controller that passes the request to an eventBus into which I pass requests and return a Promise

@RestController
@RequestMapping("/applications")
public class ApplicationsController {
   @RequestMapping(value = "/{id}", method = GET, produces = APPLICATION_JSON_VALUE)
   public Promise<Event<Application>> byApplicationId(@PathVariable final String id) {
      final Promise<Event<Application>> p = Promises.prepare(env);
      eventBus.sendAndReceive("/applications/id", Event.wrap(id), p);
      return p;
   }

}

Things work but in case of ApplicationService throwing an exception Promises value is not set but I do get following in the console:

16:46:58.003 [main] ERROR reactor.bus.EventBus - null
java.lang.reflect.UndeclaredThrowableException
    at org.springframework.util.ReflectionUtils.rethrowRuntimeException(ReflectionUtils.java:302)
...
Caused by: com.metlife.harmony.exceptions.ApplicationNotFoundException: Application `2860c555-0bc4-45e6-95ea-f724ae3f4464` could not be found.
    at com.metlife.harmony.services.ApplicationService.byApplicationId(ApplicationService.java:46) ~[classes/:?]
...
Caused by: reactor.core.support.Exceptions$ValueCause: Exception while signaling value: reactor.bus.Event.class : Event{id=null, headers={}, replyTo=reactor.bus.selector.Selectors$AnonymousKey@4, key=/applications/id, data=2860c555-0bc4-45e6-95ea-f724ae3f4464}

Questions are:

  1. do I use Reactor and eventBus in the wrong way ? and if so, what is the right way

  2. perhaps this functionality is not implemented yet

like image 643
EvgeniySharapov Avatar asked May 06 '15 20:05

EvgeniySharapov


People also ask

How do you handle errors in reactors?

Handling exceptions in Project Reactor with the onErrorReturn() operator. The onErrorReturn() will emit the specified callback value when the error occurs. In this way, the code will recover from the exception. The above example shows that the code completely recovered and continued like the error never happened.

How exception handling is done in spring?

Spring MVC Framework provides following ways to help us achieving robust exception handling. Controller Based - We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument.

How do you handle exceptions in spring and spring boots?

Exception HandlerThe @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client. Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown.


2 Answers

I guess I re-evaluated the strategy of using Reactor in my Spring application.

Now my controller looks like

@RestController
public class GreetingController {

    @Autowired
    private GreetingService greetingService;

    @RequestMapping("/greeting")
    public Promise<ResponseEntity<?>> greeting(final @RequestParam(value = "name", defaultValue = "World") String name) {
        return greetingService.provideGreetingFor(name).map(new Function<Greeting, ResponseEntity<?>>() {
            @Override
            public ResponseEntity<?> apply(Greeting t) {
                return new ResponseEntity<>(t, HttpStatus.OK);
            }
        }).onErrorReturn(WrongNameException.class, new Function<WrongNameException, ResponseEntity<?>>() {
            @Override
            public ResponseEntity<?> apply(WrongNameException t) {
                return new ResponseEntity<>(t.getMessage(), HttpStatus.BAD_REQUEST);
            }
        }).next();
    }
}

And the service looks like

@Service
public class GreetingService {
    @Autowired
    private Environment env;

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    public Stream<Greeting> provideGreetingFor(String name) {
        return Streams.just(name).dispatchOn(env).map(new Function<String, Greeting>() {
            @Override
            public Greeting apply(String t) {
                if (t == null || t.matches(".*\\d+.*"))
                    throw new WrongNameException();
                return new Greeting(counter.incrementAndGet(), String.format(template, t));
            }
        });
    }
}

What's bad is that now I have to use Stream<T> as a result of the method in the service (which is supposedly a business logic), so anyone using the service is now aware of the Stream-ish nature of the service and as a result Stream bleeds into other parts of the code, e.g. now I may have to use await() in the code using the service.

Full application is available at https://github.com/evgeniysharapov/spring-reactor-demo

like image 60
EvgeniySharapov Avatar answered Nov 07 '22 03:11

EvgeniySharapov


A dedicated chapter for handling exception and errors in the Reactive reference is here to read:

https://projectreactor.io/docs/core/release/reference/#error.handling

In any case, reactive pipeline is "contiguous" in its neutral meaning. Hardly you could prevent it from being noticed by the consumer of your methods.

like image 35
WesternGun Avatar answered Nov 07 '22 02:11

WesternGun