Vaadin 23 ErrorHandler for Async exceptions - vaadin

I have some basic ExceptionHandler view:
#ParentLayout(MainLayout.class)
#AnonymousAllowed
public class ExceptionHandler extends VerticalLayout implements HasErrorParameter<Exception> {
static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);
#Override
public int setErrorParameter(BeforeEnterEvent event, ErrorParameter<Exception> parameter) {
logger.error("Error", parameter.getException());
Label label = new Label(parameter.getException().getMessage());
add(label);
return HttpServletResponse.SC_NOT_FOUND;
}
}
which works fine when error occurs in synchronous calls.
But when I use #Async feature, like for example:
ListenableFuture<DecisionMatrixPage> listenableFuture = container.queryDataAsync();
var ui = UI.getCurrent();
listenableFuture.addCallback(page -> {
ui.access(() -> {
//do something
});
}, err -> {
logger.error("Error", err);
throw new AsyncException("Async error", err);
});
I may only log the error but unfortunately rethrowing of AsyncException is not catched by ExceptionHandler view. How to properly re-throw exception to be catched by ExceptionHandler ?

View implementing HasErrorParameter is used only when exception occurs during navigation. For more general exception handler you need to setup ErrorHandler using VaadinSession#setErrorHandler. The right place to setup the error handler is usually in the session init listener.
https://vaadin.com/docs/latest/advanced/session-and-ui-init-listener

Related

Custom Exception in Dart programming language

I created a custom exception called DepositeException and trying to access its custom exception message (errorMessage) in main method but it's throwing error. What might be the problem in following code.
void main() {
try {
depositAmount(-100);
} catch (e) {
print(e.errorMessage());
}
}
class DepositException implements Exception {
String errorMessage() {
return "you cannot enter amount less then 0";
}
}
void depositAmount(int amount) {
if (amount < 0) {
throw new DepositException();
}
}
This happen because ~~exception catching in Dart is unchecked~~ the exception type is unspecified so it will return Object (thanks to #jamesdlin for the correction).
To catch the custom exception, you need specify the type like this:
try{
depositAmount(-100);
} on DepositException catch (e){
print(e.errorMessage());
}
Reference:
How to create a custom exception and handle it in dart

SpringAMQP errorHandler and returnExceptions problem

i am not sure my understanding to errorHandler and returnExceptions is right or not.
but here is my goal: i sent a message from App_A, use #RabbitListener to receive message in App_B.
according to the doc
https://docs.spring.io/spring-amqp/docs/2.1.3.BUILD-SNAPSHOT/reference/html/_reference.html#annotation-error-handling
i assume if APP_B has a business exception during process the message,through set errorHandler and returnExceptions in a right way on #RabbitListener can let the exception back to App_A.
do I understood correctly?
if i am rigth, how to use it in a right way?
with my code, i get nothing in APP_A .
here is my code in APP_B
errorHandler:
#Component(value = "errorHandler")
public class ErrorHandler implements RabbitListenerErrorHandler {
#Override
public Object handleError(Message arg0, org.springframework.messaging.Message<?> arg1,
ListenerExecutionFailedException arg2) throws ListenerExecutionFailedException {
throw new ListenerExecutionFailedException("msg", arg2, null);
}
}
RabbitListener:
#RabbitListener(
bindings = #QueueBinding(
value = #Queue(value = "MRO.updateBaseInfo.queue", durable = "true"),
exchange = #Exchange(name = "MRO_Exchange", type = ExchangeTypes.DIRECT, durable = "true"),
key = "baseInfoUpdate"
),
// errorHandler = "errorHandler",
returnExceptions = "true"
)
public void receiveLocationChangeMessage(String message){
BaseUpdateMessage newBaseInfo = JSON.parseObject(message, BaseUpdateMessage.class);
dao.upDateBaseInfo(newBaseInfo);
}
and code in APP_A
#Component
public class MessageSender {
#Autowired
private RabbitTemplate rabbitTemplate;
public void editBaseInfo(BaseUpdateMessage message)throws Exception {
//and i am not sure set RemoteInvocationAwareMessageConverterAdapter in this way is right
rabbitTemplate.setMessageConverter(new RemoteInvocationAwareMessageConverterAdapter());
rabbitTemplate.convertAndSend("MRO_Exchange", "baseInfoUpdate", JSON.toJSONString(message));
}
}
i am very confuse with three points:
1)do i have to use errorHandler and returnExceptions at the same time? i thought errorHandler is something like a postprocessor that let me custom exception.if i don't need a custom exception can i just set returnExceptions with out errorHandler ?
2)should the method annotated with #RabbitListener return something or void is just fine?
3)in the sender side(my situation is APP_A), does have any specific config to catch the exception?
my workspace environment:
Spring boot 2.1.0
rabbitMQ server 3.7.8 on docker
1) No, you don't need en error handler, unless you want to enhance the exception.
2) If the method returns void; the sender will end up waiting for timeout for a reply that will never arrive, just in case an exception might be thrown; that is probably not a good use of resources. It's better to always send a reply, to free up the publisher side.
3) Just the RemoteInvocationAwareMessageConverterAdapter.
Here's an example:
#SpringBootApplication
public class So53846303Application {
public static void main(String[] args) {
SpringApplication.run(So53846303Application.class, args);
}
#RabbitListener(queues = "foo", returnExceptions = "true")
public String listen(String in) {
throw new RuntimeException("foo");
}
#Bean
public ApplicationRunner runner(RabbitTemplate template) {
template.setMessageConverter(new RemoteInvocationAwareMessageConverterAdapter());
return args -> {
try {
template.convertSendAndReceive("foo", "bar");
}
catch (Exception e) {
e.printStackTrace();
}
};
}
}
and
org.springframework.amqp.AmqpRemoteException: java.lang.RuntimeException: foo
at org.springframework.amqp.support.converter.RemoteInvocationAwareMessageConverterAdapter.fromMessage(RemoteInvocationAwareMessageConverterAdapter.java:74)
at org.springframework.amqp.rabbit.core.RabbitTemplate.convertSendAndReceive(RabbitTemplate.java:1500)
at org.springframework.amqp.rabbit.core.RabbitTemplate.convertSendAndReceive(RabbitTemplate.java:1433)
at org.springframework.amqp.rabbit.core.RabbitTemplate.convertSendAndReceive(RabbitTemplate.java:1425)
at com.example.So53846303Application.lambda$0(So53846303Application.java:28)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:804)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:794)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:324)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.example.So53846303Application.main(So53846303Application.java:15)
Caused by: java.lang.RuntimeException: foo
at com.example.So53846303Application.listen(So53846303Application.java:20)
As you can see, there is a local org.springframework.amqp.AmqpRemoteException with the cause being the actual exception thrown on the remote server.

Exception in step at specflow

I'm calling in a function to step (specflow),
Then(step's string)
and in step I throw an exception. I want to catch the exception with a different function and not in the step itself. Do someone know how to do it?
Thanks
This is not possible with SpecFlow.
SpecFlow interprets an exception in an step as error and stops the execution of the Scenario.
What you can do, is to catch the exception in your step and save it in a field of the binding class. Then in the second step you can check this field.
like this:
[Binding]
public class BindingClass
{
private Exception _exception;
[When("an exception is thrown")
public void ExceptionThrown()
{
try {
.... //your code that throws an exception
}
catch(Exception e)
{
_exception = e;
}
}
[Then("the exception has the message '(.*)'")]
public void ExceptionHasTheMessage(string message)
{
if (_exception != null)
{
Assert.Equal(_exception.Message, message);
}
}
}
I use the AfterStep hook available in SpecFlow.
The code looks like this:
[AfterStep]
public void AfterStep()
{
if(_scenarioContext.ScenarioExecutionStatus.ToString().Equals("TestError"))
{
Logger.LogScreenshot(exception, _scenarioContext.ScenarioInfo.Title);
}
}
This piece of code will catch your exception and remaining steps will be skipped.

SimpleRabbitListenerContainerFactory and defaultRequeueRejected

As per doc, defaultRequeueRejected's default value is true, but looking at code it seems its false. I am not sure if I am missing anything or we have to change that in SimpleRabbitListenerContainerFactory.java
EDIT
Sample code, after putting message in test queue, I expect it to stay in queue since its failing but it is throwing it out. I want message to be retried so I configured that in container factory if it fails after retry I want it to be back in queue. I am sure I am missing understanding here.
#SpringBootApplication
public class MsgRequeExampleApplication {
public static void main(String[] args) {
SpringApplication.run(MsgRequeExampleApplication.class, args);
}
#Bean(name = "myContainerFactory")
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setMissingQueuesFatal(false);
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(500);
factory.setAdviceChain(new Advice[] { org.springframework.amqp.rabbit.config.RetryInterceptorBuilder.stateless()
.maxAttempts(2).backOffPolicy(backOffPolicy).build() });
return factory;
}
#RabbitListener(queues = "test", containerFactory = "myContainerFactory")
public void processAdvisory(Message message) throws MyBusinessException {
try{
//Simulating exception while processing message
String nullString=null;
nullString.length();
}catch(Exception ex){
throw new MyBusinessException(ex.getMessage());
}
}
public class MyBusinessException extends Exception {
public MyBusinessException(String msg) {
super(msg);
}
}
}
There is a good description in the SimpleMessageListenerContainer JavaDocs:
/**
* Set the default behavior when a message is rejected, for example because the listener
* threw an exception. When true, messages will be requeued, when false, they will not. For
* versions of Rabbit that support dead-lettering, the message must not be requeued in order
* to be sent to the dead letter exchange. Setting to false causes all rejections to not
* be requeued. When true, the default can be overridden by the listener throwing an
* {#link AmqpRejectAndDontRequeueException}. Default true.
* #param defaultRequeueRejected true to reject by default.
*/
public void setDefaultRequeueRejected(boolean defaultRequeueRejected) {
this.defaultRequeueRejected = defaultRequeueRejected;
}
Does it make sense to you?
UPDATE
To requeue after retry exhausting you need to configure some custom MessageRecoverer on the RetryInterceptorBuilder with the code like:
.recoverer((message, cause) -> {
ReflectionUtils.rethrowRuntimeException(cause);
})
This way the exception will be thrown to the listener container and according its defaultRequeueRejected the message will be requeued or not.

Not able to receive onNext and onComplete call on subscribed mono

I was trying reactor library and I'm not able to figure out why below mono never return back with onNext or onComplete call. I think I missing very trivial thing. Here's a sample code.
MyServiceService service = new MyServiceService();
service.save("id")
.map(myUserMono -> new MyUser(myUserMono.getName().toUpperCase(), myUserMono.getId().toUpperCase()))
.subscribe(new Subscriber<MyUser>() {
#Override
public void onSubscribe(Subscription s) {
System.out.println("Subscribed!" + Thread.currentThread().getName());
}
#Override
public void onNext(MyUser myUser) {
System.out.println("OnNext on thread " + Thread.currentThread().getName());
}
#Override
public void onError(Throwable t) {
System.out.println("onError!" + Thread.currentThread().getName());
}
#Override
public void onComplete() {
System.out.println("onCompleted!" + Thread.currentThread().getName());
}
});
}
private static class MyServiceService {
private Repository myRepo = new Repository();
public Mono<MyUser> save(String userId) {
return myRepo.save(userId);
}
}
private static class Repository {
public Mono<MyUser> save(String userId) {
return Mono.create(myUserMonoSink -> {
Future<MyUser> submit = exe.submit(() -> this.blockingMethod(userId));
ListenableFuture<MyUser> myUserListenableFuture = JdkFutureAdapters.listenInPoolThread(submit);
Futures.addCallback(myUserListenableFuture, new FutureCallback<MyUser>() {
#Override
public void onSuccess(MyUser result) {
myUserMonoSink.success(result);
}
#Override
public void onFailure(Throwable t) {
myUserMonoSink.error(t);
}
});
});
}
private MyUser blockingMethod(String userId) throws InterruptedException {
Thread.sleep(5000);
return new MyUser("blocking", userId);
}
}
Above code only prints Subcribed!main. What I'm not able to figure out is why that future callback is not pushing values through myUserMonoSink.success
The important thing to keep in mind is that a Flux or Mono is asynchronous, most of the time.
Once you subscribe, the asynchronous processing of saving the user starts in the executor, but execution continues in your main code after .subscribe(...).
So the main thread exits, terminating your test before anything was pushed to the Mono.
[sidebar]: when is it ever synchronous?
When the source of data is a Flux/Mono synchronous factory method. BUT with the added pre-requisite that the rest of the chain of operators doesn't switch execution context. That could happen either explicitly (you use a publishOn or subscribeOn operator) or implicitly (some operators like time-related ones, eg. delayElements, run on a separate Scheduler).
Simply put, your source is ran in the ExecutorService thread of exe, so the Mono is indeed asynchronous. Your snippet on the other hand is ran on main.
How to fix the issue
To observe the correct behavior of Mono in an experiment (as opposed to fully async code in production), several possibilities are available:
keep subscribe with system.out.printlns, but add a new CountDownLatch(1) that is .countDown() inside onComplete and onError. await on the countdown latch after the subscribe.
use .log().block() instead of .subscribe(...). You lose the customization of what to do on each event, but log() will print those out for you (provided you have a logging framework configured). block() will revert to blocking mode and do pretty much what I suggested with the CountDownLatch above. It returns the value once available or throws an Exception in case of error.
instead of log() you can customize logging or other side effects using .doOnXXX(...) methods (there's one for pretty much every type of event + combinations of events, eg. doOnSubscribe, doOnNext...)
If you're doing a unit test, use StepVerifier from the reactor-tests project. It will subscribe to the flux/mono and wait for events when you call .verify(). See the reference guide chapter on testing (and the rest of the reference guide in general).
Issue is that in created anonymous class onSubscribe method does nothing.
If you look at implementation of LambdaSubscriber, it requests some number of events.
Also it's easier to extend BaseSubscriber as it has some predefined logic.
So your subscriber implementation would be:
MyServiceService service = new MyServiceService();
service.save("id")
.map(myUserMono -> new MyUser(myUserMono.getName().toUpperCase(), myUserMono.getId().toUpperCase()))
.subscribe(new BaseSubscriber<MyUser>() {
#Override
protected void hookOnSubscribe(Subscription subscription) {
System.out.println("Subscribed!" + Thread.currentThread().getName());
request(1); // or requestUnbounded();
}
#Override
protected void hookOnNext(MyUser myUser) {
System.out.println("OnNext on thread " + Thread.currentThread().getName());
// request(1); // if wasn't called requestUnbounded() 2
}
#Override
protected void hookOnComplete() {
System.out.println("onCompleted!" + Thread.currentThread().getName());
}
#Override
protected void hookOnError(Throwable throwable) {
System.out.println("onError!" + Thread.currentThread().getName());
}
});
Maybe it's not the best implementation, I'm new to reactor too.
Simon's answer has pretty good explanation about testing asynchronous code.

Resources